1use std::mem;
10
11use kvm_bindings::{kvm_fpu, kvm_regs, kvm_sregs};
12use kvm_ioctls::VcpuFd;
13
14use super::super::{BootProtocol, EntryPoint};
15use super::gdt::{gdt_entry, kvm_segment_from_gdt};
16use crate::vstate::memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap};
17
18const PML4_START: u64 = 0x9000;
20const PDPTE_START: u64 = 0xa000;
21const PDE_START: u64 = 0xb000;
22
23#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
25pub enum RegsError {
26 GetStatusRegisters(kvm_ioctls::Error),
28 SetBaseRegisters(kvm_ioctls::Error),
30 SetFPURegisters(kvm_ioctls::Error),
32 SetStatusRegisters(kvm_ioctls::Error),
34 WriteGDT,
36 WriteIDT,
38 WritePDPTEAddress,
40 WritePDEAddress,
42 WritePML4Address,
44}
45
46#[derive(Debug, derive_more::From, PartialEq, Eq, thiserror::Error)]
48#[error("Failed to setup FPU: {0}")]
49pub struct SetupFpuError(vmm_sys_util::errno::Error);
50
51pub fn setup_fpu(vcpu: &VcpuFd) -> Result<(), SetupFpuError> {
61 let fpu: kvm_fpu = kvm_fpu {
62 fcw: 0x37f,
63 mxcsr: 0x1f80,
64 ..Default::default()
65 };
66
67 vcpu.set_fpu(&fpu).map_err(SetupFpuError)
68}
69
70#[derive(Debug, derive_more::From, PartialEq, Eq, thiserror::Error)]
72#[error("Failed to setup registers: {0}")]
73pub struct SetupRegistersError(vmm_sys_util::errno::Error);
74
75pub fn setup_regs(vcpu: &VcpuFd, entry_point: EntryPoint) -> Result<(), SetupRegistersError> {
86 let regs: kvm_regs = match entry_point.protocol {
87 BootProtocol::PvhBoot => kvm_regs {
88 rflags: 0x0000_0000_0000_0002u64,
90 rbx: super::layout::PVH_INFO_START,
91 rip: entry_point.entry_addr.raw_value(),
92 ..Default::default()
93 },
94 BootProtocol::LinuxBoot => kvm_regs {
95 rflags: 0x0000_0000_0000_0002u64,
97 rip: entry_point.entry_addr.raw_value(),
98 rsp: super::layout::BOOT_STACK_POINTER,
103 rbp: super::layout::BOOT_STACK_POINTER,
105 rsi: super::layout::ZERO_PAGE_START,
107 ..Default::default()
108 },
109 };
110
111 vcpu.set_regs(®s).map_err(SetupRegistersError)
112}
113
114#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
116pub enum SetupSpecialRegistersError {
117 GetSpecialRegisters(vmm_sys_util::errno::Error),
119 ConfigureSegmentsAndSpecialRegisters(RegsError),
121 SetupPageTables(RegsError),
123 SetSpecialRegisters(vmm_sys_util::errno::Error),
125}
126
127pub fn setup_sregs(
143 mem: &GuestMemoryMmap,
144 vcpu: &VcpuFd,
145 boot_prot: BootProtocol,
146) -> Result<(), SetupSpecialRegistersError> {
147 let mut sregs: kvm_sregs = vcpu
148 .get_sregs()
149 .map_err(SetupSpecialRegistersError::GetSpecialRegisters)?;
150
151 configure_segments_and_sregs(mem, &mut sregs, boot_prot)
152 .map_err(SetupSpecialRegistersError::ConfigureSegmentsAndSpecialRegisters)?;
153 if let BootProtocol::LinuxBoot = boot_prot {
154 setup_page_tables(mem, &mut sregs).map_err(SetupSpecialRegistersError::SetupPageTables)?;
155 }
157
158 vcpu.set_sregs(&sregs)
159 .map_err(SetupSpecialRegistersError::SetSpecialRegisters)
160}
161
162const BOOT_GDT_OFFSET: u64 = 0x500;
163const BOOT_IDT_OFFSET: u64 = 0x520;
164
165const BOOT_GDT_MAX: usize = 4;
166
167const EFER_LMA: u64 = 0x400;
168const EFER_LME: u64 = 0x100;
169
170const X86_CR0_PE: u64 = 0x1;
171const X86_CR0_ET: u64 = 0x10;
172const X86_CR0_PG: u64 = 0x8000_0000;
173const X86_CR4_PAE: u64 = 0x20;
174
175fn write_gdt_table(table: &[u64], guest_mem: &GuestMemoryMmap) -> Result<(), RegsError> {
176 let boot_gdt_addr = GuestAddress(BOOT_GDT_OFFSET);
177 for (index, entry) in table.iter().enumerate() {
178 let addr = guest_mem
179 .checked_offset(boot_gdt_addr, index * mem::size_of::<u64>())
180 .ok_or(RegsError::WriteGDT)?;
181 guest_mem
182 .write_obj(*entry, addr)
183 .map_err(|_| RegsError::WriteGDT)?;
184 }
185 Ok(())
186}
187
188fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<(), RegsError> {
189 let boot_idt_addr = GuestAddress(BOOT_IDT_OFFSET);
190 guest_mem
191 .write_obj(val, boot_idt_addr)
192 .map_err(|_| RegsError::WriteIDT)
193}
194
195fn configure_segments_and_sregs(
196 mem: &GuestMemoryMmap,
197 sregs: &mut kvm_sregs,
198 boot_prot: BootProtocol,
199) -> Result<(), RegsError> {
200 let gdt_table: [u64; BOOT_GDT_MAX] = match boot_prot {
201 BootProtocol::PvhBoot => {
202 [
204 gdt_entry(0, 0, 0), gdt_entry(0xc09b, 0, 0xffff_ffff), gdt_entry(0xc093, 0, 0xffff_ffff), gdt_entry(0x008b, 0, 0x67), ]
209 }
210 BootProtocol::LinuxBoot => {
211 [
213 gdt_entry(0, 0, 0), gdt_entry(0xa09b, 0, 0xfffff), gdt_entry(0xc093, 0, 0xfffff), gdt_entry(0x808b, 0, 0xfffff), ]
218 }
219 };
220
221 let code_seg = kvm_segment_from_gdt(gdt_table[1], 1);
222 let data_seg = kvm_segment_from_gdt(gdt_table[2], 2);
223 let tss_seg = kvm_segment_from_gdt(gdt_table[3], 3);
224
225 write_gdt_table(&gdt_table[..], mem)?;
227 sregs.gdt.base = BOOT_GDT_OFFSET;
228 sregs.gdt.limit = u16::try_from(mem::size_of_val(&gdt_table)).unwrap() - 1;
229
230 write_idt_value(0, mem)?;
231 sregs.idt.base = BOOT_IDT_OFFSET;
232 sregs.idt.limit = u16::try_from(mem::size_of::<u64>()).unwrap() - 1;
233
234 sregs.cs = code_seg;
235 sregs.ds = data_seg;
236 sregs.es = data_seg;
237 sregs.fs = data_seg;
238 sregs.gs = data_seg;
239 sregs.ss = data_seg;
240 sregs.tr = tss_seg;
241
242 match boot_prot {
243 BootProtocol::PvhBoot => {
244 sregs.cr0 = X86_CR0_PE | X86_CR0_ET;
245 sregs.cr4 = 0;
246 }
247 BootProtocol::LinuxBoot => {
248 sregs.cr0 |= X86_CR0_PE;
250 sregs.efer |= EFER_LME | EFER_LMA;
251 }
252 }
253
254 Ok(())
255}
256
257fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut kvm_sregs) -> Result<(), RegsError> {
258 let boot_pml4_addr = GuestAddress(PML4_START);
260 let boot_pdpte_addr = GuestAddress(PDPTE_START);
261 let boot_pde_addr = GuestAddress(PDE_START);
262
263 mem.write_obj(boot_pdpte_addr.raw_value() | 0x03, boot_pml4_addr)
265 .map_err(|_| RegsError::WritePML4Address)?;
266
267 mem.write_obj(boot_pde_addr.raw_value() | 0x03, boot_pdpte_addr)
269 .map_err(|_| RegsError::WritePDPTEAddress)?;
270 for i in 0..512 {
273 mem.write_obj((i << 21) + 0x83u64, boot_pde_addr.unchecked_add(i * 8))
274 .map_err(|_| RegsError::WritePDEAddress)?;
275 }
276
277 sregs.cr3 = boot_pml4_addr.raw_value();
278 sregs.cr4 |= X86_CR4_PAE;
279 sregs.cr0 |= X86_CR0_PG;
280 Ok(())
281}
282
283#[cfg(test)]
284mod tests {
285 #![allow(clippy::cast_possible_truncation)]
286
287 use kvm_ioctls::Kvm;
288
289 use super::*;
290 use crate::test_utils::single_region_mem;
291 use crate::vstate::memory::{Bytes, GuestAddress, GuestMemoryMmap};
292
293 fn read_u64(gm: &GuestMemoryMmap, offset: u64) -> u64 {
294 let read_addr = GuestAddress(offset);
295 gm.read_obj(read_addr).unwrap()
296 }
297
298 fn validate_segments_and_sregs(
299 gm: &GuestMemoryMmap,
300 sregs: &kvm_sregs,
301 boot_prot: BootProtocol,
302 ) {
303 if let BootProtocol::LinuxBoot = boot_prot {
304 assert_eq!(0xaf_9b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 8));
305 assert_eq!(0xcf_9300_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 16));
306 assert_eq!(0x8f_8b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 24));
307
308 assert_eq!(0xffff_ffff, sregs.tr.limit);
309
310 assert!(sregs.cr0 & X86_CR0_PE != 0);
311 assert!(sregs.efer & EFER_LME != 0 && sregs.efer & EFER_LMA != 0);
312 } else {
313 assert_eq!(0xcf_9b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 8));
315 assert_eq!(0xcf_9300_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 16));
316 assert_eq!(0x00_8b00_0000_0067, read_u64(gm, BOOT_GDT_OFFSET + 24));
317
318 assert_eq!(0x67, sregs.tr.limit);
319 assert_eq!(0, sregs.tr.g);
320
321 assert!(sregs.cr0 & X86_CR0_PE != 0 && sregs.cr0 & X86_CR0_ET != 0);
322 assert_eq!(0, sregs.cr4);
323 }
324
325 assert_eq!(0x0, read_u64(gm, BOOT_GDT_OFFSET));
327 assert_eq!(0x0, read_u64(gm, BOOT_IDT_OFFSET));
328
329 assert_eq!(0, sregs.cs.base);
330 assert_eq!(0xffff_ffff, sregs.ds.limit);
331 assert_eq!(0x10, sregs.es.selector);
332 assert_eq!(1, sregs.fs.present);
333 assert_eq!(1, sregs.gs.g);
334 assert_eq!(0, sregs.ss.avl);
335 assert_eq!(0, sregs.tr.base);
336 assert_eq!(0, sregs.tr.avl);
337 }
338
339 fn validate_page_tables(gm: &GuestMemoryMmap, sregs: &kvm_sregs) {
340 assert_eq!(0xa003, read_u64(gm, PML4_START));
341 assert_eq!(0xb003, read_u64(gm, PDPTE_START));
342 for i in 0..512 {
343 assert_eq!((i << 21) + 0x83u64, read_u64(gm, PDE_START + (i * 8)));
344 }
345
346 assert_eq!(PML4_START, sregs.cr3);
347 assert!(sregs.cr4 & X86_CR4_PAE != 0);
348 assert!(sregs.cr0 & X86_CR0_PG != 0);
349 }
350
351 #[test]
352 fn test_setup_fpu() {
353 let kvm = Kvm::new().unwrap();
354 let vm = kvm.create_vm().unwrap();
355 let vcpu = vm.create_vcpu(0).unwrap();
356 setup_fpu(&vcpu).unwrap();
357
358 let expected_fpu: kvm_fpu = kvm_fpu {
359 fcw: 0x37f,
360 mxcsr: 0x1f80,
361 ..Default::default()
362 };
363 let actual_fpu: kvm_fpu = vcpu.get_fpu().unwrap();
364 assert_eq!(expected_fpu.fcw, actual_fpu.fcw);
366 }
372
373 #[test]
374 fn test_setup_regs() {
375 let kvm = Kvm::new().unwrap();
376 let vm = kvm.create_vm().unwrap();
377 let vcpu = vm.create_vcpu(0).unwrap();
378
379 let expected_regs: kvm_regs = kvm_regs {
380 rflags: 0x0000_0000_0000_0002u64,
381 rip: 1,
382 rsp: super::super::layout::BOOT_STACK_POINTER,
383 rbp: super::super::layout::BOOT_STACK_POINTER,
384 rsi: super::super::layout::ZERO_PAGE_START,
385 ..Default::default()
386 };
387
388 let entry_point: EntryPoint = EntryPoint {
389 entry_addr: GuestAddress(expected_regs.rip),
390 protocol: BootProtocol::LinuxBoot,
391 };
392
393 setup_regs(&vcpu, entry_point).unwrap();
394
395 let actual_regs: kvm_regs = vcpu.get_regs().unwrap();
396 assert_eq!(actual_regs, expected_regs);
397 }
398
399 #[test]
400 fn test_setup_sregs() {
401 let kvm = Kvm::new().unwrap();
402 let vm = kvm.create_vm().unwrap();
403 let vcpu = vm.create_vcpu(0).unwrap();
404 let gm = single_region_mem(0x10000);
405
406 [BootProtocol::LinuxBoot, BootProtocol::PvhBoot]
407 .iter()
408 .for_each(|boot_prot| {
409 vcpu.set_sregs(&Default::default()).unwrap();
410 setup_sregs(&gm, &vcpu, *boot_prot).unwrap();
411
412 let mut sregs: kvm_sregs = vcpu.get_sregs().unwrap();
413 sregs.gs.g = 1;
416
417 validate_segments_and_sregs(&gm, &sregs, *boot_prot);
418 if let BootProtocol::LinuxBoot = *boot_prot {
419 validate_page_tables(&gm, &sregs);
420 }
421 });
422 }
423
424 #[test]
425 fn test_write_gdt_table() {
426 let gm = single_region_mem(BOOT_GDT_OFFSET as usize);
428 let gdt_table: [u64; BOOT_GDT_MAX] = [
429 gdt_entry(0, 0, 0), gdt_entry(0xa09b, 0, 0xfffff), gdt_entry(0xc093, 0, 0xfffff), gdt_entry(0x808b, 0, 0xfffff), ];
434 write_gdt_table(&gdt_table, &gm).unwrap_err();
435
436 let gm =
438 single_region_mem(BOOT_GDT_OFFSET as usize + (mem::size_of::<u64>() * BOOT_GDT_MAX));
439
440 let gdt_table: [u64; BOOT_GDT_MAX] = [
441 gdt_entry(0, 0, 0), gdt_entry(0xa09b, 0, 0xfffff), gdt_entry(0xc093, 0, 0xfffff), gdt_entry(0x808b, 0, 0xfffff), ];
446 write_gdt_table(&gdt_table, &gm).unwrap();
447 }
448
449 #[test]
450 fn test_write_idt_table() {
451 let gm = single_region_mem(BOOT_IDT_OFFSET as usize);
453 let val = 0x100;
454 write_idt_value(val, &gm).unwrap_err();
455
456 let gm = single_region_mem(BOOT_IDT_OFFSET as usize + mem::size_of::<u64>());
457 write_idt_value(val, &gm).unwrap();
459 }
460
461 #[test]
462 fn test_configure_segments_and_sregs() {
463 let mut sregs: kvm_sregs = Default::default();
464 let gm = single_region_mem(0x10000);
465 configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::LinuxBoot).unwrap();
466
467 validate_segments_and_sregs(&gm, &sregs, BootProtocol::LinuxBoot);
468
469 configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::PvhBoot).unwrap();
470
471 validate_segments_and_sregs(&gm, &sregs, BootProtocol::PvhBoot);
472 }
473
474 #[test]
475 fn test_setup_page_tables() {
476 let mut sregs: kvm_sregs = Default::default();
477 let gm = single_region_mem(PML4_START as usize);
478 setup_page_tables(&gm, &mut sregs).unwrap_err();
479
480 let gm = single_region_mem(PDPTE_START as usize);
481 setup_page_tables(&gm, &mut sregs).unwrap_err();
482
483 let gm = single_region_mem(PDE_START as usize);
484 setup_page_tables(&gm, &mut sregs).unwrap_err();
485
486 let gm = single_region_mem(0x10000);
487 setup_page_tables(&gm, &mut sregs).unwrap();
488
489 validate_page_tables(&gm, &sregs);
490 }
491}