On Mon, Feb 27, 2023 at 04:45:46PM +0800, Robert Hoo wrote: >Emulate HW LAM masking when doing data access under 64-bit mode. > >kvm_lam_untag_addr() implements this: per CR4/CR3 LAM bits configuration, >firstly check the linear addr conforms LAM canonical, i.e. the highest >address bit matches bit 63. Then mask out meta data per LAM configuration. >If failed in above process, emulate #GP to guest. > >Signed-off-by: Robert Hoo <robert.hu@xxxxxxxxxxxxxxx> >--- > arch/x86/kvm/emulate.c | 13 ++++++++ > arch/x86/kvm/x86.h | 70 ++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 83 insertions(+) > >diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c >index 5cc3efa0e21c..77bd13f40711 100644 >--- a/arch/x86/kvm/emulate.c >+++ b/arch/x86/kvm/emulate.c >@@ -700,6 +700,19 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt, > *max_size = 0; > switch (mode) { > case X86EMUL_MODE_PROT64: >+ /* LAM applies only on data access */ >+ if (!fetch && guest_cpuid_has(ctxt->vcpu, X86_FEATURE_LAM)) { >+ enum lam_type type; >+ >+ type = kvm_vcpu_lam_type(la, ctxt->vcpu); >+ if (type == LAM_ILLEGAL) { >+ *linear = la; >+ goto bad; >+ } else { >+ la = kvm_lam_untag_addr(la, type); >+ } >+ } >+ > *linear = la; > va_bits = ctxt_virt_addr_bits(ctxt); > if (!__is_canonical_address(la, va_bits)) ... >+static inline u64 kvm_lam_untag_addr(u64 addr, enum lam_type type) >+{ >+ switch (type) { >+ case LAM_U57: >+ case LAM_S57: >+ addr = __canonical_address(addr, 57); >+ break; >+ case LAM_U48: >+ case LAM_S48: >+ addr = __canonical_address(addr, 48); >+ break; >+ case LAM_NONE: >+ default: >+ break; >+ } >+ >+ return addr; >+} LAM's change to canonicality check is: before performing the check, software metadata in pointers is masked by sign-extending the value of bit 56/47. so, to emulate this behavior, in kvm_lam_untag_addr(), we can simply: 1. determine which LAM configuration is enabled, LAM57 or LAM48. 2. mask software metadata by sign-extending the bit56/47, i.e., addr = (sign_extern64(addr, X) & ~BIT_ULL(63)) | (addr & BIT_ULL(63)); where X=56 for LAM57 and X=47 for LAM48. Note that this doesn't ensure the resulting @addr is canonical. It isn't a problem because the original canonicality check (__is_canonical_address() above) can identify non-canonical addresses and raise #GP/#SS to the guest.