Skip to content

Commit efecff4

Browse files
committed
[docs,host/mem] changed BASE_ADDRESS to 0x0
- changed our get_address macro to properly execute when in process, - fixed the PT setup for new base address, - removed superfluous test (BASE_ADDRESS - 1 causes an arithmetic overflow, which is caught at compile time), and - updated our paging documentation. Signed-off-by: danbugs <[email protected]>
1 parent d7e6521 commit efecff4

File tree

5 files changed

+54
-77
lines changed

5 files changed

+54
-77
lines changed

docs/paging-development-notes.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,35 @@ Hyperlight uses paging, which means the all addresses inside a Hyperlight VM are
99

1010
## Host-to-Guest memory mapping
1111

12-
Into each Hyperlight VM, memory from the host is mapped into the VM as physical memory. The physical memory inside the VM starts at address `0x200_000` and extends linearly to however much memory was mapped into the VM (depends on various parameters).
12+
Into each Hyperlight VM, memory from the host is mapped into the VM as physical memory. The physical memory inside the VM starts at address `0x0` and extends linearly to however much memory was mapped into the VM (depends on various parameters).
1313

1414
## Page table setup
1515

1616
The following page table structs are set up in memory before running a Hyperlight VM (See [Access Flags](#access-flags) for details on access flags that are also set on each entry)
1717

1818
### PML4 (Page Map Level 4) Table
1919

20-
The PML4 table is located at physical address specified in CR3. In Hyperlight we set `CR3=0x200_000`, which means the PML4 table is located at physical address `0x200_000`. The PML4 table comprises 512 64-bit entries.
20+
The PML4 table is located at physical address specified in CR3. In Hyperlight we set `CR3=0x0`, which means the PML4 table is located at physical address `0x0`. The PML4 table comprises 512 64-bit entries.
2121

22-
In Hyperlight, we only initialize the first entry (at address `0x200_000`), with value `0x201_000`, implying that we only have a single PDPT.
22+
In Hyperlight, we only initialize the first entry (at address `0x0`), with value `0x1_000`, implying that we only have a single PDPT.
2323

2424
### PDPT (Page-directory-pointer Table)
2525

26-
The first and only PDPT is located at physical address `0x201_000`. The PDPT comprises 512 64-bit entries. In Hyperlight, we only initialize the first entry of the PDPT (at address `0x201_000`), with the value `0x202_000`, implying that we only have a single PD.
26+
The first and only PDPT is located at physical address `0x1_000`. The PDPT comprises 512 64-bit entries. In Hyperlight, we only initialize the first entry of the PDPT (at address `0x1_000`), with the value `0x2_000`, implying that we only have a single PD.
2727

2828
### PD (Page Directory)
2929

30-
The first and only PD is located at physical address `0x202_000`. The PD comprises 512 64-bit entries, each entry `i` is set to the value `(i * 0x1000) + 0x203_000`. Thus, the first entry is `0x203_000`, the second entry is `0x204_000` and so on.
30+
The first and only PD is located at physical address `0x2_000`. The PD comprises 512 64-bit entries, each entry `i` is set to the value `(i * 0x1000) + 0x3_000`. Thus, the first entry is `0x3_000`, the second entry is `0x4_000` and so on.
3131

3232
### PT (Page Table)
3333

34-
The page tables start at physical address `0x203_000`. Each page table has 512 64-bit entries. Each entry is set to the value `p << 21|i << 12` where `p` is the page table number and `i` is the index of the entry in the page table. Thus, the first entry of the first page table is `0x000_000`, the second entry is `0x000_000 + 0x1000`, and so on. The first entry of the second page table is `0x200_000 + 0x1000`, the second entry is `0x200_000 + 0x2000`, and so on. Enough page tables are created to cover the size of memory mapped into the VM.
34+
The page tables start at physical address `0x3_000`. Each page table has 512 64-bit entries. Each entry is set to the value `p << 21|i << 12` where `p` is the page table number and `i` is the index of the entry in the page table. Thus, the first entry of the first page table is `0x000_000`, the second entry is `0x000_000 + 0x1000`, and so on. The first entry of the second page table is `0x200_000 + 0x1000`, the second entry is `0x200_000 + 0x2000`, and so on. Enough page tables are created to cover the size of memory mapped into the VM.
3535

3636
## Address Translation
3737

3838
Given a 64-bit virtual address X, the corresponding physical address is obtained as follows:
3939

40-
1. PML4 table's physical address is located using CR3 (CR3 is `0x200_000`).
40+
1. PML4 table's physical address is located using CR3 (CR3 is `0x0`).
4141
2. Bits 47:39 of X are used to index into PML4, giving us the address of the PDPT.
4242
3. Bits 38:30 of X are used to index into PDPT, giving us the address of the PD.
4343
4. Bits 29:21 of X are used to index into PD, giving us the address of the PT.
@@ -63,7 +63,7 @@ In addition to providing addresses, page table entries also contain access flags
6363

6464
PML4E, PDPTE, and PD Entries have the present flag set to 1, and the rest of the flags are not set.
6565

66-
PTE Entries all have the present flag set to 1, apart from those for the address range `0x000_000` to `0x1FF_000` which have the present flag set to 0 as we do not map memory below physical address `0x200_000`.
66+
PTE Entries all have the present flag set to 1.
6767

6868
In addition, the following flags are set according to the type of memory being mapped:
6969

src/hyperlight_host/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ pub mod hypervisor;
5757
///
5858
/// Virtual Address
5959
///
60-
/// 0x200000 PML4
61-
/// 0x201000 PDPT
62-
/// 0x202000 PD
63-
/// 0x203000 The guest PE code (When the code has been loaded using LoadLibrary to debug the guest this will not be
60+
/// 0x0000 PML4
61+
/// 0x1000 PDPT
62+
/// 0x2000 PD
63+
/// 0x3000 The guest PE code (When the code has been loaded using LoadLibrary to debug the guest this will not be
6464
/// present and code length will be zero;
6565
///
66-
/// The pointer passed to the Entrypoint in the Guest application is the 0x200000 + size of page table + size of code,
66+
/// The pointer passed to the Entrypoint in the Guest application is the ize of page table + size of code,
6767
/// at this address structs below are laid out in this order
6868
pub mod mem;
6969
/// Metric definitions and helpers

src/hyperlight_host/src/mem/layout.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ use crate::{log_then_return, new_error, Result};
6464
// | Guest Code |
6565
// +-------------------------------------------+
6666
// | PT |
67-
// +-------------------------------------------+ 0x203_000
67+
// +-------------------------------------------+ 0x3_000
6868
// | PD |
69-
// +-------------------------------------------+ 0x202_000
69+
// +-------------------------------------------+ 0x2_000
7070
// | PDPT |
71-
// +-------------------------------------------+ 0x201_000
71+
// +-------------------------------------------+ 0x1_000
7272
// | PML4 |
73-
// +-------------------------------------------+ 0x200_000
73+
// +-------------------------------------------+ 0x0_000
7474
// | ⋮ |
7575
// | Unmapped |
7676
// | ⋮ |
@@ -308,7 +308,7 @@ impl SandboxMemoryLayout {
308308
const MAX_MEMORY_SIZE: usize = 0x40000000 - Self::BASE_ADDRESS;
309309

310310
/// The base address of the sandbox's memory.
311-
pub(crate) const BASE_ADDRESS: usize = 0x0200000;
311+
pub(crate) const BASE_ADDRESS: usize = 0x0;
312312

313313
// the offset into a sandbox's input/output buffer where the stack starts
314314
const STACK_POINTER_SIZE_BYTES: u64 = 8;
@@ -699,12 +699,10 @@ impl SandboxMemoryLayout {
699699
// The size of a single table is 4K, we can map up to 1GB total memory which requires 1 PML4, 1 PDPT, 1 PD and 512 PTs
700700
// but we only need enough PTs to map the memory we are using. (In other words we only need 512 PTs to map the memory if the memory size is 1GB)
701701
//
702-
// Because we always start the physical address space at 0x200_000
703-
// we can calculate the amount of memory needed for the PTs by calculating how much memory is needed for the sandbox configuration in total,
704-
// then add 0x200_000 to that (as we start at 0x200_000),
702+
// We can calculate the amount of memory needed for the PTs by calculating how much memory is needed for the sandbox configuration in total,
705703
// and then add 3 * 4K (for the PML4, PDPT and PD) to that,
706704
// then add 2MB to that (the maximum size of memory required for the PTs themselves is 2MB when we map 1GB of memory in 4K pages),
707-
// then divide that by 0x200_000 (as we can map 2MB in each PT) and then round the result up by 1 .
705+
// then divide that by 0x200_000 (as we can map 2MB in each PT).
708706
// This will give us the total size of the PTs required for the sandbox to which we can add the size of the PML4, PDPT and PD.
709707
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
710708
fn get_total_page_table_size(
@@ -741,7 +739,6 @@ impl SandboxMemoryLayout {
741739

742740
let num_pages: usize = ((total_mapped_memory_size + AMOUNT_OF_MEMORY_PER_PT - 1)
743741
/ AMOUNT_OF_MEMORY_PER_PT)
744-
+ 1 // Round up
745742
+ 3; // PML4, PDPT, PD
746743

747744
num_pages * PAGE_SIZE_USIZE
@@ -1077,7 +1074,7 @@ impl SandboxMemoryLayout {
10771074
macro_rules! get_address {
10781075
($something:ident) => {
10791076
paste! {
1080-
if guest_offset == 0 {
1077+
if run_inprocess {
10811078
let offset = self.[<$something _offset>];
10821079
let calculated_addr = shared_mem.calculate_address(offset)?;
10831080
u64::try_from(calculated_addr)?

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ where
134134
mem_size: u64,
135135
regions: &mut [MemoryRegion],
136136
) -> Result<u64> {
137-
// Add 0x200000 because that's the start of mapped memory
138137
// For MSVC, move rsp down by 0x28. This gives the called 'main'
139138
// function the appearance that rsp was 16 byte aligned before
140139
// the 'call' that calls main (note we don't really have a return value
@@ -177,60 +176,50 @@ where
177176
// We only need to create enough PTEs to map the amount of memory we have
178177
// We need one PT for every 2MB of memory that is mapped
179178
// We can use the memory size to calculate the number of PTs we need
180-
// We round up mem_size/2MB and then we need to add 1 as we start our memory mapping at 0x200000
179+
// We round up mem_size/2MB
181180

182181
let mem_size = usize::try_from(mem_size)?;
183182

184183
let num_pages: usize =
185-
((mem_size + AMOUNT_OF_MEMORY_PER_PT - 1) / AMOUNT_OF_MEMORY_PER_PT) + 1;
184+
(mem_size + AMOUNT_OF_MEMORY_PER_PT - 1) / AMOUNT_OF_MEMORY_PER_PT;
186185

187186
// Create num_pages PT with 512 PTEs
188187
for p in 0..num_pages {
189188
for i in 0..512 {
190189
let offset = SandboxMemoryLayout::PT_OFFSET + (p * 4096) + (i * 8);
191190
// Each PTE maps a 4KB page
192-
let val_to_write = if p == 0 {
193-
(p << 21) as u64 | (i << 12) as u64
194-
} else {
195-
let flags = match Self::get_page_flags(p, i, regions) {
196-
Ok(region_type) => match region_type {
197-
// TODO: We parse and load the exe according to its sections and then
198-
// have the correct flags set rather than just marking the entire binary as executable
199-
MemoryRegionType::Code => PAGE_PRESENT | PAGE_RW | PAGE_USER,
200-
MemoryRegionType::Stack => {
201-
PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX
202-
}
203-
#[cfg(feature = "executable_heap")]
204-
MemoryRegionType::Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER,
205-
#[cfg(not(feature = "executable_heap"))]
206-
MemoryRegionType::Heap => {
207-
PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX
208-
}
209-
// The guard page is marked RW and User so that if it gets written to we can detect it in the host
210-
// If/When we implement an interrupt handler for page faults in the guest then we can remove this access and handle things properly there
211-
MemoryRegionType::GuardPage => {
212-
PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX
213-
}
214-
MemoryRegionType::InputData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
215-
MemoryRegionType::OutputData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
216-
MemoryRegionType::Peb => PAGE_PRESENT | PAGE_RW | PAGE_NX,
217-
// Host Function Definitions are readonly in the guest
218-
MemoryRegionType::HostFunctionDefinitions => PAGE_PRESENT | PAGE_NX,
219-
MemoryRegionType::PanicContext => PAGE_PRESENT | PAGE_RW | PAGE_NX,
220-
MemoryRegionType::GuestErrorData => {
221-
PAGE_PRESENT | PAGE_RW | PAGE_NX
222-
}
223-
// Host Exception Data are readonly in the guest
224-
MemoryRegionType::HostExceptionData => PAGE_PRESENT | PAGE_NX,
225-
MemoryRegionType::PageTables => PAGE_PRESENT | PAGE_RW | PAGE_NX,
226-
MemoryRegionType::KernelStack => PAGE_PRESENT | PAGE_RW | PAGE_NX,
227-
MemoryRegionType::BootStack => PAGE_PRESENT | PAGE_RW | PAGE_NX,
228-
},
229-
// If there is an error then the address isn't mapped so mark it as not present
230-
Err(_) => 0,
231-
};
232-
((p << 21) as u64 | (i << 12) as u64) | flags
191+
let flags = match Self::get_page_flags(p, i, regions) {
192+
Ok(region_type) => match region_type {
193+
// TODO: We parse and load the exe according to its sections and then
194+
// have the correct flags set rather than just marking the entire binary as executable
195+
MemoryRegionType::Code => PAGE_PRESENT | PAGE_RW | PAGE_USER,
196+
MemoryRegionType::Stack => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX,
197+
#[cfg(feature = "executable_heap")]
198+
MemoryRegionType::Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER,
199+
#[cfg(not(feature = "executable_heap"))]
200+
MemoryRegionType::Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX,
201+
// The guard page is marked RW and User so that if it gets written to we can detect it in the host
202+
// If/When we implement an interrupt handler for page faults in the guest then we can remove this access and handle things properly there
203+
MemoryRegionType::GuardPage => {
204+
PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX
205+
}
206+
MemoryRegionType::InputData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
207+
MemoryRegionType::OutputData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
208+
MemoryRegionType::Peb => PAGE_PRESENT | PAGE_RW | PAGE_NX,
209+
// Host Function Definitions are readonly in the guest
210+
MemoryRegionType::HostFunctionDefinitions => PAGE_PRESENT | PAGE_NX,
211+
MemoryRegionType::PanicContext => PAGE_PRESENT | PAGE_RW | PAGE_NX,
212+
MemoryRegionType::GuestErrorData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
213+
// Host Exception Data are readonly in the guest
214+
MemoryRegionType::HostExceptionData => PAGE_PRESENT | PAGE_NX,
215+
MemoryRegionType::PageTables => PAGE_PRESENT | PAGE_RW | PAGE_NX,
216+
MemoryRegionType::KernelStack => PAGE_PRESENT | PAGE_RW | PAGE_NX,
217+
MemoryRegionType::BootStack => PAGE_PRESENT | PAGE_RW | PAGE_NX,
218+
},
219+
// If there is an error then the address isn't mapped so mark it as not present
220+
Err(_) => 0,
233221
};
222+
let val_to_write = ((p << 21) as u64 | (i << 12) as u64) | flags;
234223
shared_mem.write_u64(offset, val_to_write)?;
235224
}
236225
}

src/hyperlight_host/src/mem/ptr.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,4 @@ mod tests {
241241
);
242242
}
243243
}
244-
245-
#[test]
246-
fn ptr_fail() {
247-
{
248-
let raw_guest_ptr = RawPtr(SandboxMemoryLayout::BASE_ADDRESS as u64 - 1);
249-
let guest_ptr = GuestPtr::try_from(raw_guest_ptr);
250-
assert!(guest_ptr.is_err());
251-
}
252-
}
253244
}

0 commit comments

Comments
 (0)