xref: /qemu/hw/i386/sgx.c (revision 43709a0c)
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 
27 #define SGX_MAX_EPC_SECTIONS            8
28 #define SGX_CPUID_EPC_INVALID           0x0
29 
30 /* A valid EPC section. */
31 #define SGX_CPUID_EPC_SECTION           0x1
32 #define SGX_CPUID_EPC_MASK              0xF
33 
34 #define SGX_MAGIC 0xA4
35 #define SGX_IOC_VEPC_REMOVE_ALL       _IO(SGX_MAGIC, 0x04)
36 
37 #define RETRY_NUM                       2
38 
39 static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
40 {
41     return (low & MAKE_64BIT_MASK(12, 20)) +
42            ((high & MAKE_64BIT_MASK(0, 20)) << 32);
43 }
44 
45 static uint64_t sgx_calc_host_epc_section_size(void)
46 {
47     uint32_t i, type;
48     uint32_t eax, ebx, ecx, edx;
49     uint64_t size = 0;
50 
51     for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
52         host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
53 
54         type = eax & SGX_CPUID_EPC_MASK;
55         if (type == SGX_CPUID_EPC_INVALID) {
56             break;
57         }
58 
59         if (type != SGX_CPUID_EPC_SECTION) {
60             break;
61         }
62 
63         size += sgx_calc_section_metric(ecx, edx);
64     }
65 
66     return size;
67 }
68 
69 static void sgx_epc_reset(void *opaque)
70 {
71     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
72     HostMemoryBackend *hostmem;
73     SGXEPCDevice *epc;
74     int failures;
75     int fd, i, j, r;
76     static bool warned = false;
77 
78     /*
79      * The second pass is needed to remove SECS pages that could not
80      * be removed during the first.
81      */
82     for (i = 0; i < RETRY_NUM; i++) {
83         failures = 0;
84         for (j = 0; j < pcms->sgx_epc.nr_sections; j++) {
85             epc = pcms->sgx_epc.sections[j];
86             hostmem = MEMORY_BACKEND(epc->hostmem);
87             fd = memory_region_get_fd(host_memory_backend_get_memory(hostmem));
88 
89             r = ioctl(fd, SGX_IOC_VEPC_REMOVE_ALL);
90             if (r == -ENOTTY && !warned) {
91                 warned = true;
92                 warn_report("kernel does not support SGX_IOC_VEPC_REMOVE_ALL");
93                 warn_report("SGX might operate incorrectly in the guest after reset");
94                 break;
95             } else if (r > 0) {
96                 /* SECS pages remain */
97                 failures++;
98                 if (i == 1) {
99                     error_report("cannot reset vEPC section %d", j);
100                 }
101             }
102         }
103         if (!failures) {
104             break;
105         }
106      }
107 }
108 
109 SGXInfo *qmp_query_sgx_capabilities(Error **errp)
110 {
111     SGXInfo *info = NULL;
112     uint32_t eax, ebx, ecx, edx;
113 
114     int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
115     if (fd < 0) {
116         error_setg(errp, "SGX is not enabled in KVM");
117         return NULL;
118     }
119 
120     info = g_new0(SGXInfo, 1);
121     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
122 
123     info->sgx = ebx & (1U << 2) ? true : false;
124     info->flc = ecx & (1U << 30) ? true : false;
125 
126     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
127     info->sgx1 = eax & (1U << 0) ? true : false;
128     info->sgx2 = eax & (1U << 1) ? true : false;
129 
130     info->section_size = sgx_calc_host_epc_section_size();
131 
132     close(fd);
133 
134     return info;
135 }
136 
137 SGXInfo *qmp_query_sgx(Error **errp)
138 {
139     SGXInfo *info = NULL;
140     X86MachineState *x86ms;
141     PCMachineState *pcms =
142         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
143                                               TYPE_PC_MACHINE);
144     if (!pcms) {
145         error_setg(errp, "SGX is only supported on PC machines");
146         return NULL;
147     }
148 
149     x86ms = X86_MACHINE(pcms);
150     if (!x86ms->sgx_epc_list) {
151         error_setg(errp, "No EPC regions defined, SGX not available");
152         return NULL;
153     }
154 
155     SGXEPCState *sgx_epc = &pcms->sgx_epc;
156     info = g_new0(SGXInfo, 1);
157 
158     info->sgx = true;
159     info->sgx1 = true;
160     info->sgx2 = true;
161     info->flc = true;
162     info->section_size = sgx_epc->size;
163 
164     return info;
165 }
166 
167 void hmp_info_sgx(Monitor *mon, const QDict *qdict)
168 {
169     Error *err = NULL;
170     g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
171 
172     if (err) {
173         error_report_err(err);
174         return;
175     }
176     monitor_printf(mon, "SGX support: %s\n",
177                    info->sgx ? "enabled" : "disabled");
178     monitor_printf(mon, "SGX1 support: %s\n",
179                    info->sgx1 ? "enabled" : "disabled");
180     monitor_printf(mon, "SGX2 support: %s\n",
181                    info->sgx2 ? "enabled" : "disabled");
182     monitor_printf(mon, "FLC support: %s\n",
183                    info->flc ? "enabled" : "disabled");
184     monitor_printf(mon, "size: %" PRIu64 "\n",
185                    info->section_size);
186 }
187 
188 bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
189 {
190     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
191     SGXEPCDevice *epc;
192 
193     if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
194         return true;
195     }
196 
197     epc = pcms->sgx_epc.sections[section_nr];
198 
199     *addr = epc->addr;
200     *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
201 
202     return false;
203 }
204 
205 void pc_machine_init_sgx_epc(PCMachineState *pcms)
206 {
207     SGXEPCState *sgx_epc = &pcms->sgx_epc;
208     X86MachineState *x86ms = X86_MACHINE(pcms);
209     SgxEPCList *list = NULL;
210     Object *obj;
211 
212     memset(sgx_epc, 0, sizeof(SGXEPCState));
213     if (!x86ms->sgx_epc_list) {
214         return;
215     }
216 
217     sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size;
218 
219     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
220     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
221                                 &sgx_epc->mr);
222 
223     for (list = x86ms->sgx_epc_list; list; list = list->next) {
224         obj = object_new("sgx-epc");
225 
226         /* set the memdev link with memory backend */
227         object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
228                               &error_fatal);
229         object_property_set_bool(obj, "realized", true, &error_fatal);
230         object_unref(obj);
231     }
232 
233     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
234         error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap",
235                      sgx_epc->size);
236         exit(EXIT_FAILURE);
237     }
238 
239     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
240 
241     /* register the reset callback for sgx epc */
242     qemu_register_reset(sgx_epc_reset, NULL);
243 }
244