Skip to content

Commit

Permalink
Merge pull request #27 from jakobvukalovic/map-invalid-blocks
Browse files Browse the repository at this point in the history
Allow mapping invalid leaf entries
  • Loading branch information
qwandor authored Apr 21, 2023
2 parents 5c222ab + c77891b commit bb05a6b
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 59 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Breaking changes

- Updated `bitflags` to 2.0.2, which changes the API of `Attributes` a bit.
- Updated `map_range` method to support mapping leaf page table entries without the `VALID` flag.
`Attributes::VALID` is no longer implicitly set when mapping leaf page table entries.

### New features

Expand Down
63 changes: 52 additions & 11 deletions src/idmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Translation for IdTranslation {
/// // Map a 2 MiB region of memory as read-write.
/// idmap.map_range(
/// &MemoryRegion::new(0x80200000, 0x80400000),
/// Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::EXECUTE_NEVER,
/// Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::EXECUTE_NEVER | Attributes::VALID,
/// ).unwrap();
/// // Set `TTBR0_EL1` to activate the page table.
/// # #[cfg(target_arch = "aarch64")]
Expand All @@ -85,7 +85,7 @@ impl Translation for IdTranslation {
/// // Now change the mapping to read-only and executable.
/// idmap.map_range(
/// &MemoryRegion::new(0x80200000, 0x80400000),
/// Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::READ_ONLY,
/// Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::READ_ONLY | Attributes::VALID,
/// ).unwrap();
/// # #[cfg(target_arch = "aarch64")]
/// idmap.activate();
Expand Down Expand Up @@ -130,7 +130,8 @@ impl IdMap {
/// This should generally only be called while the page table is not active. In particular, any
/// change that may require break-before-make per the architecture must be made while the page
/// table is inactive. Mapping a previously unmapped memory range may be done while the page
/// table is active.
/// table is active. This function writes block and page entries, but only maps them if `flags`
/// contains `Attributes::VALID`, otherwise the entries remain invalid.
///
/// # Errors
///
Expand Down Expand Up @@ -181,14 +182,20 @@ mod tests {
// A single byte at the start of the address space.
let mut idmap = IdMap::new(1, 1);
assert_eq!(
idmap.map_range(&MemoryRegion::new(0, 1), Attributes::NORMAL),
idmap.map_range(
&MemoryRegion::new(0, 1),
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);

// Two pages at the start of the address space.
let mut idmap = IdMap::new(1, 1);
assert_eq!(
idmap.map_range(&MemoryRegion::new(0, PAGE_SIZE * 2), Attributes::NORMAL),
idmap.map_range(
&MemoryRegion::new(0, PAGE_SIZE * 2),
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);

Expand All @@ -200,7 +207,7 @@ mod tests {
MAX_ADDRESS_FOR_ROOT_LEVEL_1 - 1,
MAX_ADDRESS_FOR_ROOT_LEVEL_1
),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -210,7 +217,7 @@ mod tests {
assert_eq!(
idmap.map_range(
&MemoryRegion::new(PAGE_SIZE * 1023, PAGE_SIZE * 1025),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -220,7 +227,7 @@ mod tests {
assert_eq!(
idmap.map_range(
&MemoryRegion::new(0, MAX_ADDRESS_FOR_ROOT_LEVEL_1),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -237,7 +244,7 @@ mod tests {
MAX_ADDRESS_FOR_ROOT_LEVEL_1,
MAX_ADDRESS_FOR_ROOT_LEVEL_1 + 1,
),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Err(MapError::AddressRange(VirtualAddress(
MAX_ADDRESS_FOR_ROOT_LEVEL_1 + PAGE_SIZE
Expand All @@ -248,7 +255,7 @@ mod tests {
assert_eq!(
idmap.map_range(
&MemoryRegion::new(0, MAX_ADDRESS_FOR_ROOT_LEVEL_1 + 1,),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Err(MapError::AddressRange(VirtualAddress(
MAX_ADDRESS_FOR_ROOT_LEVEL_1 + PAGE_SIZE
Expand All @@ -261,7 +268,10 @@ mod tests {
idmap
.map_range(
&MemoryRegion::new(0, PAGE_SIZE * 2),
Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::READ_ONLY,
Attributes::NORMAL
| Attributes::NON_GLOBAL
| Attributes::READ_ONLY
| Attributes::VALID,
)
.unwrap();
idmap
Expand Down Expand Up @@ -304,4 +314,35 @@ mod tests {
})
.unwrap();
}

#[test]
fn breakup_invalid_block() {
const BLOCK_RANGE: usize = 0x200000;
let mut idmap = IdMap::new(1, 1);
idmap
.map_range(
&MemoryRegion::new(0, BLOCK_RANGE),
Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::SWFLAG_0,
)
.unwrap();
idmap
.map_range(
&MemoryRegion::new(0, PAGE_SIZE),
Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::VALID,
)
.unwrap();
idmap
.modify_range(
&MemoryRegion::new(0, BLOCK_RANGE),
&|range, entry, level| {
if level == 3 {
let has_swflag = entry.flags().unwrap().contains(Attributes::SWFLAG_0);
let is_first_page = range.start().0 == 0usize;
assert!(has_swflag != is_first_page);
}
Ok(())
},
)
.unwrap();
}
}
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
//! // Map a 2 MiB region of memory as read-only.
//! idmap.map_range(
//! &MemoryRegion::new(0x80200000, 0x80400000),
//! Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::READ_ONLY,
//! Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::READ_ONLY | Attributes::VALID,
//! ).unwrap();
//! // Set `TTBR0_EL1` to activate the page table.
//! # #[cfg(target_arch = "aarch64")]
Expand Down Expand Up @@ -194,7 +194,8 @@ impl<T: Translation + Clone> Mapping<T> {
/// This should generally only be called while the page table is not active. In particular, any
/// change that may require break-before-make per the architecture must be made while the page
/// table is inactive. Mapping a previously unmapped memory range may be done while the page
/// table is active.
/// table is active. This function writes block and page entries, but only maps them if `flags`
/// contains `Attributes::VALID`, otherwise the entries remain invalid.
///
/// # Errors
///
Expand Down
68 changes: 55 additions & 13 deletions src/linearmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ impl LinearMap {
/// This should generally only be called while the page table is not active. In particular, any
/// change that may require break-before-make per the architecture must be made while the page
/// table is inactive. Mapping a previously unmapped memory range may be done while the page
/// table is active.
/// table is active. This function writes block and page entries, but only maps them if `flags`
/// contains `Attributes::VALID`, otherwise the entries remain invalid.
///
/// # Errors
///
Expand Down Expand Up @@ -199,14 +200,20 @@ mod tests {
// A single byte at the start of the address space.
let mut pagetable = LinearMap::new(1, 1, 4096, VaRange::Lower);
assert_eq!(
pagetable.map_range(&MemoryRegion::new(0, 1), Attributes::NORMAL),
pagetable.map_range(
&MemoryRegion::new(0, 1),
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);

// Two pages at the start of the address space.
let mut pagetable = LinearMap::new(1, 1, 4096, VaRange::Lower);
assert_eq!(
pagetable.map_range(&MemoryRegion::new(0, PAGE_SIZE * 2), Attributes::NORMAL),
pagetable.map_range(
&MemoryRegion::new(0, PAGE_SIZE * 2),
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);

Expand All @@ -218,7 +225,7 @@ mod tests {
MAX_ADDRESS_FOR_ROOT_LEVEL_1 - 1,
MAX_ADDRESS_FOR_ROOT_LEVEL_1
),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -230,7 +237,7 @@ mod tests {
assert_eq!(
pagetable.map_range(
&MemoryRegion::new(0, MAX_ADDRESS_FOR_ROOT_LEVEL_1),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -243,7 +250,7 @@ mod tests {
assert_eq!(
pagetable.map_range(
&MemoryRegion::new(PAGE_SIZE, PAGE_SIZE + 1),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -253,7 +260,7 @@ mod tests {
assert_eq!(
pagetable.map_range(
&MemoryRegion::new(PAGE_SIZE, PAGE_SIZE * 3),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -266,7 +273,7 @@ mod tests {
MAX_ADDRESS_FOR_ROOT_LEVEL_1 - 1,
MAX_ADDRESS_FOR_ROOT_LEVEL_1
),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -278,7 +285,7 @@ mod tests {
assert_eq!(
pagetable.map_range(
&MemoryRegion::new(LEVEL_2_BLOCK_SIZE, MAX_ADDRESS_FOR_ROOT_LEVEL_1),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Ok(())
);
Expand All @@ -295,7 +302,7 @@ mod tests {
MAX_ADDRESS_FOR_ROOT_LEVEL_1,
MAX_ADDRESS_FOR_ROOT_LEVEL_1 + 1,
),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Err(MapError::AddressRange(VirtualAddress(
MAX_ADDRESS_FOR_ROOT_LEVEL_1 + PAGE_SIZE
Expand All @@ -306,7 +313,7 @@ mod tests {
assert_eq!(
pagetable.map_range(
&MemoryRegion::new(0, MAX_ADDRESS_FOR_ROOT_LEVEL_1 + 1),
Attributes::NORMAL
Attributes::NORMAL | Attributes::VALID
),
Err(MapError::AddressRange(VirtualAddress(
MAX_ADDRESS_FOR_ROOT_LEVEL_1 + PAGE_SIZE
Expand Down Expand Up @@ -418,7 +425,10 @@ mod tests {
// Test that block mapping is used when the PA is appropriately aligned...
let mut pagetable = LinearMap::new(1, 1, 1 << 30, VaRange::Lower);
pagetable
.map_range(&MemoryRegion::new(0, 1 << 30), Attributes::NORMAL)
.map_range(
&MemoryRegion::new(0, 1 << 30),
Attributes::NORMAL | Attributes::VALID,
)
.unwrap();
assert_eq!(
pagetable.mapping.root.mapping_level(VirtualAddress(0)),
Expand All @@ -428,7 +438,10 @@ mod tests {
// ...but not when it is not.
let mut pagetable = LinearMap::new(1, 1, 1 << 29, VaRange::Lower);
pagetable
.map_range(&MemoryRegion::new(0, 1 << 30), Attributes::NORMAL)
.map_range(
&MemoryRegion::new(0, 1 << 30),
Attributes::NORMAL | Attributes::VALID,
)
.unwrap();
assert_eq!(
pagetable.mapping.root.mapping_level(VirtualAddress(0)),
Expand Down Expand Up @@ -478,4 +491,33 @@ mod tests {
})
.unwrap();
}

#[test]
fn breakup_invalid_block() {
const BLOCK_RANGE: usize = 0x200000;

let mut lmap = LinearMap::new(1, 1, 0x1000, VaRange::Lower);
lmap.map_range(
&MemoryRegion::new(0, BLOCK_RANGE),
Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::SWFLAG_0,
)
.unwrap();
lmap.map_range(
&MemoryRegion::new(0, PAGE_SIZE),
Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::VALID,
)
.unwrap();
lmap.modify_range(
&MemoryRegion::new(0, BLOCK_RANGE),
&|range, entry, level| {
if level == 3 {
let has_swflag = entry.flags().unwrap().contains(Attributes::SWFLAG_0);
let is_first_page = range.start().0 == 0usize;
assert!(has_swflag != is_first_page);
}
Ok(())
},
)
.unwrap();
}
}
Loading

0 comments on commit bb05a6b

Please sign in to comment.