xref: /qemu/hw/i386/sgx.c (revision 727385c4)
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 
25 #define SGX_MAX_EPC_SECTIONS            8
26 #define SGX_CPUID_EPC_INVALID           0x0
27 
28 /* A valid EPC section. */
29 #define SGX_CPUID_EPC_SECTION           0x1
30 #define SGX_CPUID_EPC_MASK              0xF
31 
32 static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
33 {
34     return (low & MAKE_64BIT_MASK(12, 20)) +
35            ((high & MAKE_64BIT_MASK(0, 20)) << 32);
36 }
37 
38 static uint64_t sgx_calc_host_epc_section_size(void)
39 {
40     uint32_t i, type;
41     uint32_t eax, ebx, ecx, edx;
42     uint64_t size = 0;
43 
44     for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
45         host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
46 
47         type = eax & SGX_CPUID_EPC_MASK;
48         if (type == SGX_CPUID_EPC_INVALID) {
49             break;
50         }
51 
52         if (type != SGX_CPUID_EPC_SECTION) {
53             break;
54         }
55 
56         size += sgx_calc_section_metric(ecx, edx);
57     }
58 
59     return size;
60 }
61 
62 SGXInfo *qmp_query_sgx_capabilities(Error **errp)
63 {
64     SGXInfo *info = NULL;
65     uint32_t eax, ebx, ecx, edx;
66 
67     int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
68     if (fd < 0) {
69         error_setg(errp, "SGX is not enabled in KVM");
70         return NULL;
71     }
72 
73     info = g_new0(SGXInfo, 1);
74     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
75 
76     info->sgx = ebx & (1U << 2) ? true : false;
77     info->flc = ecx & (1U << 30) ? true : false;
78 
79     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
80     info->sgx1 = eax & (1U << 0) ? true : false;
81     info->sgx2 = eax & (1U << 1) ? true : false;
82 
83     info->section_size = sgx_calc_host_epc_section_size();
84 
85     close(fd);
86 
87     return info;
88 }
89 
90 SGXInfo *qmp_query_sgx(Error **errp)
91 {
92     SGXInfo *info = NULL;
93     X86MachineState *x86ms;
94     PCMachineState *pcms =
95         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
96                                               TYPE_PC_MACHINE);
97     if (!pcms) {
98         error_setg(errp, "SGX is only supported on PC machines");
99         return NULL;
100     }
101 
102     x86ms = X86_MACHINE(pcms);
103     if (!x86ms->sgx_epc_list) {
104         error_setg(errp, "No EPC regions defined, SGX not available");
105         return NULL;
106     }
107 
108     SGXEPCState *sgx_epc = &pcms->sgx_epc;
109     info = g_new0(SGXInfo, 1);
110 
111     info->sgx = true;
112     info->sgx1 = true;
113     info->sgx2 = true;
114     info->flc = true;
115     info->section_size = sgx_epc->size;
116 
117     return info;
118 }
119 
120 void hmp_info_sgx(Monitor *mon, const QDict *qdict)
121 {
122     Error *err = NULL;
123     g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
124 
125     if (err) {
126         error_report_err(err);
127         return;
128     }
129     monitor_printf(mon, "SGX support: %s\n",
130                    info->sgx ? "enabled" : "disabled");
131     monitor_printf(mon, "SGX1 support: %s\n",
132                    info->sgx1 ? "enabled" : "disabled");
133     monitor_printf(mon, "SGX2 support: %s\n",
134                    info->sgx2 ? "enabled" : "disabled");
135     monitor_printf(mon, "FLC support: %s\n",
136                    info->flc ? "enabled" : "disabled");
137     monitor_printf(mon, "size: %" PRIu64 "\n",
138                    info->section_size);
139 }
140 
141 bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
142 {
143     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
144     SGXEPCDevice *epc;
145 
146     if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
147         return true;
148     }
149 
150     epc = pcms->sgx_epc.sections[section_nr];
151 
152     *addr = epc->addr;
153     *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
154 
155     return false;
156 }
157 
158 void pc_machine_init_sgx_epc(PCMachineState *pcms)
159 {
160     SGXEPCState *sgx_epc = &pcms->sgx_epc;
161     X86MachineState *x86ms = X86_MACHINE(pcms);
162     SgxEPCList *list = NULL;
163     Object *obj;
164 
165     memset(sgx_epc, 0, sizeof(SGXEPCState));
166     if (!x86ms->sgx_epc_list) {
167         return;
168     }
169 
170     sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size;
171 
172     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
173     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
174                                 &sgx_epc->mr);
175 
176     for (list = x86ms->sgx_epc_list; list; list = list->next) {
177         obj = object_new("sgx-epc");
178 
179         /* set the memdev link with memory backend */
180         object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
181                               &error_fatal);
182         object_property_set_bool(obj, "realized", true, &error_fatal);
183         object_unref(obj);
184     }
185 
186     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
187         error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap",
188                      sgx_epc->size);
189         exit(EXIT_FAILURE);
190     }
191 
192     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
193 }
194