diff mbox series

[10/11] sev/i386: add SEV specific MemoryDebugOps.

Message ID fad5f676176aa01eff3eac4d2c0ecbbc8c975681.1605316268.git.ashish.kalra@amd.com
State New
Headers show
Series Add QEMU debug support for SEV guests | expand

Commit Message

Ashish Kalra Nov. 16, 2020, 6:52 p.m. UTC
From: Ashish Kalra <ashish.kalra@amd.com>

Add SEV specific MemoryDebugOps which override the default MemoryDebugOps
when SEV memory encryption is enabled. The SEV specific MemoryDebugOps
invoke the generic address_space_rw_debug helpers which will then invoke
the memory region specific callbacks to handle and access encrypted memory
when guest RAM is accessed.

Also invoke the memory encryption API to override any CPU class specific
callbacks to handle memory encryption.

Specifically for SEV we override CPU class specific guest MMU/page-table walker
to invoke a SEV specific handler which can handle guest encrypted memory and
also clear C-bit when walking SEV guest page table.

Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
 accel/kvm/kvm-all.c  |   1 +
 accel/kvm/sev-stub.c |   4 +
 include/sysemu/sev.h |  11 +++
 target/i386/kvm.c    |   4 +
 target/i386/sev.c    | 185 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 205 insertions(+)
diff mbox series

Patch

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 042205e3e1..6d812d5b09 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2234,6 +2234,7 @@  static int kvm_init(MachineState *ms)
         kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
         kvm_state->memcrypt_debug_ops_memory_region =
             sev_set_debug_ops_memory_region;
+        kvm_state->memcrypt_debug_ops_cpu_state = sev_set_debug_ops_cpu_state;
     }
 
     ret = kvm_arch_init(ms, s);
diff --git a/accel/kvm/sev-stub.c b/accel/kvm/sev-stub.c
index 3f1f0ef217..ad27226058 100644
--- a/accel/kvm/sev-stub.c
+++ b/accel/kvm/sev-stub.c
@@ -19,6 +19,10 @@  void sev_set_debug_ops_memory_region(void *handle, MemoryRegion *mr)
 {
 }
 
+void sev_set_debug_ops_cpu_state(void *handle, CPUState *cpu)
+{
+}
+
 int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
 {
     abort();
diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h
index 6c37247915..e6f176b85b 100644
--- a/include/sysemu/sev.h
+++ b/include/sysemu/sev.h
@@ -19,4 +19,15 @@ 
 void *sev_guest_init(const char *id);
 int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len);
 void sev_set_debug_ops_memory_region(void *handle, MemoryRegion *mr);
+void sev_set_debug_ops_cpu_state(void *handle, CPUState *cpu);
+hwaddr sev_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
+                                         MemTxAttrs *attrs);
+MemTxResult sev_address_space_read_debug(AddressSpace *as, hwaddr addr,
+                                         MemTxAttrs attrs, void *ptr,
+                                         hwaddr len);
+MemTxResult sev_address_space_write_rom_debug(AddressSpace *as,
+                                              hwaddr addr,
+                                              MemTxAttrs attrs,
+                                              const void *ptr,
+                                              hwaddr len);
 #endif
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index cf46259534..7a2d10b745 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -1838,6 +1838,10 @@  int kvm_arch_init_vcpu(CPUState *cs)
 
     kvm_init_msrs(cpu);
 
+    if (kvm_memcrypt_enabled()) {
+        kvm_memcrypt_set_debug_ops_cpu_state(cs);
+    }
+
     r = hyperv_init_vcpu(cpu);
     if (r) {
         goto fail;
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 3036fb3e43..b942593bc8 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -843,6 +843,191 @@  sev_set_debug_ops_memory_region(void *handle, MemoryRegion *mr)
     memory_region_set_ram_debug_ops(mr, &sev_ops);
 }
 
+hwaddr sev_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
+                                         MemTxAttrs *attrs)
+{
+    X86CPU *cpu = X86_CPU(cs);
+    CPUX86State *env = &cpu->env;
+    target_ulong pde_addr, pte_addr;
+    uint64_t pte;
+    int32_t a20_mask;
+    uint32_t page_offset;
+    int page_size;
+    uint64_t me_mask;
+
+    me_mask = sev_get_me_mask();
+
+    *attrs = cpu_get_mem_attrs(env);
+
+    a20_mask = x86_get_a20_mask(env);
+    if (!(env->cr[0] & CR0_PG_MASK)) {
+        pte = addr & a20_mask;
+        page_size = 4096;
+    } else if (env->cr[4] & CR4_PAE_MASK) {
+        target_ulong pdpe_addr;
+        uint64_t pde, pdpe;
+
+#ifdef TARGET_X86_64
+        if (env->hflags & HF_LMA_MASK) {
+            bool la57 = env->cr[4] & CR4_LA57_MASK;
+            uint64_t pml5e_addr, pml5e;
+            uint64_t pml4e_addr, pml4e;
+            int32_t sext;
+
+            /* test virtual address sign extension */
+            sext = la57 ? (int64_t)addr >> 56 : (int64_t)addr >> 47;
+            if (sext != 0 && sext != -1) {
+                return -1;
+            }
+
+            if (la57) {
+                pml5e_addr = ((env->cr[3] & ~0xfff & me_mask) +
+                        (((addr >> 48) & 0x1ff) << 3)) & a20_mask;
+                pml5e = ldq_phys_debug(cs, pml5e_addr) & me_mask;
+                if (!(pml5e & PG_PRESENT_MASK)) {
+                    return -1;
+                }
+            } else {
+                pml5e = env->cr[3] & me_mask;
+            }
+
+            pml4e_addr = ((pml5e & PG_ADDRESS_MASK) +
+                    (((addr >> 39) & 0x1ff) << 3)) & a20_mask;
+            pml4e = ldq_phys_debug(cs, pml4e_addr) & me_mask;
+            if (!(pml4e & PG_PRESENT_MASK)) {
+                return -1;
+            }
+            pdpe_addr = ((pml4e & PG_ADDRESS_MASK) +
+                         (((addr >> 30) & 0x1ff) << 3)) & a20_mask;
+            pdpe = ldq_phys_debug(cs, pdpe_addr) & me_mask;
+            if (!(pdpe & PG_PRESENT_MASK)) {
+                return -1;
+            }
+            if (pdpe & PG_PSE_MASK) {
+                page_size = 1024 * 1024 * 1024;
+                pte = pdpe;
+                goto out;
+            }
+
+        } else
+#endif
+        {
+            pdpe_addr = ((env->cr[3] & ~0x1f & me_mask) +
+                         ((addr >> 27) & 0x18)) & a20_mask;
+            pdpe = ldq_phys_debug(cs, pdpe_addr) & me_mask;
+            if (!(pdpe & PG_PRESENT_MASK)) {
+                return -1;
+            }
+        }
+
+        pde_addr = ((pdpe & PG_ADDRESS_MASK) +
+                    (((addr >> 21) & 0x1ff) << 3)) & a20_mask;
+        pde = ldq_phys_debug(cs, pde_addr) & me_mask;
+        if (!(pde & PG_PRESENT_MASK)) {
+            return -1;
+        }
+        if (pde & PG_PSE_MASK) {
+            /* 2 MB page */
+            page_size = 2048 * 1024;
+            pte = pde;
+        } else {
+            /* 4 KB page */
+            pte_addr = ((pde & PG_ADDRESS_MASK) +
+                        (((addr >> 12) & 0x1ff) << 3)) & a20_mask;
+            page_size = 4096;
+            pte = ldq_phys_debug(cs, pte_addr) & me_mask;
+        }
+        if (!(pte & PG_PRESENT_MASK)) {
+            return -1;
+        }
+    } else {
+        uint32_t pde;
+
+        /* page directory entry */
+        pde_addr = ((env->cr[3] & ~0xfff & me_mask) +
+                    ((addr >> 20) & 0xffc)) & a20_mask;
+        pde = x86_ldl_phys(cs, pde_addr) & me_mask;
+        if (!(pde & PG_PRESENT_MASK)) {
+            return -1;
+        }
+        if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+            pte = pde | ((pde & 0x1fe000LL) << (32 - 13));
+            page_size = 4096 * 1024;
+        } else {
+            /* page directory entry */
+            pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & a20_mask;
+            pte = ldl_phys_debug(cs, pte_addr) & me_mask;
+            if (!(pte & PG_PRESENT_MASK)) {
+                return -1;
+            }
+            page_size = 4096;
+        }
+        pte = pte & a20_mask;
+    }
+
+#ifdef TARGET_X86_64
+out:
+#endif
+    pte &= PG_ADDRESS_MASK & ~(page_size - 1);
+    page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+    return pte | page_offset;
+}
+
+MemTxResult sev_address_space_write_rom_debug(AddressSpace *as,
+                                              hwaddr addr,
+                                              MemTxAttrs attrs,
+                                              const void *ptr,
+                                              hwaddr len)
+{
+    /* set debug attrs to indicate memory access is from the debugger */
+    attrs.debug = 1;
+
+    /*
+     * Invoke address_space_rw debug helper
+     */
+    return address_space_write_rom_debug(as, addr, attrs, ptr, len);
+}
+
+MemTxResult sev_address_space_read_debug(AddressSpace *as, hwaddr addr,
+                                         MemTxAttrs attrs, void *ptr,
+                                         hwaddr len)
+{
+    /* set debug attrs to indicate memory access is from the debugger */
+    attrs.debug = 1;
+
+    /*
+     * Invoke address_space_rw debug helper
+     */
+    return address_space_read_debug(as, addr, attrs, ptr, len);
+}
+
+static const MemoryDebugOps sev_debug_ops = {
+    .read = sev_address_space_read_debug,
+    .write = sev_address_space_write_rom_debug
+};
+
+void
+sev_set_debug_ops_cpu_state(void *handle, CPUState *cs)
+{
+    SevGuestState *s = handle;
+    CPUClass *cc;
+
+    /* If policy does not allow debug then no need to register ops */
+    if (s->policy & SEV_POLICY_NODBG) {
+        return;
+    }
+
+    cc = CPU_GET_CLASS(cs);
+
+    /*
+     * Override guest MMU lookup/page-table-walker with SEV specific
+     * callback to handle encrypted memory.
+     */
+    cc->get_phys_page_attrs_debug = sev_cpu_get_phys_page_attrs_debug;
+
+    address_space_set_debug_ops(&sev_debug_ops);
+}
+
 static void
 sev_register_types(void)
 {