Skip to main content

vmi_arch_amd64/
lib.rs

1//! AMD64 architecture definitions.
2
3mod address;
4mod cr;
5mod descriptor;
6mod dr;
7mod event;
8mod interrupt;
9mod msr;
10mod paging;
11mod registers;
12mod rflags;
13mod segment;
14mod translation;
15
16use vmi_core::{
17    AccessContext, AddressContext, Architecture, Gfn, MemoryAccess, Pa, Va, VmiCore, VmiError,
18    arch::GpRegisters as _, driver::VmiRead,
19};
20use zerocopy::FromBytes;
21
22pub use self::{
23    cr::{ControlRegister, Cr0, Cr2, Cr3, Cr4},
24    descriptor::{Gdtr, Idtr},
25    dr::{Dr0, Dr1, Dr2, Dr3, Dr6, Dr7},
26    event::{
27        EventCpuId, EventHypercall, EventInterrupt, EventIo, EventIoDirection, EventMemoryAccess,
28        EventMonitor, EventReason, EventSinglestep, EventWriteCr, EventWriteMsr, MemoryAccessFlags,
29    },
30    interrupt::{ExceptionVector, Idt, IdtAccess, IdtEntry, Interrupt, InterruptType},
31    msr::{Msr, MsrEfer},
32    paging::{PageTableEntry, PageTableLevel, PagingMode},
33    registers::{GpRegisters, Registers},
34    rflags::Rflags,
35    segment::{
36        DescriptorTable, DescriptorType, Granularity, OperationSize, SegmentAccess,
37        SegmentDescriptor, Selector,
38    },
39    translation::{TranslationEntries, TranslationEntry, VaTranslation},
40};
41
42/// AMD64 architecture.
43#[derive(Debug)]
44pub struct Amd64;
45
46impl Architecture for Amd64 {
47    const PAGE_SIZE: u64 = 0x1000;
48    const PAGE_SHIFT: u64 = 12;
49    const PAGE_MASK: u64 = 0xFFFFFFFFFFFFF000;
50
51    const BREAKPOINT: &'static [u8] = &[0xcc];
52
53    type Registers = Registers;
54    type PageTableLevel = PageTableLevel;
55    type Interrupt = Interrupt;
56    type SpecialRegister = ControlRegister;
57
58    type EventMonitor = EventMonitor;
59    type EventReason = EventReason;
60
61    fn gfn_from_pa(pa: Pa) -> Gfn {
62        Gfn(pa.0 >> Self::PAGE_SHIFT)
63    }
64
65    fn pa_from_gfn(gfn: Gfn) -> Pa {
66        Pa(gfn.0 << Self::PAGE_SHIFT)
67    }
68
69    fn pa_in_gfn(gfn: Gfn, va: Va) -> Pa {
70        Self::pa_in_gfn_for(gfn, va, PageTableLevel::Pt)
71    }
72
73    fn pa_in_gfn_for(gfn: Gfn, va: Va, level: Self::PageTableLevel) -> Pa {
74        Self::pa_from_gfn(gfn) + Self::va_offset_for(va, level)
75    }
76
77    fn pa_offset(pa: Pa) -> u64 {
78        pa.0 & !Self::PAGE_MASK
79    }
80
81    fn va_align_down(va: Va) -> Va {
82        Self::va_align_down_for(va, PageTableLevel::Pt)
83    }
84
85    fn va_align_down_for(va: Va, level: Self::PageTableLevel) -> Va {
86        let mask = match level {
87            PageTableLevel::Pt => !0xfff,
88            PageTableLevel::Pd => !0x1fffff,
89            PageTableLevel::Pdpt => !0x3fffffff,
90            PageTableLevel::Pml4 => !0x7fffffffff,
91        };
92
93        va & mask
94    }
95
96    fn va_align_up(va: Va) -> Va {
97        Self::va_align_up_for(va, PageTableLevel::Pt)
98    }
99
100    fn va_align_up_for(va: Va, level: Self::PageTableLevel) -> Va {
101        let mask = match level {
102            PageTableLevel::Pt => 0xfff,
103            PageTableLevel::Pd => 0x1fffff,
104            PageTableLevel::Pdpt => 0x3fffffff,
105            PageTableLevel::Pml4 => 0x7fffffffff,
106        };
107
108        (va + mask) & !mask
109    }
110
111    fn va_offset(va: Va) -> u64 {
112        Self::va_offset_for(va, PageTableLevel::Pt)
113    }
114
115    fn va_offset_for(va: Va, level: Self::PageTableLevel) -> u64 {
116        match level {
117            // 4KB page (4 * 1024 - 1).
118            PageTableLevel::Pt => va.0 & 0xfff,
119
120            // 2MB page (2 * 1024 * 1024 - 1).
121            PageTableLevel::Pd => va.0 & 0x1fffff,
122
123            // 1GB page (1024 * 1024 * 1024 - 1).
124            PageTableLevel::Pdpt => va.0 & 0x3fffffff,
125
126            // 512GB page (512 * 1024 * 1024 * 1024 - 1).
127            PageTableLevel::Pml4 => va.0 & 0x7fffffffff,
128        }
129    }
130
131    fn va_index(va: Va) -> u64 {
132        Self::va_index_for(va, PageTableLevel::Pt)
133    }
134
135    fn va_index_for(va: Va, level: Self::PageTableLevel) -> u64 {
136        match level {
137            PageTableLevel::Pt => (va.0 >> 12) & 0x1ff,
138            PageTableLevel::Pd => (va.0 >> 21) & 0x1ff,
139            PageTableLevel::Pdpt => (va.0 >> 30) & 0x1ff,
140            PageTableLevel::Pml4 => (va.0 >> 39) & 0x1ff,
141        }
142    }
143
144    fn translate_address<Driver>(vmi: &VmiCore<Driver>, va: Va, root: Pa) -> Result<Pa, VmiError>
145    where
146        Driver: VmiRead<Architecture = Self>,
147    {
148        // Read the PML4 table
149        let buffer = vmi.read_page(Self::gfn_from_pa(root))?;
150        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
151
152        let pml4i = Self::va_index_for(va, PageTableLevel::Pml4) as usize;
153        let pml4e = page_table[pml4i];
154
155        if !pml4e.present() {
156            return Err(VmiError::translation((va, root)));
157        }
158
159        if pml4e.large() {
160            return Ok(Self::pa_in_gfn_for(pml4e.pfn(), va, PageTableLevel::Pml4));
161        }
162
163        // Read the PDPT table
164        let buffer = vmi.read_page(pml4e.pfn())?;
165        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
166
167        let pdpti = Self::va_index_for(va, PageTableLevel::Pdpt) as usize;
168        let pdpte = page_table[pdpti];
169
170        if !pdpte.present() {
171            return Err(VmiError::translation((va, root)));
172        }
173
174        if pdpte.large() {
175            return Ok(Self::pa_in_gfn_for(pdpte.pfn(), va, PageTableLevel::Pdpt));
176        }
177
178        // Read the PD table
179        let buffer = vmi.read_page(pdpte.pfn())?;
180        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
181
182        let pdi = Self::va_index_for(va, PageTableLevel::Pd) as usize;
183        let pde = page_table[pdi];
184
185        if !pde.present() {
186            return Err(VmiError::translation((va, root)));
187        }
188
189        if pde.large() {
190            return Ok(Self::pa_in_gfn_for(pde.pfn(), va, PageTableLevel::Pd));
191        }
192
193        // Read the PT table
194        let buffer = vmi.read_page(pde.pfn())?;
195        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
196
197        let pti = Self::va_index_for(va, PageTableLevel::Pt) as usize;
198        let pte = page_table[pti];
199
200        if !pte.present() {
201            return Err(VmiError::translation((va, root)));
202        }
203
204        Ok(Self::pa_in_gfn_for(pte.pfn(), va, PageTableLevel::Pt))
205    }
206}
207
208impl Amd64 {
209    /// Canonicalize a virtual address.
210    pub fn va_canonical(va: Va) -> Va {
211        const BITS: u64 = 48;
212        const MASK: u64 = (1 << BITS) - 1;
213
214        Va(va.0 & MASK)
215    }
216
217    /// Determine the paging mode of the processor based on control register
218    /// values.
219    ///
220    /// # Paging Modes
221    ///
222    /// - **No Paging**: When paging is disabled (CR0.PG = 0)
223    /// - **32-bit Paging**: Used when CR0.PG = 1 and CR4.PAE = 0
224    /// - **PAE Paging**: Used when CR0.PG = 1, CR4.PAE = 1, and IA32_EFER.LME = 0
225    /// - **4-level Paging**: Used when CR0.PG = 1, CR4.PAE = 1, IA32_EFER.LME = 1, and CR4.LA57 = 0
226    /// - **5-level Paging**: Used when CR0.PG = 1, CR4.PAE = 1, IA32_EFER.LME = 1, and CR4.LA57 = 1
227    ///
228    /// If paging is disabled, the function returns `None`.
229    pub fn paging_mode(registers: &Registers) -> Option<PagingMode> {
230        if !registers.cr0.paging() {
231            return None;
232        }
233
234        if !registers.cr4.physical_address_extension() {
235            return Some(PagingMode::Legacy);
236        }
237
238        if !registers.msr_efer.long_mode_enable() {
239            return Some(PagingMode::PAE);
240        }
241
242        if !registers.cr4.linear_address_57_bit() {
243            return Some(PagingMode::Ia32e);
244        }
245
246        Some(PagingMode::Ia32eLA57)
247    }
248
249    /// Retrieves the Interrupt Descriptor Table (IDT) for a specific virtual
250    /// CPU.
251    pub fn interrupt_descriptor_table<Driver>(
252        vmi: &VmiCore<Driver>,
253        registers: &Registers,
254    ) -> Result<Idt, VmiError>
255    where
256        Driver: VmiRead<Architecture = Self>,
257    {
258        let idtr_base = registers.idtr.base.into();
259        vmi.read_struct::<Idt>((idtr_base, registers.cr3.into()))
260    }
261
262    /// Performs a page table walk to translate a virtual address to a physical
263    /// address.
264    ///
265    /// This function implements the 4-level paging translation process, walking
266    /// through the page tables to convert a virtual address to a physical
267    /// address.
268    ///
269    /// # Process
270    ///
271    /// 1. Initializes the translation entries vector.
272    /// 2. Ensures the virtual address is in canonical form.
273    /// 3. Walks through the 4-level paging structure:
274    ///    - PML4 (Page Map Level 4)
275    ///    - PDPT (Page Directory Pointer Table)
276    ///    - PD (Page Directory)
277    ///    - PT (Page Table)
278    /// 4. At each level:
279    ///    - Reads the relevant page table.
280    ///    - Extracts the appropriate entry.
281    ///    - Checks if the entry is present.
282    ///    - Checks if it's a large page (1GB or 2MB).
283    ///    - If it's a large page or the last level, calculates the final
284    ///      physical address.
285    ///    - Otherwise, continues to the next level.
286    ///
287    /// # Errors
288    ///
289    /// If any page table read fails, the function returns a [`VaTranslation`]
290    /// with the entries collected so far and `None` as the physical
291    /// address.
292    ///
293    /// # Notes
294    ///
295    /// - This implementation assumes x86-64 4-level paging. It doesn't handle
296    ///   5-level paging.
297    /// - The function handles large pages (1GB and 2MB) as well as standard 4KB
298    ///   pages.
299    /// - Each step of the translation is recorded, allowing for detailed
300    ///   analysis of the translation process.
301    pub fn translation<Driver>(vmi: &VmiCore<Driver>, va: Va, root: Pa) -> VaTranslation
302    where
303        Driver: VmiRead<Architecture = Self>,
304    {
305        let mut entries = TranslationEntries::new();
306        let va = Self::va_canonical(va);
307
308        // Read the PML4 table
309        let buffer = match vmi.read_page(Self::gfn_from_pa(root)) {
310            Ok(buffer) => buffer,
311            Err(_) => return VaTranslation { entries, pa: None },
312        };
313        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
314        let pml4i = Self::va_index_for(va, PageTableLevel::Pml4) as usize;
315        let pml4e = page_table[pml4i];
316
317        entries.push(TranslationEntry {
318            level: PageTableLevel::Pml4,
319            entry: pml4e,
320            entry_address: root + (pml4i * size_of::<PageTableEntry>()) as u64,
321        });
322
323        if !pml4e.present() {
324            return VaTranslation { entries, pa: None };
325        }
326
327        if pml4e.large() {
328            return VaTranslation {
329                entries,
330                pa: Some(Self::pa_in_gfn_for(pml4e.pfn(), va, PageTableLevel::Pml4)),
331            };
332        }
333
334        // Read the PDPT table
335        let buffer = match vmi.read_page(pml4e.pfn()) {
336            Ok(buffer) => buffer,
337            Err(_) => return VaTranslation { entries, pa: None },
338        };
339        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
340        let pdpti = Self::va_index_for(va, PageTableLevel::Pdpt) as usize;
341        let pdpte = page_table[pdpti];
342
343        entries.push(TranslationEntry {
344            level: PageTableLevel::Pdpt,
345            entry: pdpte,
346            entry_address: Self::pa_from_gfn(pml4e.pfn())
347                + (pdpti * size_of::<PageTableEntry>()) as u64,
348        });
349
350        if !pdpte.present() {
351            return VaTranslation { entries, pa: None };
352        }
353
354        if pdpte.large() {
355            return VaTranslation {
356                entries,
357                pa: Some(Self::pa_in_gfn_for(pdpte.pfn(), va, PageTableLevel::Pdpt)),
358            };
359        }
360
361        // Read the PD table
362        let buffer = match vmi.read_page(pdpte.pfn()) {
363            Ok(buffer) => buffer,
364            Err(_) => return VaTranslation { entries, pa: None },
365        };
366        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
367        let pdi = Self::va_index_for(va, PageTableLevel::Pd) as usize;
368        let pde = page_table[pdi];
369
370        entries.push(TranslationEntry {
371            level: PageTableLevel::Pd,
372            entry: pde,
373            entry_address: Self::pa_from_gfn(pdpte.pfn())
374                + (pdi * size_of::<PageTableEntry>()) as u64,
375        });
376
377        if !pde.present() {
378            return VaTranslation { entries, pa: None };
379        }
380
381        if pde.large() {
382            return VaTranslation {
383                entries,
384                pa: Some(Self::pa_in_gfn_for(pde.pfn(), va, PageTableLevel::Pd)),
385            };
386        }
387
388        // Read the PT table
389        let buffer = match vmi.read_page(pde.pfn()) {
390            Ok(buffer) => buffer,
391            Err(_) => return VaTranslation { entries, pa: None },
392        };
393        let page_table = <[PageTableEntry]>::ref_from_bytes(&buffer).unwrap();
394        let pti = Self::va_index_for(va, PageTableLevel::Pt) as usize;
395        let pte = page_table[pti];
396
397        entries.push(TranslationEntry {
398            level: PageTableLevel::Pt,
399            entry: pte,
400            entry_address: Self::pa_from_gfn(pde.pfn())
401                + (pti * size_of::<PageTableEntry>()) as u64,
402        });
403
404        VaTranslation {
405            entries,
406            pa: Some(Self::pa_in_gfn_for(pte.pfn(), va, PageTableLevel::Pt)),
407        }
408    }
409}
410
411impl vmi_core::arch::GpRegisters for GpRegisters {
412    type Architecture = Amd64;
413
414    fn instruction_pointer(&self) -> u64 {
415        self.rip
416    }
417
418    fn set_instruction_pointer(&mut self, ip: u64) {
419        self.rip = ip;
420    }
421
422    fn stack_pointer(&self) -> u64 {
423        self.rsp
424    }
425
426    fn set_stack_pointer(&mut self, sp: u64) {
427        self.rsp = sp;
428    }
429
430    fn result(&self) -> u64 {
431        self.rax
432    }
433
434    fn set_result(&mut self, result: u64) {
435        self.rax = result;
436    }
437}
438
439impl vmi_core::arch::Registers for Registers {
440    type Architecture = Amd64;
441
442    type GpRegisters = GpRegisters;
443
444    fn instruction_pointer(&self) -> u64 {
445        self.rip
446    }
447
448    fn set_instruction_pointer(&mut self, ip: u64) {
449        self.rip = ip;
450    }
451
452    fn stack_pointer(&self) -> u64 {
453        self.rsp
454    }
455
456    fn set_stack_pointer(&mut self, sp: u64) {
457        self.rsp = sp;
458    }
459
460    fn result(&self) -> u64 {
461        self.rax
462    }
463
464    fn set_result(&mut self, result: u64) {
465        self.rax = result;
466    }
467
468    fn gp_registers(&self) -> GpRegisters {
469        GpRegisters {
470            rax: self.rax,
471            rbx: self.rbx,
472            rcx: self.rcx,
473            rdx: self.rdx,
474            rbp: self.rbp,
475            rsi: self.rsi,
476            rdi: self.rdi,
477            rsp: self.rsp,
478            r8: self.r8,
479            r9: self.r9,
480            r10: self.r10,
481            r11: self.r11,
482            r12: self.r12,
483            r13: self.r13,
484            r14: self.r14,
485            r15: self.r15,
486            rip: self.rip,
487            rflags: self.rflags,
488        }
489    }
490
491    fn set_gp_registers(&mut self, gp: &GpRegisters) {
492        self.rax = gp.rax;
493        self.rbx = gp.rbx;
494        self.rcx = gp.rcx;
495        self.rdx = gp.rdx;
496        self.rbp = gp.rbp;
497        self.rsi = gp.rsi;
498        self.rdi = gp.rdi;
499        self.rsp = gp.rsp;
500        self.r8 = gp.r8;
501        self.r9 = gp.r9;
502        self.r10 = gp.r10;
503        self.r11 = gp.r11;
504        self.r12 = gp.r12;
505        self.r13 = gp.r13;
506        self.r14 = gp.r14;
507        self.r15 = gp.r15;
508        self.rip = gp.rip;
509        self.rflags = gp.rflags;
510    }
511
512    fn address_width(&self) -> usize {
513        Amd64::paging_mode(self).map_or(0, PagingMode::address_width)
514    }
515
516    fn effective_address_width(&self) -> usize {
517        // IA-32e mode uses a previously unused bit in the CS descriptor.
518        // Bit 53 is defined as the 64-bit (L) flag and is used to select
519        // between 64-bit mode and compatibility mode when IA-32e mode is
520        // active (IA32_EFER.LMA = 1).
521        //
522        // - If CS.L = 0 and IA-32e mode is active, the processor is running
523        //   in compatibility mode. In this case, CS.D selects the default
524        //   size for data and addresses. If CS.D = 0, the default data and
525        //   address size is 16 bits. If CS.D = 1, the default data and address
526        //   size is 32 bits.
527        //
528        // - If CS.L = 1 and IA-32e mode is active, the only valid setting
529        //   is CS.D = 0. This setting indicates a default operand size of
530        //   32 bits and a default address size of 64 bits. The CS.L = 1 and
531        //   CS.D = 1 bit combination is reserved for future use and a #GP
532        //   fault will be generated on an attempt to use a code segment
533        //   with these bits set in IA-32e mode.
534        //
535        // [Intel SDM Vol. 3A 5.2.1 (Code-Segment Descriptor in 64-bit Mode)]
536
537        match Amd64::paging_mode(self) {
538            Some(PagingMode::Ia32e | PagingMode::Ia32eLA57) if !self.cs.access.long_mode() => 4,
539            Some(paging_mode) => paging_mode.address_width(),
540            _ => 0,
541        }
542    }
543
544    fn access_context(&self, va: Va) -> AccessContext {
545        self.address_context(va).into()
546    }
547
548    fn address_context(&self, va: Va) -> AddressContext {
549        (va, self.cr3.into()).into()
550    }
551
552    fn translation_root(&self, _va: Va) -> Pa {
553        self.cr3.into()
554    }
555
556    fn set_translation_root(&mut self, root: u64, _va: Va) {
557        self.cr3 = Cr3(root);
558    }
559
560    fn return_address<Driver>(&self, vmi: &VmiCore<Driver>) -> Result<Va, VmiError>
561    where
562        Driver: VmiRead,
563    {
564        vmi.read_va(
565            (self.rsp.into(), self.cr3.into()),
566            self.effective_address_width(),
567        )
568    }
569
570    fn return_from_function<Driver>(
571        &self,
572        vmi: &VmiCore<Driver>,
573        value: u64,
574    ) -> Result<Self::GpRegisters, VmiError>
575    where
576        Driver: VmiRead<Architecture = Amd64>,
577    {
578        let return_address = self.return_address(vmi)?;
579
580        let mut gp = self.gp_registers();
581        gp.set_result(value);
582        gp.set_instruction_pointer(return_address.into());
583        gp.set_stack_pointer(self.rsp + self.effective_address_width() as u64);
584
585        Ok(gp)
586    }
587}
588
589impl vmi_core::arch::EventMemoryAccess for EventMemoryAccess {
590    type Architecture = Amd64;
591
592    fn pa(&self) -> Pa {
593        self.pa
594    }
595
596    fn va(&self) -> Va {
597        self.va
598    }
599
600    fn access(&self) -> MemoryAccess {
601        self.access
602    }
603}
604
605impl vmi_core::arch::EventInterrupt for EventInterrupt {
606    type Architecture = Amd64;
607
608    fn gfn(&self) -> Gfn {
609        self.gfn
610    }
611}
612
613impl vmi_core::arch::EventReason for EventReason {
614    type Architecture = Amd64;
615
616    fn as_memory_access(
617        &self,
618    ) -> Option<&impl vmi_core::arch::EventMemoryAccess<Architecture = Amd64>> {
619        match self {
620            EventReason::MemoryAccess(memory_access) => Some(memory_access),
621            _ => None,
622        }
623    }
624
625    fn as_interrupt(&self) -> Option<&impl vmi_core::arch::EventInterrupt<Architecture = Amd64>> {
626        match self {
627            EventReason::Interrupt(interrupt) => Some(interrupt),
628            _ => None,
629        }
630    }
631
632    fn as_software_breakpoint(
633        &self,
634    ) -> Option<&impl vmi_core::arch::EventInterrupt<Architecture = Amd64>> {
635        match self {
636            EventReason::Interrupt(interrupt)
637                if interrupt.interrupt.vector == ExceptionVector::Breakpoint
638                    && interrupt.interrupt.typ == InterruptType::SoftwareException =>
639            {
640                Some(interrupt)
641            }
642            _ => None,
643        }
644    }
645}