xref: /openbmc/qemu/hw/i386/sgx.c (revision e9d2db818ff934afb366aea566d0b33acf7bced1)
11dec2e1fSSean Christopherson /*
21dec2e1fSSean Christopherson  * SGX common code
31dec2e1fSSean Christopherson  *
41dec2e1fSSean Christopherson  * Copyright (C) 2021 Intel Corporation
51dec2e1fSSean Christopherson  *
61dec2e1fSSean Christopherson  * Authors:
71dec2e1fSSean Christopherson  *   Yang Zhong<yang.zhong@intel.com>
81dec2e1fSSean Christopherson  *   Sean Christopherson <sean.j.christopherson@intel.com>
91dec2e1fSSean Christopherson  *
101dec2e1fSSean Christopherson  * This work is licensed under the terms of the GNU GPL, version 2 or later.
111dec2e1fSSean Christopherson  * See the COPYING file in the top-level directory.
121dec2e1fSSean Christopherson  */
131dec2e1fSSean Christopherson #include "qemu/osdep.h"
141dec2e1fSSean Christopherson #include "hw/i386/pc.h"
151dec2e1fSSean Christopherson #include "hw/i386/sgx-epc.h"
161dec2e1fSSean Christopherson #include "hw/mem/memory-device.h"
171dec2e1fSSean Christopherson #include "monitor/qdev.h"
186e81733eSPhilippe Mathieu-Daudé #include "monitor/monitor.h"
196e81733eSPhilippe Mathieu-Daudé #include "monitor/hmp-target.h"
201dec2e1fSSean Christopherson #include "qapi/error.h"
21cc37d98bSRichard Henderson #include "qemu/error-report.h"
2202165856SPhilippe Mathieu-Daudé #include "qapi/qapi-commands-misc-target.h"
231dec2e1fSSean Christopherson #include "exec/address-spaces.h"
240205c4faSYang Zhong #include "sysemu/hw_accel.h"
252c313227SYang Zhong #include "sysemu/reset.h"
262c313227SYang Zhong #include <sys/ioctl.h>
2711058123SYang Zhong #include "hw/acpi/aml-build.h"
280205c4faSYang Zhong 
290205c4faSYang Zhong #define SGX_MAX_EPC_SECTIONS            8
300205c4faSYang Zhong #define SGX_CPUID_EPC_INVALID           0x0
310205c4faSYang Zhong 
320205c4faSYang Zhong /* A valid EPC section. */
330205c4faSYang Zhong #define SGX_CPUID_EPC_SECTION           0x1
340205c4faSYang Zhong #define SGX_CPUID_EPC_MASK              0xF
350205c4faSYang Zhong 
362c313227SYang Zhong #define SGX_MAGIC 0xA4
372c313227SYang Zhong #define SGX_IOC_VEPC_REMOVE_ALL       _IO(SGX_MAGIC, 0x04)
382c313227SYang Zhong 
392c313227SYang Zhong #define RETRY_NUM                       2
402c313227SYang Zhong 
sgx_epc_device_list(Object * obj,void * opaque)4111058123SYang Zhong static int sgx_epc_device_list(Object *obj, void *opaque)
4211058123SYang Zhong {
4311058123SYang Zhong     GSList **list = opaque;
4411058123SYang Zhong 
4511058123SYang Zhong     if (object_dynamic_cast(obj, TYPE_SGX_EPC)) {
4611058123SYang Zhong         *list = g_slist_append(*list, DEVICE(obj));
4711058123SYang Zhong     }
4811058123SYang Zhong 
4911058123SYang Zhong     object_child_foreach(obj, sgx_epc_device_list, opaque);
5011058123SYang Zhong     return 0;
5111058123SYang Zhong }
5211058123SYang Zhong 
sgx_epc_get_device_list(void)5311058123SYang Zhong static GSList *sgx_epc_get_device_list(void)
5411058123SYang Zhong {
5511058123SYang Zhong     GSList *list = NULL;
5611058123SYang Zhong 
5711058123SYang Zhong     object_child_foreach(qdev_get_machine(), sgx_epc_device_list, &list);
5811058123SYang Zhong     return list;
5911058123SYang Zhong }
6011058123SYang Zhong 
sgx_epc_build_srat(GArray * table_data)6111058123SYang Zhong void sgx_epc_build_srat(GArray *table_data)
6211058123SYang Zhong {
6311058123SYang Zhong     GSList *device_list = sgx_epc_get_device_list();
6411058123SYang Zhong 
6511058123SYang Zhong     for (; device_list; device_list = device_list->next) {
6611058123SYang Zhong         DeviceState *dev = device_list->data;
6711058123SYang Zhong         Object *obj = OBJECT(dev);
6811058123SYang Zhong         uint64_t addr, size;
6911058123SYang Zhong         int node;
7011058123SYang Zhong 
7111058123SYang Zhong         node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP,
7211058123SYang Zhong                                         &error_abort);
7311058123SYang Zhong         addr = object_property_get_uint(obj, SGX_EPC_ADDR_PROP, &error_abort);
7411058123SYang Zhong         size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP, &error_abort);
7511058123SYang Zhong 
7611058123SYang Zhong         build_srat_memory(table_data, addr, size, node, MEM_AFFINITY_ENABLED);
7711058123SYang Zhong     }
7811058123SYang Zhong     g_slist_free(device_list);
7911058123SYang Zhong }
8011058123SYang Zhong 
sgx_calc_section_metric(uint64_t low,uint64_t high)810205c4faSYang Zhong static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
820205c4faSYang Zhong {
830205c4faSYang Zhong     return (low & MAKE_64BIT_MASK(12, 20)) +
840205c4faSYang Zhong            ((high & MAKE_64BIT_MASK(0, 20)) << 32);
850205c4faSYang Zhong }
860205c4faSYang Zhong 
sgx_calc_host_epc_sections(void)87fb418b51SPaolo Bonzini static SGXEPCSectionList *sgx_calc_host_epc_sections(void)
880205c4faSYang Zhong {
894755927aSYang Zhong     SGXEPCSectionList *head = NULL, **tail = &head;
904755927aSYang Zhong     SGXEPCSection *section;
910205c4faSYang Zhong     uint32_t i, type;
920205c4faSYang Zhong     uint32_t eax, ebx, ecx, edx;
934755927aSYang Zhong     uint32_t j = 0;
940205c4faSYang Zhong 
950205c4faSYang Zhong     for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
960205c4faSYang Zhong         host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
970205c4faSYang Zhong 
980205c4faSYang Zhong         type = eax & SGX_CPUID_EPC_MASK;
990205c4faSYang Zhong         if (type == SGX_CPUID_EPC_INVALID) {
1000205c4faSYang Zhong             break;
1010205c4faSYang Zhong         }
1020205c4faSYang Zhong 
1030205c4faSYang Zhong         if (type != SGX_CPUID_EPC_SECTION) {
1040205c4faSYang Zhong             break;
1050205c4faSYang Zhong         }
1060205c4faSYang Zhong 
1074755927aSYang Zhong         section = g_new0(SGXEPCSection, 1);
1084755927aSYang Zhong         section->node = j++;
1094755927aSYang Zhong         section->size = sgx_calc_section_metric(ecx, edx);
1104755927aSYang Zhong         QAPI_LIST_APPEND(tail, section);
1110205c4faSYang Zhong     }
1120205c4faSYang Zhong 
1134755927aSYang Zhong     return head;
1140205c4faSYang Zhong }
1150205c4faSYang Zhong 
sgx_epc_reset(void * opaque)1162c313227SYang Zhong static void sgx_epc_reset(void *opaque)
1172c313227SYang Zhong {
1182c313227SYang Zhong     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
1192c313227SYang Zhong     HostMemoryBackend *hostmem;
1202c313227SYang Zhong     SGXEPCDevice *epc;
1212c313227SYang Zhong     int failures;
1222c313227SYang Zhong     int fd, i, j, r;
1232c313227SYang Zhong     static bool warned = false;
1242c313227SYang Zhong 
1252c313227SYang Zhong     /*
1262c313227SYang Zhong      * The second pass is needed to remove SECS pages that could not
1272c313227SYang Zhong      * be removed during the first.
1282c313227SYang Zhong      */
1292c313227SYang Zhong     for (i = 0; i < RETRY_NUM; i++) {
1302c313227SYang Zhong         failures = 0;
1312c313227SYang Zhong         for (j = 0; j < pcms->sgx_epc.nr_sections; j++) {
1322c313227SYang Zhong             epc = pcms->sgx_epc.sections[j];
1332c313227SYang Zhong             hostmem = MEMORY_BACKEND(epc->hostmem);
1342c313227SYang Zhong             fd = memory_region_get_fd(host_memory_backend_get_memory(hostmem));
1352c313227SYang Zhong 
1362c313227SYang Zhong             r = ioctl(fd, SGX_IOC_VEPC_REMOVE_ALL);
1372c313227SYang Zhong             if (r == -ENOTTY && !warned) {
1382c313227SYang Zhong                 warned = true;
1392c313227SYang Zhong                 warn_report("kernel does not support SGX_IOC_VEPC_REMOVE_ALL");
1402c313227SYang Zhong                 warn_report("SGX might operate incorrectly in the guest after reset");
1412c313227SYang Zhong                 break;
1422c313227SYang Zhong             } else if (r > 0) {
1432c313227SYang Zhong                 /* SECS pages remain */
1442c313227SYang Zhong                 failures++;
1452c313227SYang Zhong                 if (i == 1) {
1462c313227SYang Zhong                     error_report("cannot reset vEPC section %d", j);
1472c313227SYang Zhong                 }
1482c313227SYang Zhong             }
1492c313227SYang Zhong         }
1502c313227SYang Zhong         if (!failures) {
1512c313227SYang Zhong             break;
1522c313227SYang Zhong         }
1532c313227SYang Zhong      }
1542c313227SYang Zhong }
1552c313227SYang Zhong 
qmp_query_sgx_capabilities(Error ** errp)15602165856SPhilippe Mathieu-Daudé SGXInfo *qmp_query_sgx_capabilities(Error **errp)
1570205c4faSYang Zhong {
1580205c4faSYang Zhong     SGXInfo *info = NULL;
1590205c4faSYang Zhong     uint32_t eax, ebx, ecx, edx;
1601a48869cSZhao Liu     Error *local_err = NULL;
1610205c4faSYang Zhong 
1621a48869cSZhao Liu     int fd = qemu_open("/dev/sgx_vepc", O_RDWR, &local_err);
1630205c4faSYang Zhong     if (fd < 0) {
1641a48869cSZhao Liu         error_append_hint(&local_err, "SGX is not enabled in KVM");
1651a48869cSZhao Liu         error_propagate(errp, local_err);
1660205c4faSYang Zhong         return NULL;
1670205c4faSYang Zhong     }
1680205c4faSYang Zhong 
1690205c4faSYang Zhong     info = g_new0(SGXInfo, 1);
1700205c4faSYang Zhong     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
1710205c4faSYang Zhong 
1720205c4faSYang Zhong     info->sgx = ebx & (1U << 2) ? true : false;
1730205c4faSYang Zhong     info->flc = ecx & (1U << 30) ? true : false;
1740205c4faSYang Zhong 
1750205c4faSYang Zhong     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
1760205c4faSYang Zhong     info->sgx1 = eax & (1U << 0) ? true : false;
1770205c4faSYang Zhong     info->sgx2 = eax & (1U << 1) ? true : false;
1780205c4faSYang Zhong 
179fb418b51SPaolo Bonzini     info->sections = sgx_calc_host_epc_sections();
1800205c4faSYang Zhong 
1810205c4faSYang Zhong     close(fd);
1820205c4faSYang Zhong 
1830205c4faSYang Zhong     return info;
1840205c4faSYang Zhong }
18557d874c4SYang Zhong 
sgx_get_epc_sections_list(void)1864755927aSYang Zhong static SGXEPCSectionList *sgx_get_epc_sections_list(void)
1874755927aSYang Zhong {
1884755927aSYang Zhong     GSList *device_list = sgx_epc_get_device_list();
1894755927aSYang Zhong     SGXEPCSectionList *head = NULL, **tail = &head;
1904755927aSYang Zhong     SGXEPCSection *section;
1914755927aSYang Zhong 
1924755927aSYang Zhong     for (; device_list; device_list = device_list->next) {
1934755927aSYang Zhong         DeviceState *dev = device_list->data;
1944755927aSYang Zhong         Object *obj = OBJECT(dev);
1954755927aSYang Zhong 
1964755927aSYang Zhong         section = g_new0(SGXEPCSection, 1);
1974755927aSYang Zhong         section->node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP,
1984755927aSYang Zhong                                                  &error_abort);
1994755927aSYang Zhong         section->size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP,
2004755927aSYang Zhong                                                  &error_abort);
2014755927aSYang Zhong         QAPI_LIST_APPEND(tail, section);
2024755927aSYang Zhong     }
2034755927aSYang Zhong     g_slist_free(device_list);
2044755927aSYang Zhong 
2054755927aSYang Zhong     return head;
2064755927aSYang Zhong }
2074755927aSYang Zhong 
qmp_query_sgx(Error ** errp)2086e81733eSPhilippe Mathieu-Daudé SGXInfo *qmp_query_sgx(Error **errp)
20957d874c4SYang Zhong {
21057d874c4SYang Zhong     SGXInfo *info = NULL;
21157d874c4SYang Zhong     X86MachineState *x86ms;
21257d874c4SYang Zhong     PCMachineState *pcms =
21357d874c4SYang Zhong         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
21457d874c4SYang Zhong                                               TYPE_PC_MACHINE);
21557d874c4SYang Zhong     if (!pcms) {
21657d874c4SYang Zhong         error_setg(errp, "SGX is only supported on PC machines");
21757d874c4SYang Zhong         return NULL;
21857d874c4SYang Zhong     }
21957d874c4SYang Zhong 
22057d874c4SYang Zhong     x86ms = X86_MACHINE(pcms);
22157d874c4SYang Zhong     if (!x86ms->sgx_epc_list) {
22257d874c4SYang Zhong         error_setg(errp, "No EPC regions defined, SGX not available");
22357d874c4SYang Zhong         return NULL;
22457d874c4SYang Zhong     }
22557d874c4SYang Zhong 
22657d874c4SYang Zhong     info = g_new0(SGXInfo, 1);
22757d874c4SYang Zhong 
22857d874c4SYang Zhong     info->sgx = true;
22957d874c4SYang Zhong     info->sgx1 = true;
23057d874c4SYang Zhong     info->sgx2 = true;
23157d874c4SYang Zhong     info->flc = true;
2324755927aSYang Zhong     info->sections = sgx_get_epc_sections_list();
23357d874c4SYang Zhong 
23457d874c4SYang Zhong     return info;
23557d874c4SYang Zhong }
2361dec2e1fSSean Christopherson 
hmp_info_sgx(Monitor * mon,const QDict * qdict)2376e81733eSPhilippe Mathieu-Daudé void hmp_info_sgx(Monitor *mon, const QDict *qdict)
2386e81733eSPhilippe Mathieu-Daudé {
2396e81733eSPhilippe Mathieu-Daudé     Error *err = NULL;
2404755927aSYang Zhong     SGXEPCSectionList *section_list, *section;
2416e81733eSPhilippe Mathieu-Daudé     g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
242fb418b51SPaolo Bonzini     uint64_t size = 0;
2436e81733eSPhilippe Mathieu-Daudé 
2446e81733eSPhilippe Mathieu-Daudé     if (err) {
2456e81733eSPhilippe Mathieu-Daudé         error_report_err(err);
2466e81733eSPhilippe Mathieu-Daudé         return;
2476e81733eSPhilippe Mathieu-Daudé     }
2486e81733eSPhilippe Mathieu-Daudé     monitor_printf(mon, "SGX support: %s\n",
2496e81733eSPhilippe Mathieu-Daudé                    info->sgx ? "enabled" : "disabled");
2506e81733eSPhilippe Mathieu-Daudé     monitor_printf(mon, "SGX1 support: %s\n",
2516e81733eSPhilippe Mathieu-Daudé                    info->sgx1 ? "enabled" : "disabled");
2526e81733eSPhilippe Mathieu-Daudé     monitor_printf(mon, "SGX2 support: %s\n",
2536e81733eSPhilippe Mathieu-Daudé                    info->sgx2 ? "enabled" : "disabled");
2546e81733eSPhilippe Mathieu-Daudé     monitor_printf(mon, "FLC support: %s\n",
2556e81733eSPhilippe Mathieu-Daudé                    info->flc ? "enabled" : "disabled");
2564755927aSYang Zhong 
2574755927aSYang Zhong     section_list = info->sections;
2584755927aSYang Zhong     for (section = section_list; section; section = section->next) {
2594755927aSYang Zhong         monitor_printf(mon, "NUMA node #%" PRId64 ": ",
2604755927aSYang Zhong                        section->value->node);
2614755927aSYang Zhong         monitor_printf(mon, "size=%" PRIu64 "\n",
2624755927aSYang Zhong                        section->value->size);
263fb418b51SPaolo Bonzini         size += section->value->size;
2644755927aSYang Zhong     }
265fb418b51SPaolo Bonzini     monitor_printf(mon, "total size=%" PRIu64 "\n",
266fb418b51SPaolo Bonzini                    size);
2676e81733eSPhilippe Mathieu-Daudé }
2686e81733eSPhilippe Mathieu-Daudé 
check_sgx_support(void)269*ada1f3caSZhao Liu bool check_sgx_support(void)
270*ada1f3caSZhao Liu {
271*ada1f3caSZhao Liu     if (!object_dynamic_cast(qdev_get_machine(), TYPE_PC_MACHINE)) {
272*ada1f3caSZhao Liu         return false;
273*ada1f3caSZhao Liu     }
274*ada1f3caSZhao Liu     return true;
275*ada1f3caSZhao Liu }
276*ada1f3caSZhao Liu 
sgx_epc_get_section(int section_nr,uint64_t * addr,uint64_t * size)27705fc8db7SPhilippe Mathieu-Daudé bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
2781dec2e1fSSean Christopherson {
27913be929aSPaolo Bonzini     PCMachineState *pcms =
28013be929aSPaolo Bonzini         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
28113be929aSPaolo Bonzini                                               TYPE_PC_MACHINE);
2821dec2e1fSSean Christopherson     SGXEPCDevice *epc;
2831dec2e1fSSean Christopherson 
28413be929aSPaolo Bonzini     if (!pcms || pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
28505fc8db7SPhilippe Mathieu-Daudé         return true;
2861dec2e1fSSean Christopherson     }
2871dec2e1fSSean Christopherson 
2881dec2e1fSSean Christopherson     epc = pcms->sgx_epc.sections[section_nr];
2891dec2e1fSSean Christopherson 
2901dec2e1fSSean Christopherson     *addr = epc->addr;
2911dec2e1fSSean Christopherson     *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
2921dec2e1fSSean Christopherson 
29305fc8db7SPhilippe Mathieu-Daudé     return false;
2941dec2e1fSSean Christopherson }
2951dec2e1fSSean Christopherson 
pc_machine_init_sgx_epc(PCMachineState * pcms)2961dec2e1fSSean Christopherson void pc_machine_init_sgx_epc(PCMachineState *pcms)
2971dec2e1fSSean Christopherson {
2981dec2e1fSSean Christopherson     SGXEPCState *sgx_epc = &pcms->sgx_epc;
2991dec2e1fSSean Christopherson     X86MachineState *x86ms = X86_MACHINE(pcms);
3001dec2e1fSSean Christopherson     SgxEPCList *list = NULL;
3011dec2e1fSSean Christopherson 
3021dec2e1fSSean Christopherson     memset(sgx_epc, 0, sizeof(SGXEPCState));
3031dec2e1fSSean Christopherson     if (!x86ms->sgx_epc_list) {
3041dec2e1fSSean Christopherson         return;
3051dec2e1fSSean Christopherson     }
3061dec2e1fSSean Christopherson 
3074ab4c330SJoao Martins     sgx_epc->base = x86ms->above_4g_mem_start + x86ms->above_4g_mem_size;
3081dec2e1fSSean Christopherson 
3091dec2e1fSSean Christopherson     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
3101dec2e1fSSean Christopherson     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
3111dec2e1fSSean Christopherson                                 &sgx_epc->mr);
3121dec2e1fSSean Christopherson 
3131dec2e1fSSean Christopherson     for (list = x86ms->sgx_epc_list; list; list = list->next) {
3147156e82fSPhilippe Mathieu-Daudé         DeviceState *dev = qdev_new(TYPE_SGX_EPC);
3151dec2e1fSSean Christopherson 
3161dec2e1fSSean Christopherson         /* set the memdev link with memory backend */
3177156e82fSPhilippe Mathieu-Daudé         object_property_parse(OBJECT(dev), SGX_EPC_MEMDEV_PROP,
3187156e82fSPhilippe Mathieu-Daudé                               list->value->memdev, &error_fatal);
31911058123SYang Zhong         /* set the numa node property for sgx epc object */
3207156e82fSPhilippe Mathieu-Daudé         object_property_set_uint(OBJECT(dev), SGX_EPC_NUMA_NODE_PROP,
3217156e82fSPhilippe Mathieu-Daudé                                  list->value->node, &error_fatal);
3227156e82fSPhilippe Mathieu-Daudé         qdev_realize_and_unref(dev, NULL, &error_fatal);
3231dec2e1fSSean Christopherson     }
3241dec2e1fSSean Christopherson 
3251dec2e1fSSean Christopherson     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
3262539eadeSPhilippe Mathieu-Daudé         error_report("Size of all 'sgx-epc' =0x%"PRIx64" causes EPC to wrap",
3271dec2e1fSSean Christopherson                      sgx_epc->size);
3281dec2e1fSSean Christopherson         exit(EXIT_FAILURE);
3291dec2e1fSSean Christopherson     }
3301dec2e1fSSean Christopherson 
3311dec2e1fSSean Christopherson     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
3322c313227SYang Zhong 
3332c313227SYang Zhong     /* register the reset callback for sgx epc */
3342c313227SYang Zhong     qemu_register_reset(sgx_epc_reset, NULL);
3351dec2e1fSSean Christopherson }
336