1use crate::importer::BootPageAcceptance;
7use crate::importer::GuestArch;
8use crate::importer::ImageLoad;
9use crate::importer::SegmentRegister;
10use crate::importer::TableRegister;
11use crate::importer::X86Register;
12use hvdef::HV_PAGE_SIZE;
13use memory_range::MemoryRange;
14use std::io::Read;
15use std::io::Seek;
16use thiserror::Error;
17use vm_topology::memory::MemoryLayout;
18use x86defs::GdtEntry;
19use x86defs::X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES;
20use x86defs::X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES;
21use zerocopy::FromZeros;
22use zerocopy::IntoBytes;
23
24pub trait ReadSeek: Read + Seek {}
26impl<T: Read + Seek> ReadSeek for T {}
27
28const DEFAULT_GDT_COUNT: usize = 4;
29pub const DEFAULT_GDT_SIZE: u64 = HV_PAGE_SIZE;
31
32pub fn import_default_gdt(
36 importer: &mut dyn ImageLoad<X86Register>,
37 gdt_page_base: u64,
38) -> anyhow::Result<()> {
39 let default_data_attributes: u16 = X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES.into();
43 let default_code_attributes: u16 = X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES.into();
44 let gdt: [GdtEntry; DEFAULT_GDT_COUNT] = [
45 GdtEntry::new_zeroed(),
46 GdtEntry {
47 limit_low: 0xffff,
48 attr_low: default_code_attributes as u8,
49 attr_high: (default_code_attributes >> 8) as u8,
50 ..GdtEntry::new_zeroed()
51 },
52 GdtEntry {
53 limit_low: 0xffff,
54 attr_low: default_data_attributes as u8,
55 attr_high: (default_data_attributes >> 8) as u8,
56 ..GdtEntry::new_zeroed()
57 },
58 GdtEntry::new_zeroed(),
59 ];
60 let gdt_entry_size = size_of::<GdtEntry>();
61 let linear_selector_offset = 2 * gdt_entry_size;
62 let linear_code64_selector_offset = gdt_entry_size;
63
64 importer.import_pages(
66 gdt_page_base,
67 DEFAULT_GDT_SIZE / HV_PAGE_SIZE,
68 "default-gdt",
69 BootPageAcceptance::Exclusive,
70 gdt.as_bytes(),
71 )?;
72
73 let mut import_reg = |register| importer.import_vp_register(register);
75 import_reg(X86Register::Gdtr(TableRegister {
76 base: gdt_page_base * HV_PAGE_SIZE,
77 limit: (size_of::<GdtEntry>() * DEFAULT_GDT_COUNT - 1) as u16,
78 }))?;
79
80 let ds = SegmentRegister {
81 selector: linear_selector_offset as u16,
82 base: 0,
83 limit: 0xffffffff,
84 attributes: default_data_attributes,
85 };
86 import_reg(X86Register::Ds(ds))?;
87 import_reg(X86Register::Es(ds))?;
88 import_reg(X86Register::Fs(ds))?;
89 import_reg(X86Register::Gs(ds))?;
90 import_reg(X86Register::Ss(ds))?;
91
92 let cs = SegmentRegister {
93 selector: linear_code64_selector_offset as u16,
94 base: 0,
95 limit: 0xffffffff,
96 attributes: default_code_attributes,
97 };
98 import_reg(X86Register::Cs(cs))?;
99
100 Ok(())
101}
102
103pub fn compute_variable_mtrrs(
107 memory: &MemoryLayout,
108 physical_address_width: u8,
109 chipset_low_mmio: MemoryRange,
110 chipset_high_mmio: MemoryRange,
111) -> Vec<X86Register> {
112 const WRITEBACK: u64 = 0x6;
113
114 let gpa_space_size = physical_address_width.clamp(36, 52);
116
117 let mut result = Vec::with_capacity(8);
121
122 let pcat_mtrr_size = 128 * 1024 * 1024;
125
126 result.push(X86Register::MtrrPhysBase0(WRITEBACK));
127 result.push(X86Register::MtrrPhysMask0(mtrr_mask(
128 gpa_space_size,
129 pcat_mtrr_size - 1,
130 )));
131
132 if memory.end_of_ram() > pcat_mtrr_size {
135 result.push(X86Register::MtrrPhysBase1(pcat_mtrr_size | WRITEBACK));
136 result.push(X86Register::MtrrPhysMask1(mtrr_mask(
137 gpa_space_size,
138 chipset_low_mmio.start() - 1,
139 )));
140 }
141
142 if memory.end_of_ram() > chipset_low_mmio.end() {
145 result.push(X86Register::MtrrPhysBase2(
146 chipset_low_mmio.end() | WRITEBACK,
147 ));
148 if chipset_high_mmio.is_empty() {
149 result.push(X86Register::MtrrPhysMask2(mtrr_mask(
155 gpa_space_size,
156 (1 << std::cmp::min(gpa_space_size, 43)).min(memory.end_of_ram()) - 1,
157 )));
158 if memory.end_of_ram() > (1 << 43) {
159 result.push(X86Register::MtrrPhysBase3((1 << 43) | WRITEBACK));
160 result.push(X86Register::MtrrPhysMask3(mtrr_mask(
161 gpa_space_size,
162 memory.end_of_ram() - 1,
163 )));
164 }
165 } else {
166 result.push(X86Register::MtrrPhysMask2(mtrr_mask(
167 gpa_space_size,
168 chipset_high_mmio.start() - 1,
169 )));
170 }
171 }
172
173 if !chipset_high_mmio.is_empty() && memory.end_of_ram() > chipset_high_mmio.end() {
178 result.push(X86Register::MtrrPhysBase3(
179 chipset_high_mmio.end() | WRITEBACK,
180 ));
181 result.push(X86Register::MtrrPhysMask3(mtrr_mask(
182 gpa_space_size,
183 (1 << std::cmp::min(gpa_space_size, 43)) - 1,
184 )));
185 if gpa_space_size > 43 {
186 result.push(X86Register::MtrrPhysBase4((1 << 43) | WRITEBACK));
187 result.push(X86Register::MtrrPhysMask4(mtrr_mask(
188 gpa_space_size,
189 (1 << gpa_space_size) - 1,
190 )));
191 }
192 }
193
194 result
195}
196
197fn mtrr_mask(gpa_space_size: u8, maximum_address: u64) -> u64 {
198 const ENABLED: u64 = 1 << 11;
199
200 let mut result = ENABLED;
201
202 for index in 12..gpa_space_size {
204 result |= 1 << index;
205 }
206
207 for index in 12..gpa_space_size {
209 let test_maximum_address = 1 << index;
210
211 if maximum_address >= test_maximum_address {
212 result &= !(1 << index);
214 } else {
215 break;
217 }
218 }
219
220 result
221}
222
223#[derive(Debug, Error)]
225pub enum ImportFileRegionError {
226 #[error("file length {file_length} exceeds memory length {memory_length}")]
228 FileLengthExceedsMemoryLength {
229 file_length: u64,
231 memory_length: u64,
233 },
234 #[error("failed to seek file")]
236 Seek(#[source] std::io::Error),
237 #[error("failed to read file")]
239 Read(#[source] std::io::Error),
240 #[error("failed to import pages")]
242 ImportPages(#[source] anyhow::Error),
243 #[error("address computation overflowed")]
245 Overflow,
246}
247
248pub struct ImportFileRegion<'a, F: ?Sized> {
250 pub file: &'a mut F,
252 pub file_offset: u64,
254 pub file_length: u64,
256 pub gpa: u64,
258 pub memory_length: u64,
260 pub acceptance: BootPageAcceptance,
262 pub tag: &'a str,
264}
265
266pub struct ChunkBuf(Vec<u8>);
272
273impl ChunkBuf {
274 const DEFAULT_SIZE: usize = 64 * 1024;
276
277 pub fn new() -> Self {
279 Self::with_size(Self::DEFAULT_SIZE)
280 }
281
282 pub fn with_size(size: usize) -> Self {
287 let page_count = size as u64 / HV_PAGE_SIZE;
288 assert!(page_count > 0, "ChunkBuf must be at least one page");
289 Self(vec![0u8; (page_count * HV_PAGE_SIZE) as usize])
290 }
291
292 pub fn import_file_region<F, R: GuestArch>(
300 &mut self,
301 importer: &mut dyn ImageLoad<R>,
302 params: ImportFileRegion<'_, F>,
303 ) -> Result<(), ImportFileRegionError>
304 where
305 F: ReadSeek + ?Sized,
306 {
307 let ImportFileRegion {
308 file,
309 file_offset,
310 file_length,
311 gpa,
312 memory_length,
313 acceptance,
314 tag,
315 } = params;
316
317 if file_length > memory_length {
318 return Err(ImportFileRegionError::FileLengthExceedsMemoryLength {
319 file_length,
320 memory_length,
321 });
322 }
323
324 if memory_length == 0 {
325 return Ok(());
326 }
327
328 let buf = &mut self.0[..];
329 let buf_pages = buf.len() as u64 / HV_PAGE_SIZE;
330
331 let page_mask = HV_PAGE_SIZE - 1;
332 let leading_zero = gpa & page_mask;
333 let page_base = gpa / HV_PAGE_SIZE;
334 let total_page_count = leading_zero
335 .checked_add(memory_length)
336 .and_then(|v| v.checked_add(page_mask))
337 .ok_or(ImportFileRegionError::Overflow)?
338 / HV_PAGE_SIZE;
339
340 file.seek(std::io::SeekFrom::Start(file_offset))
341 .map_err(ImportFileRegionError::Seek)?;
342
343 let mut pages_done: u64 = 0;
344 let mut file_remaining = file_length;
345
346 while file_remaining > 0 {
347 let chunk_pages = (total_page_count - pages_done).min(buf_pages);
348 let chunk_bytes = (chunk_pages * HV_PAGE_SIZE) as usize;
349 let chunk_buf = &mut buf[..chunk_bytes];
350
351 let data_start = if pages_done == 0 {
352 leading_zero as usize
353 } else {
354 0
355 };
356 let data_len = file_remaining.min((chunk_bytes - data_start) as u64) as usize;
357
358 chunk_buf[..data_start].fill(0);
360
361 file.read_exact(&mut chunk_buf[data_start..data_start + data_len])
363 .map_err(ImportFileRegionError::Read)?;
364
365 file_remaining -= data_len as u64;
366
367 let import_page_count = if file_remaining == 0 {
370 total_page_count - pages_done
371 } else {
372 chunk_pages
373 };
374
375 importer
376 .import_pages(
377 page_base + pages_done,
378 import_page_count,
379 tag,
380 acceptance,
381 &chunk_buf[..data_start + data_len],
382 )
383 .map_err(ImportFileRegionError::ImportPages)?;
384
385 pages_done += import_page_count;
386 }
387
388 if file_length == 0 {
390 importer
391 .import_pages(page_base, total_page_count, tag, acceptance, &[])
392 .map_err(ImportFileRegionError::ImportPages)?;
393 }
394
395 Ok(())
396 }
397
398 pub fn crc32(&mut self, file: &mut dyn ReadSeek, len: u64) -> Result<u32, std::io::Error> {
400 file.seek(std::io::SeekFrom::Start(0))?;
401 let mut hasher = crc32fast::Hasher::new();
402 let mut remaining = len;
403 while remaining > 0 {
404 let to_read = remaining.min(self.0.len() as u64) as usize;
405 file.read_exact(&mut self.0[..to_read])?;
406 hasher.update(&self.0[..to_read]);
407 remaining -= to_read as u64;
408 }
409 file.rewind()?;
410 Ok(hasher.finalize())
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 const GB: u64 = 1024 * 1024 * 1024;
419 const MB: u64 = 1024 * 1024;
420
421 fn standard_low_mmio() -> MemoryRange {
422 MemoryRange::new(4 * GB - 128 * MB..4 * GB)
424 }
425
426 fn standard_high_mmio() -> MemoryRange {
427 MemoryRange::new(4 * GB..4 * GB + 512 * MB)
429 }
430
431 fn make_layout(ram_size: u64) -> MemoryLayout {
432 MemoryLayout::new(
433 ram_size,
434 &[standard_low_mmio(), standard_high_mmio()],
435 &[],
436 &[],
437 None,
438 )
439 .unwrap()
440 }
441
442 fn pair_count(regs: &[X86Register]) -> usize {
444 assert_eq!(regs.len() % 2, 0, "MTRR registers come in pairs");
445 regs.len() / 2
446 }
447
448 #[test]
449 fn mtrr_128mb_exactly() {
450 let layout = make_layout(128 * MB);
452 let regs = compute_variable_mtrrs(&layout, 46, standard_low_mmio(), standard_high_mmio());
453 assert_eq!(pair_count(®s), 1);
454 assert_eq!(regs[0], X86Register::MtrrPhysBase0(0x6));
455 }
456
457 #[test]
458 fn mtrr_256mb() {
459 let layout = make_layout(256 * MB);
461 let regs = compute_variable_mtrrs(&layout, 46, standard_low_mmio(), standard_high_mmio());
462 assert_eq!(pair_count(®s), 2);
463 assert_eq!(regs[0], X86Register::MtrrPhysBase0(0x6));
464 assert_eq!(regs[2], X86Register::MtrrPhysBase1((128 * MB) | 0x6));
466 }
467
468 #[test]
469 fn mtrr_2gb() {
470 let layout = make_layout(2 * GB);
472 let regs = compute_variable_mtrrs(&layout, 46, standard_low_mmio(), standard_high_mmio());
473 assert_eq!(pair_count(®s), 2);
474 assert_eq!(regs[2], X86Register::MtrrPhysBase1((128 * MB) | 0x6));
475 }
476
477 #[test]
478 fn mtrr_8gb() {
479 let layout = make_layout(8 * GB);
481 let regs = compute_variable_mtrrs(&layout, 46, standard_low_mmio(), standard_high_mmio());
482 assert_eq!(pair_count(®s), 5);
488
489 assert_eq!(regs[0], X86Register::MtrrPhysBase0(0x6));
491 assert_eq!(regs[2], X86Register::MtrrPhysBase1((128 * MB) | 0x6));
492 assert_eq!(
493 regs[4],
494 X86Register::MtrrPhysBase2(standard_low_mmio().end() | 0x6)
495 );
496 assert_eq!(
497 regs[6],
498 X86Register::MtrrPhysBase3(standard_high_mmio().end() | 0x6)
499 );
500 assert_eq!(regs[8], X86Register::MtrrPhysBase4((1u64 << 43) | 0x6));
501 }
502
503 #[test]
504 fn mtrr_8gb_no_high_mmio() {
505 let low = standard_low_mmio();
509 let layout = MemoryLayout::new(8 * GB, &[low], &[], &[], None).unwrap();
510 let regs = compute_variable_mtrrs(&layout, 46, low, MemoryRange::EMPTY);
511 assert_eq!(pair_count(®s), 3);
515 assert_eq!(regs[4], X86Register::MtrrPhysBase2(low.end() | 0x6));
516 }
517
518 #[test]
519 fn mtrr_narrow_address_width() {
520 let layout = make_layout(8 * GB);
522 let regs = compute_variable_mtrrs(&layout, 40, standard_low_mmio(), standard_high_mmio());
523 assert_eq!(pair_count(®s), 4);
525 assert_eq!(
526 regs[6],
527 X86Register::MtrrPhysBase3(standard_high_mmio().end() | 0x6)
528 );
529 }
530
531 #[test]
532 fn mtrr_masks_have_enabled_bit() {
533 let layout = make_layout(8 * GB);
535 let regs = compute_variable_mtrrs(&layout, 46, standard_low_mmio(), standard_high_mmio());
536 for (i, reg) in regs.iter().enumerate() {
537 match reg {
538 X86Register::MtrrPhysMask0(v)
539 | X86Register::MtrrPhysMask1(v)
540 | X86Register::MtrrPhysMask2(v)
541 | X86Register::MtrrPhysMask3(v)
542 | X86Register::MtrrPhysMask4(v) => {
543 assert!(v & (1 << 11) != 0, "mask at index {i} missing ENABLED bit");
544 }
545 _ => {} }
547 }
548 }
549}