KVM: arm64: Add support for hypercall services selection
Subsystems (7)
v2 cover KVM: arm64: Add support for hypercall services selection
Hello,
Continuing the discussion from [1], the series tries to add support for the user-space to elect the hypercall services that it wishes to expose to the guest, rather than the guest discovering them unconditionally. The idea employed by the series was taken from
[1] as suggested by Marc Z.
In a broad sense, the idea is similar to the current implementation of PSCI interface- create a 'firmware psuedo-register' to handle the firmware revisions. The series extends this idea to all the other hypercalls such as TRNG (True Random Number Generator), PV_TIME (Paravirtualized Time), and PTP (Precision Time protocol).
For better categorization and future scaling, these firmware registers are categorized based on the service call owners, but unlike the existing firmware psuedo-registers, they hold the features supported in the form of a bitmap. During VM (vCPU) initialization, the registers shows an upper-limit of the features supported by the corresponding registers. The VMM can simply use GET_ONE_REG to discover the features. If it's unhappy with any of the features, it can simply write-back the desired feature bitmap using SET_ONE_REG.
KVM allows these modification only until a VM has started. KVM also assumes that the VMM is unaware of a register if a register remains unaccessed (read/write), and would simply clear all the bits of the registers such that the guest accidently doesn't get exposed to the features. Finally, the set of bitmaps from all the registers are the services that are exposed to the guest.
In order to provide backward compatibility with already existing VMMs, a new capability, KVM_CAP_ARM_HVC_FW_REG_BMAP, is introduced. To enable the bitmap firmware registers extension, the capability must be explicitly enabled. If not, the behavior is similar to the previous setup.
The patches are based off of mainline kernel 5.15, with the selftest patches from [2] applied.
Patch-1 factors out the non-PSCI related interface from psci.c to hypercalls.c, as the series would extend the list in the upcoming patches.
Patches-2,3 introduces core KVM functions, kvm_vcpu_has_run_once() and kvm_vm_has_run_once() to be used in upcoming patches.
Patch-4 sets up the framework for the bitmap firmware psuedo-registers. This includes introducing the capability, KVM_CAP_ARM_HVC_FW_REG_BMAP, read/write helpers for the registers, helper to sanitize the regsiters before VM start, and another helper to check if a particular hypercall service is supported for the guest.
It also adds the register KVM_REG_ARM_STD_HYP_BMAP to support ARM's standard secure services.
Patch-5 introduces the firmware register, KVM_REG_ARM_STD_HYP_BMAP, which holds the standard hypervisor services (such as PV_TIME).
Patch-6 introduces the firmware register, KVM_REG_ARM_VENDOR_HYP_BMAP, which holds the vendor specific hypercall services.
Patch-7,8 Add the necessary documentation for the newly added capability and firmware registers.
Patch-9 imports the SMCCC definitions from linux/arm-smccc.h into tools/ for further use in selftests.
Patch-10 adds the selftest to test the guest (using 'hvc') and VMM interfaces (SET/GET_ONE_REG).
Patch-11 adds these firmware registers into the get-reg-list selftest.
[1]: https://lore.kernel.org/kvmarm/[email protected]/T/
[2]: https://lore.kernel.org/kvmarm/[email protected]/
Regards,
Raghavendra
v1 -> v2
Addressed comments by Oliver (thanks!):
- Introduced kvm_vcpu_has_run_once() and kvm_vm_has_run_once() in the core kvm code, rather than relying on ARM specific vcpu->arch.has_run_once.
- Writing to KVM_REG_ARM_PSCI_VERSION is done in hypercalls.c itself, rather than separating out to psci.c.
- Introduced KVM_CAP_ARM_HVC_FW_REG_BMAP to enable the extension.
- Tracks the register accesses from VMM to decide whether to sanitize a register or not, as opposed to sanitizing upon the first 'write' in v1.
- kvm_hvc_call_supported() is implemented using a direct switch-case statement, instead of looping over all the registers to pick the register for the function-id.
- Replaced the register bit definitions with #defines, instead of enums.
- Removed the patch v1-06/08 that imports the firmware register definitions as it's not needed.
- Separated out the documentations in its own patch, and the renaming of hypercalls.rst to psci.rst into another patch.
- Add the new firmware registers to get-reg-list KVM selftest.
v1: https://lore.kernel.org/kvmarm/[email protected]/
Raghavendra Rao Ananta (11):
KVM: arm64: Factor out firmware register handling from psci.c
KVM: Introduce kvm_vcpu_has_run_once
KVM: Introduce kvm_vm_has_run_once
KVM: arm64: Setup a framework for hypercall bitmap firmware registers
KVM: arm64: Add standard hypervisor firmware register
KVM: arm64: Add vendor hypervisor firmware register
Docs: KVM: Add doc for the bitmap firmware registers
Docs: KVM: Rename psci.rst to hypercalls.rst
tools: Import ARM SMCCC definitions
selftests: KVM: aarch64: Introduce hypercall ABI test
selftests: KVM: aarch64: Add the bitmap firmware registers to
get-reg-list
Documentation/virt/kvm/api.rst | 23 + Documentation/virt/kvm/arm/hypercalls.rst | 132 ++++++ Documentation/virt/kvm/arm/psci.rst | 77 --- arch/arm64/include/asm/kvm_host.h | 21 +- arch/arm64/include/uapi/asm/kvm.h | 12 + arch/arm64/kvm/arm.c | 31 +- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hypercalls.c | 437 +++++++++++++++++- arch/arm64/kvm/psci.c | 166 ------- arch/arm64/kvm/pvtime.c | 3 + arch/arm64/kvm/trng.c | 9 +- arch/arm64/kvm/vgic/vgic-init.c | 2 +- arch/riscv/include/asm/kvm_host.h | 3 - arch/riscv/kvm/vcpu.c | 7 +- include/kvm/arm_hypercalls.h | 20 + include/kvm/arm_psci.h | 7 - include/linux/kvm_host.h | 9 + include/uapi/linux/kvm.h | 1 + tools/include/linux/arm-smccc.h | 188 ++++++++ tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../selftests/kvm/aarch64/get-reg-list.c | 35 ++ .../selftests/kvm/aarch64/hypercalls.c | 367 +++++++++++++++ virt/kvm/kvm_main.c | 18 + 24 files changed, 1291 insertions(+), 281 deletions(-) create mode 100644 Documentation/virt/kvm/arm/hypercalls.rst delete mode 100644 Documentation/virt/kvm/arm/psci.rst create mode 100644 tools/include/linux/arm-smccc.h create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
Signature · 1 line
2.34.0.rc1.387.gb447b232ab-goog
RFC PATCH v2 1/11 KVM: arm64: Factor out firmware register handling from psci.c
Common hypercall firmware register handing is currently employed by psci.c. Since the upcoming patches add more of these registers, it's better to move the generic handling to hypercall.c for a cleaner presentation.
While we are at it, collect all the firmware registers under fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and kvm_arm_copy_fw_reg_indices() in a generic way.
No functional change intended.
--- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hypercalls.c | 170 +++++++++++++++++++++++++++++++++++ arch/arm64/kvm/psci.c | 166 ---------------------------------- include/kvm/arm_hypercalls.h | 7 ++ include/kvm/arm_psci.h | 7 -- 5 files changed, 178 insertions(+), 174 deletions(-)
arch/arm64/kvm/guest.c +1 -1
@@ -18,7 +18,7 @@#include <linux/string.h> #include <linux/vmalloc.h> #include <linux/fs.h> -#include <kvm/arm_psci.h> +#include <kvm/arm_hypercalls.h> #include <asm/cputype.h> #include <linux/uaccess.h> #include <asm/fpsimd.h>
arch/arm64/kvm/hypercalls.c +170 -0
173 lines not on this page · raw message
arch/arm64/kvm/psci.c +0 -166
169 lines not on this page · raw message
include/kvm/arm_hypercalls.h +7 -0
@@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu, vcpu_set_reg(vcpu, 3, a3); } +struct kvm_one_reg ; + +int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu); +int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices); +int kvm_arm_get_fw_reg (struct kvm_vcpu *vcpu,const struct kvm_one_reg *reg); +int kvm_arm_set_fw_reg (struct kvm_vcpu *vcpu,const struct kvm_one_reg *reg); +#endif
include/kvm/arm_psci.h +0 -7
@@ -42,11 +42,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu, struct kvm *kvm)int kvm_psci_call (struct kvm_vcpu *vcpu); -struct kvm_one_reg ; - -int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu); -int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices); -int kvm_arm_get_fw_reg (struct kvm_vcpu *vcpu,const struct kvm_one_reg *reg); -int kvm_arm_set_fw_reg (struct kvm_vcpu *vcpu,const struct kvm_one_reg *reg); -#endif /* __KVM_ARM_PSCI_H__ */
Signature · 6 lines
2.34.0.rc1.387.gb447b232ab-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [RFC PATCH v2 01/11] KVM: arm64: Factor out firmware register handling from psci.c
Quoted 49 lines · Raghavendra Rao Ananta
Common hypercall firmware register handing is currently employed by
psci.c. Since the upcoming patches add more of these registers, it's better to move the generic handling tohypercall.cfor a cleaner presentation.While we are at it, collect all the firmware registers under fw_reg_ids[] to help implement
kvm_arm_get_fw_num_regs()andkvm_arm_copy_fw_reg_indices()in a generic way.No functional change intended.
Signed-off-by Raghavendra Rao Ananta <[email protected]>--- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hypercalls.c | 170 +++++++++++++++++++++++++++++++++++ arch/arm64/kvm/psci.c | 166 ---------------------------------- include/kvm/arm_hypercalls.h | 7 ++ include/kvm/arm_psci.h | 7 -- 5 files changed, 178 insertions(+), 174 deletions(-)arch/arm64/kvm/guest.c +1
-1@@ -18,7 +18,7 @@#include <linux/string.h> #include <linux/vmalloc.h> #include <linux/fs.h> -#include <kvm/arm_psci.h> +#include <kvm/arm_hypercalls.h> #include <asm/cputype.h> #include <linux/uaccess.h> #include <asm/fpsimd.h> arch/arm64/kvm/hypercalls.c +8
-0@@ -146,3 +146,173 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)smccc_set_retval (vcpu, val[0 ], val[1 ], val[2 ], val[3 ]);return 1 ; } + +static const u64 fw_reg_ids[] = { +KVM_REG_ARM_PSCI_VERSION , +KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 , +KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 , +}; + +int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu)
arch/arm64/kvm/hypercalls.c int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) · line 166
Quoted 10 lines
arch/arm64/kvm/hypercalls.c +10
-0+{ +return ARRAY_SIZE (fw_reg_ids); +} + +int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (fw_reg_ids); i++) { +if (put_user (fw_reg_ids[i], uindices))
This is missing the ++ on uindices, so it just writes the same offset three times.
arch/arm64/kvm/hypercalls.c int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) · line 171
Quoted 5 lines
arch/arm64/kvm/hypercalls.c +5
-0+return -EFAULT ; + } + +return 0 ; +}
I assume the rest of the patch is just a cut+paste move of code.
Thanks,
drew
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [RFC PATCH v2 01/11] KVM: arm64: Factor out firmware register handling from psci.c
Quoted 56 lines · Andrew Jones
Quoted 54 lines · Raghavendra Rao Ananta
Common hypercall firmware register handing is currently employed by
psci.c. Since the upcoming patches add more of these registers, it's better to move the generic handling tohypercall.cfor a cleaner presentation.While we are at it, collect all the firmware registers under fw_reg_ids[] to help implement
kvm_arm_get_fw_num_regs()andkvm_arm_copy_fw_reg_indices()in a generic way.No functional change intended.
Signed-off-by Raghavendra Rao Ananta <[email protected]>--- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hypercalls.c | 170 +++++++++++++++++++++++++++++++++++ arch/arm64/kvm/psci.c | 166 ---------------------------------- include/kvm/arm_hypercalls.h | 7 ++ include/kvm/arm_psci.h | 7 -- 5 files changed, 178 insertions(+), 174 deletions(-)arch/arm64/kvm/guest.c +1
-1@@ -18,7 +18,7 @@#include <linux/string.h> #include <linux/vmalloc.h> #include <linux/fs.h> -#include <kvm/arm_psci.h> +#include <kvm/arm_hypercalls.h> #include <asm/cputype.h> #include <linux/uaccess.h> #include <asm/fpsimd.h> arch/arm64/kvm/hypercalls.c +13
-0@@ -146,3 +146,173 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)smccc_set_retval (vcpu, val[0 ], val[1 ], val[2 ], val[3 ]);return 1 ; } + +static const u64 fw_reg_ids[] = { +KVM_REG_ARM_PSCI_VERSION , +KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 , +KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 , +}; + +int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu) +{ +return ARRAY_SIZE (fw_reg_ids); +} + +int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices)
Quoted 9 lines
arch/arm64/kvm/hypercalls.c int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) · line 166
Quoted 5 lines
arch/arm64/kvm/hypercalls.c +5
-0+{ +int i; + +for (i =0 ; i <ARRAY_SIZE (fw_reg_ids); i++) { +if (put_user (fw_reg_ids[i], uindices))This is missing the ++ on uindices, so it just writes the same offset three times.
Thanks for catching this! I believe I realized this later and corrected it in patch-04/11 of the series and missed it here. I'll fix it here as well.
Quoted 8 lines
arch/arm64/kvm/hypercalls.c int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) · line 171
Quoted 5 lines
arch/arm64/kvm/hypercalls.c +5
-0+return -EFAULT ; + } + +return 0 ; +}I assume the rest of the patch is just a cut+paste move of code.
That's right.
Regards,
Raghavendra
Quoted 3 lines
Thanks,
drew
RFC PATCH v2 2/11 KVM: Introduce kvm_vcpu_has_run_once
In reply to Raghavendra Rao AnantaArchitectures such as arm64 and riscv uses vcpu variables such as has_run_once and ran_atleast_once, respectively, to mark if the vCPU has started running. Since these are architecture agnostic variables, introduce kvm_vcpu_has_run_once() as a core kvm functionality and use this instead of the architecture defined variables.
No functional change intended.
--- arch/arm64/include/asm/kvm_host.h | 3 --- arch/arm64/kvm/arm.c | 8 ++++---- arch/arm64/kvm/vgic/vgic-init.c | 2 +- arch/riscv/include/asm/kvm_host.h | 3 --- arch/riscv/kvm/vcpu.c | 7 ++----- include/linux/kvm_host.h | 7 +++++++ virt/kvm/kvm_main.c | 1 + 7 files changed, 15 insertions(+), 16 deletions(-)
arch/arm64/include/asm/kvm_host.h +0 -3
@@ -367,9 +367,6 @@ struct kvm_vcpu_arch {int target;DECLARE_BITMAP (features,KVM_VCPU_MAX_FEATURES ); -/* Detect first run of a vcpu */ -bool has_run_once; -/* Virtual SError ESR to restore when HCR_EL2.VSE is set */ u64 vsesr_el2;
arch/arm64/kvm/arm.c +4 -4
@@ -344,7 +344,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu) { -if (vcpu->arch.has_run_once &&unlikely (!irqchip_in_kernel (vcpu->kvm))) +if (kvm_vcpu_has_run_once (vcpu) &&unlikely (!irqchip_in_kernel (vcpu->kvm)))static_branch_dec (&userspace_irqchip_in_use);kvm_mmu_free_memory_cache (&vcpu->arch.mmu_page_cache);
@@ -582,13 +582,13 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)struct kvm *kvm = vcpu->kvm;int ret =0 ; -if (likely (vcpu->arch.has_run_once)) +if (likely (kvm_vcpu_has_run_once (vcpu)))return 0 ;if (!kvm_arm_vcpu_is_finalized (vcpu))return -EPERM ; - vcpu->arch.has_run_once = true; + vcpu->has_run_once = true;kvm_arm_vcpu_init_debug (vcpu);
@@ -1116,7 +1116,7 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,* need to invalidate the I-cache though, as FWB does *not* * imply CTR_EL0.DIC. */ -if (vcpu->arch.has_run_once) { +if (kvm_vcpu_has_run_once (vcpu)) {if (!cpus_have_final_cap (ARM64_HAS_STAGE2_FWB ))stage2_unmap_vm (vcpu->kvm);else
arch/arm64/kvm/vgic/vgic-init.c +1 -1
@@ -91,7 +91,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)return ret;kvm_for_each_vcpu (i, vcpu, kvm) { -if (vcpu->arch.has_run_once) +if (kvm_vcpu_has_run_once (vcpu)) goto out_unlock; } ret =0 ;
arch/riscv/include/asm/kvm_host.h +0 -3
@@ -147,9 +147,6 @@ struct kvm_vcpu_csr { };struct kvm_vcpu_arch { -/* VCPU ran at least once */ -bool ran_atleast_once; -/* ISA feature bits (similar to MISA) */ unsigned long isa;
arch/riscv/kvm/vcpu.c +2 -5
@@ -75,9 +75,6 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) {struct kvm_cpu_context *cntx; -/* Mark this VCPU never ran */ - vcpu->arch.ran_atleast_once = false; -/* Setup ISA features available to VCPU */ vcpu->arch.isa =riscv_isa_extension_base (NULL ) &KVM_RISCV_ISA_ALLOWED ;
@@ -190,7 +187,7 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu,switch (reg_num) {case KVM_REG_RISCV_CONFIG_REG (isa): -if (!vcpu->arch.ran_atleast_once) { +if (!kvm_vcpu_has_run_once (vcpu)) { vcpu->arch.isa = reg_val; vcpu->arch.isa &=riscv_isa_extension_base (NULL ); vcpu->arch.isa &=KVM_RISCV_ISA_ALLOWED ;
@@ -682,7 +679,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)struct kvm_run *run = vcpu->run;/* Mark this VCPU ran at least once */ - vcpu->arch.ran_atleast_once = true; + vcpu->has_run_once = true; vcpu->arch.srcu_idx =srcu_read_lock (&vcpu->kvm->srcu);
include/linux/kvm_host.h +7 -0
@@ -360,6 +360,8 @@ struct kvm_vcpu {* it is a valid slot. */ int last_used_slot; + +bool has_run_once; };/* must be called with irqs disabled */
@@ -1847,4 +1849,9 @@ static inline void kvm_handle_signal_exit(struct kvm_vcpu *vcpu)/* Max number of entries allowed for each kvm dirty ring */ #define KVM_DIRTY_RING_MAX_ENTRIES 65536 +static inline bool kvm_vcpu_has_run_once (struct kvm_vcpu *vcpu) +{ +return vcpu->has_run_once; +} +#endif
virt/kvm/kvm_main.c +1 -0
@@ -433,6 +433,7 @@ static void kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) vcpu->ready = false;preempt_notifier_init (&vcpu->preempt_notifier, &kvm_preempt_ops); vcpu->last_used_slot =0 ; + vcpu->has_run_once = false; }void kvm_vcpu_destroy (struct kvm_vcpu *vcpu)
Signature · 1 line
2.34.0.rc1.387.gb447b232ab-goog
Marc Zyngier
maintainerRe: [RFC PATCH v2 02/11] KVM: Introduce kvm_vcpu_has_run_once
Quoted 11 lines · Raghavendra Rao Ananta
Architectures such as arm64 and riscv uses vcpu variables such as
has_run_onceandran_atleast_once, respectively, to mark if the vCPU has started running. Since these are architecture agnostic variables, introducekvm_vcpu_has_run_once()as a core kvm functionality and use this instead of the architecture defined variables.No functional change intended.
Signed-off-by Raghavendra Rao Ananta <[email protected]>
arm64 is moving away from this, see [1]. You also don't need any new state, as vcpu->pid gives you exactly what you need.
Happy to queue additional patches on top if you want to deal with riscv.
Thanks,
M.
[1] https://lore.kernel.org/all/[email protected]/
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [RFC PATCH v2 02/11] KVM: Introduce kvm_vcpu_has_run_once
Hello Marc,
Quoted 11 lines · Marc Zyngier
Quoted 8 lines · Raghavendra Rao Ananta
Architectures such as arm64 and riscv uses vcpu variables such as
has_run_onceandran_atleast_once, respectively, to mark if the vCPU has started running. Since these are architecture agnostic variables, introducekvm_vcpu_has_run_once()as a core kvm functionality and use this instead of the architecture defined variables.
Quoted 6 lines
Quoted 3 lines
No functional change intended.
Signed-off-by Raghavendra Rao Ananta <[email protected]>arm64 is moving away from this, see [1]. You also don't need any new state, as
vcpu->pidgives you exactly what you need.
Thanks for the pointer. I can directly use this!
Quoted 4 lines
Happy to queue additional patches on top if you want to deal with riscv.
Just to clarify, you mean get the kvm support for riscv on kvmarm-next? If yes, then sure, I can make changes in riscv to use vcpu_has_run_once() from your series.
Regards,
Raghavendra
Quoted 8 lines
Thanks,
M.[1] https://lore.kernel.org/all/[email protected]/
--
Without deviation from the norm, progress is not possible.
RFC PATCH v2 3/11 KVM: Introduce kvm_vm_has_run_once
In reply to Raghavendra Rao AnantaThe upcoming patches need a way to detect if the VM, as a whole, has started. Hence, unionize kvm_vcpu_has_run_once() of all the vcpus of the VM and build kvm_vm_has_run_once() to achieve the functionality.
No functional change intended.
--- include/linux/kvm_host.h | 2 ++ virt/kvm/kvm_main.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+)
include/linux/kvm_host.h +2 -0
@@ -1854,4 +1854,6 @@ static inline bool kvm_vcpu_has_run_once(struct kvm_vcpu *vcpu) return vcpu->has_run_once; } +bool kvm_vm_has_run_once (struct kvm *kvm); +#endif
virt/kvm/kvm_main.c +17 -0
@@ -4339,6 +4339,23 @@ static int kvm_vm_ioctl_get_stats_fd(struct kvm *kvm)return fd; } +bool kvm_vm_has_run_once (struct kvm *kvm) +{ +int i, ret = false; +struct kvm_vcpu *vcpu; + +mutex_lock (&kvm->lock); + +kvm_for_each_vcpu (i, vcpu, kvm) { + ret =kvm_vcpu_has_run_once (vcpu); +if (ret) +break ; + } + +mutex_unlock (&kvm->lock); +return ret; +} +static long kvm_vm_ioctl (struct file *filp,unsigned int ioctl,unsigned long arg) {
Signature · 1 line
2.34.0.rc1.387.gb447b232ab-goog
Marc Zyngier
maintainerRe: [RFC PATCH v2 03/11] KVM: Introduce kvm_vm_has_run_once
Quoted 39 lines · Raghavendra Rao Ananta
The upcoming patches need a way to detect if the VM, as a whole, has started. Hence, unionize
kvm_vcpu_has_run_once()of all the vcpus of the VM and buildkvm_vm_has_run_once()to achieve the functionality.No functional change intended.
Signed-off-by Raghavendra Rao Ananta <[email protected]>--- include/linux/kvm_host.h | 2 ++ virt/kvm/kvm_main.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+)include/linux/kvm_host.h +2
-0@@ -1854,4 +1854,6 @@ static inline bool kvm_vcpu_has_run_once(struct kvm_vcpu *vcpu) return vcpu->has_run_once; } +bool kvm_vm_has_run_once (struct kvm *kvm); +#endif virt/kvm/kvm_main.c +6
-0@@ -4339,6 +4339,23 @@ static int kvm_vm_ioctl_get_stats_fd(struct kvm *kvm)return fd; } +bool kvm_vm_has_run_once (struct kvm *kvm) +{ +int i, ret = false; +struct kvm_vcpu *vcpu; + +mutex_lock (&kvm->lock);
virt/kvm/kvm_main.c static int kvm_vm_ioctl_get_stats_fd(struct kvm *kvm) · line 4357
Quoted 10 lines
virt/kvm/kvm_main.c +10
-0+ +kvm_for_each_vcpu (i, vcpu, kvm) { + ret =kvm_vcpu_has_run_once (vcpu); +if (ret) +break ; + } + +mutex_unlock (&kvm->lock); +return ret; +}
This is horribly racy. Nothing prevents a vcpu from running behind your back. If you want any sort of guarantee, look at what we do in kvm_vgic_create(). Alexandru has patches that extract it to make it generally available (at least for arm64).
M.
Signature · 5 lines
Without deviation from the norm, progress is not possible. _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [RFC PATCH v2 03/11] KVM: Introduce kvm_vm_has_run_once
Quoted 49 lines · Marc Zyngier
Quoted 46 lines · Raghavendra Rao Ananta
The upcoming patches need a way to detect if the VM, as a whole, has started. Hence, unionize
kvm_vcpu_has_run_once()of all the vcpus of the VM and buildkvm_vm_has_run_once()to achieve the functionality.No functional change intended.
Signed-off-by Raghavendra Rao Ananta <[email protected]>--- include/linux/kvm_host.h | 2 ++ virt/kvm/kvm_main.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+)include/linux/kvm_host.h +2
-0@@ -1854,4 +1854,6 @@ static inline bool kvm_vcpu_has_run_once(struct kvm_vcpu *vcpu) return vcpu->has_run_once; } +bool kvm_vm_has_run_once (struct kvm *kvm); +#endif virt/kvm/kvm_main.c +13
-0@@ -4339,6 +4339,23 @@ static int kvm_vm_ioctl_get_stats_fd(struct kvm *kvm)return fd; } +bool kvm_vm_has_run_once (struct kvm *kvm) +{ +int i, ret = false; +struct kvm_vcpu *vcpu; + +mutex_lock (&kvm->lock); + +kvm_for_each_vcpu (i, vcpu, kvm) { + ret =kvm_vcpu_has_run_once (vcpu); +if (ret) +break ; + } +
Quoted 9 lines
virt/kvm/kvm_main.c static int kvm_vm_ioctl_get_stats_fd(struct kvm *kvm) · line 4357
Quoted 3 lines
virt/kvm/kvm_main.c +3
-0+mutex_unlock (&kvm->lock); +return ret; +}This is horribly racy. Nothing prevents a vcpu from running behind your back. If you want any sort of guarantee, look at what we do in
kvm_vgic_create(). Alexandru has patches that extract it to make it generally available (at least for arm64).
Yes, I looked into kvm_lock_all_vcpus(), but the fact that the series would call the function with the current vcpu lock held caused me to back off..
Perhaps I can come up with a similar function, kvm_lock_all_vcpus_except(vcpu) ?
Regards,
Raghavendra
Quoted 4 lines
M.--
Without deviation from the norm, progress is not possible.
RFC PATCH v2 4/11 KVM: arm64: Setup a framework for hypercall bitmap firmware registers
In reply to Raghavendra Rao AnantaKVM regularly introduces new hypercall services to the guests without any consent from the Virtual Machine Manager (VMM). This means, the guests can observe hypercall services in and out as they migrate across various host kernel versions. This could be a major problem if the guest discovered a hypercall, started using it, and after getting migrated to an older kernel realizes that it's no longer available. Depending on how the guest handles the change, there's a potential chance that the guest would just panic.
As a result, there's a need for the VMM to elect the services that it wishes the guest to discover. VMM can elect these services based on the kernels spread across its (migration) fleet. To remedy this, extend the existing firmware psuedo-registers, such as KVM_REG_ARM_PSCI_VERSION, for all the hypercall services available.
These firmware registers are categorized based on the service call owners, and unlike the existing firmware psuedo-registers, they hold the features supported in the form of a bitmap. During VM (vCPU) initialization, the registers shows an upper-limit of the features supported by the corresponding registers. The VMM can simply use GET_ONE_REG to discover the features. If it's unhappy with any of the features, it can simply write-back the desired feature bitmap using SET_ONE_REG.
KVM allows these modification only until a VM has started. KVM also assumes that the VMM is unaware of a register if a register remains unaccessed (read/write), and would simply clear all the bits of the registers such that the guest accidently doesn't get exposed to the features. Finally, the set of bitmaps from all the registers are the services that are exposed to the guest.
In order to provide backward compatibility with already existing VMMs, a new capability, KVM_CAP_ARM_HVC_FW_REG_BMAP, is introduced. To enable the bitmap firmware registers extension, the capability must be explicitly enabled. If not, the behavior is similar to the previous setup.
In this patch, the framework adds the register only for ARM's standard secure services (owner value 4). Currently, this includes support only for ARM True Random Number Generator (TRNG) service, with bit-0 of the register representing mandatory features of v1.0. Other services are momentarily added in the upcoming patches.
--- arch/arm64/include/asm/kvm_host.h | 16 +++ arch/arm64/include/uapi/asm/kvm.h | 4 + arch/arm64/kvm/arm.c | 23 +++- arch/arm64/kvm/hypercalls.c | 217 +++++++++++++++++++++++++++++- arch/arm64/kvm/trng.c | 9 +- include/kvm/arm_hypercalls.h | 7 + include/uapi/linux/kvm.h | 1 + 7 files changed, 262 insertions(+), 15 deletions(-)
arch/arm64/include/asm/kvm_host.h +16 -0
@@ -102,6 +102,19 @@ struct kvm_s2_mmu {struct kvm_arch_memory_slot { }; +struct hvc_fw_reg_bmap { +bool accessed; +u64 reg_id; +u64 bmap; +}; + +struct hvc_reg_desc { +spinlock_t lock; +bool fw_reg_bmap_enabled; + +struct hvc_fw_reg_bmap hvc_std_bmap; +}; +struct kvm_arch {struct kvm_s2_mmu mmu;
@@ -137,6 +150,9 @@ struct kvm_arch {/* Memory Tagging Extension enabled for the guest */ bool mte_enabled; + +/* Hypercall firmware registers' descriptor */ +struct hvc_reg_desc hvc_desc; };struct kvm_vcpu_fault_info {
arch/arm64/include/uapi/asm/kvm.h +4 -0
@@ -281,6 +281,10 @@ struct kvm_arm_copy_mte_tags {#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) +#define KVM_REG_ARM_STD_BMAP KVM_REG_ARM_FW_REG(3) +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0 BIT(0) +#define KVM_REG_ARM_STD_BMAP_BIT_MAX 0/* Last valid bit */ +/* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)
arch/arm64/kvm/arm.c +17 -6
@@ -81,26 +81,32 @@ int kvm_arch_check_processor_compat(void *opaque)int kvm_vm_ioctl_enable_cap (struct kvm *kvm,struct kvm_enable_cap *cap) { -int r; +int r =0 ; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc;if (cap->flags)return -EINVAL ;switch (cap->cap) {case KVM_CAP_ARM_NISV_TO_USER : - r =0 ; kvm->arch.return_nisv_io_abort_to_user = true;break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { +if (!system_supports_mte () || kvm->created_vcpus) r = -EINVAL ; - }else { - r =0 ; +else kvm->arch.mte_enabled = true; - }mutex_unlock (&kvm->lock);break ; +case KVM_CAP_ARM_HVC_FW_REG_BMAP : +if (kvm_vm_has_run_once (kvm)) +return -EBUSY ; + +spin_lock (&hvc_desc->lock); + hvc_desc->fw_reg_bmap_enabled = true; +spin_unlock (&hvc_desc->lock); +break ;default : r = -EINVAL ;break ;
@@ -157,6 +163,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)set_default_spectre (kvm); +kvm_arm_init_hypercalls (kvm); +return ret; out_free_stage2_pgd:kvm_free_stage2_pgd (&kvm->arch.mmu);
@@ -215,6 +223,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)case KVM_CAP_SET_GUEST_DEBUG :case KVM_CAP_VCPU_ATTRIBUTES :case KVM_CAP_PTP_KVM : +case KVM_CAP_ARM_HVC_FW_REG_BMAP : r =1 ;break ;case KVM_CAP_SET_GUEST_DEBUG2 :
@@ -622,6 +631,8 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)if (kvm_vm_is_protected (kvm))kvm_call_hyp_nvhe (__pkvm_vcpu_init_traps, vcpu); +kvm_arm_sanitize_fw_regs (kvm); +return ret; }
arch/arm64/kvm/hypercalls.c +215 -2
276 lines not on this page · raw message
arch/arm64/kvm/trng.c +2 -7
@@ -60,14 +60,9 @@ int kvm_trng_call(struct kvm_vcpu *vcpu) val =ARM_SMCCC_TRNG_VERSION_1_0 ;break ;case ARM_SMCCC_TRNG_FEATURES : -switch (smccc_get_arg1 (vcpu)) { -case ARM_SMCCC_TRNG_VERSION : -case ARM_SMCCC_TRNG_FEATURES : -case ARM_SMCCC_TRNG_GET_UUID : -case ARM_SMCCC_TRNG_RND32 : -case ARM_SMCCC_TRNG_RND64 : +if (kvm_hvc_call_supported (vcpu,smccc_get_arg1 (vcpu))) val =TRNG_SUCCESS ; - } +break ;case ARM_SMCCC_TRNG_GET_UUID : smccc_set_retval(vcpu, le32_to_cpu(u[0]), le32_to_cpu(u[1]),
include/kvm/arm_hypercalls.h +7 -0
@@ -6,6 +6,9 @@#include <asm/kvm_emulate.h> +#define ARM_SMCCC_STD_FEATURES \ + GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0) +int kvm_hvc_call_handler (struct kvm_vcpu *vcpu);static inline u32 smccc_get_function (struct kvm_vcpu *vcpu)
@@ -42,9 +45,13 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,struct kvm_one_reg ; +void kvm_arm_init_hypercalls (struct kvm *kvm);int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu);int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices);int kvm_arm_get_fw_reg (struct kvm_vcpu *vcpu,const struct kvm_one_reg *reg);int kvm_arm_set_fw_reg (struct kvm_vcpu *vcpu,const struct kvm_one_reg *reg); +void kvm_arm_sanitize_fw_regs (struct kvm *kvm); + +bool kvm_hvc_call_supported (struct kvm_vcpu *vcpu,u32 func_id);#endif
include/uapi/linux/kvm.h +1 -0
@@ -1130,6 +1130,7 @@ struct kvm_ppc_resize_hpt {#define KVM_CAP_BINARY_STATS_FD 203#define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204#define KVM_CAP_ARM_MTE 205 +#define KVM_CAP_ARM_HVC_FW_REG_BMAP 206#ifdef KVM_CAP_IRQ_ROUTING
Signature · 1 line
2.34.0.rc1.387.gb447b232ab-goog
Marc Zyngier
maintainerRe: [RFC PATCH v2 04/11] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
Quoted 65 lines · Raghavendra Rao Ananta
KVM regularly introduces new hypercall services to the guests without any consent from the Virtual Machine Manager (VMM). This means, the guests can observe hypercall services in and out as they migrate across various host kernel versions. This could be a major problem if the guest discovered a hypercall, started using it, and after getting migrated to an older kernel realizes that it's no longer available. Depending on how the guest handles the change, there's a potential chance that the guest would just panic.
As a result, there's a need for the VMM to elect the services that it wishes the guest to discover. VMM can elect these services based on the kernels spread across its (migration) fleet. To remedy this, extend the existing firmware psuedo-registers, such as
KVM_REG_ARM_PSCI_VERSION, for all the hypercall services available.These firmware registers are categorized based on the service call owners, and unlike the existing firmware psuedo-registers, they hold the features supported in the form of a bitmap. During VM (vCPU) initialization, the registers shows an upper-limit of the features supported by the corresponding registers. The VMM can simply use
GET_ONE_REGto discover the features. If it's unhappy with any of the features, it can simply write-back the desired feature bitmap usingSET_ONE_REG.KVM allows these modification only until a VM has started. KVM also assumes that the VMM is unaware of a register if a register remains unaccessed (read/write), and would simply clear all the bits of the registers such that the guest accidently doesn't get exposed to the features. Finally, the set of bitmaps from all the registers are the services that are exposed to the guest.
In order to provide backward compatibility with already existing VMMs, a new capability,
KVM_CAP_ARM_HVC_FW_REG_BMAP, is introduced. To enable the bitmap firmware registers extension, the capability must be explicitly enabled. If not, the behavior is similar to the previous setup.In this patch, the framework adds the register only for ARM's standard secure services (owner value 4). Currently, this includes support only for ARM True Random Number Generator (TRNG) service, with bit-0 of the register representing mandatory features of v1.0. Other services are momentarily added in the upcoming patches.
Signed-off-by Raghavendra Rao Ananta <[email protected]>--- arch/arm64/include/asm/kvm_host.h | 16 +++ arch/arm64/include/uapi/asm/kvm.h | 4 + arch/arm64/kvm/arm.c | 23 +++- arch/arm64/kvm/hypercalls.c | 217 +++++++++++++++++++++++++++++- arch/arm64/kvm/trng.c | 9 +- include/kvm/arm_hypercalls.h | 7 + include/uapi/linux/kvm.h | 1 + 7 files changed, 262 insertions(+), 15 deletions(-)arch/arm64/include/asm/kvm_host.h +2
-0@@ -102,6 +102,19 @@ struct kvm_s2_mmu {struct kvm_arch_memory_slot { }; +struct hvc_fw_reg_bmap { +bool accessed;
arch/arm64/include/asm/kvm_host.h struct kvm_s2_mmu { · line 116
Quoted 10 lines
arch/arm64/include/asm/kvm_host.h +10
-0+u64 reg_id; +u64 bmap; +}; + +struct hvc_reg_desc { +spinlock_t lock; +bool fw_reg_bmap_enabled; + +struct hvc_fw_reg_bmap hvc_std_bmap; +};
Please document what these data structures track. Without any documentation, it is pretty difficult to build a mental picture of how this all fits together.
Quoted 56 lines
arch/arm64/include/asm/kvm_host.h +4
-0+struct kvm_arch {struct kvm_s2_mmu mmu;@@ -137,6 +150,9 @@ struct kvm_arch {/* Memory Tagging Extension enabled for the guest */ bool mte_enabled; + +/* Hypercall firmware registers' descriptor */ +struct hvc_reg_desc hvc_desc; };struct kvm_vcpu_fault_info {arch/arm64/include/uapi/asm/kvm.h +4
-0@@ -281,6 +281,10 @@ struct kvm_arm_copy_mte_tags {#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) +#define KVM_REG_ARM_STD_BMAP KVM_REG_ARM_FW_REG(3) +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0 BIT(0) +#define KVM_REG_ARM_STD_BMAP_BIT_MAX 0/* Last valid bit */ +/* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)arch/arm64/kvm/arm.c +3
-5@@ -81,26 +81,32 @@ int kvm_arch_check_processor_compat(void *opaque)int kvm_vm_ioctl_enable_cap (struct kvm *kvm,struct kvm_enable_cap *cap) { -int r; +int r =0 ; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc;if (cap->flags)return -EINVAL ;switch (cap->cap) {case KVM_CAP_ARM_NISV_TO_USER : - r =0 ; kvm->arch.return_nisv_io_abort_to_user = true;break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { +if (!system_supports_mte () || kvm->created_vcpus) r = -EINVAL ; - }else { - r =0 ;
arch/arm64/kvm/arm.c int kvm_arch_check_processor_compat(void *opaque) · line 106
Quoted 10 lines
arch/arm64/kvm/arm.c +6
-1+else kvm->arch.mte_enabled = true; - }mutex_unlock (&kvm->lock);break ; +case KVM_CAP_ARM_HVC_FW_REG_BMAP : +if (kvm_vm_has_run_once (kvm)) +return -EBUSY ; + +spin_lock (&hvc_desc->lock);
Does this really need to be a spin-lock? Are you ever taking it on a context where you cannot sleep? And why does it need to be a new lock when we already have a plethora of them?
Quoted 24 lines
arch/arm64/kvm/arm.c +6
-0+ hvc_desc->fw_reg_bmap_enabled = true; +spin_unlock (&hvc_desc->lock); +break ;default : r = -EINVAL ;break ;@@ -157,6 +163,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)set_default_spectre (kvm); +kvm_arm_init_hypercalls (kvm); +return ret; out_free_stage2_pgd:kvm_free_stage2_pgd (&kvm->arch.mmu);@@ -215,6 +223,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)case KVM_CAP_SET_GUEST_DEBUG :case KVM_CAP_VCPU_ATTRIBUTES :case KVM_CAP_PTP_KVM : +case KVM_CAP_ARM_HVC_FW_REG_BMAP : r =1 ;break ;case KVM_CAP_SET_GUEST_DEBUG2 :
arch/arm64/kvm/arm.c static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) · line 634
Quoted 4 lines
arch/arm64/kvm/arm.c +1
-0@@ -622,6 +631,8 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)if (kvm_vm_is_protected (kvm))kvm_call_hyp_nvhe (__pkvm_vcpu_init_traps, vcpu); +kvm_arm_sanitize_fw_regs (kvm);
What is the rational for doing this on first run? Surely this could be done at the point where userspace performs the write, couldn't it?
My mental model is that the VMM reads some data, clears some bits if it really wants to, and writes it back. There shouldn't be anything to sanitise after the facts. Or at least that's my gut feeling so far.
/me reads on.
Quoted 35 lines
arch/arm64/kvm/arm.c +1
-0+return ret; }arch/arm64/kvm/hypercalls.c +23
-0@@ -58,6 +58,41 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val) val[3 ] =lower_32_bits (cycles); } +static bool +kvm_arm_fw_reg_feat_enabled (struct hvc_fw_reg_bmap *reg_bmap,u64 feat_bit) +{ +return reg_bmap->bmap & feat_bit; +} + +bool kvm_hvc_call_supported (struct kvm_vcpu *vcpu,u32 func_id) +{ +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc; + +/* +* To ensure backward compatibility, support all the service calls, +* including new additions, if the firmware registers holding the +* feature bitmaps isn't explicitly enabled. +*/ +if (!hvc_desc->fw_reg_bmap_enabled) +return true; + +switch (func_id) { +case ARM_SMCCC_TRNG_VERSION : +case ARM_SMCCC_TRNG_FEATURES : +case ARM_SMCCC_TRNG_GET_UUID : +case ARM_SMCCC_TRNG_RND32 :
arch/arm64/kvm/hypercalls.c static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val) · line 93
Quoted 10 lines
arch/arm64/kvm/hypercalls.c +10
-0+case ARM_SMCCC_TRNG_RND64 : +return kvm_arm_fw_reg_feat_enabled (&hvc_desc->hvc_std_bmap, +KVM_REG_ARM_STD_BIT_TRNG_V1_0 ); +default : +/* By default, allow the services that aren't listed here */ +return true; + } + +/* We shouldn't be reaching here */ +return true;
So why have anything at all?
Quoted 87 lines
arch/arm64/kvm/hypercalls.c +66
-0+} +int kvm_hvc_call_handler (struct kvm_vcpu *vcpu) {u32 func_id =smccc_get_function (vcpu);@@ -65,6 +100,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)u32 feature;gpa_t gpa; +if (!kvm_hvc_call_supported (vcpu, func_id)) + goto out; +switch (func_id) {case ARM_SMCCC_VERSION_FUNC_ID : val[0 ] =ARM_SMCCC_VERSION_1_1 ;@@ -143,6 +181,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)return kvm_psci_call (vcpu); } +out:smccc_set_retval (vcpu, val[0 ], val[1 ], val[2 ], val[3 ]);return 1 ; }@@ -153,17 +192,178 @@ static const u64 fw_reg_ids[] = { KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, }; +static const u64 fw_reg_bmap_ids[] = { +KVM_REG_ARM_STD_BMAP , +}; + +static void kvm_arm_fw_reg_init_hvc (struct hvc_reg_desc *hvc_desc, +struct hvc_fw_reg_bmap *fw_reg_bmap, +u64 reg_id,u64 default_map) +{ + fw_reg_bmap->reg_id = reg_id; + fw_reg_bmap->bmap = default_map; +} + +void kvm_arm_init_hypercalls (struct kvm *kvm) +{ +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc; + +spin_lock_init (&hvc_desc->lock); + +kvm_arm_fw_reg_init_hvc (hvc_desc, &hvc_desc->hvc_std_bmap, +KVM_REG_ARM_STD_BMAP ,ARM_SMCCC_STD_FEATURES ); +} + +static void kvm_arm_fw_reg_sanitize (struct hvc_fw_reg_bmap *fw_reg_bmap) +{ +if (!fw_reg_bmap->accessed) + fw_reg_bmap->bmap =0 ; +} + +/* +* kvm_arm_sanitize_fw_regs: Sanitize the hypercall firmware registers +* +* Sanitization, in the case of hypercall firmware registers, is basically +* clearing out the feature bitmaps so that the guests are not exposed to +* the services corresponding to a particular register. The registers that +* needs sanitization is decided on two factors on the user-space part: +* 1. Enablement of KVM_CAP_ARM_HVC_FW_REG_BMAP: +* If the user-space hasn't enabled the capability, it either means +* that it's unaware of its existence, or it simply doesn't want to +* participate in the arrangement and is okay with the default settings. +* The former case is to ensure backward compatibility. +* +* 2. Has the user-space accessed (read/write) the register? : +* If yes, it means that the user-space is aware of the register's +* existence and can set the bits as it sees fit for the guest. A +* read-only access from user-space indicates that the user-space is +* happy with the default settings, and doesn't wish to change it. +* +* The logic for sanitizing a register will then be: +* --------------------------------------------------------------------------- +* | CAP enabled | Accessed reg | Clear reg | Comments | +* --------------------------------------------------------------------------- +* | N | N | N | | +* | N | Y | N | -ENOENT returned during access | +* | Y | N | Y | | +* | Y | Y | N | | +* --------------------------------------------------------------------------- +*/ +void kvm_arm_sanitize_fw_regs (struct kvm *kvm) +{ +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc;
arch/arm64/kvm/hypercalls.c static const u64 fw_reg_ids[] = { · line 264
Quoted 10 lines
arch/arm64/kvm/hypercalls.c +10
-0+ +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) + goto out; + +kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_std_bmap); + +out: +spin_unlock (&hvc_desc->lock);
I keep being baffled by this. Why should we track the VMM accesses or the VMM writeback? This logic doesn't seem to bring anything useful as far as I can tell. All we need to ensure is that what is written to the pseudo-register is an acceptable subset of the previous value, and I cannot see why this can't be done at write-time.
If you want to hide this behind a capability, fine (although my guts feeling is that we don't need that either). But I really want to be convinced about all this tracking.
Quoted 95 lines
arch/arm64/kvm/hypercalls.c +84
-2+} + +static int kvm_arm_fw_reg_get_bmap (struct kvm *kvm, +struct hvc_fw_reg_bmap *fw_reg_bmap,u64 *val) +{ +int ret =0 ; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc; + +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) { + ret = -ENOENT ; + goto out; + } + + fw_reg_bmap->accessed = true; + *val = fw_reg_bmap->bmap; +out: +spin_unlock (&hvc_desc->lock); +return ret; +} + +static int kvm_arm_fw_reg_set_bmap (struct kvm *kvm, +struct hvc_fw_reg_bmap *fw_reg_bmap,u64 val) +{ +int ret =0 ; +u64 fw_reg_features; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc; + +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) { + ret = -ENOENT ; + goto out; + } + +if (fw_reg_bmap->bmap == val) + goto out; + +if (kvm_vm_has_run_once (kvm)) { + ret = -EBUSY ; + goto out; + } + +switch (fw_reg_bmap->reg_id) { +case KVM_REG_ARM_STD_BMAP : + fw_reg_features =ARM_SMCCC_STD_FEATURES ; +break ; +default : + ret = -EINVAL ; + goto out; + } + +/* Check for unsupported feature bit */ +if (val & ~fw_reg_features) { + ret = -EINVAL ; + goto out; + } + + fw_reg_bmap->accessed = true; + fw_reg_bmap->bmap = val; +out: +spin_unlock (&hvc_desc->lock); +return ret; +} +int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu) { -return ARRAY_SIZE (fw_reg_ids); +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc; +int n_regs =ARRAY_SIZE (fw_reg_ids); + +spin_lock (&hvc_desc->lock); + +if (hvc_desc->fw_reg_bmap_enabled) + n_regs +=ARRAY_SIZE (fw_reg_bmap_ids); + +spin_unlock (&hvc_desc->lock); + +return n_regs; }int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices) { +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc;int i;for (i =0 ; i <ARRAY_SIZE (fw_reg_ids); i++) { -if (put_user (fw_reg_ids[i], uindices)) +if (put_user (fw_reg_ids[i], uindices++)) +return -EFAULT ; + } + +spin_lock (&hvc_desc->lock); +
arch/arm64/kvm/hypercalls.c static const u64 fw_reg_ids[] = { · line 367
Quoted 10 lines
arch/arm64/kvm/hypercalls.c +9
-0+if (!hvc_desc->fw_reg_bmap_enabled) { +spin_unlock (&hvc_desc->lock); +return 0 ; + } + +spin_unlock (&hvc_desc->lock); + +for (i =0 ; i <ARRAY_SIZE (fw_reg_bmap_ids); i++) { +if (put_user (fw_reg_bmap_ids[i], uindices++))return -EFAULT ;
I really don't get what you are trying to achieve with this locking. You guard the 'enabled' bit, but you still allow the kernel view to be copied to userspace while another thread is writing to it?
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [RFC PATCH v2 04/11] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
Quoted 66 lines · Marc Zyngier
Quoted 63 lines · Raghavendra Rao Ananta
KVM regularly introduces new hypercall services to the guests without any consent from the Virtual Machine Manager (VMM). This means, the guests can observe hypercall services in and out as they migrate across various host kernel versions. This could be a major problem if the guest discovered a hypercall, started using it, and after getting migrated to an older kernel realizes that it's no longer available. Depending on how the guest handles the change, there's a potential chance that the guest would just panic.
As a result, there's a need for the VMM to elect the services that it wishes the guest to discover. VMM can elect these services based on the kernels spread across its (migration) fleet. To remedy this, extend the existing firmware psuedo-registers, such as
KVM_REG_ARM_PSCI_VERSION, for all the hypercall services available.These firmware registers are categorized based on the service call owners, and unlike the existing firmware psuedo-registers, they hold the features supported in the form of a bitmap. During VM (vCPU) initialization, the registers shows an upper-limit of the features supported by the corresponding registers. The VMM can simply use
GET_ONE_REGto discover the features. If it's unhappy with any of the features, it can simply write-back the desired feature bitmap usingSET_ONE_REG.KVM allows these modification only until a VM has started. KVM also assumes that the VMM is unaware of a register if a register remains unaccessed (read/write), and would simply clear all the bits of the registers such that the guest accidently doesn't get exposed to the features. Finally, the set of bitmaps from all the registers are the services that are exposed to the guest.
In order to provide backward compatibility with already existing VMMs, a new capability,
KVM_CAP_ARM_HVC_FW_REG_BMAP, is introduced. To enable the bitmap firmware registers extension, the capability must be explicitly enabled. If not, the behavior is similar to the previous setup.In this patch, the framework adds the register only for ARM's standard secure services (owner value 4). Currently, this includes support only for ARM True Random Number Generator (TRNG) service, with bit-0 of the register representing mandatory features of v1.0. Other services are momentarily added in the upcoming patches.
Signed-off-by Raghavendra Rao Ananta <[email protected]>--- arch/arm64/include/asm/kvm_host.h | 16 +++ arch/arm64/include/uapi/asm/kvm.h | 4 + arch/arm64/kvm/arm.c | 23 +++- arch/arm64/kvm/hypercalls.c | 217 +++++++++++++++++++++++++++++- arch/arm64/kvm/trng.c | 9 +- include/kvm/arm_hypercalls.h | 7 + include/uapi/linux/kvm.h | 1 + 7 files changed, 262 insertions(+), 15 deletions(-)arch/arm64/include/asm/kvm_host.h +0
-0@@ -102,6 +102,19 @@ struct kvm_s2_mmu {struct kvm_arch_memory_slot { };
Quoted 17 lines
arch/arm64/include/asm/kvm_host.h struct kvm_s2_mmu { · line 116
Quoted 12 lines
arch/arm64/include/asm/kvm_host.h +12
-0+struct hvc_fw_reg_bmap { +bool accessed; +u64 reg_id; +u64 bmap; +}; + +struct hvc_reg_desc { +spinlock_t lock; +bool fw_reg_bmap_enabled; + +struct hvc_fw_reg_bmap hvc_std_bmap; +};Please document what these data structures track. Without any documentation, it is pretty difficult to build a mental picture of how this all fits together.
Sure, will do.
Quoted 62 lines
Quoted 62 lines
arch/arm64/include/asm/kvm_host.h +4
-0+struct kvm_arch {struct kvm_s2_mmu mmu;@@ -137,6 +150,9 @@ struct kvm_arch {/* Memory Tagging Extension enabled for the guest */ bool mte_enabled; + +/* Hypercall firmware registers' descriptor */ +struct hvc_reg_desc hvc_desc; };struct kvm_vcpu_fault_info {arch/arm64/include/uapi/asm/kvm.h +4
-0@@ -281,6 +281,10 @@ struct kvm_arm_copy_mte_tags {#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) +#define KVM_REG_ARM_STD_BMAP KVM_REG_ARM_FW_REG(3) +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0 BIT(0) +#define KVM_REG_ARM_STD_BMAP_BIT_MAX 0/* Last valid bit */ +/* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)arch/arm64/kvm/arm.c +5
-6@@ -81,26 +81,32 @@ int kvm_arch_check_processor_compat(void *opaque)int kvm_vm_ioctl_enable_cap (struct kvm *kvm,struct kvm_enable_cap *cap) { -int r; +int r =0 ; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc;if (cap->flags)return -EINVAL ;switch (cap->cap) {case KVM_CAP_ARM_NISV_TO_USER : - r =0 ; kvm->arch.return_nisv_io_abort_to_user = true;break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { +if (!system_supports_mte () || kvm->created_vcpus) r = -EINVAL ; - }else { - r =0 ; +else kvm->arch.mte_enabled = true; - }mutex_unlock (&kvm->lock);break ; +case KVM_CAP_ARM_HVC_FW_REG_BMAP :
Quoted 9 lines
arch/arm64/kvm/arm.c int kvm_arch_check_processor_compat(void *opaque) · line 106
Quoted 4 lines
arch/arm64/kvm/arm.c +4
-0+if (kvm_vm_has_run_once (kvm)) +return -EBUSY ; + +spin_lock (&hvc_desc->lock);Does this really need to be a spin-lock? Are you ever taking it on a context where you cannot sleep? And why does it need to be a new lock when we already have a plethora of them?
I suppose I was going with the fact that we have very small critical sections and could just go with a spinlock without interfering with any other paths. But I suppose that's not needed. I can go with the kvm->lock mutex instead.
Quoted 27 lines
Quoted 27 lines
arch/arm64/kvm/arm.c +6
-0+ hvc_desc->fw_reg_bmap_enabled = true; +spin_unlock (&hvc_desc->lock); +break ;default : r = -EINVAL ;break ;@@ -157,6 +163,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)set_default_spectre (kvm); +kvm_arm_init_hypercalls (kvm); +return ret; out_free_stage2_pgd:kvm_free_stage2_pgd (&kvm->arch.mmu);@@ -215,6 +223,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)case KVM_CAP_SET_GUEST_DEBUG :case KVM_CAP_VCPU_ATTRIBUTES :case KVM_CAP_PTP_KVM : +case KVM_CAP_ARM_HVC_FW_REG_BMAP : r =1 ;break ;case KVM_CAP_SET_GUEST_DEBUG2 :@@ -622,6 +631,8 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)if (kvm_vm_is_protected (kvm))kvm_call_hyp_nvhe (__pkvm_vcpu_init_traps, vcpu);
Quoted 9 lines
arch/arm64/kvm/arm.c static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) · line 634
Quoted 1 line
arch/arm64/kvm/arm.c +1
-0+kvm_arm_sanitize_fw_regs (kvm);What is the rational for doing this on first run? Surely this could be done at the point where userspace performs the write, couldn't it?
My mental model is that the VMM reads some data, clears some bits if it really wants to, and writes it back. There shouldn't be anything to sanitise after the facts. Or at least that's my gut feeling so far.
I tried to explain things below..
Quoted 41 lines
/me reads on.
Quoted 39 lines
arch/arm64/kvm/arm.c +1
-0+return ret; }arch/arm64/kvm/hypercalls.c +27
-0@@ -58,6 +58,41 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val) val[3 ] =lower_32_bits (cycles); } +static bool +kvm_arm_fw_reg_feat_enabled (struct hvc_fw_reg_bmap *reg_bmap,u64 feat_bit) +{ +return reg_bmap->bmap & feat_bit; +} + +bool kvm_hvc_call_supported (struct kvm_vcpu *vcpu,u32 func_id) +{ +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc; + +/* +* To ensure backward compatibility, support all the service calls, +* including new additions, if the firmware registers holding the +* feature bitmaps isn't explicitly enabled. +*/ +if (!hvc_desc->fw_reg_bmap_enabled) +return true; + +switch (func_id) { +case ARM_SMCCC_TRNG_VERSION : +case ARM_SMCCC_TRNG_FEATURES : +case ARM_SMCCC_TRNG_GET_UUID : +case ARM_SMCCC_TRNG_RND32 : +case ARM_SMCCC_TRNG_RND64 : +return kvm_arm_fw_reg_feat_enabled (&hvc_desc->hvc_std_bmap, +KVM_REG_ARM_STD_BIT_TRNG_V1_0 ); +default :
Quoted 9 lines
arch/arm64/kvm/hypercalls.c static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val) · line 93
Quoted 6 lines
arch/arm64/kvm/hypercalls.c +6
-0+/* By default, allow the services that aren't listed here */ +return true; + } + +/* We shouldn't be reaching here */ +return true;So why have anything at all?
I was expecting the compiler might complain, but guess not, I'll remove this.
Quoted 104 lines
Quoted 87 lines
arch/arm64/kvm/hypercalls.c +66
-0+} +int kvm_hvc_call_handler (struct kvm_vcpu *vcpu) {u32 func_id =smccc_get_function (vcpu);@@ -65,6 +100,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)u32 feature;gpa_t gpa; +if (!kvm_hvc_call_supported (vcpu, func_id)) + goto out; +switch (func_id) {case ARM_SMCCC_VERSION_FUNC_ID : val[0 ] =ARM_SMCCC_VERSION_1_1 ;@@ -143,6 +181,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)return kvm_psci_call (vcpu); } +out:smccc_set_retval (vcpu, val[0 ], val[1 ], val[2 ], val[3 ]);return 1 ; }@@ -153,17 +192,178 @@ static const u64 fw_reg_ids[] = { KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, }; +static const u64 fw_reg_bmap_ids[] = { +KVM_REG_ARM_STD_BMAP , +}; + +static void kvm_arm_fw_reg_init_hvc (struct hvc_reg_desc *hvc_desc, +struct hvc_fw_reg_bmap *fw_reg_bmap, +u64 reg_id,u64 default_map) +{ + fw_reg_bmap->reg_id = reg_id; + fw_reg_bmap->bmap = default_map; +} + +void kvm_arm_init_hypercalls (struct kvm *kvm) +{ +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc; + +spin_lock_init (&hvc_desc->lock); + +kvm_arm_fw_reg_init_hvc (hvc_desc, &hvc_desc->hvc_std_bmap, +KVM_REG_ARM_STD_BMAP ,ARM_SMCCC_STD_FEATURES ); +} + +static void kvm_arm_fw_reg_sanitize (struct hvc_fw_reg_bmap *fw_reg_bmap) +{ +if (!fw_reg_bmap->accessed) + fw_reg_bmap->bmap =0 ; +} + +/* +* kvm_arm_sanitize_fw_regs: Sanitize the hypercall firmware registers +* +* Sanitization, in the case of hypercall firmware registers, is basically +* clearing out the feature bitmaps so that the guests are not exposed to +* the services corresponding to a particular register. The registers that +* needs sanitization is decided on two factors on the user-space part: +* 1. Enablement of KVM_CAP_ARM_HVC_FW_REG_BMAP: +* If the user-space hasn't enabled the capability, it either means +* that it's unaware of its existence, or it simply doesn't want to +* participate in the arrangement and is okay with the default settings. +* The former case is to ensure backward compatibility. +* +* 2. Has the user-space accessed (read/write) the register? : +* If yes, it means that the user-space is aware of the register's +* existence and can set the bits as it sees fit for the guest. A +* read-only access from user-space indicates that the user-space is +* happy with the default settings, and doesn't wish to change it. +* +* The logic for sanitizing a register will then be: +* --------------------------------------------------------------------------- +* | CAP enabled | Accessed reg | Clear reg | Comments | +* --------------------------------------------------------------------------- +* | N | N | N | | +* | N | Y | N | -ENOENT returned during access | +* | Y | N | Y | | +* | Y | Y | N | | +* --------------------------------------------------------------------------- +*/ +void kvm_arm_sanitize_fw_regs (struct kvm *kvm) +{ +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc;arch/arm64/kvm/hypercalls.c static const u64 fw_reg_ids[] = { · line 264
Quoted 10 lines
arch/arm64/kvm/hypercalls.c +10
-0+ +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) + goto out; + +kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_std_bmap); + +out: +spin_unlock (&hvc_desc->lock);I keep being baffled by this. Why should we track the VMM accesses or the VMM writeback? This logic doesn't seem to bring anything useful as far as I can tell. All we need to ensure is that what is written to the pseudo-register is an acceptable subset of the previous value, and I cannot see why this can't be done at write-time.
Quoted 4 lines
If you want to hide this behind a capability, fine (although my guts feeling is that we don't need that either). But I really want to be convinced about all this tracking.
The tracking of each owner register is necessary here to safe-guard the possibility that the user-space may not be aware of a newly introduced register, and hence, hasn't accessed it. If it had at least read the register, but not write-back, we assume that the user-space is happy with the configuration. But the fact that the register has not even been read would state that user-space is unaware of the existence of this new register. In such a case, if we don't sanitize (clear all the bits) this register, the features will be exposed unconditionally to the guest.
The capability is introduced here to make sure that this new infrastructure is backward compatible with old VMMs. If the VMMs don't enable this capability, they are probably unaware of this, and this will work as it always has- expose new services to the guest unconditionally as and when they are introduced.
Quoted 101 lines
Quoted 101 lines
arch/arm64/kvm/hypercalls.c +90
-2+} + +static int kvm_arm_fw_reg_get_bmap (struct kvm *kvm, +struct hvc_fw_reg_bmap *fw_reg_bmap,u64 *val) +{ +int ret =0 ; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc; + +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) { + ret = -ENOENT ; + goto out; + } + + fw_reg_bmap->accessed = true; + *val = fw_reg_bmap->bmap; +out: +spin_unlock (&hvc_desc->lock); +return ret; +} + +static int kvm_arm_fw_reg_set_bmap (struct kvm *kvm, +struct hvc_fw_reg_bmap *fw_reg_bmap,u64 val) +{ +int ret =0 ; +u64 fw_reg_features; +struct hvc_reg_desc *hvc_desc = &kvm->arch.hvc_desc; + +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) { + ret = -ENOENT ; + goto out; + } + +if (fw_reg_bmap->bmap == val) + goto out; + +if (kvm_vm_has_run_once (kvm)) { + ret = -EBUSY ; + goto out; + } + +switch (fw_reg_bmap->reg_id) { +case KVM_REG_ARM_STD_BMAP : + fw_reg_features =ARM_SMCCC_STD_FEATURES ; +break ; +default : + ret = -EINVAL ; + goto out; + } + +/* Check for unsupported feature bit */ +if (val & ~fw_reg_features) { + ret = -EINVAL ; + goto out; + } + + fw_reg_bmap->accessed = true; + fw_reg_bmap->bmap = val; +out: +spin_unlock (&hvc_desc->lock); +return ret; +} +int kvm_arm_get_fw_num_regs (struct kvm_vcpu *vcpu) { -return ARRAY_SIZE (fw_reg_ids); +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc; +int n_regs =ARRAY_SIZE (fw_reg_ids); + +spin_lock (&hvc_desc->lock); + +if (hvc_desc->fw_reg_bmap_enabled) + n_regs +=ARRAY_SIZE (fw_reg_bmap_ids); + +spin_unlock (&hvc_desc->lock); + +return n_regs; }int kvm_arm_copy_fw_reg_indices (struct kvm_vcpu *vcpu,u64 __user *uindices) { +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc;int i;for (i =0 ; i <ARRAY_SIZE (fw_reg_ids); i++) { -if (put_user (fw_reg_ids[i], uindices)) +if (put_user (fw_reg_ids[i], uindices++)) +return -EFAULT ; + } + +spin_lock (&hvc_desc->lock); + +if (!hvc_desc->fw_reg_bmap_enabled) { +spin_unlock (&hvc_desc->lock); +return 0 ; + } + +spin_unlock (&hvc_desc->lock);
Quoted 9 lines
arch/arm64/kvm/hypercalls.c static const u64 fw_reg_ids[] = { · line 367
Quoted 4 lines
arch/arm64/kvm/hypercalls.c +3
-0+ +for (i =0 ; i <ARRAY_SIZE (fw_reg_bmap_ids); i++) { +if (put_user (fw_reg_bmap_ids[i], uindices++))return -EFAULT ;I really don't get what you are trying to achieve with this locking. You guard the 'enabled' bit, but you still allow the kernel view to be copied to userspace while another thread is writing to it?
That's my mistake. Thanks for pointing it out. I'll get rid of the spinlock and use the existing ones correctly.
Regards,
Raghavendra
Quoted 6 lines
Thanks,
M.--
Without deviation from the norm, progress is not possible.
Re: [RFC PATCH v2 04/11] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
Quoted 21 lines · Raghavendra Rao Ananta
Quoted 10 lines · Marc Zyngier
I keep being baffled by this. Why should we track the VMM accesses or the VMM writeback? This logic doesn't seem to bring anything useful as far as I can tell. All we need to ensure is that what is written to the pseudo-register is an acceptable subset of the previous value, and I cannot see why this can't be done at write-time.
If you want to hide this behind a capability, fine (although my guts feeling is that we don't need that either). But I really want to be convinced about all this tracking.
The tracking of each owner register is necessary here to safe-guard the possibility that the user-space may not be aware of a newly introduced register, and hence, hasn't accessed it. If it had at least read the register, but not write-back, we assume that the user-space is happy with the configuration. But the fact that the register has not even been read would state that user-space is unaware of the existence of this new register. In such a case, if we don't sanitize (clear all the bits) this register, the features will be exposed unconditionally to the guest.
Quoted 5 lines
The capability is introduced here to make sure that this new infrastructure is backward compatible with old VMMs. If the VMMs don't enable this capability, they are probably unaware of this, and this will work as it always has- expose new services to the guest unconditionally as and when they are introduced.
Hi Raghavendra,
I don't think we need a CAP that has to be enabled or to make any assumptions or policy decisions in the kernel. I think we just need to provide a bit more information to the VMM when it checks if KVM has the CAP. If KVM would tell the VMM how may pseudo registers there are, which can be done with the return value of the CAP, then the VMM code could be something like this
r = check_cap(KVM_CAP_ARM_HVC_FW_REG_BMAP);
if (r) {
num_regs = r; for (idx = 0; idx < num_regs; ++idx) {
reg = hvc_fw_reg(idx);
if (idx > vmm_last_known_idx) {
...
} else {
...
}
}}
With this, the VMM is free to decide if it wants to clear all registers greater than the last index it was aware of or if it wants to let those registers just get exposed to the guest without knowing what's getting exposed. Along with documenting that by default everything gets exposed by KVM, which is the backwards compatible thing to do, then the VMM has been warned and given everything it needs to manage its guests.
Another thing that might be nice is giving userspace control of how many pseudo registers show up in get-reg-list. In order to migrate from a host with a more recent KVM to a host with an older KVM[*] we should only expose the number of pseudo registers that the older host is aware of. The VMM would zero these registers out anyway, in order to be compatible for migration, but that's not enough when they also show up in the list (at least not with QEMU that aborts migration when the destination expects less registers than what get-reg-list provides)
[*] This isn't a great idea, but it'd be nice if we can make it work, because users may want to rollback upgrades or, after migrating to a host with a newer kernel, they may want to migrate back to where they started.
Thanks,
drew
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [RFC PATCH v2 04/11] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
Quoted 51 lines · Andrew Jones
Quoted 21 lines · Raghavendra Rao Ananta
Quoted 10 lines · Marc Zyngier
I keep being baffled by this. Why should we track the VMM accesses or the VMM writeback? This logic doesn't seem to bring anything useful as far as I can tell. All we need to ensure is that what is written to the pseudo-register is an acceptable subset of the previous value, and I cannot see why this can't be done at write-time.
If you want to hide this behind a capability, fine (although my guts feeling is that we don't need that either). But I really want to be convinced about all this tracking.
The tracking of each owner register is necessary here to safe-guard the possibility that the user-space may not be aware of a newly introduced register, and hence, hasn't accessed it. If it had at least read the register, but not write-back, we assume that the user-space is happy with the configuration. But the fact that the register has not even been read would state that user-space is unaware of the existence of this new register. In such a case, if we don't sanitize (clear all the bits) this register, the features will be exposed unconditionally to the guest.
Quoted 5 lines
The capability is introduced here to make sure that this new infrastructure is backward compatible with old VMMs. If the VMMs don't enable this capability, they are probably unaware of this, and this will work as it always has- expose new services to the guest unconditionally as and when they are introduced.
Hi Raghavendra,
I don't think we need a CAP that has to be enabled or to make any assumptions or policy decisions in the kernel. I think we just need to provide a bit more information to the VMM when it checks if KVM has the CAP. If KVM would tell the VMM how may pseudo registers there are, which can be done with the return value of the CAP, then the VMM code could be something like this
r = check_cap(KVM_CAP_ARM_HVC_FW_REG_BMAP); if (r) { num_regs = r;for (idx = 0; idx < num_regs; ++idx) { reg = hvc_fw_reg(idx); if (idx > vmm_last_known_idx) { ... } else { ... } }
Quoted 9 lines
}
With this, the VMM is free to decide if it wants to clear all registers greater than the last index it was aware of or if it wants to let those registers just get exposed to the guest without knowing what's getting exposed. Along with documenting that by default everything gets exposed by KVM, which is the backwards compatible thing to do, then the VMM has been warned and given everything it needs to manage its guests.
Hi Andrew,
Thanks for your comments and suggestions!
I like the idea of sharing info via a read of the CAP, and not having to explicitly sanitize/clear the registers before the guest begins to run.
However the handshake is done over an API doc, which is a little concerning. The user-space must remember and explicitly clear any new register that it doesn't want to expose to the guest, while the current approach does this automatically.
Any bug in VMM's implementation could be risky and unintentionally expose features to the guest. What do you think?
Quoted 9 lines
Another thing that might be nice is giving userspace control of how many pseudo registers show up in get-reg-list. In order to migrate from a host with a more recent KVM to a host with an older KVM[*] we should only expose the number of pseudo registers that the older host is aware of. The VMM would zero these registers out anyway, in order to be compatible for migration, but that's not enough when they also show up in the list (at least not with QEMU that aborts migration when the destination expects less registers than what get-reg-list provides)
Quoted 5 lines
[*] This isn't a great idea, but it'd be nice if we can make it work, because users may want to rollback upgrades or, after migrating to a host with a newer kernel, they may want to migrate back to where they started.
Good point. But IIUC, if the user-space is able to communicate the info that it's expecting a certain get-reg-list, do you think it can handle it at its end too, rather than relying on the kernel to send a list back?
My assumption was that VMM would statically maintain a known set of registers that it wants to work with and are to be modified by hand, rather than relying on get-reg-list. This could be the least common set of registers that are present in all the host kernels (higher or lower versions) of the migration fleet. This config doesn't change even with get-reg-list declaring a new register as the features exposed by it could still be untested. Although, migrating to a host with a missing register shouldn't be possible in this setting, but if it encounters the scenario, it should be able to avoid migration to the host (similar to QEMU).
Please correct me if you think it's a false assumption to proceed with.
Regards,
Raghavendra
Quoted 3 lines
Thanks,
drew
Re: [RFC PATCH v2 04/11] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
Quoted 65 lines · Raghavendra Rao Ananta
Quoted 51 lines · Andrew Jones
Quoted 21 lines · Raghavendra Rao Ananta
Quoted 10 lines · Marc Zyngier
I keep being baffled by this. Why should we track the VMM accesses or the VMM writeback? This logic doesn't seem to bring anything useful as far as I can tell. All we need to ensure is that what is written to the pseudo-register is an acceptable subset of the previous value, and I cannot see why this can't be done at write-time.
If you want to hide this behind a capability, fine (although my guts feeling is that we don't need that either). But I really want to be convinced about all this tracking.
The tracking of each owner register is necessary here to safe-guard the possibility that the user-space may not be aware of a newly introduced register, and hence, hasn't accessed it. If it had at least read the register, but not write-back, we assume that the user-space is happy with the configuration. But the fact that the register has not even been read would state that user-space is unaware of the existence of this new register. In such a case, if we don't sanitize (clear all the bits) this register, the features will be exposed unconditionally to the guest.
Quoted 5 lines
The capability is introduced here to make sure that this new infrastructure is backward compatible with old VMMs. If the VMMs don't enable this capability, they are probably unaware of this, and this will work as it always has- expose new services to the guest unconditionally as and when they are introduced.
Hi Raghavendra,
I don't think we need a CAP that has to be enabled or to make any assumptions or policy decisions in the kernel. I think we just need to provide a bit more information to the VMM when it checks if KVM has the CAP. If KVM would tell the VMM how may pseudo registers there are, which can be done with the return value of the CAP, then the VMM code could be something like this
r = check_cap(KVM_CAP_ARM_HVC_FW_REG_BMAP); if (r) { num_regs = r;for (idx = 0; idx < num_regs; ++idx) { reg = hvc_fw_reg(idx); if (idx > vmm_last_known_idx) { ... } else { ... } }Quoted 9 lines
}
With this, the VMM is free to decide if it wants to clear all registers greater than the last index it was aware of or if it wants to let those registers just get exposed to the guest without knowing what's getting exposed. Along with documenting that by default everything gets exposed by KVM, which is the backwards compatible thing to do, then the VMM has been warned and given everything it needs to manage its guests.
Hi Andrew,
Thanks for your comments and suggestions!
Quoted 9 lines
I like the idea of sharing info via a read of the CAP, and not having to explicitly sanitize/clear the registers before the guest begins to run.
However the handshake is done over an API doc, which is a little concerning. The user-space must remember and explicitly clear any new register that it doesn't want to expose to the guest, while the current approach does this automatically.
Any bug in VMM's implementation could be risky and unintentionally expose features to the guest. What do you think?
The VMM can mess things up in many ways. While KVM should protect itself from the VMM, it shouldn't try to protect the VMM from the VMM itself. In this case, the risk here isn't that we allow the VMM to do something that can harm KVM, or even the guest. The risk is only that the VMM fails to do what it wanted to do (assuming it didn't want to expose unknown features to the guest). I.e. the risk here is only that the VMM has a bug, and it's an easily detectable bug. I say let the VMM developers manage it.
Quoted 15 lines
Quoted 10 lines
Another thing that might be nice is giving userspace control of how many pseudo registers show up in get-reg-list. In order to migrate from a host with a more recent KVM to a host with an older KVM[*] we should only expose the number of pseudo registers that the older host is aware of. The VMM would zero these registers out anyway, in order to be compatible for migration, but that's not enough when they also show up in the list (at least not with QEMU that aborts migration when the destination expects less registers than what get-reg-list provides)
Quoted 5 lines
[*] This isn't a great idea, but it'd be nice if we can make it work, because users may want to rollback upgrades or, after migrating to a host with a newer kernel, they may want to migrate back to where they started.
Quoted 4 lines
Good point. But IIUC, if the user-space is able to communicate the info that it's expecting a certain get-reg-list, do you think it can handle it at its end too, rather than relying on the kernel to send a list back?
Yes, I think we can probably manage this in the VMM, and maybe/probably that's the better place to manage it.
Quoted 12 lines
My assumption was that VMM would statically maintain a known set of registers that it wants to work with and are to be modified by hand, rather than relying on get-reg-list. This could be the least common set of registers that are present in all the host kernels (higher or lower versions) of the migration fleet. This config doesn't change even with get-reg-list declaring a new register as the features exposed by it could still be untested. Although, migrating to a host with a missing register shouldn't be possible in this setting, but if it encounters the scenario, it should be able to avoid migration to the host (similar to QEMU).
Quoted 1 line
Please correct me if you think it's a false assumption to proceed with.
Your assumptions align with mine. It seems as we move towards CPU models, get-reg-list's role will likely only be to confirm a host supports the minimum required. We should probably implement/change the VMM to allow migrating from a host with more registers to one with less as long as the one with less includes all the required registers. Of course we also need to ensure that any registers we don't want to require are not exposed to the guest, but I guess that's precisely what we're trying to do with this series for at least some pseudo registers.
Thanks,
drew
RFC PATCH v2 5/11 KVM: arm64: Add standard hypervisor firmware register
In reply to Raghavendra Rao AnantaIntroduce the firmware register to hold the standard hypervisor service calls (owner value 5) as a bitmap. The bitmap represents the features that'll be enabled for the guest, as configured by the user-space. Currently, this includes support only for Paravirtualized time, represented by bit-0.
--- arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/uapi/asm/kvm.h | 4 ++++ arch/arm64/kvm/hypercalls.c | 24 ++++++++++++++++++++++++ arch/arm64/kvm/pvtime.c | 3 +++ include/kvm/arm_hypercalls.h | 3 +++ 5 files changed, 35 insertions(+)
arch/arm64/include/asm/kvm_host.h +1 -0
@@ -113,6 +113,7 @@ struct hvc_reg_desc {bool fw_reg_bmap_enabled;struct hvc_fw_reg_bmap hvc_std_bmap; +struct hvc_fw_reg_bmap hvc_std_hyp_bmap; };struct kvm_arch {
arch/arm64/include/uapi/asm/kvm.h +4 -0
@@ -285,6 +285,10 @@ struct kvm_arm_copy_mte_tags {#define KVM_REG_ARM_STD_BIT_TRNG_V1_0 BIT(0)#define KVM_REG_ARM_STD_BMAP_BIT_MAX 0/* Last valid bit */ +#define KVM_REG_ARM_STD_HYP_BMAP KVM_REG_ARM_FW_REG(4) +#define KVM_REG_ARM_STD_HYP_BIT_PV_TIME BIT(0) +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX 0/* Last valid bit */ +/* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)
arch/arm64/kvm/hypercalls.c +24 -0
@@ -84,6 +84,10 @@ bool kvm_hvc_call_supported(struct kvm_vcpu *vcpu, u32 func_id)case ARM_SMCCC_TRNG_RND64 :return kvm_arm_fw_reg_feat_enabled (&hvc_desc->hvc_std_bmap,KVM_REG_ARM_STD_BIT_TRNG_V1_0 ); +case ARM_SMCCC_HV_PV_TIME_FEATURES : +case ARM_SMCCC_HV_PV_TIME_ST : +return kvm_arm_fw_reg_feat_enabled (&hvc_desc->hvc_std_hyp_bmap, +KVM_REG_ARM_STD_HYP_BIT_PV_TIME );default :/* By default, allow the services that aren't listed here */ return true;
@@ -109,6 +113,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)break ;case ARM_SMCCC_ARCH_FEATURES_FUNC_ID : feature =smccc_get_arg1 (vcpu); +if (!kvm_hvc_call_supported (vcpu, feature)) +break ; +switch (feature) {case ARM_SMCCC_ARCH_WORKAROUND_1 :switch (arm64_get_spectre_v2_state ()) {
@@ -194,6 +201,7 @@ static const u64 fw_reg_ids[] = {static const u64 fw_reg_bmap_ids[] = {KVM_REG_ARM_STD_BMAP , +KVM_REG_ARM_STD_HYP_BMAP , }; static void kvm_arm_fw_reg_init_hvc(struct hvc_reg_desc *hvc_desc,
@@ -212,6 +220,8 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)kvm_arm_fw_reg_init_hvc (hvc_desc, &hvc_desc->hvc_std_bmap,KVM_REG_ARM_STD_BMAP ,ARM_SMCCC_STD_FEATURES ); +kvm_arm_fw_reg_init_hvc (hvc_desc, &hvc_desc->hvc_std_hyp_bmap, +KVM_REG_ARM_STD_HYP_BMAP ,ARM_SMCCC_STD_HYP_FEATURES ); }static void kvm_arm_fw_reg_sanitize (struct hvc_fw_reg_bmap *fw_reg_bmap)
@@ -259,6 +269,7 @@ void kvm_arm_sanitize_fw_regs(struct kvm *kvm) goto out;kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_std_bmap); +kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_std_hyp_bmap); out:spin_unlock (&hvc_desc->lock);
@@ -310,6 +321,9 @@ static int kvm_arm_fw_reg_set_bmap(struct kvm *kvm,case KVM_REG_ARM_STD_BMAP : fw_reg_features =ARM_SMCCC_STD_FEATURES ;break ; +case KVM_REG_ARM_STD_HYP_BMAP : + fw_reg_features =ARM_SMCCC_STD_HYP_FEATURES ; +break ;default : ret = -EINVAL ; goto out;
@@ -432,6 +446,13 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)if (ret)return ret; +break ; +case KVM_REG_ARM_STD_HYP_BMAP : + ret =kvm_arm_fw_reg_get_bmap (kvm, + &hvc_desc->hvc_std_hyp_bmap, &val); +if (ret) +return ret; +break ;default :return -ENOENT ;
@@ -523,6 +544,9 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)return 0 ;case KVM_REG_ARM_STD_BMAP :return kvm_arm_fw_reg_set_bmap (kvm, &hvc_desc->hvc_std_bmap, val); +case KVM_REG_ARM_STD_HYP_BMAP : +return kvm_arm_fw_reg_set_bmap (kvm, + &hvc_desc->hvc_std_hyp_bmap, val);default :return -ENOENT ; }
arch/arm64/kvm/pvtime.c +3 -0
@@ -37,6 +37,9 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)u32 feature =smccc_get_arg1 (vcpu);long val =SMCCC_RET_NOT_SUPPORTED ; +if (!kvm_hvc_call_supported (vcpu, feature)) +return val; +switch (feature) {case ARM_SMCCC_HV_PV_TIME_FEATURES :case ARM_SMCCC_HV_PV_TIME_ST :
include/kvm/arm_hypercalls.h +3 -0
@@ -9,6 +9,9 @@#define ARM_SMCCC_STD_FEATURES \ GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0) +#define ARM_SMCCC_STD_HYP_FEATURES \ + GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0) +int kvm_hvc_call_handler (struct kvm_vcpu *vcpu);static inline u32 smccc_get_function (struct kvm_vcpu *vcpu)
Signature · 1 line
2.34.0.rc1.387.gb447b232ab-goog
RFC PATCH v2 6/11 KVM: arm64: Add vendor hypervisor firmware register
In reply to Raghavendra Rao AnantaIntroduce the firmware register to hold the vendor specific hypervisor service calls (owner value 6) as a bitmap. The bitmap represents the features that'll be enabled for the guest, as configured by the user-space. Currently, this includes support only for Precision Time Protocol (PTP), represented by bit-0.
--- arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/uapi/asm/kvm.h | 4 ++++ arch/arm64/kvm/hypercalls.c | 30 +++++++++++++++++++++++++++++- include/kvm/arm_hypercalls.h | 3 +++ 4 files changed, 37 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_host.h +1 -0
@@ -114,6 +114,7 @@ struct hvc_reg_desc {struct hvc_fw_reg_bmap hvc_std_bmap;struct hvc_fw_reg_bmap hvc_std_hyp_bmap; +struct hvc_fw_reg_bmap hvc_vendor_hyp_bmap; };struct kvm_arch {
arch/arm64/include/uapi/asm/kvm.h +4 -0
@@ -289,6 +289,10 @@ struct kvm_arm_copy_mte_tags {#define KVM_REG_ARM_STD_HYP_BIT_PV_TIME BIT(0)#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX 0/* Last valid bit */ +#define KVM_REG_ARM_VENDOR_HYP_BMAP KVM_REG_ARM_FW_REG(5) +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP BIT(0) +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX 0/* Last valid bit */ +/* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)
arch/arm64/kvm/hypercalls.c +29 -1
@@ -88,6 +88,9 @@ bool kvm_hvc_call_supported(struct kvm_vcpu *vcpu, u32 func_id)case ARM_SMCCC_HV_PV_TIME_ST :return kvm_arm_fw_reg_feat_enabled (&hvc_desc->hvc_std_hyp_bmap,KVM_REG_ARM_STD_HYP_BIT_PV_TIME ); +case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID : +return kvm_arm_fw_reg_feat_enabled (&hvc_desc->hvc_vendor_hyp_bmap, +KVM_REG_ARM_VENDOR_HYP_BIT_PTP );default :/* By default, allow the services that aren't listed here */ return true;
@@ -99,6 +102,7 @@ bool kvm_hvc_call_supported(struct kvm_vcpu *vcpu, u32 func_id)int kvm_hvc_call_handler (struct kvm_vcpu *vcpu) { +struct hvc_reg_desc *hvc_desc = &vcpu->kvm->arch.hvc_desc;u32 func_id =smccc_get_function (vcpu);u64 val[4 ] = {SMCCC_RET_NOT_SUPPORTED };u32 feature;
@@ -173,7 +177,14 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)break ;case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID : val[0 ] =BIT (ARM_SMCCC_KVM_FUNC_FEATURES ); - val[0 ] |=BIT (ARM_SMCCC_KVM_FUNC_PTP ); + +/* +* The feature bits exposed to user-space doesn't include +* ARM_SMCCC_KVM_FUNC_FEATURES. However, we expose this to +* the guest as bit-0. Hence, left-shift the user-space +* exposed bitmap by 1 to accommodate this. +*/ + val[0 ] |= hvc_desc->hvc_vendor_hyp_bmap.bmap <<1 ;break ;case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID :kvm_ptp_get_time (vcpu, val);
@@ -202,6 +213,7 @@ static const u64 fw_reg_ids[] = {static const u64 fw_reg_bmap_ids[] = {KVM_REG_ARM_STD_BMAP ,KVM_REG_ARM_STD_HYP_BMAP , +KVM_REG_ARM_VENDOR_HYP_BMAP , }; static void kvm_arm_fw_reg_init_hvc(struct hvc_reg_desc *hvc_desc,
@@ -222,6 +234,8 @@ void kvm_arm_init_hypercalls(struct kvm *kvm) KVM_REG_ARM_STD_BMAP, ARM_SMCCC_STD_FEATURES);kvm_arm_fw_reg_init_hvc (hvc_desc, &hvc_desc->hvc_std_hyp_bmap,KVM_REG_ARM_STD_HYP_BMAP ,ARM_SMCCC_STD_HYP_FEATURES ); +kvm_arm_fw_reg_init_hvc (hvc_desc, &hvc_desc->hvc_vendor_hyp_bmap, +KVM_REG_ARM_VENDOR_HYP_BMAP ,ARM_SMCCC_VENDOR_HYP_FEATURES ); }static void kvm_arm_fw_reg_sanitize (struct hvc_fw_reg_bmap *fw_reg_bmap)
@@ -270,6 +284,7 @@ void kvm_arm_sanitize_fw_regs(struct kvm *kvm)kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_std_bmap);kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_std_hyp_bmap); +kvm_arm_fw_reg_sanitize (&hvc_desc->hvc_vendor_hyp_bmap); out:spin_unlock (&hvc_desc->lock);
@@ -324,6 +339,9 @@ static int kvm_arm_fw_reg_set_bmap(struct kvm *kvm,case KVM_REG_ARM_STD_HYP_BMAP : fw_reg_features =ARM_SMCCC_STD_HYP_FEATURES ;break ; +case KVM_REG_ARM_VENDOR_HYP_BMAP : + fw_reg_features =ARM_SMCCC_VENDOR_HYP_FEATURES ; +break ;default : ret = -EINVAL ; goto out;
@@ -453,6 +471,13 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)if (ret)return ret; +break ; +case KVM_REG_ARM_VENDOR_HYP_BMAP : + ret =kvm_arm_fw_reg_get_bmap (kvm, + &hvc_desc->hvc_vendor_hyp_bmap, &val); +if (ret) +return ret; +break ;default :return -ENOENT ;
@@ -547,6 +572,9 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)case KVM_REG_ARM_STD_HYP_BMAP :return kvm_arm_fw_reg_set_bmap (kvm, &hvc_desc->hvc_std_hyp_bmap, val); +case KVM_REG_ARM_VENDOR_HYP_BMAP : +return kvm_arm_fw_reg_set_bmap (kvm, + &hvc_desc->hvc_vendor_hyp_bmap, val);default :return -ENOENT ; }
include/kvm/arm_hypercalls.h +3 -0
@@ -12,6 +12,9 @@#define ARM_SMCCC_STD_HYP_FEATURES \ GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0) +#define ARM_SMCCC_VENDOR_HYP_FEATURES \ + GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0) +int kvm_hvc_call_handler (struct kvm_vcpu *vcpu);static inline u32 smccc_get_function (struct kvm_vcpu *vcpu)
Signature · 6 lines
2.34.0.rc1.387.gb447b232ab-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
RFC PATCH v2 7/11 Docs: KVM: Add doc for the bitmap firmware registers
In reply to Raghavendra Rao Ananta- Add documentation for the capability, KVM_CAP_ARM_HVC_FW_REG_BMAP, in KVM's api.rst.
- Add the documentation for the bitmap firmware registers in psci.rst. This includes the details for KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and KVM_REG_ARM_VENDOR_HYP_BMAP registers.
--- Documentation/virt/kvm/api.rst | 23 ++++++++ Documentation/virt/kvm/arm/psci.rst | 89 +++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 17 deletions(-)
Documentation/virt/kvm/api.rst +23 -0
@@ -6911,6 +6911,29 @@ MAP_SHARED mmap will result in an -EINVAL return. When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to perform a bulk copy of tags to/from the guest. +7.29 KVM_CAP_ARM_HVC_FW_REG_BMAP +-------------------------------- + +:Architecture: arm64 +:Parameters: none + +This capability indicates that KVM for arm64 supports the psuedo-firmware +register bitmap extension. It must be explicitly enabled. Once enabled, +KVM allows access to the firmware registers that hold the bitmap of the +hypercall services that should be exposed to the guest. + +By default, the registers are set with the upper-limit of the features +exposed to the guest. User-space can discover them via the GET_ONE_REG +interface. If unsatisfied with the configuration, it can write-back the +bitmap that it sees fit for the guest via SET_ONE_REG interface. The +registers that are never accessed by the user-space (read/write) are +by default cleared just before the vCPU runs. This is to make sure that +the features are not accidentally exposed to the guest without the +consent of user-space. + +Note that the capability has to be enabled before running any vCPU. Also, +the capability cannot be disabled. The VM has to be restarted for that. + 8. Other capabilities. ======================
Documentation/virt/kvm/arm/psci.rst +72 -17
108 lines not on this page · raw message
Signature · 1 line
2.34.0.rc1.387.gb447b232ab-goog
RFC PATCH v2 8/11 Docs: KVM: Rename psci.rst to hypercalls.rst
In reply to Raghavendra Rao AnantaSince the doc now covers more of general hypercalls' details, rather than just PSCI, rename the file to a more appropriate name- hypercalls.rst.
---
Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} (100%)Documentation/virt/kvm/arm/hypercalls.rst renamed from Documentation/virt/kvm/arm/psci.rst +0 -0
Signature · 6 lines
2.34.0.rc1.387.gb447b232ab-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
RFC PATCH v2 9/11 tools: Import ARM SMCCC definitions
In reply to Raghavendra Rao AnantaImport the standard SMCCC definitions from include/linux/arm-smccc.h.
--- tools/include/linux/arm-smccc.h | 188 ++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 tools/include/linux/arm-smccc.h
tools/include/linux/arm-smccc.h new file +188 -0
@@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* Copyright (c) 2015, Linaro Limited +*/ +#ifndef __LINUX_ARM_SMCCC_H +#define __LINUX_ARM_SMCCC_H + +#include <linux/const.h> + +/* +* This file provides common defines for ARM SMC Calling Convention as +* specified in +* https://developer.arm.com/docs/den0028/latest +* +* This code is up-to-date with version DEN 0028 C +*/ + +#define ARM_SMCCC_STD_CALL _AC(0,U) +#define ARM_SMCCC_FAST_CALL _AC(1,U) +#define ARM_SMCCC_TYPE_SHIFT 31 + +#define ARM_SMCCC_SMC_32 0 +#define ARM_SMCCC_SMC_64 1 +#define ARM_SMCCC_CALL_CONV_SHIFT 30 + +#define ARM_SMCCC_OWNER_MASK 0x3F +#define ARM_SMCCC_OWNER_SHIFT 24 + +#define ARM_SMCCC_FUNC_MASK 0xFFFF + +#define ARM_SMCCC_IS_FAST_CALL (smc_val) \ + ((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT)) +#define ARM_SMCCC_IS_64 (smc_val) \ + ((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT)) +#define ARM_SMCCC_FUNC_NUM (smc_val) ((smc_val) & ARM_SMCCC_FUNC_MASK) +#define ARM_SMCCC_OWNER_NUM (smc_val) \ + (((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK) + +#define ARM_SMCCC_CALL_VAL (type, calling_convention, owner, func_num) \ + (((type) << ARM_SMCCC_TYPE_SHIFT) | \ + ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \ + (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \ + ((func_num) & ARM_SMCCC_FUNC_MASK)) + +#define ARM_SMCCC_OWNER_ARCH 0 +#define ARM_SMCCC_OWNER_CPU 1 +#define ARM_SMCCC_OWNER_SIP 2 +#define ARM_SMCCC_OWNER_OEM 3 +#define ARM_SMCCC_OWNER_STANDARD 4 +#define ARM_SMCCC_OWNER_STANDARD_HYP 5 +#define ARM_SMCCC_OWNER_VENDOR_HYP 6 +#define ARM_SMCCC_OWNER_TRUSTED_APP 48 +#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49 +#define ARM_SMCCC_OWNER_TRUSTED_OS 50 +#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63 + +#define ARM_SMCCC_FUNC_QUERY_CALL_UID 0xff01 + +#define ARM_SMCCC_QUIRK_NONE 0 +#define ARM_SMCCC_QUIRK_QCOM_A6 1/* Save/restore register a6 */ + +#define ARM_SMCCC_VERSION_1_0 0x10000 +#define ARM_SMCCC_VERSION_1_1 0x10001 +#define ARM_SMCCC_VERSION_1_2 0x10002 +#define ARM_SMCCC_VERSION_1_3 0x10003 + +#define ARM_SMCCC_1_3_SVE_HINT 0x10000 + +#define ARM_SMCCC_VERSION_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + 0, 0) + +#define ARM_SMCCC_ARCH_FEATURES_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + 0, 1) + +#define ARM_SMCCC_ARCH_SOC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + 0, 2) + +#define ARM_SMCCC_ARCH_WORKAROUND_1 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + 0, 0x8000) + +#define ARM_SMCCC_ARCH_WORKAROUND_2 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + 0, 0x7fff) + +#define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_FUNC_QUERY_CALL_UID) + +/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */ +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 0xb66fb428U +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 0xe911c52eU +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 0x564bcaa9U +#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3 0x743a004dU + +/* KVM "vendor specific" services */ +#define ARM_SMCCC_KVM_FUNC_FEATURES 0 +#define ARM_SMCCC_KVM_FUNC_PTP 1 +#define ARM_SMCCC_KVM_FUNC_FEATURES_2 127 +#define ARM_SMCCC_KVM_NUM_FUNCS 128 + +#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_FEATURES) + +#define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED 1 + +/* +* ptp_kvm is a feature used for time sync between vm and host. +* ptp_kvm module in guest kernel will get service from host using +* this hypercall ID. +*/ +#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_PTP) + +/* ptp_kvm counter type ID */ +#define KVM_PTP_VIRT_COUNTER 0 +#define KVM_PTP_PHYS_COUNTER 1 + +/* Paravirtualised time calls (defined by ARM DEN0057A) */ +#define ARM_SMCCC_HV_PV_TIME_FEATURES \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD_HYP, \ + 0x20) + +#define ARM_SMCCC_HV_PV_TIME_ST \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD_HYP, \ + 0x21) + +/* TRNG entropy source calls (defined by ARM DEN0098) */ +#define ARM_SMCCC_TRNG_VERSION \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_STANDARD, \ + 0x50) + +#define ARM_SMCCC_TRNG_FEATURES \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_STANDARD, \ + 0x51) + +#define ARM_SMCCC_TRNG_GET_UUID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_STANDARD, \ + 0x52) + +#define ARM_SMCCC_TRNG_RND32 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_STANDARD, \ + 0x53) + +#define ARM_SMCCC_TRNG_RND64 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD, \ + 0x53) + +/* +* Return codes defined in ARM DEN 0070A +* ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C +*/ +#define SMCCC_RET_SUCCESS 0 +#define SMCCC_RET_NOT_SUPPORTED -1 +#define SMCCC_RET_NOT_REQUIRED -2 +#define SMCCC_RET_INVALID_PARAMETER -3 + +#endif /*__LINUX_ARM_SMCCC_H*/
Signature · 6 lines
2.34.0.rc1.387.gb447b232ab-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
RFC PATCH v2 10/11 selftests: KVM: aarch64: Introduce hypercall ABI test
In reply to Raghavendra Rao AnantaIntroduce a KVM selftest to check the hypercall interface for arm64 platforms. The test validates the user-space's IOCTL interface to read/write the psuedo-firmware registers as well as its effects on the guest upon certain configurations.
--- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../selftests/kvm/aarch64/hypercalls.c | 367 ++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
tools/testing/selftests/kvm/.gitignore +1 -0
@@ -2,6 +2,7 @@ /aarch64/arch_timer /aarch64/debug-exceptions /aarch64/get-reg-list +/aarch64/hypercalls /aarch64/psci_test /aarch64/vgic_init /s390x/memop
tools/testing/selftests/kvm/Makefile +1 -0
@@ -91,6 +91,7 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test TEST_GEN_PROGS_aarch64 += aarch64/arch_timer TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list +TEST_GEN_PROGS_aarch64 += aarch64/hypercalls TEST_GEN_PROGS_aarch64 += aarch64/psci_test TEST_GEN_PROGS_aarch64 += aarch64/vgic_init TEST_GEN_PROGS_aarch64 += demand_paging_test
tools/testing/selftests/kvm/aarch64/hypercalls.c new file +367 -0
367 lines not on this page · raw message
Signature · 6 lines
2.34.0.rc1.387.gb447b232ab-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
RFC PATCH v2 11/11 selftests: KVM: aarch64: Add the bitmap firmware registers to get-reg-list
In reply to Raghavendra Rao AnantaThe bitmap firmware psuedo-registers needs special handling, such as enabling the capability KVM_CAP_ARM_HVC_FW_REG_BMAP. Since there's no support as of yet in get-reg-list to enable a capability, add a field 'enable_capability' in the 'struct reg_sublist' to incorporate this. Also, to not mess with the existing configurations, create a new vcpu_config to hold these bitmap firmware registers.
--- .../selftests/kvm/aarch64/get-reg-list.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+)
tools/testing/selftests/kvm/aarch64/get-reg-list.c +35 -0
@@ -40,6 +40,7 @@ static __u64 *blessed_reg, blessed_n;struct reg_sublist {const char *name;long capability; +long enable_capability;int feature;bool finalize;__u64 *regs;
@@ -397,6 +398,19 @@ static void check_supported(struct vcpu_config *c) } } +static void enable_capabilities (struct kvm_vm *vm,struct vcpu_config *c) +{ +struct reg_sublist *s; + +for_each_sublist (c, s) { +if (s->enable_capability) { +struct kvm_enable_cap cap = {.cap = s->enable_capability}; + +vm_enable_cap (vm, &cap); + } + } +} +static bool print_list;static bool print_filtered;static bool fixup_core_regs;
@@ -414,6 +428,7 @@ static void run_test(struct vcpu_config *c) vm =vm_create (VM_MODE_DEFAULT ,DEFAULT_GUEST_PHY_PAGES ,O_RDWR );prepare_vcpu_init (c, &init);aarch64_vcpu_add_default (vm,0 , &init,NULL ); +enable_capabilities (vm, c);finalize_vcpu (vm,0 , c); reg_list =vcpu_get_reg_list (vm,0 );
@@ -1014,6 +1029,12 @@ static __u64 sve_rejects_set[] = { KVM_REG_ARM64_SVE_VLS, }; +static __u64 fw_reg_bmap_set[] = { +KVM_REG_ARM_FW_REG (3 ),/* KVM_REG_ARM_STD_BMAP */ +KVM_REG_ARM_FW_REG (4 ),/* KVM_REG_ARM_STD_HYP_BMAP */ +KVM_REG_ARM_FW_REG (5 ),/* KVM_REG_ARM_VENDOR_HYP_BMAP */ +}; +#define BASE_SUBLIST \ { "base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), }#define VREGS_SUBLIST \
@@ -1025,6 +1046,10 @@ static __u64 sve_rejects_set[] = { { "sve", .capability = KVM_CAP_ARM_SVE, .feature = KVM_ARM_VCPU_SVE, .finalize = true, \ .regs = sve_regs, .regs_n = ARRAY_SIZE(sve_regs), \ .rejects_set = sve_rejects_set, .rejects_set_n = ARRAY_SIZE(sve_rejects_set), } +#define FW_REG_BMAP_SUBLIST \ + { "fw_reg_bmap", .regs = fw_reg_bmap_set, .regs_n = ARRAY_SIZE(fw_reg_bmap_set), \ + .capability = KVM_CAP_ARM_HVC_FW_REG_BMAP, \ + .enable_capability = KVM_CAP_ARM_HVC_FW_REG_BMAP, }static struct vcpu_config vregs_config = { .sublists = {
@@ -1057,10 +1082,20 @@ static struct vcpu_config sve_pmu_config = { }, }; +static struct vcpu_config vregs_fw_regs_bmap_config = { + .sublists = { +BASE_SUBLIST , +VREGS_SUBLIST , +FW_REG_BMAP_SUBLIST , + {0 }, + }, +}; +static struct vcpu_config *vcpu_configs[] = { &vregs_config, &vregs_pmu_config, &sve_config, &sve_pmu_config, + &vregs_fw_regs_bmap_config, };static int vcpu_configs_n =ARRAY_SIZE (vcpu_configs);
Signature · 6 lines
2.34.0.rc1.387.gb447b232ab-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm