Skip to content
This repository was archived by the owner on Sep 1, 2024. It is now read-only.

Commit 83a6c64

Browse files
committed
Added functions to do gva->gpa & gpa->hpa
1 parent b6ca1a0 commit 83a6c64

File tree

9 files changed

+169
-146
lines changed

9 files changed

+169
-146
lines changed

hypervisor/src/intel/addresses.rs

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55
//! as well as methods for extracting page frame numbers (PFNs) and other address-related information.
66
77
use {
8-
crate::intel::paging::PageTables,
9-
core::ops::{Deref, DerefMut},
10-
x86::bits64::paging::{PAddr, BASE_PAGE_SHIFT},
8+
crate::{
9+
error::HypervisorError,
10+
intel::{ept::Ept, paging::PageTables, support::vmread},
11+
},
12+
log::trace,
13+
x86::{
14+
bits64::paging::{PAddr, BASE_PAGE_SHIFT},
15+
vmx::vmcs,
16+
},
1117
};
1218

1319
/// A representation of physical addresses.
@@ -23,16 +29,16 @@ impl PhysicalAddress {
2329
Self(PAddr::from(pa))
2430
}
2531

32+
/// Constructs a `PhysicalAddress` from a given virtual address.
33+
pub fn from_va(va: u64) -> Result<Self, HypervisorError> {
34+
Ok(Self(PAddr::from(Self::pa_from_va(va)?)))
35+
}
36+
2637
/// Constructs a `PhysicalAddress` from a given page frame number (PFN).
2738
pub fn from_pfn(pfn: u64) -> Self {
2839
Self(PAddr::from(pfn << BASE_PAGE_SHIFT))
2940
}
3041

31-
/// Constructs a `PhysicalAddress` from a given virtual address.
32-
pub fn from_va(va: u64) -> Self {
33-
Self(PAddr::from(Self::pa_from_va(va)))
34-
}
35-
3642
/// Retrieves the page frame number (PFN) for the physical address.
3743
pub fn pfn(&self) -> u64 {
3844
self.0.as_u64() >> BASE_PAGE_SHIFT
@@ -43,60 +49,44 @@ impl PhysicalAddress {
4349
self.0.as_u64()
4450
}
4551

46-
/// Converts a virtual address to its corresponding physical address.
47-
pub fn pa_from_va(va: u64) -> u64 {
48-
let guest_cr3 = PageTables::get_guest_cr3();
49-
PageTables::translate_guest_virtual_to_physical(guest_cr3 as usize, va as _).unwrap() as u64
50-
}
51-
52-
/// Reads a value of a specified type from guest memory at the provided virtual address, ensuring safety by internal validation.
52+
/// Converts a guest virtual address to its corresponding host physical address.
5353
///
54-
/// # Arguments
54+
/// This function first translates the guest virtual address to a guest physical address
55+
/// using the guest's CR3. It then translates the guest physical address to a host physical
56+
/// address using the EPT (Extended Page Table).
5557
///
56-
/// * `guest_cr3` - The base address of the guest's page table hierarchy.
57-
/// * `guest_va` - The guest virtual address from which to read.
58+
/// # Arguments
5859
///
59-
/// # Returns
60+
/// * `va` - The guest virtual address to translate.
6061
///
61-
/// * Returns an `Option<T>` which is `Some(value)` if the read is successful and safe, or `None` if the address cannot be translated or if safety conditions are not met.
62+
/// # Safety
6263
///
63-
/// # Type Parameters
64+
/// This function is unsafe because it involves raw memory access and relies on the integrity
65+
/// of the VMCS (Virtual Machine Control Structure).
6466
///
65-
/// * `T` - The type of the value to read. This can be any type that implements the `Copy` trait and has a size that can be read atomically.
67+
/// # Returns
6668
///
67-
/// # Credits
68-
/// Credits to Jessie (jessiep_) for the initial concept.
69-
pub fn read_guest_memory<T: Copy>(guest_cr3: usize, guest_va: usize) -> Option<T> {
70-
// Safety justification:
71-
// The translation function ensures that the physical address is valid and maps to a real physical memory location.
72-
// The dereference is only performed if the translation succeeds, and it's constrained to types that are Copy, implying they can be safely duplicated and do not manage resources that require manual cleanup.
73-
// Still, the caller must ensure that reading from this specific address does not violate any safety contracts.
74-
let pa = PageTables::translate_guest_virtual_to_physical(guest_cr3, guest_va)?;
75-
unsafe { Some(*(pa as *const T)) }
76-
}
77-
}
69+
/// A `Result<u64, HypervisorError>` containing the host physical address on success, or an error if the translation fails.
70+
pub fn pa_from_va(va: u64) -> Result<u64, HypervisorError> {
71+
let guest_cr3 = vmread(vmcs::guest::CR3);
72+
trace!("Guest CR3: {:#x}", guest_cr3);
7873

79-
impl Deref for PhysicalAddress {
80-
type Target = PAddr;
74+
let guest_pa = unsafe { PageTables::translate_guest_virtual_to_guest_physical(guest_cr3, va)? };
75+
trace!("Guest VA: {:#x} -> Guest PA: {:#x}", va, guest_pa);
8176

82-
/// Dereferences the `PhysicalAddress` to retrieve the underlying `PAddr`.
83-
fn deref(&self) -> &Self::Target {
84-
&self.0
85-
}
86-
}
77+
// Translate guest physical address (GPA) to host physical address (HPA) using Extended Page Tables (EPT)
78+
// In a 1:1 mapping, the guest physical address is the same as the host physical address.
79+
// This translation is not required in a 1:1 mapping but is done for demonstration purposes
80+
// and in case changes are made to the Paging/EPT.
81+
let vmcs_eptp = vmread(vmcs::control::EPTP_FULL);
82+
trace!("VMCS EPTP: {:#x}", vmcs_eptp);
8783

88-
impl DerefMut for PhysicalAddress {
89-
/// Provides mutable access to the underlying `PAddr`.
90-
fn deref_mut(&mut self) -> &mut Self::Target {
91-
&mut self.0
92-
}
93-
}
84+
let (pml4_address, _, _) = Ept::decode_eptp(vmcs_eptp)?;
85+
trace!("EPT PML4 Address: {:#x}", pml4_address);
9486

95-
/// Converts a virtual address to its corresponding physical address.
96-
///
97-
/// # Arguments
98-
///
99-
/// * `ptr` - The virtual address to convert.
100-
pub fn physical_address(ptr: *const u64) -> PAddr {
101-
PhysicalAddress::from_va(ptr as u64).0
87+
let host_pa = unsafe { Ept::translate_guest_pa_to_host_pa(pml4_address, guest_pa)? };
88+
trace!("Guest PA: {:#x} -> Host PA: {:#x}", guest_pa, host_pa);
89+
90+
Ok(host_pa)
91+
}
10292
}

hypervisor/src/intel/ept.rs

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -121,94 +121,90 @@ impl Ept {
121121
Ok(())
122122
}
123123

124-
/// Reads a value from a given guest physical address.
124+
/// Translates a guest physical address to a host physical address using the EPT.
125125
///
126-
/// This function translates the guest physical address (GPA) to the corresponding host physical address (HPA)
127-
/// using the EPT, and then reads the value at the HPA.
126+
/// This function traverses the EPT hierarchy (PML4, PDPT, PD, PT) to translate the given
127+
/// guest physical address (GPA) to its corresponding host physical address (HPA).
128128
///
129129
/// # Arguments
130130
///
131-
/// * `guest_pa` - The guest physical address to read from.
131+
/// * `guest_pa` - The guest physical address to translate.
132132
///
133133
/// # Returns
134134
///
135-
/// A `Result<u64, HypervisorError>` containing the value read from the address on success.
136-
pub fn read_guest_pa(&self, guest_pa: u64) -> Result<u64, HypervisorError> {
137-
// Translate the guest physical address to host physical address.
138-
// In a 1:1 mapping, the guest physical address is the same as the host physical address.
139-
// Assuming the environment allows direct memory access to those addresses.
140-
let host_pa = self.translate_guest_pa_to_host_pa(guest_pa)?;
141-
trace!("Reading from GPA {:#x} (HPA: {:#x})", guest_pa, host_pa);
142-
143-
// Read the value at the host physical address.
144-
// Assuming the host physical address can be directly dereferenced.
145-
// You may need to adjust the pointer dereferencing method based on your environment.
146-
let value = unsafe { *(host_pa as *const u64) };
147-
trace!("Read value: {:#x}", value);
148-
149-
Ok(value)
150-
}
151-
135+
/// A `Result<u64, HypervisorError>` containing the host physical address on success.
152136
/// Translates a guest physical address to a host physical address using the EPT.
153-
///
154137
/// This function traverses the EPT hierarchy (PML4, PDPT, PD, PT) to translate the given
155138
/// guest physical address (GPA) to its corresponding host physical address (HPA).
156139
///
157140
/// # Arguments
158141
///
142+
/// * `ept_base` - The base address of the EPT structure.
159143
/// * `guest_pa` - The guest physical address to translate.
160144
///
161145
/// # Returns
162146
///
163147
/// A `Result<u64, HypervisorError>` containing the host physical address on success.
164-
pub fn translate_guest_pa_to_host_pa(&self, guest_pa: u64) -> Result<u64, HypervisorError> {
148+
pub unsafe fn translate_guest_pa_to_host_pa(ept_base: u64, guest_pa: u64) -> Result<u64, HypervisorError> {
165149
let guest_pa = VAddr::from(guest_pa);
166150

151+
// Cast the EPT base to the PML4 table structure.
152+
let pml4_table = ept_base as *const Pml4;
153+
167154
// Calculate the PML4 index and access the corresponding entry.
168-
let pmld4_index = pml4_index(guest_pa);
169-
let pml4_entry = &self.pml4.0.entries[pmld4_index];
155+
let pml4_index = pml4_index(guest_pa);
156+
let pml4_entry = &(*pml4_table).0.entries[pml4_index];
170157

171158
// Check if the PML4 entry is present (readable).
172159
if !pml4_entry.readable() {
173160
error!("PML4 entry is not present: {:#x}", guest_pa);
174161
return Err(HypervisorError::InvalidPml4Entry);
175162
}
176163

164+
// Cast the entry to the PDPT table structure.
165+
let pdpt_table = (pml4_entry.pfn() << BASE_PAGE_SHIFT) as *const Pdpt;
166+
177167
// Calculate the PDPT index and access the corresponding entry.
178168
let pdpt_index = pdpt_index(guest_pa);
179-
let pdpt_entry = &self.pdpt.0.entries[pdpt_index];
169+
let pdpt_entry = &(*pdpt_table).0.entries[pdpt_index];
180170

181171
// Check if the PDPT entry is present (readable).
182172
if !pdpt_entry.readable() {
183173
error!("PDPT entry is not present: {:#x}", guest_pa);
184174
return Err(HypervisorError::InvalidPdptEntry);
185175
}
186176

187-
// Check if the PDPT entry is huge page (1 GB), if so, calculate the host physical address.
177+
// Check if the PDPT entry is a huge page (1 GB), if so, calculate the host physical address.
188178
if pdpt_entry.large() {
189179
let host_pa = (pdpt_entry.pfn() << BASE_PAGE_SHIFT) + (guest_pa.as_u64() % HUGE_PAGE_SIZE as u64);
190180
return Ok(host_pa);
191181
}
192182

183+
// Cast the entry to the PD table structure.
184+
let pd_table = (pdpt_entry.pfn() << BASE_PAGE_SHIFT) as *const Pd;
185+
193186
// Calculate the PD index and access the corresponding entry.
194187
let pd_index = pd_index(guest_pa);
195-
let pd_entry = &self.pd[pdpt_index].0.entries[pd_index];
188+
let pd_entry = &(*pd_table).0.entries[pd_index];
196189

197190
// Check if the PD entry is present (readable).
198191
if !pd_entry.readable() {
199192
error!("PD entry is not present: {:#x}", guest_pa);
200193
return Err(HypervisorError::InvalidPdEntry);
201194
}
202195

203-
// Check if the PD entry is large page (2 MB), if so, calculate the host physical address.
196+
// Check if the PD entry is a large page (2 MB), if so, calculate the host physical address.
204197
if pd_entry.large() {
205198
let host_pa = (pd_entry.pfn() << BASE_PAGE_SHIFT) + (guest_pa.as_u64() % LARGE_PAGE_SIZE as u64);
206199
return Ok(host_pa);
207200
}
208201

202+
// Cast the entry to the PT table structure.
203+
let pt_table = (pd_entry.pfn() << BASE_PAGE_SHIFT) as *const Pt;
204+
209205
// Calculate the PT index and access the corresponding entry.
210206
let pt_index = pt_index(guest_pa);
211-
let pt_entry = &self.pt.0.entries[pt_index];
207+
let pt_entry = &(*pt_table).0.entries[pt_index];
212208

213209
// Check if the PT entry is present (readable).
214210
if !pt_entry.readable() {

hypervisor/src/intel/hooks/hook_manager.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,10 @@ impl HookManager {
142142
/// * `Ok(())` - The kernel base and size were set successfully.
143143
pub fn set_kernel_base_and_size(&mut self, guest_va: u64) -> Result<(), HypervisorError> {
144144
// Get the base address of ntoskrnl.exe.
145-
self.ntoskrnl_base_va = unsafe { get_image_base_address(guest_va).ok_or(HypervisorError::FailedToGetImageBaseAddress)? };
145+
self.ntoskrnl_base_va = unsafe { get_image_base_address(guest_va)? };
146146

147147
// Get the physical address of ntoskrnl.exe using GUEST_CR3 and the virtual address.
148-
self.ntoskrnl_base_pa = PhysicalAddress::pa_from_va(self.ntoskrnl_base_va);
148+
self.ntoskrnl_base_pa = PhysicalAddress::pa_from_va(self.ntoskrnl_base_va)?;
149149

150150
// Get the size of ntoskrnl.exe.
151151
self.ntoskrnl_size = unsafe { get_size_of_image(self.ntoskrnl_base_pa as _).ok_or(HypervisorError::FailedToGetKernelSize)? } as u64;
@@ -323,7 +323,7 @@ impl HookManager {
323323
) -> Result<(), HypervisorError> {
324324
debug!("Creating EPT hook for function at VA: {:#x}", guest_function_va);
325325

326-
let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va));
326+
let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va)?);
327327
debug!("Guest function PA: {:#x}", guest_function_pa.as_u64());
328328

329329
let guest_page_pa = guest_function_pa.align_down_to_base_page();
@@ -424,7 +424,7 @@ impl HookManager {
424424
pub fn ept_unhook_function(&mut self, vm: &mut Vm, guest_function_va: u64, _ept_hook_type: EptHookType) -> Result<(), HypervisorError> {
425425
debug!("Removing EPT hook for function at VA: {:#x}", guest_function_va);
426426

427-
let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va));
427+
let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va)?);
428428
debug!("Guest function PA: {:#x}", guest_function_pa.as_u64());
429429

430430
let guest_page_pa = guest_function_pa.align_down_to_base_page();

0 commit comments

Comments
 (0)