master
c 304 lines 6.61 KB
Raw
1 /*
2 * QEMU MSHV support
3 *
4 * Copyright Microsoft, Corp. 2025
5 *
6 * Authors: Magnus Kulke <magnuskulke@microsoft.com>
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "qemu/osdep.h"
12
13 #include "cpu.h"
14 #include "emulate/x86_decode.h"
15 #include "emulate/x86_emu.h"
16 #include "emulate/x86_mmu.h"
17 #include "qemu/error-report.h"
18 #include "system/mshv.h"
19
20 /* RW or Exec segment */
21 static const uint8_t RWRX_SEGMENT_TYPE = 0x2;
22 static const uint8_t CODE_SEGMENT_TYPE = 0x8;
23 static const uint8_t EXPAND_DOWN_SEGMENT_TYPE = 0x4;
24
25 typedef enum CpuMode {
26 REAL_MODE,
27 PROTECTED_MODE,
28 LONG_MODE,
29 } CpuMode;
30
31 static CpuMode cpu_mode(CPUState *cpu)
32 {
33 enum CpuMode m = REAL_MODE;
34
35 if (x86_is_protected(cpu)) {
36 m = PROTECTED_MODE;
37
38 if (x86_is_long_mode(cpu)) {
39 m = LONG_MODE;
40 }
41 }
42
43 return m;
44 }
45
46 static bool segment_type_ro(const x86_segment_descriptor desc)
47 {
48 uint32_t type_ = desc.type;
49 return (type_ & (~RWRX_SEGMENT_TYPE)) == 0;
50 }
51
52 static bool segment_type_code(const x86_segment_descriptor desc)
53 {
54 uint32_t type_ = desc.type;
55 return (type_ & CODE_SEGMENT_TYPE) != 0;
56 }
57
58 static bool segment_expands_down(const x86_segment_descriptor desc)
59 {
60 uint32_t type_ = desc.type;
61
62 if (segment_type_code(desc)) {
63 return false;
64 }
65
66 return (type_ & EXPAND_DOWN_SEGMENT_TYPE) != 0;
67 }
68
69 static uint8_t segment_db(const x86_segment_descriptor desc)
70 {
71 return desc.db;
72 }
73
74 static uint32_t segment_max_limit(const x86_segment_descriptor desc)
75 {
76 if (segment_db(desc) != 0) {
77 return 0xFFFFFFFF;
78 }
79 return 0xFFFF;
80 }
81
82 static int linearize(CPUState *cpu,
83 target_ulong logical_addr, target_ulong *linear_addr,
84 X86Seg seg_idx)
85 {
86 enum CpuMode mode;
87 struct x86_segment_descriptor desc;
88 target_ulong base;
89 target_ulong logical_addr_32b;
90 uint32_t limit;
91 /* TODO: the emulator will not pass us "write" indicator yet */
92 bool write = false;
93
94 emul_ops->read_segment_descriptor(cpu, &desc, seg_idx);
95 base = x86_segment_base(&desc);
96 mode = cpu_mode(cpu);
97
98 switch (mode) {
99 case LONG_MODE:
100 if (__builtin_add_overflow(logical_addr, base, linear_addr)) {
101 error_report("Address overflow");
102 return -1;
103 }
104 break;
105 case PROTECTED_MODE:
106 case REAL_MODE:
107 if (segment_type_ro(desc) && write) {
108 error_report("Cannot write to read-only segment");
109 return -1;
110 }
111
112 logical_addr_32b = logical_addr & 0xFFFFFFFF;
113 limit = x86_segment_limit(&desc);
114
115 if (segment_expands_down(desc)) {
116 if (logical_addr_32b >= limit) {
117 error_report("Address exceeds limit (expands down)");
118 return -1;
119 }
120
121 limit = segment_max_limit(desc);
122 }
123
124 if (logical_addr_32b > limit) {
125 error_report("Address exceeds limit %u", limit);
126 return -1;
127 }
128 *linear_addr = logical_addr_32b + base;
129 break;
130 default:
131 error_report("Unknown cpu mode: %d", mode);
132 return -1;
133 }
134
135 return 0;
136 }
137
138 bool x86_read_segment_descriptor(CPUState *cpu,
139 struct x86_segment_descriptor *desc,
140 x86_segment_selector sel)
141 {
142 target_ulong base;
143 uint32_t limit;
144 X86CPU *x86_cpu = X86_CPU(cpu);
145 CPUX86State *env = &x86_cpu->env;
146 target_ulong gva;
147
148 memset(desc, 0, sizeof(*desc));
149
150 /* valid gdt descriptors start from index 1 */
151 if (!sel.index && GDT_SEL == sel.ti) {
152 return false;
153 }
154
155 if (GDT_SEL == sel.ti) {
156 base = env->gdt.base;
157 limit = env->gdt.limit;
158 } else {
159 base = env->ldt.base;
160 limit = env->ldt.limit;
161 }
162
163 if (sel.index * 8 >= limit) {
164 return false;
165 }
166
167 gva = base + sel.index * 8;
168 x86_read_mem_priv(cpu, desc, gva, sizeof(*desc));
169
170 return true;
171 }
172
173 bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc,
174 int gate)
175 {
176 target_ulong base;
177 uint32_t limit;
178 X86CPU *x86_cpu = X86_CPU(cpu);
179 CPUX86State *env = &x86_cpu->env;
180 target_ulong gva;
181
182 base = env->idt.base;
183 limit = env->idt.limit;
184
185 memset(idt_desc, 0, sizeof(*idt_desc));
186 if (gate * 8 >= limit) {
187 perror("call gate exceeds idt limit");
188 return false;
189 }
190
191 gva = base + gate * 8;
192 x86_read_mem_priv(cpu, idt_desc, gva, sizeof(*idt_desc));
193
194 return true;
195 }
196
197 target_ulong x86_read_cr(CPUState *cpu, int cr)
198 {
199 X86CPU *x86_cpu = X86_CPU(cpu);
200 CPUX86State *env = &x86_cpu->env;
201
202 if (emul_ops->read_cr) {
203 return emul_ops->read_cr(cpu, cr);
204 }
205 return env->cr[cr];
206 }
207
208 bool x86_is_protected(CPUState *cpu)
209 {
210 uint64_t cr0;
211
212 if (emul_ops->is_protected_mode) {
213 return emul_ops->is_protected_mode(cpu);
214 }
215
216 cr0 = x86_read_cr(cpu, 0);
217 return cr0 & CR0_PE_MASK;
218 }
219
220 bool x86_is_real(CPUState *cpu)
221 {
222 return !x86_is_protected(cpu);
223 }
224
225 bool x86_is_v8086(CPUState *cpu)
226 {
227 X86CPU *x86_cpu = X86_CPU(cpu);
228 CPUX86State *env = &x86_cpu->env;
229 return x86_is_protected(cpu) && (env->eflags & VM_MASK);
230 }
231
232 bool x86_is_long_mode(CPUState *cpu)
233 {
234 X86CPU *x86_cpu = X86_CPU(cpu);
235 CPUX86State *env = &x86_cpu->env;
236 uint64_t efer = env->efer;
237 uint64_t lme_lma = (MSR_EFER_LME | MSR_EFER_LMA);
238
239 if (emul_ops->is_long_mode) {
240 return emul_ops->is_long_mode(cpu);
241 }
242 return ((efer & lme_lma) == lme_lma);
243 }
244
245 bool x86_is_la57(CPUState *cpu)
246 {
247 uint64_t is_la57 = x86_read_cr(cpu, 4) & CR4_LA57_MASK;
248 return is_la57;
249 }
250
251 bool x86_is_long64_mode(CPUState *cpu)
252 {
253 error_report("unimplemented: is_long64_mode()");
254 abort();
255 }
256
257 bool x86_is_paging_mode(CPUState *cpu)
258 {
259 uint64_t cr0 = x86_read_cr(cpu, 0);
260
261 return cr0 & CR0_PG_MASK;
262 }
263
264 bool x86_is_pae_enabled(CPUState *cpu)
265 {
266 uint64_t cr4 = x86_read_cr(cpu, 4);
267
268 return cr4 & CR4_PAE_MASK;
269 }
270
271 target_ulong linear_addr(CPUState *cpu, target_ulong addr, X86Seg seg)
272 {
273 int ret;
274 target_ulong linear_addr;
275
276 ret = linearize(cpu, addr, &linear_addr, seg);
277 if (ret < 0) {
278 error_report("failed to linearize address");
279 abort();
280 }
281
282 return linear_addr;
283 }
284
285 target_ulong linear_addr_size(CPUState *cpu, target_ulong addr, int size,
286 X86Seg seg)
287 {
288 switch (size) {
289 case 2:
290 addr = (uint16_t)addr;
291 break;
292 case 4:
293 addr = (uint32_t)addr;
294 break;
295 default:
296 break;
297 }
298 return linear_addr(cpu, addr, seg);
299 }
300
301 target_ulong linear_rip(CPUState *cpu, target_ulong rip)
302 {
303 return linear_addr(cpu, rip, R_CS);
304 }