Skip to content

Commit

Permalink
aarch64: subtract TTn base address from virtual address during map op…
Browse files Browse the repository at this point in the history
…erations
  • Loading branch information
Qix- committed Jul 27, 2024
1 parent 90b3298 commit ea21296
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 14 deletions.
79 changes: 69 additions & 10 deletions oro-arch-aarch64/src/mem/address_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ use crate::{
},
segment::Segment,
},
reg::tcr_el1::TcrEl1,
};
use oro_common::mem::{AddressSpace, PageFrameAllocate, PhysicalAddressTranslator};

/// A lightweight handle to an address space.
pub struct AddressSpaceHandle {
/// The base physical address of the root of the page tables
/// associated with an address space.
pub base_phys: u64,
pub base_phys: u64,
/// Lower bound of the address range covered by this address space.
///
/// All virtual addresses mapped/unmapped have this value subtracted from
/// them before being passed to the page table walker.
pub virt_start: usize,
}

/// The Oro-specific address space layout implementation for the Aarch64 architecture.
Expand Down Expand Up @@ -103,6 +109,49 @@ impl AddressSpaceLayout {

&DESCRIPTOR
}

/// Returns a new supervisor address space handle with the given virtual start address.
///
/// Returns `None` if any allocation(s) fail.
///
/// # Safety
/// The caller must ensure that the given virtual start address is valid.
unsafe fn new_supervisor_space_with_start<A, P>(
alloc: &mut A,
translator: &P,
virt_start: usize,
) -> Option<AddressSpaceHandle>
where
A: PageFrameAllocate,
P: PhysicalAddressTranslator,
{
let base_phys = alloc.allocate()?;

unsafe {
(*(translator.to_virtual_addr(base_phys) as *mut PageTable)).reset();
}

Some(AddressSpaceHandle {
base_phys,
virt_start,
})
}

/// Creates a new supervisor (EL1) address space that addresses
/// the TT0 address range (i.e. for use with `TTBR0_EL1`).
///
/// This probably isn't used by the kernel, but instead by the
/// preboot environment to map stubs.
pub(crate) fn new_supervisor_space_tt0<A, P>(
alloc: &mut A,
translator: &P,
) -> Option<<Self as AddressSpace>::SupervisorHandle>
where
A: PageFrameAllocate,
P: PhysicalAddressTranslator,
{
unsafe { Self::new_supervisor_space_with_start(alloc, translator, 0) }
}
}

unsafe impl AddressSpace for AddressSpaceLayout {
Expand All @@ -113,22 +162,29 @@ unsafe impl AddressSpace for AddressSpaceLayout {
where
P: PhysicalAddressTranslator,
{
// NOTE(qix-): Technically this isn't required since the kernel currently
// NOTE(qix-): requires `TCR_EL1.TnSZ=16`, but it's cheap and not often
// NOTE(qix-): called, so we'll just do it anyway.
#[allow(clippy::cast_possible_truncation)]
let (tt1_start, _) = TcrEl1::load().tt1_range();

let base_phys = crate::asm::load_ttbr1();
Self::SupervisorHandle { base_phys }
Self::SupervisorHandle {
base_phys,
virt_start: tt1_start,
}
}

fn new_supervisor_space<A, P>(alloc: &mut A, translator: &P) -> Option<Self::SupervisorHandle>
where
A: PageFrameAllocate,
P: PhysicalAddressTranslator,
{
let base_phys = alloc.allocate()?;

unsafe {
(*(translator.to_virtual_addr(base_phys) as *mut PageTable)).reset();
}

Some(Self::SupervisorHandle { base_phys })
// NOTE(qix-): We currently specify that the kernel uses `TCR_EL1.TnSZ=16`,
// NOTE(qix-): so we hard-code this value here (as opposed to `current_supervisor_space`).
// NOTE(qix-): Unlike `current_supervisor_space`, this function will probably have to be
// NOTE(qix-): updated in the future if other `TnSZ` values are supported or used.
unsafe { Self::new_supervisor_space_with_start(alloc, translator, 0xFFFF_0000_0000_0000) }
}

fn duplicate_supervisor_space_shallow<A, P>(
Expand All @@ -150,7 +206,10 @@ unsafe impl AddressSpace for AddressSpaceLayout {
);
}

Some(Self::SupervisorHandle { base_phys })
Some(Self::SupervisorHandle {
base_phys,
virt_start: space.virt_start,
})
}

fn kernel_code() -> Self::SupervisorSegment {
Expand Down
8 changes: 8 additions & 0 deletions oro-arch-aarch64/src/mem/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ impl Segment {
A: PageFrameAllocate + PageFrameFree,
P: PhysicalAddressTranslator,
{
let virt = virt
.checked_sub(space.virt_start)
.ok_or(MapError::VirtOutOfAddressSpaceRange)?;

let l0_idx = (virt >> 39) & 0x1FF;

if l0_idx < self.valid_range.0 || l0_idx > self.valid_range.1 {
Expand Down Expand Up @@ -201,6 +205,10 @@ impl Segment {
A: PageFrameAllocate + PageFrameFree,
P: PhysicalAddressTranslator,
{
let virt = virt
.checked_sub(space.virt_start)
.ok_or(UnmapError::VirtOutOfAddressSpaceRange)?;

if unlikely!(virt & 0xFFF != 0) {
return Err(UnmapError::VirtNotAligned);
}
Expand Down
17 changes: 17 additions & 0 deletions oro-arch-aarch64/src/reg/tcr_el1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,23 @@ impl TcrEl1 {
pub const fn new() -> Self {
Self(0)
}

/// Returns the range of the TT0 address range based on `T0SZ`.
///
/// The upper bound is inclusive.
pub fn tt0_range(self) -> (usize, usize) {
(0, (1 << (64 - self.t0sz())) - 1)
}

/// Returns the range of the TT1 address range based on `T1SZ`.
///
/// The upper bound is inclusive.
pub fn tt1_range(self) -> (usize, usize) {
(
0xFFFF_FFFF_FFFF_FFFF - (1 << (64 - self.t1sz())) + 1,
0xFFFF_FFFF_FFFF_FFFF,
)
}
}

impl From<u64> for TcrEl1 {
Expand Down
7 changes: 3 additions & 4 deletions oro-arch-aarch64/src/xfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ use crate::{
};
use core::arch::asm;
use oro_common::mem::{
AddressSegment, AddressSpace, MapError, PageFrameAllocate, PageFrameFree,
PhysicalAddressTranslator,
AddressSegment, MapError, PageFrameAllocate, PageFrameFree, PhysicalAddressTranslator,
};

extern "C" {
Expand Down Expand Up @@ -68,8 +67,8 @@ where
// Map the stubs into the new page table using an identity mapping.
// SAFETY(qix-): We specify that TTBR0 must be 4KiB upon transferring to the kernel,
// SAFETY(qix-): and that TTBR0_EL1 is left undefined (for our usage).
let page_table =
AddressSpaceLayout::new_supervisor_space(alloc, translator).ok_or(MapError::OutOfMemory)?;
let page_table = AddressSpaceLayout::new_supervisor_space_tt0(alloc, translator)
.ok_or(MapError::OutOfMemory)?;

// Identity map it.
#[allow(clippy::cast_possible_truncation)]
Expand Down
14 changes: 14 additions & 0 deletions oro-common/src/mem/mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ pub enum MapError {
/// The virtual address passed to the map function
/// is out of range for the given mapper.
VirtOutOfRange,
/// On some architectures, the virtual address must be within
/// a certain range that is larger than the logical Oro segment
/// range (e.g. TTBR0/TTBR1 on AArch64). This error indicates that
/// the virtual address is out of the range of the overall address
/// space within which the caller is attempting to perform a mapping
/// operation.
VirtOutOfAddressSpaceRange,
/// The virtual address passed to the map function
/// is not page-aligned.
VirtNotAligned,
Expand All @@ -168,6 +175,13 @@ pub enum UnmapError {
/// The virtual address passed to the map function
/// is out of range for the given mapper.
VirtOutOfRange,
/// On some architectures, the virtual address must be within
/// a certain range that is larger than the logical Oro segment
/// range (e.g. TTBR0/TTBR1 on AArch64). This error indicates that
/// the virtual address is out of the range of the overall address
/// space within which the caller is attempting to perform a mapping
/// operation.
VirtOutOfAddressSpaceRange,
/// The virtual address passed to the map function
/// is not page-aligned.
VirtNotAligned,
Expand Down

0 comments on commit ea21296

Please sign in to comment.