KVM: PKS Virtualization support
v7 cover KVM: PKS Virtualization support
This patch series is based on top of v10 PKS core support kernel patchset:
https://lore.kernel.org/lkml/[email protected]/
---
Protection Keys for Supervisor Pages(PKS) is a feature that extends the Protection Keys architecture to support thread-specific permission restrictions on supervisor pages.
PKS works similar to an existing feature named PKU(protecting user pages). They both perform an additional check after normal paging permission checks are done. Access or Writes can be disabled via a MSR update without TLB flushes when permissions changes. If violating this addional check, #PF occurs and PFEC.PK bit will be set.
PKS introduces MSR IA32_PKRS to manage supervisor protection key rights. The MSR contains 16 pairs of ADi and WDi bits. Each pair advertises on a group of pages with the same key which is set in the leaf paging-structure entries(bits[62:59]). Currently, IA32_PKRS is not supported by XSAVES architecture.
This patchset aims to add the virtualization of PKS in KVM. It implemented PKS CPUID enumeration, vmentry/vmexit configuration, MSR exposure, nested supported etc. Currently, PKS is not yet supported for shadow paging.
Detailed information about PKS can be found in the latest Intel 64 and IA-32 Architectures Software Developer's Manual.
---
Changelogs:
v6->v7
- Add documentation to note that it's nice-to-have cache tracking for PKRS, and we also needn't hesitate to rip it out in the future if there's a strong reason to drop the caching. (Sean)
- Blindly reading PKRU/PKRS is wrong, fixed. (Sean)
- Add a non-inline helper kvm_mmu_pkr_bits() to read PKR bits. (Sean)
- Delete the comment for exposing the PKS because the pattern is common and the behavior is self-explanatory. (Sean)
- Add a helper vmx_set_host_pkrs() for setting host pkrs and rewrite the related code for concise. (Sean)
- Align an indentation in arch/x86/kvm/vmx/nested.c. (Sean)
- Read the current PKRS if from_vmentry == false under the nested condition. (Sean)
- v6: https://lore.kernel.org/lkml/[email protected]/
v5->v6
- PKRS is preserved on INIT. Add the PKRS reset operation in kvm_vcpu_reset. (Sean)
- Track the pkrs as u32. Add the code WARN on bits 64:32 being set in VMCS field. (Sean)
- Adjust the MSR intercept and entry/exit control in VMCS according to guest CPUID. This resolve the issue when userspace re-enable this feature. (Sean)
- Split VMX restriction on PKS support(entry/exit load controls) out of common x86. And put tdp restriction together with PKU in common x86. (Sean)
- Thanks for Sean to revise the comments in mmu.c related to update_pkr_bitmap, which make it more clear for pkr bitmask cache usage.
- v5: https://lore.kernel.org/lkml/[email protected]/
v4->v5
- Make setting of MSR intercept/vmcs control bits not dependent on guest.CR4.PKS. And set them if PKS is exposed to guest. (Suggested by Sean)
- Add pkrs to standard register caching mechanism to help update vcpu->arch.pkrs on demand. Add related helper functions. (Suggested by Sean)
- Do the real pkrs update in VMCS field in vmx_vcpu_reset and vmx_sync_vmcs_host_state(). (Sean)
- Add a new mmu_role cr4_pks instead of smushing PKU and PKS together. (Sean & Paolo)
- v4: https://lore.kernel.org/lkml/[email protected]/
v3->v4
- Make the MSR intercept and load-controls setting depend on CR4.PKS value
- shadow the guest pkrs and make it usable in PKS emultion
- add the cr4_pke and cr4_pks check in pkr_mask update
- squash PATCH 2 and PATCH 5 to make the dependencies read more clear
- v3: https://lore.kernel.org/lkml/[email protected]/
v2->v3:
- No function changes since last submit
- rebase on the latest PKS kernel support:
https://lore.kernel.org/lkml/[email protected]/
- add MSR_IA32_PKRS to the vmx_possible_passthrough_msrs[]
- RFC v2: https://lore.kernel.org/lkml/[email protected]/
v1->v2:
- rebase on the latest PKS kernel support:
https://github.com/weiny2/linux-kernel/tree/pks-rfc-v3
- add a kvm-unit-tests for PKS
- add the check in kvm_init_msr_list for PKRS
- place the X86_CR4_PKS in mmu_role_bits in kvm_set_cr4
- add the support to expose VM_{ENTRY, EXIT}_LOAD_IA32_PKRS in nested VMX MSR
- RFC v1: https://lore.kernel.org/lkml/[email protected]/
---
Chenyi Qiang (7):
KVM: VMX: Introduce PKS VMCS fields
KVM: VMX: Add proper cache tracking for PKRS
KVM: X86: Expose IA32_PKRS MSR
KVM: MMU: Rename the pkru to pkr
KVM: MMU: Add support for PKS emulation
KVM: VMX: Expose PKS to guest
KVM: VMX: Enable PKS for nested VM
Lei Wang (1):
KVM: MMU: Add helper function to get pkr bits
arch/x86/include/asm/kvm_host.h | 17 +++-- arch/x86/include/asm/vmx.h | 6 ++ arch/x86/kvm/cpuid.c | 13 +++- arch/x86/kvm/kvm_cache_regs.h | 7 ++ arch/x86/kvm/mmu.h | 29 +++---- arch/x86/kvm/mmu/mmu.c | 130 +++++++++++++++++++++++--------- arch/x86/kvm/vmx/capabilities.h | 6 ++ arch/x86/kvm/vmx/nested.c | 36 ++++++++- arch/x86/kvm/vmx/vmcs.h | 1 + arch/x86/kvm/vmx/vmcs12.c | 2 + arch/x86/kvm/vmx/vmcs12.h | 4 + arch/x86/kvm/vmx/vmx.c | 85 +++++++++++++++++++-- arch/x86/kvm/vmx/vmx.h | 14 +++- arch/x86/kvm/x86.c | 9 ++- arch/x86/kvm/x86.h | 8 ++ arch/x86/mm/pkeys.c | 6 ++ include/linux/pks.h | 7 ++ 17 files changed, 301 insertions(+), 79 deletions(-)
Signature · 1 line
2.25.1
PATCH v7 1/8 KVM: VMX: Introduce PKS VMCS fields
From: Chenyi Qiang <[email protected]>
PKS(Protection Keys for Supervisor Pages) is a feature that extends the Protection Key architecture to support thread-specific permission restrictions on supervisor pages.
A new PKS MSR(PKRS) is defined in kernel to support PKS, which holds a set of permissions associated with each protection domain.
Two VMCS fields {HOST,GUEST}_IA32_PKRS are introduced in {host,guest}-state area to store the respective values of PKRS.
Every VM exit saves PKRS into guest-state area.
If VM_EXIT_LOAD_IA32_PKRS = 1, VM exit loads PKRS from the host-state area.
If VM_ENTRY_LOAD_IA32_PKRS = 1, VM entry loads PKRS from the guest-state area.
--- arch/x86/include/asm/vmx.h | 6 ++++++ 1 file changed, 6 insertions(+)
arch/x86/include/asm/vmx.h +6 -0
@@ -95,6 +95,7 @@#define VM_EXIT_CLEAR_BNDCFGS 0x00800000#define VM_EXIT_PT_CONCEAL_PIP 0x01000000#define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 +#define VM_EXIT_LOAD_IA32_PKRS 0x20000000#define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff
@@ -108,6 +109,7 @@#define VM_ENTRY_LOAD_BNDCFGS 0x00010000#define VM_ENTRY_PT_CONCEAL_PIP 0x00020000#define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 +#define VM_ENTRY_LOAD_IA32_PKRS 0x00400000#define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff
@@ -245,12 +247,16 @@ enum vmcs_field {GUEST_BNDCFGS_HIGH =0x00002813 ,GUEST_IA32_RTIT_CTL =0x00002814 ,GUEST_IA32_RTIT_CTL_HIGH =0x00002815 , +GUEST_IA32_PKRS =0x00002818 , +GUEST_IA32_PKRS_HIGH =0x00002819 ,HOST_IA32_PAT =0x00002c00 ,HOST_IA32_PAT_HIGH =0x00002c01 ,HOST_IA32_EFER =0x00002c02 ,HOST_IA32_EFER_HIGH =0x00002c03 ,HOST_IA32_PERF_GLOBAL_CTRL =0x00002c04 ,HOST_IA32_PERF_GLOBAL_CTRL_HIGH =0x00002c05 , +HOST_IA32_PKRS =0x00002c06 , +HOST_IA32_PKRS_HIGH =0x00002c07 ,PIN_BASED_VM_EXEC_CONTROL =0x00004000 ,CPU_BASED_VM_EXEC_CONTROL =0x00004002 , EXCEPTION_BITMAP = 0x00004004,
Signature · 1 line
2.25.1
Re: [PATCH v7 1/8] KVM: VMX: Introduce PKS VMCS fields
Quoted 6 lines · Lei Wang
From: Chenyi Qiang <[email protected]>
PKS(Protection Keys for Supervisor Pages) is a feature that extends the Protection Key architecture to support thread-specific permission restrictions on supervisor pages.
Quoted 7 lines
A new PKS MSR(PKRS) is defined in kernel to support PKS, which holds a set of permissions associated with each protection domain.
Two VMCS fields {HOST,GUEST}_IA32_PKRS are introduced in {host,guest}-state area to store the respective values of PKRS.
Every VM exit saves PKRS into guest-state area.
Uber nit, PKRS isn't saved if VMX doesn't support the entry control.
Every VM exit saves PKRS into guest-state area if VM_ENTRY_LOAD_IA32_PKRS is supported by the CPU.
With that tweak,
Re: [PATCH v7 1/8] KVM: VMX: Introduce PKS VMCS fields
Quoted 5 lines · Sean Christopherson
Uber nit, PKRS isn't saved if VMX doesn't support the entry control.
Every VM exit saves PKRS into guest-state area ifVM_ENTRY_LOAD_IA32_PKRSis supported by the CPU.With that tweak,
Make sense, will fix it.
PATCH v7 2/8 KVM: VMX: Add proper cache tracking for PKRS
In reply to Lei WangFrom: Chenyi Qiang <[email protected]>
Add PKRS caching into the standard register caching mechanism in order to take advantage of the availability checks provided by regs_avail.
This is because vcpu->arch.pkrs will be rarely acceesed by KVM, only in the case of host userspace MSR reads and GVA->GPA translation in following patches. It is unnecessary to keep it up-to-date at all times.
It also should be noted that the potential benefits of this caching are tenuous because the MSR read is not a hot path. it's nice-to-have so that we don't hesitate to rip it out in the future if there's a strong reason to drop the caching.
--- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/kvm_cache_regs.h | 7 +++++++ arch/x86/kvm/vmx/vmx.c | 11 +++++++++++ arch/x86/kvm/vmx/vmx.h | 3 ++- 4 files changed, 22 insertions(+), 1 deletion(-)
arch/x86/include/asm/kvm_host.h +2 -0
@@ -180,6 +180,7 @@ enum kvm_reg {VCPU_EXREG_SEGMENTS ,VCPU_EXREG_EXIT_INFO_1 ,VCPU_EXREG_EXIT_INFO_2 , + VCPU_EXREG_PKRS, }; enum {
@@ -638,6 +639,7 @@ struct kvm_vcpu_arch {unsigned long cr8;u32 host_pkru;u32 pkru; +u32 pkrs;u32 hflags;u64 efer;u64 apic_base;
arch/x86/kvm/kvm_cache_regs.h +7 -0
@@ -177,6 +177,13 @@ static inline u64 kvm_read_edx_eax(struct kvm_vcpu *vcpu) | ((u64)(kvm_rdx_read(vcpu) & -1u) << 32); } +static inline u32 kvm_read_pkrs (struct kvm_vcpu *vcpu) +{ +if (!kvm_register_is_available (vcpu,VCPU_EXREG_PKRS )) +static_call (kvm_x86_cache_reg)(vcpu,VCPU_EXREG_PKRS ); +return vcpu->arch.pkrs; +} +static inline void enter_guest_mode (struct kvm_vcpu *vcpu) { vcpu->arch.hflags |=HF_GUEST_MASK ;
arch/x86/kvm/vmx/vmx.c +11 -0
@@ -2258,6 +2258,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)static void vmx_cache_reg (struct kvm_vcpu *vcpu,enum kvm_reg reg) {unsigned long guest_owned_bits; +u64 ia32_pkrs;kvm_register_mark_available (vcpu, reg);
@@ -2292,6 +2293,16 @@ static void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) vcpu->arch.cr4 &= ~guest_owned_bits; vcpu->arch.cr4 |=vmcs_readl (GUEST_CR4 ) & guest_owned_bits;break ; +case VCPU_EXREG_PKRS : +/* +* The high 32 bits of PKRS are reserved and attempting to write +* non-zero value will cause #GP. KVM intentionally drops those +* bits. +*/ + ia32_pkrs =vmcs_read64 (GUEST_IA32_PKRS ); +WARN_ON_ONCE (ia32_pkrs >>32 ); + vcpu->arch.pkrs = ia32_pkrs; +break ;default :KVM_BUG_ON (1 , vcpu->kvm);break ;
arch/x86/kvm/vmx/vmx.h +2 -1
@@ -499,7 +499,8 @@ BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL) (1 << VCPU_EXREG_CR3) | \ (1 << VCPU_EXREG_CR4) | \ (1 << VCPU_EXREG_EXIT_INFO_1) | \ - (1 << VCPU_EXREG_EXIT_INFO_2)) + (1 << VCPU_EXREG_EXIT_INFO_2) | \ + (1 << VCPU_EXREG_PKRS))static inline struct kvm_vmx *to_kvm_vmx (struct kvm *kvm) {
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v7 2/8] KVM: VMX: Add proper cache tracking for PKRS
Quoted 5 lines · Lei Wang
From: Chenyi Qiang <[email protected]>
Add PKRS caching into the standard register caching mechanism in order to take advantage of the availability checks provided by
regs_avail.
Quoted 8 lines
This is because
vcpu->arch.pkrswill be rarely acceesed by KVM, only in the case of host userspace MSR reads andGVA->GPAtranslation in following patches. It is unnecessary to keep it up-to-date at all times.It also should be noted that the potential benefits of this caching are tenuous because the MSR read is not a hot path. it's nice-to-have so that we don't hesitate to rip it out in the future if there's a strong reason to drop the caching.
The patch looks fine, but this needs to be moved to the end of the series. Definitely after "KVM: VMX: Expose PKS to guest", and maybe even after "KVM: VMX:
Enable PKS for nested VM".
If there's a bug with the caching logic, I want to be able to bisect to that. By implementing caching before any of the other PKS support, a bug in either the caching or the virtualization will bisect to the PKS virtualization
Re: [PATCH v7 2/8] KVM: VMX: Add proper cache tracking for PKRS
Quoted 3 lines · Sean Christopherson
The patch looks fine, but this needs to be moved to the end of the series. Definitely after "KVM: VMX: Expose PKS to guest", and maybe even after "KVM: VMX:
Enable PKS for nested VM".
OK, will do that.
PATCH v7 3/8 KVM: X86: Expose IA32_PKRS MSR
In reply to Lei WangFrom: Chenyi Qiang <[email protected]>
Protection Key for Superviosr Pages (PKS) uses IA32_PKRS MSR (PKRS) at index 0x6E1 to allow software to manage superviosr key rights, i.e. it can enforce additional permissions checks besides normal paging protections via a MSR update without TLB flushes when permissions change.
For performance consideration, PKRS intercept in KVM will be disabled when PKS is supported in guest so that PKRS can be accessed without VM exit.
PKS introduces dedicated control fields in VMCS to switch PKRS, which only does the retore part. In addition, every VM exit saves PKRS into the guest-state area in VMCS, while VM enter won't save the host value due to the expectation that the host won't change the MSR often. Update the host's value in VMCS manually if the MSR has been changed by the kernel since the last time the VMCS was run.
Introduce a function get_current_pkrs() in arch/x86/mm/pkeys.c to export the per-cpu variable pkrs_cache to avoid frequent rdmsr of PKRS.
--- arch/x86/kvm/vmx/vmcs.h | 1 + arch/x86/kvm/vmx/vmx.c | 63 +++++++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/vmx.h | 9 +++++- arch/x86/kvm/x86.c | 9 +++++- arch/x86/kvm/x86.h | 6 ++++ arch/x86/mm/pkeys.c | 6 ++++ include/linux/pks.h | 7 +++++ 7 files changed, 94 insertions(+), 7 deletions(-)
arch/x86/kvm/vmx/vmcs.h +1 -0
@@ -42,6 +42,7 @@ struct vmcs_host_state {#ifdef CONFIG_X86_64 u16 ds_sel, es_sel;#endif +u32 pkrs; };struct vmcs_controls_shadow {
arch/x86/kvm/vmx/vmx.c +58 -5
141 lines not on this page · raw message
arch/x86/kvm/vmx/vmx.h +8 -1
@@ -352,7 +352,7 @@ struct vcpu_vmx {struct lbr_desc lbr_desc;/* Save desired MSR intercept (read: pass-through) state */ -#define MAX_POSSIBLE_PASSTHROUGH_MSRS 15 +#define MAX_POSSIBLE_PASSTHROUGH_MSRS 16struct { DECLARE_BITMAP(read, MAX_POSSIBLE_PASSTHROUGH_MSRS); DECLARE_BITMAP(write, MAX_POSSIBLE_PASSTHROUGH_MSRS);
@@ -580,4 +580,11 @@ static inline int vmx_get_instr_info_reg2(u32 vmx_instr_info) return (vmx_instr_info >> 28) & 0xf; } +static inline void vmx_set_host_pkrs (struct vmcs_host_state *host,u32 pkrs){ +if (unlikely (pkrs != host->pkrs)) { +vmcs_write64 (HOST_IA32_PKRS , pkrs); + host->pkrs = pkrs; + } +} +#endif /* __KVM_X86_VMX_H */
arch/x86/kvm/x86.c +8 -1
@@ -1396,7 +1396,7 @@ static const u32 msrs_to_save_all[] = {MSR_IA32_RTIT_ADDR1_A ,MSR_IA32_RTIT_ADDR1_B ,MSR_IA32_RTIT_ADDR2_A ,MSR_IA32_RTIT_ADDR2_B ,MSR_IA32_RTIT_ADDR3_A ,MSR_IA32_RTIT_ADDR3_B , -MSR_IA32_UMWAIT_CONTROL , +MSR_IA32_UMWAIT_CONTROL ,MSR_IA32_PKRS ,MSR_ARCH_PERFMON_FIXED_CTR0 ,MSR_ARCH_PERFMON_FIXED_CTR1 , MSR_ARCH_PERFMON_FIXED_CTR0 + 2,
@@ -6638,6 +6638,10 @@ static void kvm_init_msr_list(void) intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2)continue ;break ; +case MSR_IA32_PKRS : +if (!kvm_cpu_cap_has (X86_FEATURE_PKS )) +continue ; +break ; case MSR_ARCH_PERFMON_PERFCTR0 ... MSR_ARCH_PERFMON_PERFCTR0 + 17:if (msrs_to_save_all[i] -MSR_ARCH_PERFMON_PERFCTR0 >=min (INTEL_PMC_MAX_GENERIC , x86_pmu.num_counters_gp))
@@ -11410,6 +11414,9 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)kvm_set_rflags (vcpu,X86_EFLAGS_FIXED );kvm_rip_write (vcpu,0xfff0 ); +if (!init_event &&kvm_cpu_cap_has (X86_FEATURE_PKS )) +__kvm_set_msr (vcpu,MSR_IA32_PKRS ,0 , true); + vcpu->arch.cr3 =0 ;kvm_register_mark_dirty (vcpu,VCPU_EXREG_CR3 );
arch/x86/kvm/x86.h +6 -0
@@ -404,6 +404,12 @@ static inline void kvm_machine_check(void)#endif } +static inline bool kvm_pkrs_valid (u64 data) +{ +/* bit[63,32] must be zero */ +return !(data >>32 ); +} +void kvm_load_guest_xsave_state (struct kvm_vcpu *vcpu);void kvm_load_host_xsave_state (struct kvm_vcpu *vcpu);int kvm_spec_ctrl_test_value (u64 value);
arch/x86/mm/pkeys.c +6 -0
@@ -495,4 +495,10 @@ void pks_update_exception(struct pt_regs *regs, u8 pkey, u8 protection) }EXPORT_SYMBOL_GPL (pks_update_exception); +u32 get_current_pkrs (void ) +{ +return this_cpu_read (pkrs_cache); +} +EXPORT_SYMBOL_GPL (get_current_pkrs); +#endif /* CONFIG_ARCH_ENABLE_SUPERVISOR_PKEYS */
include/linux/pks.h +7 -0
@@ -53,6 +53,8 @@ static inline void pks_set_readwrite(u8 pkey)typedef bool (*pks_key_callback )(struct pt_regs *regs,unsigned long address,bool write); +u32 get_current_pkrs (void ); +#else /* !CONFIG_ARCH_ENABLE_SUPERVISOR_PKEYS */ static inline bool pks_available (void )
@@ -68,6 +70,11 @@ static inline void pks_update_exception(struct pt_regs *regs, u8 protection) { } +static inline u32 get_current_pkrs (void ) +{ +return 0 ; +} +#endif /* CONFIG_ARCH_ENABLE_SUPERVISOR_PKEYS */ #ifdef CONFIG_PKS_TEST
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v7 3/8] KVM: X86: Expose IA32_PKRS MSR
Nit, something like:
KVM: X86: Virtualize and pass-through IA32_PKRS MSR when supported
because "expose" doesn't precisely cover the pass-through behavior
Quoted 9 lines · Lei Wang
(quoted diff) +1
-0@@ -1111,6 +1113,7 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) #endif unsigned long fs_base, gs_base; u16 fs_sel, gs_sel; + u32 host_pkrs; int i; vmx->req_immediate_exit = false;
Review void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) · line 1157
Quoted 9 lines
(quoted diff) +6
-0@@ -1146,6 +1149,17 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) */ host_state->ldt_sel = kvm_read_ldt(); + /* + * Update the host pkrs vmcs field before vcpu runs. + * The setting of VM_EXIT_LOAD_IA32_PKRS can ensure + * kvm_cpu_cap_has(X86_FEATURE_PKS) && + * guest_cpuid_has(vcpu, X86_FEATURE_PKS) + */
Eh, I don't think this comment adds anything. And practically speaking, whether or not X86_FEATURE_PKS is reported by kvm_cpu_cap_has() and/or guest_cpuid_has() is irrelevant. If KVM is loading PKRS on exit, then the VMCS needs to hold an up-to-date value. E.g. if for some reason KVM enables the control even if PKS isn't exposed to the guest (see below), this code still needs to refresh the value.
Review void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) · line 1159
Quoted 2 lines
(quoted diff) +2
-0+ if (vm_exit_controls_get(vmx) & VM_EXIT_LOAD_IA32_PKRS) { + host_pkrs = get_current_pkrs();
No need for an intermediate host_pkrs. I.e. this can simply be:
if (vm_exit_controls_get(vmx) & VM_EXIT_LOAD_IA32_PKRS) vmx_set_host_pkrs(host_state, get_current_pkrs());
Quoted 7 lines
(quoted diff) +3
-0+ vmx_set_host_pkrs(host_state, host_pkrs); + } + #ifdef CONFIG_X86_64 savesegment(ds, host_state->ds_sel); savesegment(es, host_state->es_sel);
Review static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) · line 1921
Quoted 7 lines
(quoted diff) +4
-0@@ -1901,6 +1915,13 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_IA32_DEBUGCTLMSR: msr_info->data = vmcs_read64(GUEST_IA32_DEBUGCTL); break; + case MSR_IA32_PKRS: + if (!kvm_cpu_cap_has(X86_FEATURE_PKS) || + (!msr_info->host_initiated && + !guest_cpuid_has(vcpu, X86_FEATURE_PKS)))
Nit, please align the lines that are inside a pair of parantheses, i.e.
if (!kvm_cpu_cap_has(X86_FEATURE_PKS) || (!msr_info->host_initiated && !guest_cpuid_has(vcpu, X86_FEATURE_PKS))) return 1;
Review static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) · line 1923
Quoted 2 lines
(quoted diff) +2
-0+ return 1; + msr_info->data = kvm_read_pkrs(vcpu);
A comment on the caching patch, please use kvm_pkrs_read() to follow the GPR, RIP, PDPTR, etc... terminology (CR0/3/4 got grandfathered in).
Review static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) · line 2268
Quoted 12 lines
(quoted diff) +4
-1+ break; default: find_uret_msr: msr = vmx_find_uret_msr(vmx, msr_info->index);@@ -2242,7 +2263,17 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) } ret = kvm_set_msr_common(vcpu, msr_info); break; - + case MSR_IA32_PKRS: + if (!kvm_pkrs_valid(data)) + return 1;
Nit, please move this to after the capability checks. Does not affect functionality at all, but logically it doesn't make sense to check for a valid value of a register that doesn't exist.
Review static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) · line 2275
Quoted 7 lines
(quoted diff) +7
-0+ if (!kvm_cpu_cap_has(X86_FEATURE_PKS) || + (!msr_info->host_initiated && + !guest_cpuid_has(vcpu, X86_FEATURE_PKS))) + return 1; + vcpu->arch.pkrs = data; + kvm_register_mark_available(vcpu, VCPU_EXREG_PKRS); + vmcs_write64(GUEST_IA32_PKRS, data);
The caching patch should add kvm_write_pkrs(). And in there, I think I'd vote for a WARN_ON_ONCE() that the incoming value doesn't set bits 63:32. It's redundant with kvm_pkrs_valid()
Quoted 4 lines
(quoted diff) +1
-0+ break; default: find_uret_msr: msr = vmx_find_uret_msr(vmx, msr_index);
...
Review static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 7454
Quoted 11 lines
(quoted diff) +7
-0@@ -7406,6 +7445,20 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) /* Refresh #PF interception to account for MAXPHYADDR changes. */ vmx_update_exception_bitmap(vcpu); + + if (kvm_cpu_cap_has(X86_FEATURE_PKS)) {a + if (guest_cpuid_has(vcpu, X86_FEATURE_PKS)) { + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_PKRS, MSR_TYPE_RW); + + vm_entry_controls_setbit(vmx, VM_ENTRY_LOAD_IA32_PKRS); + vm_exit_controls_setbit(vmx, VM_EXIT_LOAD_IA32_PKRS);
Ugh, toggling the entry/exit controls here won't do the correct thing if L2 is active. The MSR intercept logic works because it always operates on vmcs01's bitmap.
Let's keep this as simple as possible and set the controls if they're supported. KVM will waste a few cycles on entry/exit if PKS is supported in the host but not exposed to the guest, but that's not the end of the world. If we want to optimize that, then we can do that in the future and in a more generic way.
So this becomes:
if (kvm_cpu_cap_has(X86_FEATURE_PKS)) vmx_set_intercept_for_msr(vcpu, MSR_IA32_PKRS, MSR_TYPE_RW, !guest_cpuid_has(vcpu, X86_FEATURE_PKS));
And please hoist that up to be next to the handling of X86_FEATURE_XFD to try and bunch together code that does similar things.
The last edge case to deal with is if only one of the controls is supported, e.g. if an L0 hypervisor is being evil. Big surprise, BNDCFGS doesn't get this right and needs a bug fix patch, e.g. it could retain the guest's value after exit, or host's value after entry. It's a silly case because it basically requires broken host "hardware", but it'd be nice to get it right in KVM. And that'd also be a good opportunity to handle all of the pairs, i.e. clear the bits during setup_vmcs_config() instead of checking both the entry and exit flags in cpu_has_load_*().
So for this series, optimistically assume my idea will pan out and a adjust_vm_entry_exit_pair() helper will exit by the time this is fully baked.
arch/x86/kvm/vmx/vmx.c +16 -0
@@ -2434,6 +2434,16 @@ static bool cpu_has_sgx(void)return cpuid_eax (0 ) >=0x12 && (cpuid_eax (0x12 ) &BIT (0 )); } +static __init void adjust_vm_entry_exit_pair (u32 *entry_controls,u32 entry_bit, +u32 *exit_controls,u32 exit_bit) +{ +if ((*entry_controls & entry_bit) && (*exit_controls & exit_bit)) +return ; + + *entry_controls &= ~entry_bit; + *exit_controls &= ~exit_bit; +} +static __init int adjust_vmx_controls (u32 ctl_min,u32 ctl_opt,u32 msr,u32 *result) {
@@ -2614,6 +2624,9 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, &_vmentry_control) < 0)return -EIO ; +adjust_vm_entry_exit_pair (&_vmentry_control,VM_ENTRY_LOAD_IA32_PKRS , + &_vmexit_control,VM_EXIT_LOAD_IA32_PKRS ); +/* * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they * can't be used due to an errata where VM Exit may incorrectly clear
@@ -7536,6 +7549,9 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)vmx_set_intercept_for_msr (vcpu,MSR_IA32_XFD_ERR ,MSR_TYPE_R , !guest_cpuid_has (vcpu,X86_FEATURE_XFD )); +if (kvm_cpu_cap_has (X86_FEATURE_PKS )) +vmx_set_intercept_for_msr (vcpu,MSR_IA32_PKRS ,MSR_TYPE_RW , + !guest_cpuid_has (vcpu,X86_FEATURE_PKS ));set_cr4_guest_host_mask (vmx);
Quoted 9 lines
(quoted diff) +7
-0+ } else { + vmx_enable_intercept_for_msr(vcpu, MSR_IA32_PKRS, MSR_TYPE_RW); + + vm_entry_controls_clearbit(vmx, VM_ENTRY_LOAD_IA32_PKRS); + vm_exit_controls_clearbit(vmx, VM_EXIT_LOAD_IA32_PKRS); + } + } }
...
Review void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) · line 11418
Quoted 6 lines
(quoted diff) +2
-0@@ -11410,6 +11414,9 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) kvm_set_rflags(vcpu, X86_EFLAGS_FIXED); kvm_rip_write(vcpu, 0xfff0); + if (!init_event && kvm_cpu_cap_has(X86_FEATURE_PKS)) + __kvm_set_msr(vcpu, MSR_IA32_PKRS, 0, true);
Please put this with the other !init_event stuff, e.g. look for MSR_IA32_XSS.
Quoted 3 lines
(quoted diff) +0
-0vcpu->arch.cr3 = 0; kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3);
Re: [PATCH v7 3/8] KVM: X86: Expose IA32_PKRS MSR
Quoted 5 lines · Sean Christopherson
Nit, something like:
KVM: X86: Virtualize and pass-through
IA32_PKRSMSR when supportedbecause "expose" doesn't precisely cover the pass-through behavior
Will change it.
Quoted 5 lines
Eh, I don't think this comment adds anything. And practically speaking, whether or not
X86_FEATURE_PKSis reported bykvm_cpu_cap_has()and/orguest_cpuid_has()is irrelevant. If KVM is loading PKRS on exit, then the VMCS needs to hold an up-to-date value. E.g. if for some reason KVM enables the control even if PKS isn't exposed to the guest (see below), this code still needs to refresh the value.
Will remove the irrelevant statement, just left the "Update the host pkrs vmcs field before vcpu runs."
Quoted 4 lines
No need for an intermediate
host_pkrs. I.e. this can simply be:if (vm_exit_controls_get(vmx) & VM_EXIT_LOAD_IA32_PKRS) vmx_set_host_pkrs(host_state, get_current_pkrs());
OK, will write the code in one line.
Quoted 1 line
Nit, please align the lines that are inside a pair of parantheses, i.e.
My mistake, will align the lines.
Quoted 2 lines
A comment on the caching patch, please use
kvm_pkrs_read()to follow the GPR, RIP, PDPTR, etc... terminology (CR0/3/4 got grandfathered in).
Will rename the function.
Quoted 3 lines
Nit, please move this to after the capability checks. Does not affect functionality at all, but logically it doesn't make sense to check for a valid value of a register that doesn't exist.
Make sense, will move it.
Quoted 3 lines
The caching patch should add
kvm_write_pkrs(). And in there, I think I'd vote for aWARN_ON_ONCE()that the incoming value doesn't set bits 63:32. It's redundant withkvm_pkrs_valid()
Will add a new function at the caching patch.
Quoted 60 lines
Ugh, toggling the entry/exit controls here won't do the correct thing if L2 is active. The MSR intercept logic works because it always operates on vmcs01's bitmap.
Let's keep this as simple as possible and set the controls if they're supported. KVM will waste a few cycles on entry/exit if PKS is supported in the host but not exposed to the guest, but that's not the end of the world. If we want to optimize that, then we can do that in the future and in a more generic way.
So this becomes:
if (kvm_cpu_cap_has(X86_FEATURE_PKS)) vmx_set_intercept_for_msr(vcpu, MSR_IA32_PKRS, MSR_TYPE_RW, !guest_cpuid_has(vcpu, X86_FEATURE_PKS));And please hoist that up to be next to the handling of
X86_FEATURE_XFDto try and bunch together code that does similar things.The last edge case to deal with is if only one of the controls is supported, e.g. if an L0 hypervisor is being evil. Big surprise, BNDCFGS doesn't get this right and needs a bug fix patch, e.g. it could retain the guest's value after exit, or host's value after entry. It's a silly case because it basically requires broken host "hardware", but it'd be nice to get it right in KVM. And that'd also be a good opportunity to handle all of the pairs, i.e. clear the bits during
setup_vmcs_config()instead of checking both the entry and exit flags in cpu_has_load_*().So for this series, optimistically assume my idea will pan out and a
adjust_vm_entry_exit_pair()helper will exit by the time this is fully baked.arch/x86/kvm/vmx/vmx.c +13
-0@@ -2434,6 +2434,16 @@ static bool cpu_has_sgx(void)return cpuid_eax (0 ) >=0x12 && (cpuid_eax (0x12 ) &BIT (0 )); } +static __init void adjust_vm_entry_exit_pair (u32 *entry_controls,u32 entry_bit, +u32 *exit_controls,u32 exit_bit) +{ +if ((*entry_controls & entry_bit) && (*exit_controls & exit_bit)) +return ; + + *entry_controls &= ~entry_bit; + *exit_controls &= ~exit_bit; +} +static __init int adjust_vmx_controls (u32 ctl_min,u32 ctl_opt,u32 msr,u32 *result) {@@ -2614,6 +2624,9 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, &_vmentry_control) < 0)return -EIO ; +adjust_vm_entry_exit_pair (&_vmentry_control,VM_ENTRY_LOAD_IA32_PKRS , + &_vmexit_control,VM_EXIT_LOAD_IA32_PKRS ); +/* * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they * can't be used due to an errata where VM Exit may incorrectly clear
arch/x86/kvm/vmx/vmx.c static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 7556
Quoted 8 lines
arch/x86/kvm/vmx/vmx.c +3
-0@@ -7536,6 +7549,9 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)vmx_set_intercept_for_msr (vcpu,MSR_IA32_XFD_ERR ,MSR_TYPE_R , !guest_cpuid_has (vcpu,X86_FEATURE_XFD )); +if (kvm_cpu_cap_has (X86_FEATURE_PKS )) +vmx_set_intercept_for_msr (vcpu,MSR_IA32_PKRS ,MSR_TYPE_RW , + !guest_cpuid_has (vcpu,X86_FEATURE_PKS ));set_cr4_guest_host_mask (vmx);
Thanks for your suggestions, will try your helper.
Quoted 1 line
Please put this with the other !init_event stuff, e.g. look for
MSR_IA32_XSS.
Will do it.
PATCH v7 4/8 KVM: MMU: Rename the pkru to pkr
In reply to Lei WangFrom: Chenyi Qiang <[email protected]>
PKRU represents the PKU register utilized in the protection key rights check for user pages. Protection Keys for Superviosr Pages (PKS) extends the protection key architecture to cover supervisor pages.
Rename the *pkru* related variables and functions to *pkr* which stands for both of the PKRU and PKRS. It makes sense because PKS and PKU each have:
- a single control register (PKRU and PKRS)
- the same number of keys (16 in total)
- the same format in control registers (Access and Write disable bits)
PKS and PKU can also share the same bitmap pkr_mask cache conditions where protection key checks are needed, because they can share almost the same requirements for PK restrictions to cause a fault, except they focus on different pages (supervisor and user pages).
--- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/mmu.h | 12 ++++++------ arch/x86/kvm/mmu/mmu.c | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -1
@@ -459,7 +459,7 @@ struct kvm_mmu {* with PFEC.RSVD replaced by ACC_USER_MASK from the page tables. * Each domain has 2 bits which are ANDed with AD and WD from PKRU. */ -u32 pkru_mask; +u32 pkr_mask;u64 *pae_root;u64 *pml4_root;
arch/x86/kvm/mmu.h +6 -6
@@ -239,8 +239,8 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,u32 errcode =PFERR_PRESENT_MASK ;WARN_ON (pfec & (PFERR_PK_MASK |PFERR_RSVD_MASK )); -if (unlikely (mmu->pkru_mask)) { -u32 pkru_bits, offset; +if (unlikely (mmu->pkr_mask)) { +u32 pkr_bits, offset;/* * PKRU defines 32 bits, there are 16 domains and 2
@@ -248,15 +248,15 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,* index of the protection domain, so pte_pkey * 2 is * is the index of the first bit for the domain. */ - pkru_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; + pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ;/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ offset = (pfec & ~1 ) + ((pte_access &PT_USER_MASK ) << (PFERR_RSVD_BIT -PT_USER_SHIFT )); - pkru_bits &= mmu->pkru_mask >> offset; - errcode |= -pkru_bits &PFERR_PK_MASK ; - fault |= (pkru_bits !=0 ); + pkr_bits &= mmu->pkr_mask >> offset; + errcode |= -pkr_bits &PFERR_PK_MASK ; + fault |= (pkr_bits !=0 ); }return -(u32 )fault & errcode;
arch/x86/kvm/mmu/mmu.c +5 -5
@@ -4631,12 +4631,12 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept)* away both AD and WD. For all reads or if the last condition holds, WD * only will be masked away. */ -static void update_pkru_bitmask (struct kvm_mmu *mmu) +static void update_pkr_bitmask (struct kvm_mmu *mmu) {unsigned bit;bool wp; - mmu->pkru_mask =0 ; + mmu->pkr_mask =0 ;if (!is_cr4_pke (mmu))return ;
@@ -4671,7 +4671,7 @@ static void update_pkru_bitmask(struct kvm_mmu *mmu)/* PKRU.WD stops write access. */ pkey_bits |= (!!check_write) <<1 ; - mmu->pkru_mask |= (pkey_bits &3 ) << pfec; + mmu->pkr_mask |= (pkey_bits &3 ) << pfec; } }
@@ -4683,7 +4683,7 @@ static void reset_guest_paging_metadata(struct kvm_vcpu *vcpu,reset_rsvds_bits_mask (vcpu, mmu);update_permission_bitmask (mmu, false); -update_pkru_bitmask (mmu); +update_pkr_bitmask (mmu); }static void paging64_init_context (struct kvm_mmu *context)
@@ -4951,7 +4951,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, context->root_level = level; context->direct_map = false;update_permission_bitmask (context, true); - context->pkru_mask =0 ; + context->pkr_mask =0 ;reset_rsvds_bits_mask_ept (vcpu, context, execonly, huge_page_level);reset_ept_shadow_zero_bits_mask (context, execonly); }
Signature · 1 line
2.25.1
PATCH v7 5/8 KVM: MMU: Add helper function to get pkr bits
In reply to Lei WangExtra the PKR stuff to a separate, non-inline helper, which is a preparation to introduce pks support.
--- arch/x86/kvm/mmu.h | 20 +++++--------------- arch/x86/kvm/mmu/mmu.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-)
arch/x86/kvm/mmu.h +5 -15
@@ -204,6 +204,9 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,return vcpu->arch.mmu->page_fault (vcpu, &fault); } +u32 kvm_mmu_pkr_bits (struct kvm_vcpu *vcpu,struct kvm_mmu *mmu, +unsigned pte_access,unsigned pte_pkey,unsigned int pfec); +/* * Check if a given access (described through the I/D, W/R and U/S bits of a * page fault error code pfec) causes a permission fault with the given PTE
@@ -240,21 +243,8 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,WARN_ON (pfec & (PFERR_PK_MASK |PFERR_RSVD_MASK ));if (unlikely (mmu->pkr_mask)) { -u32 pkr_bits, offset; - -/* -* PKRU defines 32 bits, there are 16 domains and 2 -* attribute bits per domain in pkru. pte_pkey is the -* index of the protection domain, so pte_pkey * 2 is -* is the index of the first bit for the domain. -*/ - pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; - -/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ - offset = (pfec & ~1 ) + - ((pte_access &PT_USER_MASK ) << (PFERR_RSVD_BIT -PT_USER_SHIFT )); - - pkr_bits &= mmu->pkr_mask >> offset; +u32 pkr_bits = +kvm_mmu_pkr_bits (vcpu, mmu, pte_access, pte_pkey, pfec); errcode |= -pkr_bits &PFERR_PK_MASK ; fault |= (pkr_bits !=0 ); }
arch/x86/kvm/mmu/mmu.c +21 -0
@@ -6477,3 +6477,24 @@ void kvm_mmu_pre_destroy_vm(struct kvm *kvm)if (kvm->arch.nx_lpage_recovery_thread)kthread_stop (kvm->arch.nx_lpage_recovery_thread); } + +u32 kvm_mmu_pkr_bits (struct kvm_vcpu *vcpu,struct kvm_mmu *mmu, +unsigned pte_access,unsigned pte_pkey,unsigned int pfec) +{ +u32 pkr_bits, offset; + +/* +* PKRU defines 32 bits, there are 16 domains and 2 +* attribute bits per domain in pkru. pte_pkey is the +* index of the protection domain, so pte_pkey * 2 is +* is the index of the first bit for the domain. +*/ + pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; + +/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ + offset = (pfec & ~1 ) + ((pte_access &PT_USER_MASK ) + << (PFERR_RSVD_BIT -PT_USER_SHIFT )); + + pkr_bits &= mmu->pkr_mask >> offset; +return pkr_bits; +}
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v7 5/8] KVM: MMU: Add helper function to get pkr bits
Quoted 1 line · Lei Wang
Extra the PKR stuff to a separate, non-inline helper, which is a
s/Extra/Extract
Quoted 1 line
preparation to introduce pks support.
Please provide more justification. The change is justified, by random readers of this patch/commit will be clueless.
Extract getting the effective PKR bits to a helper that lives in mmu.c in order to keep the is_cr4_*() helpers contained to mmu.c. Support for PKRS (versus just PKRU) will require querying MMU state to see if the relevant CR4 bit is enabled because pkr_mask will be non-zero if _either_ bit is enabled).
PKR{U,S} are exposed to the guest if and only if TDP is enabled, and while permission_fault() is performance critical for ia32 shadow paging, it's a rarely used path with TDP is enabled. I.e. moving the PKR code out-of-line is not a performance concern.
Quoted 11 lines
Signed-off-by Lei Wang <[email protected]>--- arch/x86/kvm/mmu.h | 20 +++++--------------- arch/x86/kvm/mmu/mmu.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-)arch/x86/kvm/mmu.h +0
-0
arch/x86/kvm/mmu.h static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, · line 207
Quoted 4 lines
arch/x86/kvm/mmu.h +1
-0@@ -204,6 +204,9 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,return vcpu->arch.mmu->page_fault (vcpu, &fault); } +u32 kvm_mmu_pkr_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
kvm_mmu_get_pkr_bits() so that there's a verb in there.
Quoted 16 lines
arch/x86/kvm/mmu.h +2
-7+ unsigned pte_access, unsigned pte_pkey, unsigned int pfec); +/* * Check if a given access (described through the I/D, W/R and U/S bits of a * page fault error code pfec) causes a permission fault with the given PTE @@ -240,21 +243,8 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,WARN_ON (pfec & (PFERR_PK_MASK |PFERR_RSVD_MASK ));if (unlikely (mmu->pkr_mask)) { -u32 pkr_bits, offset; - -/* -* PKRU defines 32 bits, there are 16 domains and 2 -* attribute bits per domain in pkru. pte_pkey is the -* index of the protection domain, so pte_pkey * 2 is -* is the index of the first bit for the domain.
arch/x86/kvm/mmu.h static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, · line 247
Quoted 10 lines
arch/x86/kvm/mmu.h +2
-8-*/ - pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; - -/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ - offset = (pfec & ~1 ) + - ((pte_access &PT_USER_MASK ) << (PFERR_RSVD_BIT -PT_USER_SHIFT )); - - pkr_bits &= mmu->pkr_mask >> offset; +u32 pkr_bits = +kvm_mmu_pkr_bits (vcpu, mmu, pte_access, pte_pkey, pfec);
Nit, I prefer wrapping in the params, that way the first line shows the most important information, e.g. what variable is being set and how (by a function call). And then there won't be overflow with the longer helper name:
u32 pkr_bits = kvm_mmu_get_pkr_bits(vcpu, mmu, pte_access, pte_pkey, pfec);
Quoted 9 lines
arch/x86/kvm/mmu.h +0
-0errcode |= -pkr_bits &PFERR_PK_MASK ; fault |= (pkr_bits !=0 ); }arch/x86/kvm/mmu/mmu.c +0
-0@@ -6477,3 +6477,24 @@ void kvm_mmu_pre_destroy_vm(struct kvm *kvm)if (kvm->arch.nx_lpage_recovery_thread)
arch/x86/kvm/mmu/mmu.c void kvm_mmu_pre_destroy_vm(struct kvm *kvm) · line 6487
Quoted 10 lines
arch/x86/kvm/mmu/mmu.c +8
-0kthread_stop (kvm->arch.nx_lpage_recovery_thread); } + +u32 kvm_mmu_pkr_bits (struct kvm_vcpu *vcpu,struct kvm_mmu *mmu, +unsigned pte_access,unsigned pte_pkey,unsigned int pfec) +{ +u32 pkr_bits, offset; + +/* +* PKRU defines 32 bits, there are 16 domains and 2
Comment needs to be aligned, and it can be adjust to wrap at 80 chars (its indentation has changed).
/*
* PKRU and PKRS both define 32 bits. There are 16 domains and 2
* attribute bits per domain in them. pte_key is the index of the
* protection domain, so pte_pkey * 2 is the index of the first bit for
* the domain. The use of PKRU versus PKRS is selected by the address
* type, as determined by the U/S bit in the paging-structure entries. */
Quoted 16 lines
arch/x86/kvm/mmu/mmu.c +13
-0+* attribute bits per domain in pkru. pte_pkey is the +* index of the protection domain, so pte_pkey * 2 is +* is the index of the first bit for the domain. +*/ + pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; + +/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ + offset = (pfec & ~1 ) + ((pte_access &PT_USER_MASK ) + << (PFERR_RSVD_BIT -PT_USER_SHIFT )); + + pkr_bits &= mmu->pkr_mask >> offset; +return pkr_bits; +}Signature · 1 line
2.25.1
Re: [PATCH v7 5/8] KVM: MMU: Add helper function to get pkr bits
Quoted 3 lines · Sean Christopherson
Quoted 1 line · Lei Wang
Extra the PKR stuff to a separate, non-inline helper, which is a
s/Extra/Extract
My mistake, will fix it.
Quoted 10 lines
Quoted 1 line
preparation to introduce pks support.
Please provide more justification. The change is justified, by random readers of this patch/commit will be clueless.
Extract getting the effective PKR bits to a helper that lives in
mmu.cin order to keep the is_cr4_*() helpers contained tommu.c. Support for PKRS (versus just PKRU) will require querying MMU state to see if the relevant CR4 bit is enabled becausepkr_maskwill be non-zero if _either_ bit is enabled).
Quoted 4 lines
PKR{U,S} are exposed to the guest if and only if TDP is enabled, and while
permission_fault()is performance critical for ia32 shadow paging, it's a rarely used path with TDP is enabled. I.e. moving the PKR code out-of-line is not a performance concern.
Will add more justification to make it clearer to random readers.
Quoted 14 lines
Quoted 14 lines
Signed-off-by Lei Wang <[email protected]>--- arch/x86/kvm/mmu.h | 20 +++++--------------- arch/x86/kvm/mmu/mmu.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-)arch/x86/kvm/mmu.h +0
-0@@ -204,6 +204,9 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,return vcpu->arch.mmu->page_fault (vcpu, &fault); }
Quoted 2 lines
arch/x86/kvm/mmu.h static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, · line 207
Quoted 1 line
arch/x86/kvm/mmu.h +1
-0+u32 kvm_mmu_pkr_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
kvm_mmu_get_pkr_bits()so that there's a verb in there.
Will fix it.
Quoted 30 lines
Quoted 16 lines
arch/x86/kvm/mmu.h +2
-7+ unsigned pte_access, unsigned pte_pkey, unsigned int pfec); +/* * Check if a given access (described through the I/D, W/R and U/S bits of a * page fault error code pfec) causes a permission fault with the given PTE @@ -240,21 +243,8 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,WARN_ON (pfec & (PFERR_PK_MASK |PFERR_RSVD_MASK ));if (unlikely (mmu->pkr_mask)) { -u32 pkr_bits, offset; - -/* -* PKRU defines 32 bits, there are 16 domains and 2 -* attribute bits per domain in pkru. pte_pkey is the -* index of the protection domain, so pte_pkey * 2 is -* is the index of the first bit for the domain. arch/x86/kvm/mmu.h static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, · line 247
Quoted 10 lines
arch/x86/kvm/mmu.h +2
-8-*/ - pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; - -/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ - offset = (pfec & ~1 ) + - ((pte_access &PT_USER_MASK ) << (PFERR_RSVD_BIT -PT_USER_SHIFT )); - - pkr_bits &= mmu->pkr_mask >> offset; +u32 pkr_bits = +kvm_mmu_pkr_bits (vcpu, mmu, pte_access, pte_pkey, pfec);Nit, I prefer wrapping in the params, that way the first line shows the most important information, e.g. what variable is being set and how (by a function call). And then there won't be overflow with the longer helper name:
Quoted 2 lines
u32 pkr_bits = kvm_mmu_get_pkr_bits(vcpu, mmu, pte_access, pte_pkey, pfec);
Make sense, will make the wrapping more reasonable.
Quoted 10 lines
Comment needs to be aligned, and it can be adjust to wrap at 80 chars (its indentation has changed).
/*
* PKRU and PKRS both define 32 bits. There are 16 domains and 2
* attribute bits per domain in them.pte_keyis the index of the
* protection domain, sopte_pkey* 2 is the index of the first bit for
* the domain. The use of PKRU versus PKRS is selected by the address
* type, as determined by the U/S bit in the paging-structure entries. */
Will align and adjust it.
PATCH v7 6/8 KVM: MMU: Add support for PKS emulation
In reply to Lei WangFrom: Chenyi Qiang <[email protected]>
Up until now, pkr_mask had 0 bits for supervisor pages (the U/S bit in page tables replaces the PFEC.RSVD in page fault error code). For PKS support, fill in the bits using the same algorithm used for user mode pages, but with CR4.PKE replaced by CR4.PKS. Because of this change, CR4.PKS must also be included in the MMU role.
--- arch/x86/include/asm/kvm_host.h | 10 +-- arch/x86/kvm/mmu.h | 3 +- arch/x86/kvm/mmu/mmu.c | 109 +++++++++++++++++++++----------- 3 files changed, 80 insertions(+), 42 deletions(-)
arch/x86/include/asm/kvm_host.h +6 -4
@@ -375,6 +375,7 @@ union kvm_mmu_extended_role { unsigned int cr4_smap:1; unsigned int cr4_smep:1; unsigned int cr4_la57:1; + unsigned int cr4_pks:1; unsigned int efer_lma:1; }; };
@@ -454,10 +455,11 @@ struct kvm_mmu {u8 permissions[16 ];/* -* The pkru_mask indicates if protection key checks are needed. It -* consists of 16 domains indexed by page fault error code bits [4:1], -* with PFEC.RSVD replaced by ACC_USER_MASK from the page tables. -* Each domain has 2 bits which are ANDed with AD and WD from PKRU. +* The pkr_mask indicates if protection key checks are needed. +* It consists of 16 domains indexed by page fault error code +* bits[4:1] with PFEC.RSVD replaced by ACC_USER_MASK from the +* page tables. Each domain has 2 bits which are ANDed with AD +* and WD from PKRU/PKRS. */ u32 pkr_mask;
arch/x86/kvm/mmu.h +2 -1
@@ -45,7 +45,8 @@#define PT32E_ROOT_LEVEL 3#define KVM_MMU_CR4_ROLE_BITS (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_LA57 | \ - X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE) + X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE | \ + X86_CR4_PKS)#define KVM_MMU_CR0_ROLE_BITS (X86_CR0_PG | X86_CR0_WP)#define KVM_MMU_EFER_ROLE_BITS (EFER_LME | EFER_NX)
arch/x86/kvm/mmu/mmu.c +72 -37
160 lines not on this page · raw message
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v7 6/8] KVM: MMU: Add support for PKS emulation
arch/x86/include/asm/kvm_host.h struct kvm_mmu { · line 462
Quoted 13 lines · Lei Wang
arch/x86/include/asm/kvm_host.h +5
-4@@ -454,10 +455,11 @@ struct kvm_mmu {u8 permissions[16 ];/* -* The pkru_mask indicates if protection key checks are needed. It -* consists of 16 domains indexed by page fault error code bits [4:1], -* with PFEC.RSVD replaced by ACC_USER_MASK from the page tables. -* Each domain has 2 bits which are ANDed with AD and WD from PKRU. +* The pkr_mask indicates if protection key checks are needed. +* It consists of 16 domains indexed by page fault error code +* bits[4:1] with PFEC.RSVD replaced by ACC_USER_MASK from the +* page tables. Each domain has 2 bits which are ANDed with AD +* and WD from PKRU/PKRS.
Same comments, align and wrap closer to 80 please.
Quoted 41 lines
arch/x86/include/asm/kvm_host.h +0
-0*/ u32 pkr_mask;arch/x86/kvm/mmu.h +2
-1@@ -45,7 +45,8 @@#define PT32E_ROOT_LEVEL 3#define KVM_MMU_CR4_ROLE_BITS (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_LA57 | \ - X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE) + X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE | \ + X86_CR4_PKS)#define KVM_MMU_CR0_ROLE_BITS (X86_CR0_PG | X86_CR0_WP)#define KVM_MMU_EFER_ROLE_BITS (EFER_LME | EFER_NX)arch/x86/kvm/mmu/mmu.c +2
-0@@ -209,6 +209,7 @@ BUILD_MMU_ROLE_REGS_ACCESSOR(cr4, smep, X86_CR4_SMEP);BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, smap,X86_CR4_SMAP );BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, pke,X86_CR4_PKE );BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, la57,X86_CR4_LA57 ); +BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, pks,X86_CR4_PKS );BUILD_MMU_ROLE_REGS_ACCESSOR (efer, nx,EFER_NX );BUILD_MMU_ROLE_REGS_ACCESSOR (efer, lma,EFER_LMA );@@ -231,6 +232,7 @@ BUILD_MMU_ROLE_ACCESSOR(ext, cr4, smep);BUILD_MMU_ROLE_ACCESSOR (ext, cr4, smap);BUILD_MMU_ROLE_ACCESSOR (ext, cr4, pke);BUILD_MMU_ROLE_ACCESSOR (ext, cr4, la57); +BUILD_MMU_ROLE_ACCESSOR (ext, cr4, pks);BUILD_MMU_ROLE_ACCESSOR (base, efer, nx);static struct kvm_mmu_role_regs vcpu_to_role_regs (struct kvm_vcpu *vcpu)@@ -4608,37 +4610,58 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) }/*
...
Quoted 9 lines
+ * Protection Key Rights (PKR) is an additional mechanism by which data accesses
+ * with 4-level or 5-level paging (EFER.LMA=1) may be disabled based on the
+ * Protection Key Rights Userspace (PRKU) or Protection Key Rights Supervisor
+ * (PKRS) registers. The Protection Key (PK) used for an access is a 4-bit
+ * value specified in bits 62:59 of the leaf PTE used to translate the address.
+ *
+ * PKRU and PKRS are 32-bit registers, with 16 2-bit entries consisting of an
+ * access-disable (AD) and write-disable (WD) bit. The PK from the leaf PTE is
+ * used to index the approriate PKR (see below), e.g. PK=1 would consume bits
s/approriate/appropriate
Quoted 8 lines
+ * 3:2 (bit 3 == write-disable, bit 2 == access-disable).
+ *
+ * The PK register (PKRU vs. PKRS) indexed by the PK depends on the type of
+ * _address_ (not access type!). For a user-mode address, PKRU is used; for a
+ * supervisor-mode address, PKRS is used. An address is supervisor-mode if the
+ * U/S flag (bit 2) is 0 in at least one of the paging-structure entries, i.e.
+ * an address is user-mode if the U/S flag is 1 in _all_ entries. Again, this
+ * is the address type, not the the access type, e.g. a supervisor-mode _access_
Double "the the" can be a single "the".
Quoted 28 lines
+ * will consume PKRU if the _address_ is a user-mode address.
+ *
+ * As alluded to above, PKR checks are only performed for data accesses; code
+ * fetches are not subject to PKR checks. Terminal page faults (!PRESENT or
+ * PFEC.RSVD=1) are also not subject to PKR checks.
+ *
+ * PKR write-disable checks for superivsor-mode _accesses_ are performed if and
+ * only if CR0.WP=1 (though access-disable checks still apply).
+ *
+ * In summary, PKR checks are based on (a) EFER.LMA, (b) CR4.PKE or CR4.PKS,
+ * (c) CR0.WP, (d) the PK in the leaf PTE, (e) two bits from the corresponding
+ * PKR{S,U} entry, (f) the access type (derived from the other PFEC bits), and
+ * (g) the address type (retrieved from the paging-structure entries).
+ *
+ * To avoid conditional branches inpermission_fault(), the PKR bitmask caches
+ * the above inputs, except for (e) the PKR{S,U} entry. The FETCH, USER, and
+ * WRITE bits of the PFEC and the effective value of the paging-structures' U/S
+ * bit (slotted into the PFEC.RSVD position, bit 3) are used to index into the
+ * PKR bitmask (similar to the 4-bit Protection Key itself). The two bits of
+ * the PKR bitmask "entry" are then extracted and ANDed with the two bits of
+ * the PKR{S,U} register corresponding to the address type and protection key.
+ *
+ * E.g. for all values where PFEC.FETCH=1, the correspondingpkr_bitmaskbits
+ * will be 00b, thus masking away the AD and WD bits from the PKR{S,U} register
+ * to suppress PKR checks on code fetches.
+ */
static voidupdate_pkr_bitmask(struct kvm_mmu *mmu)
{
arch/x86/kvm/mmu/mmu.c static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) · line 4638
Quoted 3 lines
arch/x86/kvm/mmu/mmu.c +0
-1unsigned bit;bool wp; -
Please keep this newline, i.e. after the declaration of the cr4 booleans. That helps isolate the clearing of mmu->pkr_mask, which makes the functional affect of the earlier return more obvious.
Ah, and use reverse fir tree for the variable declarations, i.e.
bool cr4_pke = is_cr4_pke(mmu); bool cr4_pks = is_cr4_pks(mmu); unsigned bit; bool wp; mmu->pkr_mask = 0; if (!cr4_pke && !cr4_pks) return;
Quoted 9 lines
arch/x86/kvm/mmu/mmu.c +3
-1+bool cr4_pke =is_cr4_pke (mmu); +bool cr4_pks =is_cr4_pks (mmu); mmu->pkr_mask =0 ; -if (!is_cr4_pke (mmu)) +if (!cr4_pke && !cr4_pks)return ; wp =is_cr0_wp (mmu);
...
Quoted 7 lines
arch/x86/kvm/mmu/mmu.c +1
-0@@ -6482,14 +6509,22 @@ u32 kvm_mmu_pkr_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, unsigned pte_access, unsigned pte_pkey, unsigned int pfec) { u32 pkr_bits, offset; +u32 pkr;/*
arch/x86/kvm/mmu/mmu.c u32 kvm_mmu_pkr_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, · line 6520
Quoted 10 lines
arch/x86/kvm/mmu/mmu.c +6
-4-* PKRU defines 32 bits, there are 16 domains and 2 -* attribute bits per domain in pkru. pte_pkey is the -* index of the protection domain, so pte_pkey * 2 is -* is the index of the first bit for the domain. +* PKRU and PKRS both define 32 bits. There are 16 domains +* and 2 attribute bits per domain in them. pte_key is the +* index of the protection domain, so pte_pkey * 2 is the +* index of the first bit for the domain. The use of PKRU +* versus PKRS is selected by the address type, as determined +* by the U/S bit in the paging-structure entries.
Align and wrap closer to 80 please.
Quoted 14 lines
arch/x86/kvm/mmu/mmu.c +6
-1*/ - pkr_bits = (vcpu->arch.pkru >> (pte_pkey *2 )) &3 ; +if (pte_access &PT_USER_MASK ) + pkr =is_cr4_pke (mmu) ? vcpu->arch.pkru :0 ; +else + pkr =is_cr4_pks (mmu) ?kvm_read_pkrs (vcpu) :0 ; + + pkr_bits = (pkr >> pte_pkey *2 ) &3 ;/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */ offset = (pfec & ~1) + ((pte_access & PT_USER_MASK)Signature · 1 line
2.25.1
Re: [PATCH v7 6/8] KVM: MMU: Add support for PKS emulation
Quoted 4 lines · Sean Christopherson
Quoted 3 lines · Lei Wang
arch/x86/include/asm/kvm_host.h +0
-0@@ -454,10 +455,11 @@ struct kvm_mmu {u8 permissions[16 ];
Quoted 11 lines
arch/x86/include/asm/kvm_host.h struct kvm_mmu { · line 462
Quoted 10 lines
arch/x86/include/asm/kvm_host.h +5
-4/* -* The pkru_mask indicates if protection key checks are needed. It -* consists of 16 domains indexed by page fault error code bits [4:1], -* with PFEC.RSVD replaced by ACC_USER_MASK from the page tables. -* Each domain has 2 bits which are ANDed with AD and WD from PKRU. +* The pkr_mask indicates if protection key checks are needed. +* It consists of 16 domains indexed by page fault error code +* bits[4:1] with PFEC.RSVD replaced by ACC_USER_MASK from the +* page tables. Each domain has 2 bits which are ANDed with AD +* and WD from PKRU/PKRS. Same comments, align and wrap closer to 80 please.
Will do it.
Quoted 52 lines
Quoted 41 lines
arch/x86/include/asm/kvm_host.h +0
-0*/ u32 pkr_mask;arch/x86/kvm/mmu.h +2
-1@@ -45,7 +45,8 @@#define PT32E_ROOT_LEVEL 3#define KVM_MMU_CR4_ROLE_BITS (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_LA57 | \ - X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE) + X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE | \ + X86_CR4_PKS)#define KVM_MMU_CR0_ROLE_BITS (X86_CR0_PG | X86_CR0_WP)#define KVM_MMU_EFER_ROLE_BITS (EFER_LME | EFER_NX)arch/x86/kvm/mmu/mmu.c +2
-0@@ -209,6 +209,7 @@ BUILD_MMU_ROLE_REGS_ACCESSOR(cr4, smep, X86_CR4_SMEP);BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, smap,X86_CR4_SMAP );BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, pke,X86_CR4_PKE );BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, la57,X86_CR4_LA57 ); +BUILD_MMU_ROLE_REGS_ACCESSOR (cr4, pks,X86_CR4_PKS );BUILD_MMU_ROLE_REGS_ACCESSOR (efer, nx,EFER_NX );BUILD_MMU_ROLE_REGS_ACCESSOR (efer, lma,EFER_LMA );@@ -231,6 +232,7 @@ BUILD_MMU_ROLE_ACCESSOR(ext, cr4, smep);BUILD_MMU_ROLE_ACCESSOR (ext, cr4, smap);BUILD_MMU_ROLE_ACCESSOR (ext, cr4, pke);BUILD_MMU_ROLE_ACCESSOR (ext, cr4, la57); +BUILD_MMU_ROLE_ACCESSOR (ext, cr4, pks);BUILD_MMU_ROLE_ACCESSOR (base, efer, nx);static struct kvm_mmu_role_regs vcpu_to_role_regs (struct kvm_vcpu *vcpu)@@ -4608,37 +4610,58 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) }/* ...
Quoted 9 lines
+ * Protection Key Rights (PKR) is an additional mechanism by which data accesses
+ * with 4-level or 5-level paging (EFER.LMA=1) may be disabled based on the
+ * Protection Key Rights Userspace (PRKU) or Protection Key Rights Supervisor
+ * (PKRS) registers. The Protection Key (PK) used for an access is a 4-bit
+ * value specified in bits 62:59 of the leaf PTE used to translate the address.
+ *
+ * PKRU and PKRS are 32-bit registers, with 16 2-bit entries consisting of an
+ * access-disable (AD) and write-disable (WD) bit. The PK from the leaf PTE is
+ * used to index the approriate PKR (see below), e.g. PK=1 would consume bits
Quoted 1 line
s/approriate/appropriate
Will correct it.
Quoted 9 lines
Quoted 8 lines
+ * 3:2 (bit 3 == write-disable, bit 2 == access-disable).
+ *
+ * The PK register (PKRU vs. PKRS) indexed by the PK depends on the type of
+ * _address_ (not access type!). For a user-mode address, PKRU is used; for a
+ * supervisor-mode address, PKRS is used. An address is supervisor-mode if the
+ * U/S flag (bit 2) is 0 in at least one of the paging-structure entries, i.e.
+ * an address is user-mode if the U/S flag is 1 in _all_ entries. Again, this
+ * is the address type, not the the access type, e.g. a supervisor-mode _access_Double "the the" can be a single "the".
Will remove it.
Quoted 37 lines
Quoted 23 lines
+ * will consume PKRU if the _address_ is a user-mode address.
+ *
+ * As alluded to above, PKR checks are only performed for data accesses; code
+ * fetches are not subject to PKR checks. Terminal page faults (!PRESENT or
+ * PFEC.RSVD=1) are also not subject to PKR checks.
+ *
+ * PKR write-disable checks for superivsor-mode _accesses_ are performed if and
+ * only if CR0.WP=1 (though access-disable checks still apply).
+ *
+ * In summary, PKR checks are based on (a) EFER.LMA, (b) CR4.PKE or CR4.PKS,
+ * (c) CR0.WP, (d) the PK in the leaf PTE, (e) two bits from the corresponding
+ * PKR{S,U} entry, (f) the access type (derived from the other PFEC bits), and
+ * (g) the address type (retrieved from the paging-structure entries).
+ *
+ * To avoid conditional branches inpermission_fault(), the PKR bitmask caches
+ * the above inputs, except for (e) the PKR{S,U} entry. The FETCH, USER, and
+ * WRITE bits of the PFEC and the effective value of the paging-structures' U/S
+ * bit (slotted into the PFEC.RSVD position, bit 3) are used to index into the
+ * PKR bitmask (similar to the 4-bit Protection Key itself). The two bits of
+ * the PKR bitmask "entry" are then extracted and ANDed with the two bits of
+ * the PKR{S,U} register corresponding to the address type and protection key.
+ *
+ * E.g. for all values where PFEC.FETCH=1, the correspondingpkr_bitmaskbitsQuoted 8 lines
+ * will be 00b, thus masking away the AD and WD bits from the PKR{S,U} register
+ * to suppress PKR checks on code fetches.
+ */
static voidupdate_pkr_bitmask(struct kvm_mmu *mmu)
{
unsigned bit;
bool wp;
-Please keep this newline, i.e. after the declaration of the cr4 booleans. That helps isolate the clearing of
mmu->pkr_mask, which makes the functional affect of the earlier return more obvious.Ah, and use reverse fir tree for the variable declarations, i.e.
Quoted 9 lines
bool cr4_pke = is_cr4_pke(mmu); bool cr4_pks = is_cr4_pks(mmu); unsigned bit; bool wp; mmu->pkr_mask = 0; if (!cr4_pke && !cr4_pks) return;
Very nice of you, will use reverse fir tree for the declaration.
Quoted 19 lines
arch/x86/kvm/mmu/mmu.c static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) · line 4667
Quoted 9 lines
arch/x86/kvm/mmu/mmu.c +3
-1+bool cr4_pke =is_cr4_pke (mmu); +bool cr4_pks =is_cr4_pks (mmu); mmu->pkr_mask =0 ; -if (!is_cr4_pke (mmu)) +if (!cr4_pke && !cr4_pks)return ; wp =is_cr0_wp (mmu);...Quoted 6 lines
arch/x86/kvm/mmu/mmu.c +1
-0@@ -6482,14 +6509,22 @@ u32 kvm_mmu_pkr_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, unsigned pte_access, unsigned pte_pkey, unsigned int pfec) { u32 pkr_bits, offset; +u32 pkr;
Quoted 13 lines
arch/x86/kvm/mmu/mmu.c u32 kvm_mmu_pkr_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, · line 6520
Quoted 11 lines
arch/x86/kvm/mmu/mmu.c +6
-4/* -* PKRU defines 32 bits, there are 16 domains and 2 -* attribute bits per domain in pkru. pte_pkey is the -* index of the protection domain, so pte_pkey * 2 is -* is the index of the first bit for the domain. +* PKRU and PKRS both define 32 bits. There are 16 domains +* and 2 attribute bits per domain in them. pte_key is the +* index of the protection domain, so pte_pkey * 2 is the +* index of the first bit for the domain. The use of PKRU +* versus PKRS is selected by the address type, as determined +* by the U/S bit in the paging-structure entries. Align and wrap closer to 80 please.
Will do it.
PATCH v7 7/8 KVM: VMX: Expose PKS to guest
In reply to Lei WangFrom: Chenyi Qiang <[email protected]>
Existence of PKS is enumerated via CPUID.(EAX=7,ECX=0):ECX[31]. It is enabled by setting CR4.PKS when long mode is active. PKS is only implemented when EPT is enabled and requires the support of VM_{ENTRY,EXIT}_LOAD_IA32_PKRS VMCS controls currently.
--- arch/x86/include/asm/kvm_host.h | 3 ++- arch/x86/kvm/cpuid.c | 13 +++++++++---- arch/x86/kvm/vmx/capabilities.h | 6 ++++++ arch/x86/kvm/vmx/vmx.c | 10 +++++++--- arch/x86/kvm/x86.h | 2 ++ 5 files changed, 26 insertions(+), 8 deletions(-)
arch/x86/include/asm/kvm_host.h +2 -1
@@ -117,7 +117,8 @@ | X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR | X86_CR4_PCIDE \ | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ - | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP)) + | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ + | X86_CR4_PKS))#define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR)
arch/x86/kvm/cpuid.c +9 -4
@@ -570,18 +570,23 @@ void kvm_set_cpu_caps(void)F (AVX512_VPOPCNTDQ ) |F (UMIP ) |F (AVX512_VBMI2 ) |F (GFNI ) |F (VAES ) |F (VPCLMULQDQ ) |F (AVX512_VNNI ) |F (AVX512_BITALG ) |F (CLDEMOTE ) |F (MOVDIRI ) |F (MOVDIR64B ) |0 /*WAITPKG*/ | -F (SGX_LC ) |F (BUS_LOCK_DETECT ) +F (SGX_LC ) |F (BUS_LOCK_DETECT ) |F (PKS ) );/* Set LA57 based on hardware capability. */ if (cpuid_ecx (7 ) &F (LA57 ))kvm_cpu_cap_set (X86_FEATURE_LA57 );/* -* PKU not yet implemented for shadow paging and requires OSPKE -* to be set on the host. Clear it if that is not the case +* Protection Keys are not supported for shadow paging. PKU further +* requires OSPKE to be set on the host in order to use {RD,WR}PKRU to +* save/restore the guests PKRU. */ -if (!tdp_enabled || !boot_cpu_has (X86_FEATURE_OSPKE )) +if (!tdp_enabled) {kvm_cpu_cap_clear (X86_FEATURE_PKU ); +kvm_cpu_cap_clear (X86_FEATURE_PKS ); + }else if (!boot_cpu_has (X86_FEATURE_OSPKE )) { +kvm_cpu_cap_clear (X86_FEATURE_PKU ); + } kvm_cpu_cap_mask(CPUID_7_EDX, F(AVX512_4VNNIW) | F(AVX512_4FMAPS) | F(SPEC_CTRL) |
arch/x86/kvm/vmx/capabilities.h +6 -0
@@ -104,6 +104,12 @@ static inline bool cpu_has_load_perf_global_ctrl(void) (vmcs_config.vmexit_ctrl &VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL ); } +static inline bool cpu_has_load_ia32_pkrs (void ) +{ +return (vmcs_config.vmentry_ctrl &VM_ENTRY_LOAD_IA32_PKRS ) && + (vmcs_config.vmexit_ctrl &VM_EXIT_LOAD_IA32_PKRS ); +} +static inline bool cpu_has_vmx_mpx (void ) { return (vmcs_config.vmexit_ctrl & VM_EXIT_CLEAR_BNDCFGS) &&
arch/x86/kvm/vmx/vmx.c +7 -3
@@ -3250,7 +3250,7 @@ void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) }/* -* SMEP/SMAP/PKU is disabled if CPU is in non-paging mode in +* SMEP/SMAP/PKU/PKS is disabled if CPU is in non-paging mode in * hardware. To emulate this behavior, SMEP/SMAP/PKU needs * to be manually disabled when guest switches to non-paging * mode.
@@ -3258,10 +3258,11 @@ void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)* If !enable_unrestricted_guest, the CPU is always running * with CR0.PG=1 and CR4 needs to be modified. * If enable_unrestricted_guest, the CPU automatically -* disables SMEP/SMAP/PKU when the guest sets CR0.PG=0. +* disables SMEP/SMAP/PKU/PKS when the guest sets CR0.PG=0. */ if (!is_paging (vcpu)) - hw_cr4 &= ~(X86_CR4_SMEP |X86_CR4_SMAP |X86_CR4_PKE ); + hw_cr4 &= ~(X86_CR4_SMEP |X86_CR4_SMAP |X86_CR4_PKE | +X86_CR4_PKS ); }vmcs_writel (CR4_READ_SHADOW , cr4);
@@ -7500,6 +7501,9 @@ static __init void vmx_set_cpu_caps(void)if (cpu_has_vmx_waitpkg ())kvm_cpu_cap_check_and_set (X86_FEATURE_WAITPKG ); + +if (cpu_has_load_ia32_pkrs ()) +kvm_cpu_cap_check_and_set (X86_FEATURE_PKS ); }static void vmx_request_immediate_exit (struct kvm_vcpu *vcpu)
arch/x86/kvm/x86.h +2 -0
@@ -449,6 +449,8 @@ bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type); __reserved_bits |=X86_CR4_VMXE ; \if (!__cpu_has (__c,X86_FEATURE_PCID )) \ __reserved_bits |=X86_CR4_PCIDE ; \ +if (!__cpu_has (__c,X86_FEATURE_PKS )) \ + __reserved_bits |=X86_CR4_PKS ; \ __reserved_bits; \ })
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v7 7/8] KVM: VMX: Expose PKS to guest
arch/x86/kvm/vmx/vmx.c void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) · line 3254
Quoted 11 lines · Lei Wang
arch/x86/kvm/vmx/vmx.c +1
-1@@ -3250,7 +3250,7 @@ void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) }/* -* SMEP/SMAP/PKU is disabled if CPU is in non-paging mode in +* SMEP/SMAP/PKU/PKS is disabled if CPU is in non-paging mode in * hardware. To emulate this behavior, SMEP/SMAP/PKU needs
Heh, missed one ;-) Let's reduce future pain and reword this whole comment:
/*
* SMEP/SMAP/PKU/PKS are effectively disabled if the CPU is in
* non-paging mode in hardware. To emulate this behavior,
* clear them in the hardware CR4 when the guest switches to
* non-paging mode and unrestricted guest is disabled, as KVM
* must run the guest with hardware CR0.PG=1.
*/
Quoted 2 lines
arch/x86/kvm/vmx/vmx.c +0
-0* to be manually disabled when guest switches to non-paging * mode.
Re: [PATCH v7 7/8] KVM: VMX: Expose PKS to guest
Quoted 14 lines · Sean Christopherson
arch/x86/kvm/vmx/vmx.c void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) · line 3254
Quoted 11 lines · Lei Wang
arch/x86/kvm/vmx/vmx.c +1
-1@@ -3250,7 +3250,7 @@ void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) }/* -* SMEP/SMAP/PKU is disabled if CPU is in non-paging mode in +* SMEP/SMAP/PKU/PKS is disabled if CPU is in non-paging mode in * hardware. To emulate this behavior, SMEP/SMAP/PKU needs Heh, missed one ;-) Let's reduce future pain and reword this whole comment:
Quoted 8 lines
/*
* SMEP/SMAP/PKU/PKS are effectively disabled if the CPU is in
* non-paging mode in hardware. To emulate this behavior,
* clear them in the hardware CR4 when the guest switches to
* non-paging mode and unrestricted guest is disabled, as KVM
* must run the guest with hardware CR0.PG=1.
*/
Nice catch, will fix it ;-)
PATCH v7 8/8 KVM: VMX: Enable PKS for nested VM
In reply to Lei WangFrom: Chenyi Qiang <[email protected]>
PKS MSR passes through guest directly. Configure the MSR to match the L0/L1 settings so that nested VM runs PKS properly.
--- arch/x86/kvm/vmx/nested.c | 36 ++++++++++++++++++++++++++++++++++-- arch/x86/kvm/vmx/vmcs12.c | 2 ++ arch/x86/kvm/vmx/vmcs12.h | 4 ++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/vmx/vmx.h | 2 ++ 5 files changed, 43 insertions(+), 2 deletions(-)
arch/x86/kvm/vmx/nested.c +34 -2
@@ -252,6 +252,7 @@ static void vmx_sync_vmcs_host_state(struct vcpu_vmx *vmx, dest->ds_sel = src->ds_sel; dest->es_sel = src->es_sel;#endif +vmx_set_host_pkrs (dest, src->pkrs); }static void vmx_switch_vmcs (struct kvm_vcpu *vcpu,struct loaded_vmcs *vmcs)
@@ -685,6 +686,9 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu,nested_vmx_set_intercept_for_msr (vmx, msr_bitmap_l1, msr_bitmap_l0,MSR_IA32_PRED_CMD ,MSR_TYPE_W ); +nested_vmx_set_intercept_for_msr (vmx, msr_bitmap_l1, msr_bitmap_l0, +MSR_IA32_PKRS ,MSR_TYPE_RW ); +kvm_vcpu_unmap (vcpu, &vmx->nested.msr_bitmap_map, false); vmx->nested.force_msr_bitmap_recalc = false;
@@ -2433,6 +2437,10 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)if (kvm_mpx_supported () && vmx->nested.nested_run_pending && (vmcs12->vm_entry_controls &VM_ENTRY_LOAD_BNDCFGS ))vmcs_write64 (GUEST_BNDCFGS , vmcs12->guest_bndcfgs); + +if (vmx->nested.nested_run_pending && + (vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS )) +vmcs_write64 (GUEST_IA32_PKRS , vmcs12->guest_ia32_pkrs); }if (nested_cpu_has_xsaves (vmcs12))
@@ -2521,6 +2529,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,if (kvm_mpx_supported () && (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls &VM_ENTRY_LOAD_BNDCFGS )))vmcs_write64 (GUEST_BNDCFGS , vmx->nested.vmcs01_guest_bndcfgs); +if (kvm_cpu_cap_has (X86_FEATURE_PKS ) && + (!vmx->nested.nested_run_pending || + !(vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS ))) +vmcs_write64 (GUEST_IA32_PKRS , vmx->nested.vmcs01_guest_pkrs); +vmx_set_rflags (vcpu, vmcs12->guest_rflags);/* EXCEPTION_BITMAP and CR0_GUEST_HOST_MASK should basically be the
@@ -2897,6 +2910,10 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, vmcs12->host_ia32_perf_global_ctrl)))return -EINVAL ; +if ((vmcs12->vm_exit_controls &VM_EXIT_LOAD_IA32_PKRS ) && +CC (!kvm_pkrs_valid (vmcs12->host_ia32_pkrs))) +return -EINVAL ; +#ifdef CONFIG_X86_64 ia32e = !!(vmcs12->vm_exit_controls &VM_EXIT_HOST_ADDR_SPACE_SIZE );#else
@@ -3049,6 +3066,10 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu,if (nested_check_guest_non_reg_state (vmcs12))return -EINVAL ; +if ((vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS ) && +CC (!kvm_pkrs_valid (vmcs12->guest_ia32_pkrs))) +return -EINVAL ; +return 0 ; }
@@ -3384,6 +3405,10 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, (!from_vmentry || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) vmx->nested.vmcs01_guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS); +if (kvm_cpu_cap_has (X86_FEATURE_PKS ) && + (!from_vmentry || + !(vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS ))) + vmx->nested.vmcs01_guest_pkrs =vmcs_read64 (GUEST_IA32_PKRS );/* * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and*
@@ -4029,6 +4054,7 @@ static bool is_vmcs12_ext_field(unsigned long field)case GUEST_IDTR_BASE :case GUEST_PENDING_DBG_EXCEPTIONS :case GUEST_BNDCFGS : +case GUEST_IA32_PKRS :return true;default :break ;
@@ -4080,6 +4106,8 @@ static void sync_vmcs02_to_vmcs12_rare(struct kvm_vcpu *vcpu,vmcs_readl (GUEST_PENDING_DBG_EXCEPTIONS );if (kvm_mpx_supported ()) vmcs12->guest_bndcfgs =vmcs_read64 (GUEST_BNDCFGS ); +if (vmx->nested.msrs.entry_ctls_high &VM_ENTRY_LOAD_IA32_PKRS ) + vmcs12->guest_ia32_pkrs =vmcs_read64 (GUEST_IA32_PKRS ); vmx->nested.need_sync_vmcs02_to_vmcs12_rare = false; }
@@ -4317,6 +4345,9 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,WARN_ON_ONCE (kvm_set_msr (vcpu,MSR_CORE_PERF_GLOBAL_CTRL , vmcs12->host_ia32_perf_global_ctrl)); +if (vmcs12->vm_exit_controls &VM_EXIT_LOAD_IA32_PKRS ) +vmcs_write64 (GUEST_IA32_PKRS , vmcs12->host_ia32_pkrs); +/* Set L1 segment info according to Intel SDM 27.5.2 Loading Host Segment and Descriptor-Table Registers */ seg = (struct kvm_segment ) {
@@ -6559,7 +6590,8 @@ void nested_vmx_setup_ctls_msrs(struct nested_vmx_msrs *msrs, u32 ept_caps)VM_EXIT_HOST_ADDR_SPACE_SIZE | #endifVM_EXIT_LOAD_IA32_PAT |VM_EXIT_SAVE_IA32_PAT | -VM_EXIT_CLEAR_BNDCFGS |VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL ; +VM_EXIT_CLEAR_BNDCFGS |VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | +VM_EXIT_LOAD_IA32_PKRS ; msrs->exit_ctls_high |=VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER |
@@ -6579,7 +6611,7 @@ void nested_vmx_setup_ctls_msrs(struct nested_vmx_msrs *msrs, u32 ept_caps)VM_ENTRY_IA32E_MODE | #endifVM_ENTRY_LOAD_IA32_PAT |VM_ENTRY_LOAD_BNDCFGS | -VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL ; +VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL |VM_ENTRY_LOAD_IA32_PKRS ; msrs->entry_ctls_high |= (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR |VM_ENTRY_LOAD_IA32_EFER );
arch/x86/kvm/vmx/vmcs12.c +2 -0
@@ -62,9 +62,11 @@ const unsigned short vmcs12_field_offsets[] = {FIELD64 (GUEST_PDPTR2 , guest_pdptr2),FIELD64 (GUEST_PDPTR3 , guest_pdptr3),FIELD64 (GUEST_BNDCFGS , guest_bndcfgs), +FIELD64 (GUEST_IA32_PKRS , guest_ia32_pkrs),FIELD64 (HOST_IA32_PAT , host_ia32_pat),FIELD64 (HOST_IA32_EFER , host_ia32_efer),FIELD64 (HOST_IA32_PERF_GLOBAL_CTRL , host_ia32_perf_global_ctrl), +FIELD64 (HOST_IA32_PKRS , host_ia32_pkrs),FIELD (PIN_BASED_VM_EXEC_CONTROL , pin_based_vm_exec_control),FIELD (CPU_BASED_VM_EXEC_CONTROL , cpu_based_vm_exec_control), FIELD(EXCEPTION_BITMAP, exception_bitmap),
arch/x86/kvm/vmx/vmcs12.h +4 -0
@@ -185,6 +185,8 @@ struct __packed vmcs12 {u16 host_gs_selector;u16 host_tr_selector;u16 guest_pml_index; +u64 host_ia32_pkrs; +u64 guest_ia32_pkrs; };/*
@@ -359,6 +361,8 @@ static inline void vmx_check_vmcs12_offsets(void)CHECK_OFFSET (host_gs_selector,992 );CHECK_OFFSET (host_tr_selector,994 );CHECK_OFFSET (guest_pml_index,996 ); +CHECK_OFFSET (host_ia32_pkrs,998 ); +CHECK_OFFSET (guest_ia32_pkrs,1006 ); }extern const unsigned short vmcs12_field_offsets[];
arch/x86/kvm/vmx/vmx.c +1 -0
@@ -7294,6 +7294,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu)cr4_fixed1_update (X86_CR4_PKE , ecx,feature_bit (PKU ));cr4_fixed1_update (X86_CR4_UMIP , ecx,feature_bit (UMIP ));cr4_fixed1_update (X86_CR4_LA57 , ecx,feature_bit (LA57 )); +cr4_fixed1_update (X86_CR4_PKS , ecx,feature_bit (PKS ));#undef cr4_fixed1_update }
arch/x86/kvm/vmx/vmx.h +2 -0
@@ -222,6 +222,8 @@ struct nested_vmx {u64 vmcs01_debugctl;u64 vmcs01_guest_bndcfgs; +u64 vmcs01_guest_pkrs; +/* to migrate it to L1 if L2 writes to L1's CR8 directly */ int l1_tpr_threshold;
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v7 8/8] KVM: VMX: Enable PKS for nested VM
Nit, use "KVM: nVMX:" for the shortlog scope.
arch/x86/kvm/vmx/nested.c static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) · line 2443
Quoted 8 lines · Lei Wang
arch/x86/kvm/vmx/nested.c +4
-0@@ -2433,6 +2437,10 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)if (kvm_mpx_supported () && vmx->nested.nested_run_pending && (vmcs12->vm_entry_controls &VM_ENTRY_LOAD_BNDCFGS ))vmcs_write64 (GUEST_BNDCFGS , vmcs12->guest_bndcfgs); + +if (vmx->nested.nested_run_pending && + (vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS )) +vmcs_write64 (GUEST_IA32_PKRS , vmcs12->guest_ia32_pkrs);
As mentioned in the BNDCFGS thread, this does the wrong thing for SMM. But, after a lot of thought, handling this in nested_vmx_enter_non_root_mode() would be little more than a band-aid, and a messy one at that, because KVM's SMM emulation is horrifically broken with respect to nVMX.
Entry does to SMM does not modify _any_ state that is not saved in SMRAM. That we're having to deal with this crap is a symptom of KVM doing the complete wrong thing by piggybacking nested_vmx_vmexit() and nested_vmx_enter_non_root_mode().
The SDM's description of CET spells this out very, very clearly:
On processors that support CET shadow stacks, when the processor enters SMM, the processor saves the SSP register to the SMRAM state save area (see Table 31-3) and clears CR4.CET to 0. Thus, the initial execution environment of the SMI handler has CET disabled and all of the CET state of the interrupted program is still in the machine. An SMM that uses CET is required to save the interrupted program’s CET state and restore the CET state prior to exiting SMM.
It mostly works because no guest SMM handler does anything with most of the MSRs, but it's all wildy wrong. A concrete example of a lurking bug is if vmcs12 uses the VM-Exit MSR load list, in which case the forced nested_vmx_vmexit() will load state that is never undone.
So, my very strong vote is to ignore SMM and let someone who actually cares about SMM fix that mess properly by adding custom flows for exiting/re-entering L2 on SMI/RSM.
arch/x86/kvm/vmx/nested.c static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, · line 2532
Quoted 8 lines
arch/x86/kvm/vmx/nested.c +1
-0}if (nested_cpu_has_xsaves (vmcs12))@@ -2521,6 +2529,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,if (kvm_mpx_supported () && (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls &VM_ENTRY_LOAD_BNDCFGS )))vmcs_write64 (GUEST_BNDCFGS , vmx->nested.vmcs01_guest_bndcfgs); + if (kvm_cpu_cap_has(X86_FEATURE_PKS) &&
ERROR: trailing whitespace
#85: FILE: arch/x86/kvm/vmx/nested.c:3407:
+^Iif (kvm_cpu_cap_has(X86_FEATURE_PKS) && $
Quoted 30 lines
arch/x86/kvm/vmx/nested.c +12
-0+ (!vmx->nested.nested_run_pending || + !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PKRS))) +vmcs_write64 (GUEST_IA32_PKRS , vmx->nested.vmcs01_guest_pkrs); +vmx_set_rflags (vcpu, vmcs12->guest_rflags);/* EXCEPTION_BITMAP and CR0_GUEST_HOST_MASK should basically be the @@ -2897,6 +2910,10 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, vmcs12->host_ia32_perf_global_ctrl)))return -EINVAL ; +if ((vmcs12->vm_exit_controls &VM_EXIT_LOAD_IA32_PKRS ) && +CC (!kvm_pkrs_valid (vmcs12->host_ia32_pkrs))) +return -EINVAL ; +#ifdef CONFIG_X86_64 ia32e = !!(vmcs12->vm_exit_controls &VM_EXIT_HOST_ADDR_SPACE_SIZE );#else @@ -3049,6 +3066,10 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu,if (nested_check_guest_non_reg_state (vmcs12))return -EINVAL ; +if ((vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS ) && +CC (!kvm_pkrs_valid (vmcs12->guest_ia32_pkrs))) +return -EINVAL ; +return 0 ; }
arch/x86/kvm/vmx/nested.c enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, · line 3409
Quoted 5 lines
arch/x86/kvm/vmx/nested.c +2
-0@@ -3384,6 +3405,10 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, (!from_vmentry || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) vmx->nested.vmcs01_guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS); + if (kvm_cpu_cap_has(X86_FEATURE_PKS) && + (!from_vmentry ||
This should be "!vmx->nested.nested_run_pending" instead of "!from_vmentry" to avoid the unnecessary VMREAD when restoring L2 with a pending VM-Enter.
Quoted 5 lines
arch/x86/kvm/vmx/nested.c +2
-0+ !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PKRS))) + vmx->nested.vmcs01_guest_pkrs = vmcs_read64(GUEST_IA32_PKRS);/* * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and*
...
arch/x86/kvm/vmx/vmx.h struct nested_vmx { · line 224
Quoted 8 lines
arch/x86/kvm/vmx/vmx.h +0
-0@@ -222,6 +222,8 @@ struct nested_vmx {u64 vmcs01_debugctl;u64 vmcs01_guest_bndcfgs;
Please pack these together, i.e. don't have a blank line between the various vmcs01_* fields.
Quoted 8 lines
arch/x86/kvm/vmx/vmx.h +2
-0+u64 vmcs01_guest_pkrs; +/* to migrate it to L1 if L2 writes to L1's CR8 directly */ int l1_tpr_threshold;Signature · 1 line
2.25.1
Re: [PATCH v7 8/8] KVM: VMX: Enable PKS for nested VM
Quoted 1 line · Sean Christopherson
Nit, use "KVM: nVMX:" for the shortlog scope.
Will change it.
Quoted 27 lines · Lei Wang
arch/x86/kvm/vmx/nested.c static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) · line 2443
Quoted 8 lines
arch/x86/kvm/vmx/nested.c +4
-0@@ -2433,6 +2437,10 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)if (kvm_mpx_supported () && vmx->nested.nested_run_pending && (vmcs12->vm_entry_controls &VM_ENTRY_LOAD_BNDCFGS ))vmcs_write64 (GUEST_BNDCFGS , vmcs12->guest_bndcfgs); + +if (vmx->nested.nested_run_pending && + (vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS )) +vmcs_write64 (GUEST_IA32_PKRS , vmcs12->guest_ia32_pkrs);As mentioned in the BNDCFGS thread, this does the wrong thing for SMM. But, after a lot of thought, handling this in
nested_vmx_enter_non_root_mode()would be little more than a band-aid, and a messy one at that, because KVM's SMM emulation is horrifically broken with respect to nVMX.Entry does to SMM does not modify _any_ state that is not saved in SMRAM. That we're having to deal with this crap is a symptom of KVM doing the complete wrong thing by piggybacking
nested_vmx_vmexit()andnested_vmx_enter_non_root_mode().The SDM's description of CET spells this out very, very clearly:
On processors that support CET shadow stacks, when the processor enters SMM, the processor saves the SSP register to the SMRAM state save area (see Table 31-3) and clears CR4.CET to 0. Thus, the initial execution environment of the SMI handler has CET disabled and all of the CET state of the interrupted program is still in the machine. An SMM that uses CET is required to save the interrupted program’s CET state and restore the CET state prior to exiting SMM.
Quoted 8 lines
It mostly works because no guest SMM handler does anything with most of the MSRs, but it's all wildy wrong. A concrete example of a lurking bug is if vmcs12 uses the VM-Exit MSR load list, in which case the forced
nested_vmx_vmexit()will load state that is never undone.So, my very strong vote is to ignore SMM and let someone who actually cares about SMM fix that mess properly by adding custom flows for exiting/re-entering L2 on SMI/RSM.
OK, I will leave the mess alone.
Quoted 11 lines
arch/x86/kvm/vmx/nested.c static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, · line 2532
Quoted 8 lines
arch/x86/kvm/vmx/nested.c +1
-0}if (nested_cpu_has_xsaves (vmcs12))@@ -2521,6 +2529,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,if (kvm_mpx_supported () && (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls &VM_ENTRY_LOAD_BNDCFGS )))vmcs_write64 (GUEST_BNDCFGS , vmx->nested.vmcs01_guest_bndcfgs); + if (kvm_cpu_cap_has(X86_FEATURE_PKS) &&ERROR: trailing whitespace
#85: FILE:arch/x86/kvm/vmx/nested.c:3407:
+^Iif (kvm_cpu_cap_has(X86_FEATURE_PKS)&& $
Sorry for my carelessness, will remove the trailing whitespace.
Quoted 29 lines
Quoted 29 lines
arch/x86/kvm/vmx/nested.c +12
-0+ (!vmx->nested.nested_run_pending || + !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PKRS))) +vmcs_write64 (GUEST_IA32_PKRS , vmx->nested.vmcs01_guest_pkrs); +vmx_set_rflags (vcpu, vmcs12->guest_rflags);/* EXCEPTION_BITMAP and CR0_GUEST_HOST_MASK should basically be the @@ -2897,6 +2910,10 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, vmcs12->host_ia32_perf_global_ctrl)))return -EINVAL ; +if ((vmcs12->vm_exit_controls &VM_EXIT_LOAD_IA32_PKRS ) && +CC (!kvm_pkrs_valid (vmcs12->host_ia32_pkrs))) +return -EINVAL ; +#ifdef CONFIG_X86_64 ia32e = !!(vmcs12->vm_exit_controls &VM_EXIT_HOST_ADDR_SPACE_SIZE );#else @@ -3049,6 +3066,10 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu,if (nested_check_guest_non_reg_state (vmcs12))return -EINVAL ; +if ((vmcs12->vm_entry_controls &VM_ENTRY_LOAD_IA32_PKRS ) && +CC (!kvm_pkrs_valid (vmcs12->guest_ia32_pkrs))) +return -EINVAL ; +return 0 ; }
Quoted 8 lines
arch/x86/kvm/vmx/nested.c enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, · line 3409
Quoted 6 lines
arch/x86/kvm/vmx/nested.c +2
-0@@ -3384,6 +3405,10 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, (!from_vmentry || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) vmx->nested.vmcs01_guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS); + if (kvm_cpu_cap_has(X86_FEATURE_PKS) && + (!from_vmentry ||This should be "!vmx->nested.nested_run_pending" instead of "!from_vmentry" to avoid the unnecessary VMREAD when restoring L2 with a pending VM-Enter.
Will fix that.
Quoted 7 lines
Quoted 5 lines
arch/x86/kvm/vmx/nested.c +2
-0+ !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PKRS))) + vmx->nested.vmcs01_guest_pkrs = vmcs_read64(GUEST_IA32_PKRS);/* * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* ...
Quoted 10 lines
arch/x86/kvm/vmx/vmx.h struct nested_vmx { · line 224
Quoted 8 lines
arch/x86/kvm/vmx/vmx.h +0
-0@@ -222,6 +222,8 @@ struct nested_vmx {u64 vmcs01_debugctl;u64 vmcs01_guest_bndcfgs;Please pack these together, i.e. don't have a blank line between the various vmcs01_* fields.
OK, will check them and remove the blank lines.
Kindly ping for the comments.
Quoted 131 lines · Lei Wang
This patch series is based on top of v10 PKS core support kernel patchset:
https://lore.kernel.org/lkml/[email protected]/---
Protection Keys for Supervisor Pages(PKS) is a feature that extends the Protection Keys architecture to support thread-specific permission restrictions on supervisor pages.
PKS works similar to an existing feature named PKU(protecting user pages). They both perform an additional check after normal paging permission checks are done. Access or Writes can be disabled via a MSR update without TLB flushes when permissions changes. If violating this addional check, #PF occurs and PFEC.PK bit will be set.
PKS introduces MSR
IA32_PKRSto manage supervisor protection key rights. The MSR contains 16 pairs of ADi and WDi bits. Each pair advertises on a group of pages with the same key which is set in the leaf paging-structureentries(bits[62:59]). Currently,IA32_PKRSis not supported by XSAVES architecture.This patchset aims to add the virtualization of PKS in KVM. It implemented PKS CPUID enumeration, vmentry/vmexit configuration, MSR exposure, nested supported etc. Currently, PKS is not yet supported for shadow paging.
Detailed information about PKS can be found in the latest Intel 64 and IA-32 Architectures Software Developer's Manual.
---
Changelogs:
v6->v7
- Add documentation to note that it's nice-to-have cache tracking for PKRS, and we also needn't hesitate to rip it out in the future if there's a strong reason to drop the caching. (Sean)
- Blindly reading PKRU/PKRS is wrong, fixed. (Sean)
- Add a non-inline helperkvm_mmu_pkr_bits()to read PKR bits. (Sean)
- Delete the comment for exposing the PKS because the pattern is common and the behavior is self-explanatory. (Sean)
- Add a helpervmx_set_host_pkrs()for setting host pkrs and rewrite the related code for concise. (Sean)
- Align an indentation inarch/x86/kvm/vmx/nested.c. (Sean)
- Read the current PKRS iffrom_vmentry== false under the nested condition. (Sean)
- v6: https://lore.kernel.org/lkml/[email protected]/
v5->v6
- PKRS is preserved on INIT. Add the PKRS reset operation inkvm_vcpu_reset. (Sean)
- Track the pkrs as u32. Add the code WARN on bits 64:32 being set in VMCS field. (Sean)
- Adjust the MSR intercept and entry/exit control in VMCS according to guest CPUID. This resolve the issue when userspace re-enable this feature. (Sean)
- Split VMX restriction on PKS support(entry/exit load controls) out of common x86. And put tdp restriction together with PKU in common x86. (Sean)
- Thanks for Sean to revise the comments inmmu.crelated toupdate_pkr_bitmap, which make it more clear for pkr bitmask cache usage.
- v5: https://lore.kernel.org/lkml/[email protected]/
v4->v5
- Make setting of MSR intercept/vmcs control bits not dependent on guest.CR4.PKS. And set them if PKS is exposed to guest. (Suggested by Sean)
- Add pkrs to standard register caching mechanism to help updatevcpu->arch.pkrson demand. Add related helper functions. (Suggested by Sean)
- Do the real pkrs update in VMCS field invmx_vcpu_resetandvmx_sync_vmcs_host_state(). (Sean)
- Add a newmmu_rolecr4_pksinstead of smushing PKU and PKS together. (Sean & Paolo)
- v4: https://lore.kernel.org/lkml/[email protected]/
v3->v4
- Make the MSR intercept and load-controls setting depend on CR4.PKS value
- shadow the guest pkrs and make it usable in PKS emultion
- add thecr4_pkeandcr4_pkscheck inpkr_maskupdate
- squash PATCH 2 and PATCH 5 to make the dependencies read more clear
- v3: https://lore.kernel.org/lkml/[email protected]/
v2->v3:
- No function changes since last submit
- rebase on the latest PKS kernel support:https://lore.kernel.org/lkml/[email protected]/- add
MSR_IA32_PKRSto the vmx_possible_passthrough_msrs[]
- RFC v2: https://lore.kernel.org/lkml/[email protected]/
v1->v2:
- rebase on the latest PKS kernel support:
https://github.com/weiny2/linux-kernel/tree/pks-rfc-v3
- add a kvm-unit-tests for PKS
- add the check inkvm_init_msr_listfor PKRS
- place theX86_CR4_PKSinmmu_role_bitsinkvm_set_cr4
- add the support to expose VM_{ENTRY, EXIT}_LOAD_IA32_PKRS in nested VMX MSR
- RFC v1: https://lore.kernel.org/lkml/[email protected]/---
Chenyi Qiang (7):
KVM: VMX: Introduce PKS VMCS fields
KVM: VMX: Add proper cache tracking for PKRS
KVM: X86: ExposeIA32_PKRSMSR
KVM: MMU: Rename the pkru to pkr
KVM: MMU: Add support for PKS emulation
KVM: VMX: Expose PKS to guest
KVM: VMX: Enable PKS for nested VMLei Wang (1):
KVM: MMU: Add helper function to get pkr bitsarch/x86/include/asm/kvm_host.h | 17 +++-- arch/x86/include/asm/vmx.h | 6 ++ arch/x86/kvm/cpuid.c | 13 +++- arch/x86/kvm/kvm_cache_regs.h | 7 ++ arch/x86/kvm/mmu.h | 29 +++---- arch/x86/kvm/mmu/mmu.c | 130 +++++++++++++++++++++++--------- arch/x86/kvm/vmx/capabilities.h | 6 ++ arch/x86/kvm/vmx/nested.c | 36 ++++++++- arch/x86/kvm/vmx/vmcs.h | 1 + arch/x86/kvm/vmx/vmcs12.c | 2 + arch/x86/kvm/vmx/vmcs12.h | 4 + arch/x86/kvm/vmx/vmx.c | 85 +++++++++++++++++++-- arch/x86/kvm/vmx/vmx.h | 14 +++- arch/x86/kvm/x86.c | 9 ++- arch/x86/kvm/x86.h | 8 ++ arch/x86/mm/pkeys.c | 6 ++ include/linux/pks.h | 7 ++ 17 files changed, 301 insertions(+), 79 deletions(-)
The current status of PKS virtualization
In reply to Lei WangHi,
I'm sorry to bother you by replying to the email from years ago. I would like to learn about the current status of PKS virtualization.
In short, I tried to rebase this patch series on the latest kernel. The result was a working kernel that supports PKS virtualization, which would be useful for my purposes. Would PKS virtualization be accepted even if the kernel itself does not use PKS?
Here's a longer explanation: I noticed that this patch series is built on top of basic PKS support. Meanwhile, it appears that the basic PKS support "was dropped after the main use case was rejected (pmem stray write protection)" [1]. I suppose that's why this patch series won't be merged into the kernel?
[1]: https://lore.kernel.org/lkml/[email protected]/
For my purposes, I don't need the Linux kernel to use PKS. I do want the kernel to support PKS virtualization so that I can run another OS that requires PKS support with the help of KVM. Fundamentally, I don't think this patch series has to be built on top of basic PKS support. But I am unsure whether there is a policy or convention that states virtualization support can only be added after basic support.
One problem is that if the Linux kernel does not use PKS, we will be unable to test PKS virtualization with a guest Linux kernel. However, given that we have KVM unit test infrastructure, I believe we can find a way to properly test PKS virtualization for its correctness?
I'd like to hear from you to know whether I understand things correctly. Thank you in advance for any feedback.
Thanks,
Ruihan Li
Paolo Bonzini
maintainerRe: The current status of PKS virtualization
Il lun 10 nov 2025, 17:32 Ruihan Li <[email protected]> ha scritto:
Quoted 10 lines
Hi,
I'm sorry to bother you by replying to the email from years ago. I would like to learn about the current status of PKS virtualization.
In short, I tried to rebase this patch series on the latest kernel. The result was a working kernel that supports PKS virtualization, which would be useful for my purposes. Would PKS virtualization be accepted even if the kernel itself does not use PKS?
Yes, I think it should.
Virtualized PKS does not depend on host PKS, because it uses an MSR rather than XSAVE areas (which are harder to add to KVM without host support).
Quoted 4 lines
Fundamentally, I don't think this patch series
has to be built on top of basic PKS support. But I am unsure whether there is a policy or convention that states virtualization support can only be added after basic support.
No, there is none. In fact, the only dependency of the original series on host PKS was for functions to read/write the host PKRS MSR. Without host PKS support it could be loaded with all-ones, or technically it could even be left with the guest value. Since the host clears CR4.PKS, the actual value won't matter.
Quoted 4 lines
One problem is that if the Linux kernel does not use PKS, we will be unable to test PKS virtualization with a guest Linux kernel. However, given that we have KVM unit test infrastructure, I believe we can find a way to properly test PKS virtualization for its correctness?
I agree. Thanks!
Paolo
Quoted 6 lines
I'd like to hear from you to know whether I understand things correctly. Thank you in advance for any feedback.
Thanks,
Ruihan Li
Re: The current status of PKS virtualization
Quoted 5 lines · Paolo Bonzini
No, there is none. In fact, the only dependency of the original series on host PKS was for functions to read/write the host PKRS MSR. Without host PKS support it could be loaded with all-ones, or technically it could even be left with the guest value. Since the host clears CR4.PKS, the actual value won't matter.
Thanks a lot for your quick and detailed reply! That's good news for me. Then I plan to spend some time tidying up my rebased version to see if I can get PKS virtualization upstreamed.
As a side note, I can no longer contact the original author of this patch series. At least, my previous email was returned by Intel's server. However, since more than three years have passed, I assume it's okay for me to post a new version after I have the code ready.
Thanks,
Ruihan Li
Re: The current status of PKS virtualization
Quoted 7 lines · Ruihan Li
Quoted 5 lines · Paolo Bonzini
No, there is none. In fact, the only dependency of the original series on host PKS was for functions to read/write the host PKRS MSR. Without host PKS support it could be loaded with all-ones, or technically it could even be left with the guest value. Since the host clears CR4.PKS, the actual value won't matter.
Quoted 8 lines
Thanks a lot for your quick and detailed reply! That's good news for me. Then I plan to spend some time tidying up my rebased version to see if I can get PKS virtualization upstreamed.
As a side note, I can no longer contact the original author of this patch series. At least, my previous email was returned by Intel's server. However, since more than three years have passed, I assume it's okay for me to post a new version after I have the code ready.
Lei has left Intel so the mail address in unreachable.
And as you found, we dropped the PKS KVM upstream along with the base PKS support due to no valid use case in Linux. You can feel free to continue the upstream work. But I'm not sure if your use case is compelling enough to be accepted by maintainers.
Quoted 4 lines
Thanks,
Ruihan Li
Re: The current status of PKS virtualization
Quoted 4 lines · Chenyi Qiang
Lei has left Intel so the mail address in unreachable.
And as you found, we dropped the PKS KVM upstream along with the base PKS support due to no valid use case in Linux. You can feel free to continue the upstream work.
Thanks for the reply and for the information!
By the way, I'm just curious (feel free to ignore this question): Is there an on-list discussion that rejects the originally proposed PKS use cases?
I found that pmem stray write protection was rejected [1], but there is no reason given nor any reference provided. After searching the list, I found the latest patch series that attempts to add pmem stray protection [2]. However, I didn't find any discussion rejecting the use case. Maybe the discussion happened off the list? Or did I miss something?
[1]: https://lore.kernel.org/lkml/[email protected]/
[2]: https://lore.kernel.org/lkml/[email protected]/
Quoted 1 line
But I'm not sure if your use case is compelling enough to be accepted by maintainers.
Yeah, I have the same concern. So, I asked about the current status first. So far, Paolo's reply seems positive, so maybe I can try it out if I can find enough time.
Thanks,
Ruihan Li
Re: The current status of PKS virtualization
Quoted 12 lines · Ruihan Li
Quoted 4 lines · Chenyi Qiang
Lei has left Intel so the mail address in unreachable.
And as you found, we dropped the PKS KVM upstream along with the base PKS support due to no valid use case in Linux. You can feel free to continue the upstream work.
Thanks for the reply and for the information!
By the way, I'm just curious (feel free to ignore this question): Is there an on-list discussion that rejects the originally proposed PKS use cases?
Quoted 5 lines
I found that pmem stray write protection was rejected [1], but there is no reason given nor any reference provided. After searching the list, I found the latest patch series that attempts to add pmem stray protection [2]. However, I didn't find any discussion rejecting the use case. Maybe the discussion happened off the list? Or did I miss something?
The discussion happened off the list. The customer demand for this feature declined to such an extent that it was not worth working on any longer.