1
2
3
4
5
6
7
8
9
10
11
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
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
39static 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
45static 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
69static 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
80
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
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
109SGXInfo *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
137SGXInfo *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
167void 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
188bool 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
205void 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
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
242 qemu_register_reset(sgx_epc_reset, NULL);
243}
244