xref: /openbmc/qemu/hw/i386/sgx.c (revision c79aa350)
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(void)
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         QAPI_LIST_APPEND(tail, section);
110     }
111 
112     return head;
113 }
114 
115 static void sgx_epc_reset(void *opaque)
116 {
117     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
118     HostMemoryBackend *hostmem;
119     SGXEPCDevice *epc;
120     int failures;
121     int fd, i, j, r;
122     static bool warned = false;
123 
124     /*
125      * The second pass is needed to remove SECS pages that could not
126      * be removed during the first.
127      */
128     for (i = 0; i < RETRY_NUM; i++) {
129         failures = 0;
130         for (j = 0; j < pcms->sgx_epc.nr_sections; j++) {
131             epc = pcms->sgx_epc.sections[j];
132             hostmem = MEMORY_BACKEND(epc->hostmem);
133             fd = memory_region_get_fd(host_memory_backend_get_memory(hostmem));
134 
135             r = ioctl(fd, SGX_IOC_VEPC_REMOVE_ALL);
136             if (r == -ENOTTY && !warned) {
137                 warned = true;
138                 warn_report("kernel does not support SGX_IOC_VEPC_REMOVE_ALL");
139                 warn_report("SGX might operate incorrectly in the guest after reset");
140                 break;
141             } else if (r > 0) {
142                 /* SECS pages remain */
143                 failures++;
144                 if (i == 1) {
145                     error_report("cannot reset vEPC section %d", j);
146                 }
147             }
148         }
149         if (!failures) {
150             break;
151         }
152      }
153 }
154 
155 SGXInfo *qmp_query_sgx_capabilities(Error **errp)
156 {
157     SGXInfo *info = NULL;
158     uint32_t eax, ebx, ecx, edx;
159 
160     int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
161     if (fd < 0) {
162         error_setg(errp, "SGX is not enabled in KVM");
163         return NULL;
164     }
165 
166     info = g_new0(SGXInfo, 1);
167     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
168 
169     info->sgx = ebx & (1U << 2) ? true : false;
170     info->flc = ecx & (1U << 30) ? true : false;
171 
172     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
173     info->sgx1 = eax & (1U << 0) ? true : false;
174     info->sgx2 = eax & (1U << 1) ? true : false;
175 
176     info->sections = sgx_calc_host_epc_sections();
177 
178     close(fd);
179 
180     return info;
181 }
182 
183 static SGXEPCSectionList *sgx_get_epc_sections_list(void)
184 {
185     GSList *device_list = sgx_epc_get_device_list();
186     SGXEPCSectionList *head = NULL, **tail = &head;
187     SGXEPCSection *section;
188 
189     for (; device_list; device_list = device_list->next) {
190         DeviceState *dev = device_list->data;
191         Object *obj = OBJECT(dev);
192 
193         section = g_new0(SGXEPCSection, 1);
194         section->node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP,
195                                                  &error_abort);
196         section->size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP,
197                                                  &error_abort);
198         QAPI_LIST_APPEND(tail, section);
199     }
200     g_slist_free(device_list);
201 
202     return head;
203 }
204 
205 SGXInfo *qmp_query_sgx(Error **errp)
206 {
207     SGXInfo *info = NULL;
208     X86MachineState *x86ms;
209     PCMachineState *pcms =
210         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
211                                               TYPE_PC_MACHINE);
212     if (!pcms) {
213         error_setg(errp, "SGX is only supported on PC machines");
214         return NULL;
215     }
216 
217     x86ms = X86_MACHINE(pcms);
218     if (!x86ms->sgx_epc_list) {
219         error_setg(errp, "No EPC regions defined, SGX not available");
220         return NULL;
221     }
222 
223     info = g_new0(SGXInfo, 1);
224 
225     info->sgx = true;
226     info->sgx1 = true;
227     info->sgx2 = true;
228     info->flc = true;
229     info->sections = sgx_get_epc_sections_list();
230 
231     return info;
232 }
233 
234 void hmp_info_sgx(Monitor *mon, const QDict *qdict)
235 {
236     Error *err = NULL;
237     SGXEPCSectionList *section_list, *section;
238     g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
239     uint64_t size = 0;
240 
241     if (err) {
242         error_report_err(err);
243         return;
244     }
245     monitor_printf(mon, "SGX support: %s\n",
246                    info->sgx ? "enabled" : "disabled");
247     monitor_printf(mon, "SGX1 support: %s\n",
248                    info->sgx1 ? "enabled" : "disabled");
249     monitor_printf(mon, "SGX2 support: %s\n",
250                    info->sgx2 ? "enabled" : "disabled");
251     monitor_printf(mon, "FLC support: %s\n",
252                    info->flc ? "enabled" : "disabled");
253 
254     section_list = info->sections;
255     for (section = section_list; section; section = section->next) {
256         monitor_printf(mon, "NUMA node #%" PRId64 ": ",
257                        section->value->node);
258         monitor_printf(mon, "size=%" PRIu64 "\n",
259                        section->value->size);
260         size += section->value->size;
261     }
262     monitor_printf(mon, "total size=%" PRIu64 "\n",
263                    size);
264 }
265 
266 bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
267 {
268     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
269     SGXEPCDevice *epc;
270 
271     if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
272         return true;
273     }
274 
275     epc = pcms->sgx_epc.sections[section_nr];
276 
277     *addr = epc->addr;
278     *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
279 
280     return false;
281 }
282 
283 void pc_machine_init_sgx_epc(PCMachineState *pcms)
284 {
285     SGXEPCState *sgx_epc = &pcms->sgx_epc;
286     X86MachineState *x86ms = X86_MACHINE(pcms);
287     SgxEPCList *list = NULL;
288     Object *obj;
289 
290     memset(sgx_epc, 0, sizeof(SGXEPCState));
291     if (!x86ms->sgx_epc_list) {
292         return;
293     }
294 
295     sgx_epc->base = x86ms->above_4g_mem_start + x86ms->above_4g_mem_size;
296 
297     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
298     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
299                                 &sgx_epc->mr);
300 
301     for (list = x86ms->sgx_epc_list; list; list = list->next) {
302         obj = object_new("sgx-epc");
303 
304         /* set the memdev link with memory backend */
305         object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
306                               &error_fatal);
307         /* set the numa node property for sgx epc object */
308         object_property_set_uint(obj, SGX_EPC_NUMA_NODE_PROP, list->value->node,
309                              &error_fatal);
310         object_property_set_bool(obj, "realized", true, &error_fatal);
311         object_unref(obj);
312     }
313 
314     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
315         error_report("Size of all 'sgx-epc' =0x%"PRIx64" causes EPC to wrap",
316                      sgx_epc->size);
317         exit(EXIT_FAILURE);
318     }
319 
320     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
321 
322     /* register the reset callback for sgx epc */
323     qemu_register_reset(sgx_epc_reset, NULL);
324 }
325