xref: /openbmc/qemu/hw/i386/sgx.c (revision 9edc6313da34699ebd2bae4573ea22339b26450a)
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(uint64_t *size)
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          *size += section->size;
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      uint64_t size = 0;
161  
162      int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
163      if (fd < 0) {
164          error_setg(errp, "SGX is not enabled in KVM");
165          return NULL;
166      }
167  
168      info = g_new0(SGXInfo, 1);
169      host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
170  
171      info->sgx = ebx & (1U << 2) ? true : false;
172      info->flc = ecx & (1U << 30) ? true : false;
173  
174      host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
175      info->sgx1 = eax & (1U << 0) ? true : false;
176      info->sgx2 = eax & (1U << 1) ? true : false;
177  
178      info->sections = sgx_calc_host_epc_sections(&size);
179      info->section_size = size;
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      SGXEPCState *sgx_epc = &pcms->sgx_epc;
227      info = g_new0(SGXInfo, 1);
228  
229      info->sgx = true;
230      info->sgx1 = true;
231      info->sgx2 = true;
232      info->flc = true;
233      info->section_size = sgx_epc->size;
234      info->sections = sgx_get_epc_sections_list();
235  
236      return info;
237  }
238  
239  void hmp_info_sgx(Monitor *mon, const QDict *qdict)
240  {
241      Error *err = NULL;
242      SGXEPCSectionList *section_list, *section;
243      g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
244  
245      if (err) {
246          error_report_err(err);
247          return;
248      }
249      monitor_printf(mon, "SGX support: %s\n",
250                     info->sgx ? "enabled" : "disabled");
251      monitor_printf(mon, "SGX1 support: %s\n",
252                     info->sgx1 ? "enabled" : "disabled");
253      monitor_printf(mon, "SGX2 support: %s\n",
254                     info->sgx2 ? "enabled" : "disabled");
255      monitor_printf(mon, "FLC support: %s\n",
256                     info->flc ? "enabled" : "disabled");
257      monitor_printf(mon, "size: %" PRIu64 "\n",
258                     info->section_size);
259  
260      section_list = info->sections;
261      for (section = section_list; section; section = section->next) {
262          monitor_printf(mon, "NUMA node #%" PRId64 ": ",
263                         section->value->node);
264          monitor_printf(mon, "size=%" PRIu64 "\n",
265                         section->value->size);
266      }
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      Object *obj;
292  
293      memset(sgx_epc, 0, sizeof(SGXEPCState));
294      if (!x86ms->sgx_epc_list) {
295          return;
296      }
297  
298      sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size;
299  
300      memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
301      memory_region_add_subregion(get_system_memory(), sgx_epc->base,
302                                  &sgx_epc->mr);
303  
304      for (list = x86ms->sgx_epc_list; list; list = list->next) {
305          obj = object_new("sgx-epc");
306  
307          /* set the memdev link with memory backend */
308          object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
309                                &error_fatal);
310          /* set the numa node property for sgx epc object */
311          object_property_set_uint(obj, SGX_EPC_NUMA_NODE_PROP, list->value->node,
312                               &error_fatal);
313          object_property_set_bool(obj, "realized", true, &error_fatal);
314          object_unref(obj);
315      }
316  
317      if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
318          error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap",
319                       sgx_epc->size);
320          exit(EXIT_FAILURE);
321      }
322  
323      memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
324  
325      /* register the reset callback for sgx epc */
326      qemu_register_reset(sgx_epc_reset, NULL);
327  }
328