qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

sgx.c (9645B)


      1 /*
      2  * SGX common code
      3  *
      4  * Copyright (C) 2021 Intel Corporation
      5  *
      6  * Authors:
      7  *   Yang Zhong<yang.zhong@intel.com>
      8  *   Sean Christopherson <sean.j.christopherson@intel.com>
      9  *
     10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
     11  * See the COPYING file in the top-level directory.
     12  */
     13 #include "qemu/osdep.h"
     14 #include "hw/i386/pc.h"
     15 #include "hw/i386/sgx-epc.h"
     16 #include "hw/mem/memory-device.h"
     17 #include "monitor/qdev.h"
     18 #include "monitor/monitor.h"
     19 #include "monitor/hmp-target.h"
     20 #include "qapi/error.h"
     21 #include "qapi/qapi-commands-misc-target.h"
     22 #include "exec/address-spaces.h"
     23 #include "sysemu/hw_accel.h"
     24 #include "sysemu/reset.h"
     25 #include <sys/ioctl.h>
     26 #include "hw/acpi/aml-build.h"
     27 
     28 #define SGX_MAX_EPC_SECTIONS            8
     29 #define SGX_CPUID_EPC_INVALID           0x0
     30 
     31 /* A valid EPC section. */
     32 #define SGX_CPUID_EPC_SECTION           0x1
     33 #define SGX_CPUID_EPC_MASK              0xF
     34 
     35 #define SGX_MAGIC 0xA4
     36 #define SGX_IOC_VEPC_REMOVE_ALL       _IO(SGX_MAGIC, 0x04)
     37 
     38 #define RETRY_NUM                       2
     39 
     40 static int sgx_epc_device_list(Object *obj, void *opaque)
     41 {
     42     GSList **list = opaque;
     43 
     44     if (object_dynamic_cast(obj, TYPE_SGX_EPC)) {
     45         *list = g_slist_append(*list, DEVICE(obj));
     46     }
     47 
     48     object_child_foreach(obj, sgx_epc_device_list, opaque);
     49     return 0;
     50 }
     51 
     52 static GSList *sgx_epc_get_device_list(void)
     53 {
     54     GSList *list = NULL;
     55 
     56     object_child_foreach(qdev_get_machine(), sgx_epc_device_list, &list);
     57     return list;
     58 }
     59 
     60 void sgx_epc_build_srat(GArray *table_data)
     61 {
     62     GSList *device_list = sgx_epc_get_device_list();
     63 
     64     for (; device_list; device_list = device_list->next) {
     65         DeviceState *dev = device_list->data;
     66         Object *obj = OBJECT(dev);
     67         uint64_t addr, size;
     68         int node;
     69 
     70         node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP,
     71                                         &error_abort);
     72         addr = object_property_get_uint(obj, SGX_EPC_ADDR_PROP, &error_abort);
     73         size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP, &error_abort);
     74 
     75         build_srat_memory(table_data, addr, size, node, MEM_AFFINITY_ENABLED);
     76     }
     77     g_slist_free(device_list);
     78 }
     79 
     80 static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
     81 {
     82     return (low & MAKE_64BIT_MASK(12, 20)) +
     83            ((high & MAKE_64BIT_MASK(0, 20)) << 32);
     84 }
     85 
     86 static SGXEPCSectionList *sgx_calc_host_epc_sections(uint64_t *size)
     87 {
     88     SGXEPCSectionList *head = NULL, **tail = &head;
     89     SGXEPCSection *section;
     90     uint32_t i, type;
     91     uint32_t eax, ebx, ecx, edx;
     92     uint32_t j = 0;
     93 
     94     for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
     95         host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
     96 
     97         type = eax & SGX_CPUID_EPC_MASK;
     98         if (type == SGX_CPUID_EPC_INVALID) {
     99             break;
    100         }
    101 
    102         if (type != SGX_CPUID_EPC_SECTION) {
    103             break;
    104         }
    105 
    106         section = g_new0(SGXEPCSection, 1);
    107         section->node = j++;
    108         section->size = sgx_calc_section_metric(ecx, edx);
    109         *size += section->size;
    110         QAPI_LIST_APPEND(tail, section);
    111     }
    112 
    113     return head;
    114 }
    115 
    116 static void sgx_epc_reset(void *opaque)
    117 {
    118     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
    119     HostMemoryBackend *hostmem;
    120     SGXEPCDevice *epc;
    121     int failures;
    122     int fd, i, j, r;
    123     static bool warned = false;
    124 
    125     /*
    126      * The second pass is needed to remove SECS pages that could not
    127      * be removed during the first.
    128      */
    129     for (i = 0; i < RETRY_NUM; i++) {
    130         failures = 0;
    131         for (j = 0; j < pcms->sgx_epc.nr_sections; j++) {
    132             epc = pcms->sgx_epc.sections[j];
    133             hostmem = MEMORY_BACKEND(epc->hostmem);
    134             fd = memory_region_get_fd(host_memory_backend_get_memory(hostmem));
    135 
    136             r = ioctl(fd, SGX_IOC_VEPC_REMOVE_ALL);
    137             if (r == -ENOTTY && !warned) {
    138                 warned = true;
    139                 warn_report("kernel does not support SGX_IOC_VEPC_REMOVE_ALL");
    140                 warn_report("SGX might operate incorrectly in the guest after reset");
    141                 break;
    142             } else if (r > 0) {
    143                 /* SECS pages remain */
    144                 failures++;
    145                 if (i == 1) {
    146                     error_report("cannot reset vEPC section %d", j);
    147                 }
    148             }
    149         }
    150         if (!failures) {
    151             break;
    152         }
    153      }
    154 }
    155 
    156 SGXInfo *qmp_query_sgx_capabilities(Error **errp)
    157 {
    158     SGXInfo *info = NULL;
    159     uint32_t eax, ebx, ecx, edx;
    160     uint64_t size = 0;
    161 
    162     int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
    163     if (fd < 0) {
    164         error_setg(errp, "SGX is not enabled in KVM");
    165         return NULL;
    166     }
    167 
    168     info = g_new0(SGXInfo, 1);
    169     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
    170 
    171     info->sgx = ebx & (1U << 2) ? true : false;
    172     info->flc = ecx & (1U << 30) ? true : false;
    173 
    174     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
    175     info->sgx1 = eax & (1U << 0) ? true : false;
    176     info->sgx2 = eax & (1U << 1) ? true : false;
    177 
    178     info->sections = sgx_calc_host_epc_sections(&size);
    179     info->section_size = size;
    180 
    181     close(fd);
    182 
    183     return info;
    184 }
    185 
    186 static SGXEPCSectionList *sgx_get_epc_sections_list(void)
    187 {
    188     GSList *device_list = sgx_epc_get_device_list();
    189     SGXEPCSectionList *head = NULL, **tail = &head;
    190     SGXEPCSection *section;
    191 
    192     for (; device_list; device_list = device_list->next) {
    193         DeviceState *dev = device_list->data;
    194         Object *obj = OBJECT(dev);
    195 
    196         section = g_new0(SGXEPCSection, 1);
    197         section->node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP,
    198                                                  &error_abort);
    199         section->size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP,
    200                                                  &error_abort);
    201         QAPI_LIST_APPEND(tail, section);
    202     }
    203     g_slist_free(device_list);
    204 
    205     return head;
    206 }
    207 
    208 SGXInfo *qmp_query_sgx(Error **errp)
    209 {
    210     SGXInfo *info = NULL;
    211     X86MachineState *x86ms;
    212     PCMachineState *pcms =
    213         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
    214                                               TYPE_PC_MACHINE);
    215     if (!pcms) {
    216         error_setg(errp, "SGX is only supported on PC machines");
    217         return NULL;
    218     }
    219 
    220     x86ms = X86_MACHINE(pcms);
    221     if (!x86ms->sgx_epc_list) {
    222         error_setg(errp, "No EPC regions defined, SGX not available");
    223         return NULL;
    224     }
    225 
    226     SGXEPCState *sgx_epc = &pcms->sgx_epc;
    227     info = g_new0(SGXInfo, 1);
    228 
    229     info->sgx = true;
    230     info->sgx1 = true;
    231     info->sgx2 = true;
    232     info->flc = true;
    233     info->section_size = sgx_epc->size;
    234     info->sections = sgx_get_epc_sections_list();
    235 
    236     return info;
    237 }
    238 
    239 void hmp_info_sgx(Monitor *mon, const QDict *qdict)
    240 {
    241     Error *err = NULL;
    242     SGXEPCSectionList *section_list, *section;
    243     g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
    244 
    245     if (err) {
    246         error_report_err(err);
    247         return;
    248     }
    249     monitor_printf(mon, "SGX support: %s\n",
    250                    info->sgx ? "enabled" : "disabled");
    251     monitor_printf(mon, "SGX1 support: %s\n",
    252                    info->sgx1 ? "enabled" : "disabled");
    253     monitor_printf(mon, "SGX2 support: %s\n",
    254                    info->sgx2 ? "enabled" : "disabled");
    255     monitor_printf(mon, "FLC support: %s\n",
    256                    info->flc ? "enabled" : "disabled");
    257     monitor_printf(mon, "size: %" PRIu64 "\n",
    258                    info->section_size);
    259 
    260     section_list = info->sections;
    261     for (section = section_list; section; section = section->next) {
    262         monitor_printf(mon, "NUMA node #%" PRId64 ": ",
    263                        section->value->node);
    264         monitor_printf(mon, "size=%" PRIu64 "\n",
    265                        section->value->size);
    266     }
    267 }
    268 
    269 bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
    270 {
    271     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
    272     SGXEPCDevice *epc;
    273 
    274     if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
    275         return true;
    276     }
    277 
    278     epc = pcms->sgx_epc.sections[section_nr];
    279 
    280     *addr = epc->addr;
    281     *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
    282 
    283     return false;
    284 }
    285 
    286 void pc_machine_init_sgx_epc(PCMachineState *pcms)
    287 {
    288     SGXEPCState *sgx_epc = &pcms->sgx_epc;
    289     X86MachineState *x86ms = X86_MACHINE(pcms);
    290     SgxEPCList *list = NULL;
    291     Object *obj;
    292 
    293     memset(sgx_epc, 0, sizeof(SGXEPCState));
    294     if (!x86ms->sgx_epc_list) {
    295         return;
    296     }
    297 
    298     sgx_epc->base = x86ms->above_4g_mem_start + x86ms->above_4g_mem_size;
    299 
    300     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
    301     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
    302                                 &sgx_epc->mr);
    303 
    304     for (list = x86ms->sgx_epc_list; list; list = list->next) {
    305         obj = object_new("sgx-epc");
    306 
    307         /* set the memdev link with memory backend */
    308         object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
    309                               &error_fatal);
    310         /* set the numa node property for sgx epc object */
    311         object_property_set_uint(obj, SGX_EPC_NUMA_NODE_PROP, list->value->node,
    312                              &error_fatal);
    313         object_property_set_bool(obj, "realized", true, &error_fatal);
    314         object_unref(obj);
    315     }
    316 
    317     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
    318         error_report("Size of all 'sgx-epc' =0x%"PRIx64" causes EPC to wrap",
    319                      sgx_epc->size);
    320         exit(EXIT_FAILURE);
    321     }
    322 
    323     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
    324 
    325     /* register the reset callback for sgx epc */
    326     qemu_register_reset(sgx_epc_reset, NULL);
    327 }