xref: /openbmc/qemu/hw/i386/sgx.c (revision 0af3dfa5)
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 "qemu/error-report.h"
22 #include "qapi/qapi-commands-misc-target.h"
23 #include "exec/address-spaces.h"
24 #include "sysemu/hw_accel.h"
25 #include "sysemu/reset.h"
26 #include <sys/ioctl.h>
27 #include "hw/acpi/aml-build.h"
28 
29 #define SGX_MAX_EPC_SECTIONS            8
30 #define SGX_CPUID_EPC_INVALID           0x0
31 
32 /* A valid EPC section. */
33 #define SGX_CPUID_EPC_SECTION           0x1
34 #define SGX_CPUID_EPC_MASK              0xF
35 
36 #define SGX_MAGIC 0xA4
37 #define SGX_IOC_VEPC_REMOVE_ALL       _IO(SGX_MAGIC, 0x04)
38 
39 #define RETRY_NUM                       2
40 
41 static int sgx_epc_device_list(Object *obj, void *opaque)
42 {
43     GSList **list = opaque;
44 
45     if (object_dynamic_cast(obj, TYPE_SGX_EPC)) {
46         *list = g_slist_append(*list, DEVICE(obj));
47     }
48 
49     object_child_foreach(obj, sgx_epc_device_list, opaque);
50     return 0;
51 }
52 
53 static GSList *sgx_epc_get_device_list(void)
54 {
55     GSList *list = NULL;
56 
57     object_child_foreach(qdev_get_machine(), sgx_epc_device_list, &list);
58     return list;
59 }
60 
61 void sgx_epc_build_srat(GArray *table_data)
62 {
63     GSList *device_list = sgx_epc_get_device_list();
64 
65     for (; device_list; device_list = device_list->next) {
66         DeviceState *dev = device_list->data;
67         Object *obj = OBJECT(dev);
68         uint64_t addr, size;
69         int node;
70 
71         node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP,
72                                         &error_abort);
73         addr = object_property_get_uint(obj, SGX_EPC_ADDR_PROP, &error_abort);
74         size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP, &error_abort);
75 
76         build_srat_memory(table_data, addr, size, node, MEM_AFFINITY_ENABLED);
77     }
78     g_slist_free(device_list);
79 }
80 
81 static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
82 {
83     return (low & MAKE_64BIT_MASK(12, 20)) +
84            ((high & MAKE_64BIT_MASK(0, 20)) << 32);
85 }
86 
87 static SGXEPCSectionList *sgx_calc_host_epc_sections(void)
88 {
89     SGXEPCSectionList *head = NULL, **tail = &head;
90     SGXEPCSection *section;
91     uint32_t i, type;
92     uint32_t eax, ebx, ecx, edx;
93     uint32_t j = 0;
94 
95     for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
96         host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
97 
98         type = eax & SGX_CPUID_EPC_MASK;
99         if (type == SGX_CPUID_EPC_INVALID) {
100             break;
101         }
102 
103         if (type != SGX_CPUID_EPC_SECTION) {
104             break;
105         }
106 
107         section = g_new0(SGXEPCSection, 1);
108         section->node = j++;
109         section->size = sgx_calc_section_metric(ecx, edx);
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     Error *local_err = NULL;
161 
162     int fd = qemu_open("/dev/sgx_vepc", O_RDWR, &local_err);
163     if (fd < 0) {
164         error_append_hint(&local_err, "SGX is not enabled in KVM");
165         error_propagate(errp, local_err);
166         return NULL;
167     }
168 
169     info = g_new0(SGXInfo, 1);
170     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
171 
172     info->sgx = ebx & (1U << 2) ? true : false;
173     info->flc = ecx & (1U << 30) ? true : false;
174 
175     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
176     info->sgx1 = eax & (1U << 0) ? true : false;
177     info->sgx2 = eax & (1U << 1) ? true : false;
178 
179     info->sections = sgx_calc_host_epc_sections();
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     info = g_new0(SGXInfo, 1);
227 
228     info->sgx = true;
229     info->sgx1 = true;
230     info->sgx2 = true;
231     info->flc = true;
232     info->sections = sgx_get_epc_sections_list();
233 
234     return info;
235 }
236 
237 void hmp_info_sgx(Monitor *mon, const QDict *qdict)
238 {
239     Error *err = NULL;
240     SGXEPCSectionList *section_list, *section;
241     g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
242     uint64_t size = 0;
243 
244     if (err) {
245         error_report_err(err);
246         return;
247     }
248     monitor_printf(mon, "SGX support: %s\n",
249                    info->sgx ? "enabled" : "disabled");
250     monitor_printf(mon, "SGX1 support: %s\n",
251                    info->sgx1 ? "enabled" : "disabled");
252     monitor_printf(mon, "SGX2 support: %s\n",
253                    info->sgx2 ? "enabled" : "disabled");
254     monitor_printf(mon, "FLC support: %s\n",
255                    info->flc ? "enabled" : "disabled");
256 
257     section_list = info->sections;
258     for (section = section_list; section; section = section->next) {
259         monitor_printf(mon, "NUMA node #%" PRId64 ": ",
260                        section->value->node);
261         monitor_printf(mon, "size=%" PRIu64 "\n",
262                        section->value->size);
263         size += section->value->size;
264     }
265     monitor_printf(mon, "total size=%" PRIu64 "\n",
266                    size);
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 
292     memset(sgx_epc, 0, sizeof(SGXEPCState));
293     if (!x86ms->sgx_epc_list) {
294         return;
295     }
296 
297     sgx_epc->base = x86ms->above_4g_mem_start + x86ms->above_4g_mem_size;
298 
299     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
300     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
301                                 &sgx_epc->mr);
302 
303     for (list = x86ms->sgx_epc_list; list; list = list->next) {
304         DeviceState *dev = qdev_new(TYPE_SGX_EPC);
305 
306         /* set the memdev link with memory backend */
307         object_property_parse(OBJECT(dev), SGX_EPC_MEMDEV_PROP,
308                               list->value->memdev, &error_fatal);
309         /* set the numa node property for sgx epc object */
310         object_property_set_uint(OBJECT(dev), SGX_EPC_NUMA_NODE_PROP,
311                                  list->value->node, &error_fatal);
312         qdev_realize_and_unref(dev, NULL, &error_fatal);
313     }
314 
315     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
316         error_report("Size of all 'sgx-epc' =0x%"PRIx64" causes EPC to wrap",
317                      sgx_epc->size);
318         exit(EXIT_FAILURE);
319     }
320 
321     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
322 
323     /* register the reset callback for sgx epc */
324     qemu_register_reset(sgx_epc_reset, NULL);
325 }
326