From: Huaitong Han <hidden> Date: 2015-11-09 11:56:21
The protection-key feature provides an additional mechanism by which IA-32e
paging controls access to usermode addresses.
Hardware support for protection keys for user pages is enumerated with CPUID
feature flag CPUID.7.0.ECX[3]:PKU. Software support is CPUID.7.0.ECX[4]:OSPKE
with the setting of CR4.PKE(bit 22).
When CR4.PKE = 1, every linear address is associated with the 4-bit protection
key located in bits 62:59 of the paging-structure entry that mapped the page
containing the linear address. The PKRU register determines, for each
protection key, whether user-mode addresses with that protection key may be
read or written.
The PKRU register (protection key rights for user pages) is a 32-bit register
with the following format: for each i (0 ≤ i ≤ 15), PKRU[2i] is the
access-disable bit for protection key i (ADi); PKRU[2i+1] is the write-disable
bit for protection key i (WDi).
Software can use the RDPKRU and WRPKRU instructions with ECX = 0 to read and
write PKRU. In addition, the PKRU register is XSAVE-managed state and can thus
be read and written by instructions in the XSAVE feature set.
PFEC.PK (bit 5) is defined as protection key violations.
The specification of Protection Keys can be found at SDM (4.6.2, volume 3)
http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf.
The kernel native patchset have not yet been merged to upstream, you can found
at git://git.kernel.org/pub/scm/linux/kernel/git/daveh/x86-pkeys.git pkeys-v007.
Huaitong Han (9):
KVM, pkeys: expose CPUID:PKU to guest
KVM, pkeys: add pkeys support when setting CR4
KVM, pkeys: expose CPUID:OSPKE to guest
KVM, pkeys: disable pkeys for guests in non-paging mode
KVM, pkeys: update memeory permission bitmask for pkeys
KVM, pkeys: add pkeys support for permission_fault logic
KVM, pkeys: Add pkeys support for gva_to_gpa funcions
KVM, pkeys: add pkeys support for xsave state
KVM, pkeys: disable PKU feature without ept
arch/x86/include/asm/kvm_host.h | 9 +++++--
arch/x86/kvm/cpuid.c | 23 ++++++++++++++--
arch/x86/kvm/cpuid.h | 8 ++++++
arch/x86/kvm/mmu.c | 32 ++++++++++++++++++++--
arch/x86/kvm/mmu.h | 59 +++++++++++++++++++++++++++++++++++++----
arch/x86/kvm/paging_tmpl.h | 20 +++++++++++---
arch/x86/kvm/vmx.c | 10 +++----
arch/x86/kvm/x86.c | 34 +++++++++++++++++++-----
arch/x86/kvm/x86.h | 3 ++-
9 files changed, 171 insertions(+), 27 deletions(-)
--
2.4.3
From: Huaitong Han <hidden> Date: 2015-11-09 11:56:22
This patch expose X86_FEATURE_PKU to guest, X86_FEATURE_PKU is referred to
as "PKU" in the hardware documentation: CPUID.7.0.ECX[3]:PKU.
Signed-off-by: Huaitong Han <redacted>
@@ -354,6 +354,9 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,constu32kvm_supported_word10_x86_features=F(XSAVEOPT)|F(XSAVEC)|F(XGETBV1)|f_xsaves;+/* cpuid 7.0.ecx*/+constu32kvm_supported_word11_x86_features=F(PKU)|0/*OSPKE*/;+/* all calls to cpuid_count() should be made on the same cpu */get_cpu();
@@ -431,10 +434,13 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,cpuid_mask(&entry->ebx,9);// TSC_ADJUST is emulatedentry->ebx|=F(TSC_ADJUST);-}else+entry->ecx&=kvm_supported_word11_x86_features;+cpuid_mask(&entry->ecx,13);+}else{entry->ebx=0;+entry->ecx=0;+}entry->eax=0;-entry->ecx=0;entry->edx=0;break;}
From: Huaitong Han <hidden> Date: 2015-11-09 11:56:24
This patch exposes X86_FEATURE_OSPKE to guest, X86_FEATURE_OSPKE is
software support for pkeys, enumerated with CPUID.7.0.ECX[4]:OSPKE,
and it reflects the setting of CR4.PKE.
Signed-off-by: Huaitong Han <redacted>
From: Huaitong Han <hidden> Date: 2015-11-09 11:56:26
Pkeys is disabled if CPU is in non-paging mode in hardware. However KVM
always uses paging mode to emulate guest non-paging, mode with TDP. To
emulate this behavior, pkeys needs to be manually disabled when guest
switches to non-paging mode.
Signed-off-by: Huaitong Han <redacted>
From: Huaitong Han <hidden> Date: 2015-11-09 11:56:57
Pkeys define a new status bit in the PFEC. PFEC.PK (bit 5), if some
conditions is true, the fault is considered as a PKU violation.
This patch updates memeory permission bitmask for pkeys.
Signed-off-by: Huaitong Han <redacted>
From: Huaitong Han <hidden> Date: 2015-11-09 11:57:00
Protection keys define a new 4-bit protection key field (PKEY) in bits
62:59 of leaf entries of the page tables, the PKEY is an index to PKRU
register(16 domains), every domain has 2 bits(write disable bit, access
disable bit).
Static logic has been produced in update_permission_bitmask, dynamic logic
need read pkey from page table entries, get pkru value, and deduce the
correct result.
Signed-off-by: Huaitong Han <redacted>
From: Paolo Bonzini <pbonzini@redhat.com> Date: 2015-11-09 12:32:21
On 09/11/2015 12:54, Huaitong Han wrote:
quoted hunk
This patch exposes X86_FEATURE_OSPKE to guest, X86_FEATURE_OSPKE is
software support for pkeys, enumerated with CPUID.7.0.ECX[4]:OSPKE,
and it reflects the setting of CR4.PKE.
Signed-off-by: Huaitong Han <redacted>
From: Paolo Bonzini <pbonzini@redhat.com> Date: 2015-11-09 12:43:07
On 09/11/2015 12:54, Huaitong Han wrote:
quoted hunk
Protection keys define a new 4-bit protection key field (PKEY) in bits
62:59 of leaf entries of the page tables, the PKEY is an index to PKRU
register(16 domains), every domain has 2 bits(write disable bit, access
disable bit).
Static logic has been produced in update_permission_bitmask, dynamic logic
need read pkey from page table entries, get pkru value, and deduce the
correct result.
Signed-off-by: Huaitong Han <redacted>
PK is only applicable to guest page tables, but if you do not support
PKRU without EPT (patch 9), none of this is necessary, is it?
quoted hunk
/*
* If CPL < 3, SMAP prevention are disabled if EFLAGS.AC = 1.
@@ -163,8 +212,8 @@ static inline bool permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, * but it will be one in index if SMAP checks are being overridden. * It is important to keep this branchless. */- unsigned long smap = (cpl - 3) & (rflags & X86_EFLAGS_AC);- int index = (pfec >> 1) ++ smap = (cpl - 3) & (rflags & X86_EFLAGS_AC);+ index = (pfec >> 1) + (smap >> (X86_EFLAGS_AC_BIT - PFERR_RSVD_BIT + 1)); WARN_ON(pfec & PFERR_RSVD_MASK);
From: Paolo Bonzini <pbonzini@redhat.com> Date: 2015-11-09 13:17:25
On 09/11/2015 13:43, Paolo Bonzini wrote:
On 09/11/2015 12:54, Huaitong Han wrote:
quoted
Protection keys define a new 4-bit protection key field (PKEY) in bits
62:59 of leaf entries of the page tables, the PKEY is an index to PKRU
register(16 domains), every domain has 2 bits(write disable bit, access
disable bit).
Static logic has been produced in update_permission_bitmask, dynamic logic
need read pkey from page table entries, get pkru value, and deduce the
correct result.
Signed-off-by: Huaitong Han <redacted>
A little more optimized:
pkru_bits = (pkru >> (pte_pkeys * PKRU_ATTRS)) & 3;
/*
* Ignore PKRU.WD if not relevant to this access (a read,
* or a supervisor mode access if CR0.WP=0).
*/
if (!wf || (!uf && !is_write_protection(vcpu)))
pkru_bits &= ~(1 << PKRU_WRITE);
... and then just check pkru_bits != 0.
quoted
+ /*+ * PK bit right value in pfec equal to+ * PK bit current value in pfec and pk value.+ */+ pfec &= (pk << PFERR_PK_BIT) + ~PFERR_PK_MASK;
PK is only applicable to guest page tables, but if you do not support
PKRU without EPT (patch 9), none of this is necessary, is it?
Doh. :( Sorry, this is of course needed for the emulation case.
I think you should optimize this for the common case where pkru is zero,
hence pk will always be zero. So something like
pkru = is_long_mode(vcpu) ? read_pkru() : 0;
if (unlikely(pkru) && (pfec & PFERR_PK_MASK)) {
... from above ... */
/* Flip PFERR_PK_MASK if pkru_bits is non-zero */
pfec ^= -pkru_bits & PFERR_PK_MASK;
}
Paolo
quoted
/*
* If CPL < 3, SMAP prevention are disabled if EFLAGS.AC = 1.
@@ -163,8 +212,8 @@ static inline bool permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, * but it will be one in index if SMAP checks are being overridden. * It is important to keep this branchless. */- unsigned long smap = (cpl - 3) & (rflags & X86_EFLAGS_AC);- int index = (pfec >> 1) ++ smap = (cpl - 3) & (rflags & X86_EFLAGS_AC);+ index = (pfec >> 1) + (smap >> (X86_EFLAGS_AC_BIT - PFERR_RSVD_BIT + 1)); WARN_ON(pfec & PFERR_RSVD_MASK);
@@ -4107,7 +4107,7 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva,if(vcpu_match_mmio_gva(vcpu,gva)&&!permission_fault(vcpu,vcpu->arch.walk_mmu,-vcpu->arch.access,access)){+vcpu->arch.access,0,access)){*gpa=vcpu->arch.mmio_gfn<<PAGE_SHIFT|(gva&(PAGE_SIZE-1));trace_vcpu_match_mmio(gva,*gpa,write,false);
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
I think checking is_long_mode is not necessary here, since is_long_mode
is not checked in update_permission_bitmask but (dynamically) in
permission_fault.
From: Paolo Bonzini <pbonzini@redhat.com> Date: 2015-11-09 13:26:37
On 09/11/2015 12:54, Huaitong Han wrote:
The protection-key feature provides an additional mechanism by which IA-32e
paging controls access to usermode addresses.
Hardware support for protection keys for user pages is enumerated with CPUID
feature flag CPUID.7.0.ECX[3]:PKU. Software support is CPUID.7.0.ECX[4]:OSPKE
with the setting of CR4.PKE(bit 22).
When CR4.PKE = 1, every linear address is associated with the 4-bit protection
key located in bits 62:59 of the paging-structure entry that mapped the page
containing the linear address. The PKRU register determines, for each
protection key, whether user-mode addresses with that protection key may be
read or written.
The PKRU register (protection key rights for user pages) is a 32-bit register
with the following format: for each i (0 ≤ i ≤ 15), PKRU[2i] is the
access-disable bit for protection key i (ADi); PKRU[2i+1] is the write-disable
bit for protection key i (WDi).
Software can use the RDPKRU and WRPKRU instructions with ECX = 0 to read and
write PKRU. In addition, the PKRU register is XSAVE-managed state and can thus
be read and written by instructions in the XSAVE feature set.
Hi, this looks more or less okay. I made a few comments on the
individual patches.
Please add a test for PKRU to kvm-unit-tests' access.c. I will _not_
merge this feature without unit tests. I have merged nested VPID
without, and it was a mistake because they were never submitted and
probably never will.
Thanks,
Paolo
On Mon, 2015-11-09 at 14:17 +0100, Paolo Bonzini wrote:
quoted
quoted
static inline bool permission_fault(struct kvm_vcpu *vcpu,
struct kvm_mmu *mmu,
- unsigned pte_access,
unsigned pfec)
+ unsigned pte_access, unsigned pte_pkeys,
unsigned pfec)
{
- int cpl = kvm_x86_ops->get_cpl(vcpu);
- unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
+ unsigned long smap, rflags;
+ u32 pkru;
+ int cpl, index;
+ bool wf, uf, pk, pkru_ad, pkru_wd;
+
+ cpl = kvm_x86_ops->get_cpl(vcpu);
+ rflags = kvm_x86_ops->get_rflags(vcpu);
+
+ pkru = read_pkru();
+
+ /*
+ * PKRU defines 32 bits, there are 16 domains and 2
attribute bits per
+ * domain in pkru, pkey is index to a defined domain, so
the value of
+ * pkey * PKRU_ATTRS + R/W is offset of a defined domain
attribute.
+ */
+ pkru_ad = (pkru >> (pte_pkeys * PKRU_ATTRS + PKRU_READ))
& 1;
+ pkru_wd = (pkru >> (pte_pkeys * PKRU_ATTRS +
PKRU_WRITE)) & 1;
+
+ wf = pfec & PFERR_WRITE_MASK;
+ uf = pfec & PFERR_USER_MASK;
+
+ /*
+ * PKeys 2nd and 6th conditions:
+ * 2.EFER_LMA=1
+ * 6.PKRU.AD=1
+ * or The access is a data write and PKRU.WD=1 and
+ * either CR0.WP=1 or it is a user mode
access
+ */
+ pk = is_long_mode(vcpu) && (pkru_ad ||
+ (pkru_wd && wf &&
(is_write_protection(vcpu) || uf)));
A little more optimized:
pkru_bits = (pkru >> (pte_pkeys * PKRU_ATTRS)) & 3;
/*
* Ignore PKRU.WD if not relevant to this access (a read,
* or a supervisor mode access if CR0.WP=0).
*/
if (!wf || (!uf && !is_write_protection(vcpu)))
pkru_bits &= ~(1 << PKRU_WRITE);
... and then just check pkru_bits != 0.
quoted
quoted
+ /*+ * PK bit right value in pfec equal to+ * PK bit current value in pfec and pk value.+ */+ pfec &= (pk << PFERR_PK_BIT) + ~PFERR_PK_MASK;
PK is only applicable to guest page tables, but if you do not
support
PKRU without EPT (patch 9), none of this is necessary, is it?
Doh. :( Sorry, this is of course needed for the emulation case.
I think you should optimize this for the common case where pkru is
zero,
hence pk will always be zero. So something like
pkru = is_long_mode(vcpu) ? read_pkru() : 0;
if (unlikely(pkru) && (pfec & PFERR_PK_MASK)) {
... from above ... */
/* Flip PFERR_PK_MASK if pkru_bits is non-zero */
pfec ^= -pkru_bits & PFERR_PK_MASK;
If pkru_bits is zero, it means dynamically conditions is not met for
protection-key violations, so pfec on PK bit should be flipped. So I
guess it should be:
pfec ^= pkru_bits ? 0 : PFERR_PK_MASK;
From: Paolo Bonzini <pbonzini@redhat.com> Date: 2015-11-10 09:35:38
On 10/11/2015 10:28, Han, Huaitong wrote:
quoted
pkru = is_long_mode(vcpu) ? read_pkru() : 0;
if (unlikely(pkru) && (pfec & PFERR_PK_MASK)) {
... from above ... */
/* Flip PFERR_PK_MASK if pkru_bits is non-zero */
pfec ^= -pkru_bits & PFERR_PK_MASK;
If pkru_bits is zero, it means dynamically conditions is not met for
protection-key violations, so pfec on PK bit should be flipped. So I
guess it should be:
pfec ^= pkru_bits ? 0 : PFERR_PK_MASK;