master
c 213 lines 5.55 KB
Raw
1 /*
2 * QEMU IGVM, support for native x86 guests
3 *
4 * Copyright (C) 2026 Red Hat
5 *
6 * Authors:
7 * Gerd Hoffmann <kraxel@redhat.com>
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "qemu/osdep.h"
13
14 #include "cpu.h"
15 #include "hw/i386/e820_memory_layout.h"
16 #include "hw/i386/acpi-build.h"
17 #include "system/igvm.h"
18 #include "system/igvm-internal.h"
19
20 struct IgvmNativeVpContextX64 {
21 uint64_t rax;
22 uint64_t rcx;
23 uint64_t rdx;
24 uint64_t rbx;
25 uint64_t rsp;
26 uint64_t rbp;
27 uint64_t rsi;
28 uint64_t rdi;
29 uint64_t r8;
30 uint64_t r9;
31 uint64_t r10;
32 uint64_t r11;
33 uint64_t r12;
34 uint64_t r13;
35 uint64_t r14;
36 uint64_t r15;
37 uint64_t rip;
38 uint64_t rflags;
39 uint64_t idtr_base;
40 uint16_t idtr_limit;
41 uint16_t reserved[2];
42 uint16_t gdtr_limit;
43 uint64_t gdtr_base;
44
45 uint16_t code_selector;
46 uint16_t code_attributes;
47 uint32_t code_base;
48 uint32_t code_limit;
49
50 uint16_t data_selector;
51 uint16_t data_attributes;
52 uint32_t data_base;
53 uint32_t data_limit;
54
55 uint64_t gs_base;
56 uint64_t cr0;
57 uint64_t cr3;
58 uint64_t cr4;
59 uint64_t efer;
60 };
61
62 #define FLAGS_TO_SEGCACHE(flags) \
63 (((unsigned int)flags) << 8)
64
65 static void qigvm_x86_load_context(struct IgvmNativeVpContextX64 *context,
66 CPUX86State *env)
67 {
68 cpu_load_efer(env, context->efer);
69 cpu_x86_update_cr4(env, context->cr4);
70 cpu_x86_update_cr0(env, context->cr0);
71 cpu_x86_update_cr3(env, context->cr3);
72
73 cpu_x86_load_seg_cache(
74 env, R_CS, context->code_selector,
75 context->code_base, context->code_limit,
76 FLAGS_TO_SEGCACHE(context->code_attributes));
77 cpu_x86_load_seg_cache(
78 env, R_DS, context->data_selector,
79 context->data_base, context->data_limit,
80 FLAGS_TO_SEGCACHE(context->data_attributes));
81 cpu_x86_load_seg_cache(
82 env, R_ES, context->data_selector,
83 context->data_base, context->data_limit,
84 FLAGS_TO_SEGCACHE(context->data_attributes));
85 cpu_x86_load_seg_cache(
86 env, R_FS, context->data_selector,
87 context->data_base, context->data_limit,
88 FLAGS_TO_SEGCACHE(context->data_attributes));
89 cpu_x86_load_seg_cache(
90 env, R_GS, context->data_selector,
91 context->data_base, context->data_limit,
92 FLAGS_TO_SEGCACHE(context->data_attributes));
93 cpu_x86_load_seg_cache(
94 env, R_SS, context->data_selector,
95 context->data_base, context->data_limit,
96 FLAGS_TO_SEGCACHE(context->data_attributes));
97
98 env->gdt.base = context->gdtr_base;
99 env->gdt.limit = context->gdtr_limit;
100 env->idt.base = context->idtr_base;
101 env->idt.limit = context->idtr_limit;
102
103 env->regs[R_EAX] = context->rax;
104 env->regs[R_ECX] = context->rcx;
105 env->regs[R_EDX] = context->rdx;
106 env->regs[R_EBX] = context->rbx;
107 env->regs[R_ESP] = context->rsp;
108 env->regs[R_EBP] = context->rbp;
109 env->regs[R_ESI] = context->rsi;
110 env->regs[R_EDI] = context->rdi;
111 #ifdef TARGET_X86_64
112 env->regs[R_R8] = context->r8;
113 env->regs[R_R9] = context->r9;
114 env->regs[R_R10] = context->r10;
115 env->regs[R_R11] = context->r11;
116 env->regs[R_R12] = context->r12;
117 env->regs[R_R13] = context->r13;
118 env->regs[R_R14] = context->r14;
119 env->regs[R_R15] = context->r15;
120 #endif
121 env->eip = context->rip;
122 env->eflags = context->rflags;
123 }
124
125 /*
126 * convert e820 table into igvm memory map
127 */
128 int qigvm_x86_get_mem_map_entry(int index,
129 ConfidentialGuestMemoryMapEntry *entry,
130 Error **errp)
131 {
132 struct e820_entry *table;
133 int num_entries;
134
135 num_entries = e820_get_table(&table);
136 if ((index < 0) || (index >= num_entries)) {
137 return 1;
138 }
139 entry->gpa = table[index].address;
140 entry->size = table[index].length;
141 switch (table[index].type) {
142 case E820_RAM:
143 entry->type = CGS_MEM_RAM;
144 break;
145 case E820_RESERVED:
146 entry->type = CGS_MEM_RESERVED;
147 break;
148 default:
149 /* should not happen */
150 error_setg(errp, "unknown e820 type");
151 return -1;
152 }
153 return 0;
154 }
155
156 /*
157 * set initial cpu context
158 */
159 static struct IgvmNativeVpContextX64 *bsp_context;
160
161 int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
162 {
163 if (index != 0) {
164 error_setg(errp, "context can be set for BSP only");
165 return -1;
166 }
167
168 if (bsp_context == NULL) {
169 bsp_context = g_new0(struct IgvmNativeVpContextX64, 1);
170 }
171 memcpy(bsp_context, data, sizeof(struct IgvmNativeVpContextX64));
172 return 0;
173 }
174
175 void qigvm_x86_bsp_reset(CPUX86State *env)
176 {
177 if (bsp_context == NULL) {
178 return;
179 }
180
181 qigvm_x86_load_context(bsp_context, env);
182 }
183
184 /*
185 * Process MADT IGVM parameter
186 */
187 int qigvm_directive_madt(QIgvm *ctx, const uint8_t *header_data, Error **errp)
188 {
189 const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
190 QIgvmParameterData *param_entry;
191 int result = 0;
192
193 /* Find the parameter area that should hold the MADT data */
194 param_entry = qigvm_find_param_entry(ctx,
195 param->parameter_area_index, errp);
196 if (param_entry == NULL) {
197 return -1;
198 }
199
200 GArray *madt = acpi_build_madt_standalone(ctx->machine_state);
201
202 if (madt->len <= param_entry->size) {
203 memcpy(param_entry->data, madt->data, madt->len);
204 } else {
205 error_setg(
206 errp,
207 "IGVM: MADT size exceeds parameter area defined in IGVM file");
208 result = -1;
209 }
210
211 g_array_free(madt, true);
212 return result;
213 }