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