From f8e79f7bdd3b868479f740c83725a9e0ac85ce12 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Mon, 10 Oct 2022 18:00:46 +0900 Subject: [PATCH] arch/aarch64: Implement paging This commit adds initial paging support for aarch64. This implementation creates identity mapping translation tables for Cloud Hypervisor. The most of paging implementation is based on [1]. It also introduces use of `asm_const` [2] to parameterize FDT base address and stack base address. [1] https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials/tree/master/10_virtual_mem_part1_identity_mapping [2] https://github.com/rust-lang/rust/issues/93332 Signed-off-by: Akira Moroo --- Cargo.lock | 17 ++ Cargo.toml | 4 + src/arch/aarch64/asm.rs | 5 +- src/arch/aarch64/layout.rs | 81 ++++++++++ src/arch/aarch64/mod.rs | 3 + src/arch/aarch64/paging.rs | 276 ++++++++++++++++++++++++++++++++ src/arch/aarch64/ram64.s | 4 +- src/arch/aarch64/translation.rs | 265 ++++++++++++++++++++++++++++++ src/main.rs | 1 + 9 files changed, 653 insertions(+), 3 deletions(-) create mode 100644 src/arch/aarch64/layout.rs create mode 100644 src/arch/aarch64/paging.rs create mode 100644 src/arch/aarch64/translation.rs diff --git a/Cargo.lock b/Cargo.lock index 4606deee..2ab5a973 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cortex-a" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cd4524931a4e0ec50ae91f0d55f571f31ffe11dd9ce2f9905b8343c018c25bb" +dependencies = [ + "tock-registers", +] + [[package]] name = "dirs" version = "4.0.0" @@ -84,12 +93,14 @@ version = "0.4.1" dependencies = [ "atomic_refcell", "bitflags", + "cortex-a", "dirs", "linked_list_allocator", "r-efi", "rand", "ssh2", "tempfile", + "tock-registers", "uart_16550", "x86_64", ] @@ -320,6 +331,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" + [[package]] name = "uart_16550" version = "0.2.18" diff --git a/Cargo.toml b/Cargo.toml index 5cadf23b..e249a626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,10 @@ atomic_refcell = "0.1.8" r-efi = "4.1.0" linked_list_allocator = "0.10.4" +[target.'cfg(target_arch = "aarch64")'.dependencies] +tock-registers = "0.8.1" +cortex-a = "8.0.0" + [target.'cfg(target_arch = "x86_64")'.dependencies] uart_16550 = "0.2.18" x86_64 = "0.14.10" diff --git a/src/arch/aarch64/asm.rs b/src/arch/aarch64/asm.rs index 93af368f..751b81ab 100644 --- a/src/arch/aarch64/asm.rs +++ b/src/arch/aarch64/asm.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (C) 2022 Akira Moroo +use super::layout::map; use core::arch::global_asm; -global_asm!(include_str!("ram64.s")); +global_asm!(include_str!("ram64.s"), + FDT_START = const map::dram::FDT_START, + STACK_END = const map::dram::STACK_END); diff --git a/src/arch/aarch64/layout.rs b/src/arch/aarch64/layout.rs new file mode 100644 index 00000000..c327131f --- /dev/null +++ b/src/arch/aarch64/layout.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2022 Akira Moroo +// Copyright (c) 2021-2022 Andre Richter + +use core::ops::RangeInclusive; + +use super::paging::*; + +pub mod map { + pub const END: usize = 0x1_0000_0000; + + pub mod fw { + pub const START: usize = 0x0000_0000; + pub const END: usize = 0x0040_0000; + } + pub mod mmio { + pub const START: usize = super::fw::END; + pub const PL011_START: usize = 0x0900_0000; + pub const PL031_START: usize = 0x0901_0000; + pub const END: usize = 0x4000_0000; + } + + pub mod dram { + const FDT_SIZE: usize = 0x0020_0000; + const ACPI_SIZE: usize = 0x0020_0000; + pub const STACK_SIZE: usize = 0x0800_0000; + + pub const START: usize = super::mmio::END; + pub const FDT_START: usize = START; + pub const ACPI_START: usize = FDT_START + FDT_SIZE; + pub const KERNEL_START: usize = ACPI_START + ACPI_SIZE; + pub const STACK_START: usize = STACK_END - STACK_SIZE; + pub const STACK_END: usize = RESERVED_START; + pub const RESERVED_START: usize = 0xfc00_0000; + pub const END: usize = super::END; + } +} + +pub type KernelAddrSpace = AddressSpace<{ map::END }>; + +const NUM_MEM_RANGES: usize = 3; + +pub static LAYOUT: KernelVirtualLayout = KernelVirtualLayout::new( + map::END - 1, + [ + TranslationDescriptor { + name: "Firmware", + virtual_range: RangeInclusive::new(map::fw::START, map::fw::END - 1), + physical_range_translation: Translation::Identity, + attribute_fields: AttributeFields { + mem_attributes: MemAttributes::CacheableDRAM, + acc_perms: AccessPermissions::ReadWrite, + execute_never: false, + }, + }, + TranslationDescriptor { + name: "Device MMIO", + virtual_range: RangeInclusive::new(map::mmio::START, map::mmio::END - 1), + physical_range_translation: Translation::Identity, + attribute_fields: AttributeFields { + mem_attributes: MemAttributes::Device, + acc_perms: AccessPermissions::ReadWrite, + execute_never: true, + }, + }, + TranslationDescriptor { + name: "System Memory", + virtual_range: RangeInclusive::new(map::dram::START, map::dram::END - 1), + physical_range_translation: Translation::Identity, + attribute_fields: AttributeFields { + mem_attributes: MemAttributes::CacheableDRAM, + acc_perms: AccessPermissions::ReadWrite, // FIXME + execute_never: false, + }, + }, + ], +); + +pub fn virt_mem_layout() -> &'static KernelVirtualLayout { + &LAYOUT +} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index c4b7f2b1..1ad69e8b 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -3,3 +3,6 @@ #[cfg(not(test))] pub mod asm; +pub mod layout; +pub mod paging; +mod translation; diff --git a/src/arch/aarch64/paging.rs b/src/arch/aarch64/paging.rs new file mode 100644 index 00000000..25785e14 --- /dev/null +++ b/src/arch/aarch64/paging.rs @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2022 Akira Moroo +// Copyright (c) 2021-2022 Andre Richter + +use core::ops::RangeInclusive; + +use cortex_a::{asm::barrier, registers::*}; +use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; + +use super::{layout::KernelAddrSpace, translation::TranslationTable}; + +/// MMU enable errors variants. +#[derive(Debug)] +pub enum MmuEnableError { + AlreadyEnabled, + Other(&'static str), +} + +/// Memory Management interfaces. +pub mod interface { + use super::*; + + /// MMU functions. + pub trait Mmu { + unsafe fn enable_mmu_and_caching(&self) -> Result<(), MmuEnableError>; + + fn is_enabled(&self) -> bool; + } +} + +/// Describes the characteristics of a translation granule. +pub struct TranslationGranule; + +/// Describes properties of an address space. +pub struct AddressSpace; + +/// Architecture agnostic translation types. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum Translation { + Identity, + Offset(usize), +} + +/// Architecture agnostic memory attributes. +#[derive(Copy, Clone)] +pub enum MemAttributes { + CacheableDRAM, + Device, +} + +/// Architecture agnostic access permissions. +#[derive(Copy, Clone)] +pub enum AccessPermissions { + ReadOnly, + ReadWrite, +} + +/// Collection of memory attributes. +#[derive(Copy, Clone)] +pub struct AttributeFields { + pub mem_attributes: MemAttributes, + pub acc_perms: AccessPermissions, + pub execute_never: bool, +} + +impl Default for AttributeFields { + fn default() -> AttributeFields { + AttributeFields { + mem_attributes: MemAttributes::CacheableDRAM, + acc_perms: AccessPermissions::ReadWrite, + execute_never: true, + } + } +} + +/// Architecture agnostic descriptor for a memory range. +pub struct TranslationDescriptor { + pub name: &'static str, + pub virtual_range: RangeInclusive, + pub physical_range_translation: Translation, + pub attribute_fields: AttributeFields, +} + +/// Type for expressing the kernel's virtual memory layout. +pub struct KernelVirtualLayout { + /// The last (inclusive) address of the address space. + max_virt_addr_inclusive: usize, + + /// Array of descriptors for non-standard (normal cacheable DRAM) memory regions. + inner: [TranslationDescriptor; NUM_SPECIAL_RANGES], +} + +impl TranslationGranule { + /// The granule's size. + pub const SIZE: usize = Self::size_checked(); + + /// The granule's shift, aka log2(size). + pub const SHIFT: usize = Self::SIZE.trailing_zeros() as usize; + + const fn size_checked() -> usize { + assert!(GRANULE_SIZE.is_power_of_two()); + + GRANULE_SIZE + } +} + +impl AddressSpace { + /// The address space size. + pub const SIZE: usize = Self::size_checked(); + + /// The address space shift, aka log2(size). + pub const SIZE_SHIFT: usize = Self::SIZE.trailing_zeros() as usize; + + const fn size_checked() -> usize { + assert!(AS_SIZE.is_power_of_two()); + + // Check for architectural restrictions as well. + Self::arch_address_space_size_sanity_checks(); + + AS_SIZE + } +} + +impl KernelVirtualLayout<{ NUM_SPECIAL_RANGES }> { + /// Create a new instance. + pub const fn new(max: usize, layout: [TranslationDescriptor; NUM_SPECIAL_RANGES]) -> Self { + Self { + max_virt_addr_inclusive: max, + inner: layout, + } + } + + /// For a virtual address, find and return the physical output address and corresponding + /// attributes. + /// + /// If the address is not found in `inner`, return an identity mapped default with normal + /// cacheable DRAM attributes. + pub fn virt_addr_properties( + &self, + virt_addr: usize, + ) -> Result<(usize, AttributeFields), &'static str> { + if virt_addr > self.max_virt_addr_inclusive { + return Err("Address out of range"); + } + + for i in self.inner.iter() { + if i.virtual_range.contains(&virt_addr) { + let output_addr = match i.physical_range_translation { + Translation::Identity => virt_addr, + Translation::Offset(a) => a + (virt_addr - (i.virtual_range).start()), + }; + + return Ok((output_addr, i.attribute_fields)); + } + } + + Ok((virt_addr, AttributeFields::default())) + } +} + +/// Memory Management Unit type. +struct MemoryManagementUnit; + +pub type Granule512MiB = TranslationGranule<{ 512 * 1024 * 1024 }>; +pub type Granule64KiB = TranslationGranule<{ 64 * 1024 }>; + +/// Constants for indexing the MAIR_EL1. +pub mod mair { + pub const DEVICE: u64 = 0; + pub const NORMAL: u64 = 1; +} + +/// The kernel translation tables. +/// +/// # Safety +/// +/// - Supposed to land in `.bss`. Therefore, ensure that all initial member values boil down to "0". +static mut KERNEL_TABLES: TranslationTable = TranslationTable::new(); + +static MMU: MemoryManagementUnit = MemoryManagementUnit; + +impl AddressSpace { + /// Checks for architectural restrictions. + pub const fn arch_address_space_size_sanity_checks() { + // Size must be at least one full 512 MiB table. + assert!((AS_SIZE % Granule512MiB::SIZE) == 0); + + // Check for 48 bit virtual address size as maximum, which is supported by any ARMv8 + // version. + assert!(AS_SIZE <= (1 << 48)); + } +} + +impl MemoryManagementUnit { + /// Setup function for the MAIR_EL1 register. + fn setup_mair(&self) { + // Define the memory types being mapped. + MAIR_EL1.write( + // Attribute 1 - Cacheable normal DRAM. + MAIR_EL1::Attr1_Normal_Outer::WriteBack_NonTransient_ReadWriteAlloc + + MAIR_EL1::Attr1_Normal_Inner::WriteBack_NonTransient_ReadWriteAlloc + // Attribute 0 - Device. + + MAIR_EL1::Attr0_Device::nonGathering_nonReordering_EarlyWriteAck, + ); + } + + /// Configure various settings of stage 1 of the EL1 translation regime. + fn configure_translation_control(&self) { + let t0sz = (64 - KernelAddrSpace::SIZE_SHIFT) as u64; + + TCR_EL1.write( + TCR_EL1::TBI0::Used + + TCR_EL1::IPS::Bits_40 + + TCR_EL1::TG0::KiB_64 + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks + + TCR_EL1::A1::TTBR0 + + TCR_EL1::T0SZ.val(t0sz) + + TCR_EL1::EPD1::DisableTTBR1Walks, + ); + } +} + +/// Return a reference to the MMU instance. +pub fn mmu() -> &'static impl interface::Mmu { + &MMU +} + +impl interface::Mmu for MemoryManagementUnit { + unsafe fn enable_mmu_and_caching(&self) -> Result<(), MmuEnableError> { + if self.is_enabled() { + return Err(MmuEnableError::AlreadyEnabled); + } + + // Fail early if translation granule is not supported. + if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported) { + return Err(MmuEnableError::Other( + "Translation granule not supported in HW", + )); + } + + // Prepare the memory attribute indirection register. + self.setup_mair(); + + // Populate translation tables. + KERNEL_TABLES + .populate_tt_entries() + .map_err(MmuEnableError::Other)?; + + // Set the "Translation Table Base Register". + TTBR0_EL1.set_baddr(KERNEL_TABLES.phys_base_address()); + + self.configure_translation_control(); + + // Switch the MMU on. + // + // First, force all previous changes to be seen before the MMU is enabled. + barrier::isb(barrier::SY); + + // Enable the MMU and turn on data and instruction caching. + SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); + + // Force MMU init to complete before next instruction. + barrier::isb(barrier::SY); + + Ok(()) + } + + #[inline(always)] + fn is_enabled(&self) -> bool { + SCTLR_EL1.matches_all(SCTLR_EL1::M::Enable) + } +} diff --git a/src/arch/aarch64/ram64.s b/src/arch/aarch64/ram64.s index 612e09fb..73d255ca 100644 --- a/src/arch/aarch64/ram64.s +++ b/src/arch/aarch64/ram64.s @@ -22,10 +22,10 @@ ram64_start: jump_to_rust: /* x0 typically points to device tree at entry */ - ldr x0, =0x40000000 + ldr x0, ={FDT_START} /* setup stack */ - ldr x30, =0xfc000000 + ldr x30, ={STACK_END} mov sp, x30 /* x0: pointer to device tree */ diff --git a/src/arch/aarch64/translation.rs b/src/arch/aarch64/translation.rs new file mode 100644 index 00000000..903712e7 --- /dev/null +++ b/src/arch/aarch64/translation.rs @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2022 Akira Moroo +// Copyright (c) 2021-2022 Andre Richter + +//! Architectural translation table. +//! +//! Only 64 KiB granule is supported. + +use core::convert; + +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, + registers::InMemoryRegister, +}; + +use super::{layout, paging::*}; + +// A table descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-15. +register_bitfields! {u64, + STAGE1_TABLE_DESCRIPTOR [ + /// Physical address of the next descriptor. + NEXT_LEVEL_TABLE_ADDR_64KiB OFFSET(16) NUMBITS(32) [], // [47:16] + + TYPE OFFSET(1) NUMBITS(1) [ + Block = 0, + Table = 1 + ], + + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} + +// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. +register_bitfields! {u64, + STAGE1_PAGE_DESCRIPTOR [ + /// Unprivileged execute-never. + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Privileged execute-never. + PXN OFFSET(53) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Physical address of the next table descriptor (lvl2) or the page descriptor (lvl3). + OUTPUT_ADDR_64KiB OFFSET(16) NUMBITS(32) [], + + /// Access flag. + AF OFFSET(10) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Shareability field. + SH OFFSET(8) NUMBITS(2) [ + OuterShareable = 0b10, + InnerShareable = 0b11 + ], + + /// Access Permissions. + AP OFFSET(6) NUMBITS(2) [ + RW_EL1 = 0b00, + RW_EL1_EL0 = 0b01, + RO_EL1 = 0b10, + RO_EL1_EL0 = 0b11 + ], + + /// Memory attributes index into the MAIR_EL1 register. + AttrIndx OFFSET(2) NUMBITS(3) [], + + TYPE OFFSET(1) NUMBITS(1) [ + Reserved_Invalid = 0, + Page = 1 + ], + + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} + +/// A table descriptor for 64 KiB aperture. +/// +/// The output points to the next table. +#[derive(Copy, Clone)] +#[repr(C)] +struct TableDescriptor { + value: u64, +} + +/// A page descriptor with 64 KiB aperture. +/// +/// The output points to physical memory. +#[derive(Copy, Clone)] +#[repr(C)] +struct PageDescriptor { + value: u64, +} + +trait StartAddr { + fn phys_start_addr_u64(&self) -> u64; + fn phys_start_addr_usize(&self) -> usize; +} + +const NUM_LVL2_TABLES: usize = layout::KernelAddrSpace::SIZE >> Granule512MiB::SHIFT; + +/// Big monolithic struct for storing the translation tables. Individual levels must be 64 KiB +/// aligned, so the lvl3 is put first. +#[repr(C)] +#[repr(align(65536))] +pub struct FixedSizeTranslationTable { + /// Page descriptors, covering 64 KiB windows per entry. + lvl3: [[PageDescriptor; 8192]; NUM_TABLES], + + /// Table descriptors, covering 512 MiB windows. + lvl2: [TableDescriptor; NUM_TABLES], +} + +/// A translation table type for the kernel space. +pub type TranslationTable = FixedSizeTranslationTable; + +// The binary is still identity mapped, so we don't need to convert here. +impl StartAddr for [T; N] { + fn phys_start_addr_u64(&self) -> u64 { + self as *const T as u64 + } + + fn phys_start_addr_usize(&self) -> usize { + self as *const _ as usize + } +} + +impl TableDescriptor { + /// Create an instance. + /// + /// Descriptor is invalid by default. + pub const fn new_zeroed() -> Self { + Self { value: 0 } + } + + /// Create an instance pointing to the supplied address. + pub fn from_next_lvl_table_addr(phys_next_lvl_table_addr: usize) -> Self { + let val = InMemoryRegister::::new(0); + + let shifted = phys_next_lvl_table_addr >> Granule64KiB::SHIFT; + val.write( + STAGE1_TABLE_DESCRIPTOR::NEXT_LEVEL_TABLE_ADDR_64KiB.val(shifted as u64) + + STAGE1_TABLE_DESCRIPTOR::TYPE::Table + + STAGE1_TABLE_DESCRIPTOR::VALID::True, + ); + + Self { value: val.get() } + } +} + +/// Convert the kernel's generic memory attributes to HW-specific attributes of the MMU. +impl convert::From + for tock_registers::fields::FieldValue +{ + fn from(attribute_fields: AttributeFields) -> Self { + // Memory attributes. + let mut desc = match attribute_fields.mem_attributes { + MemAttributes::CacheableDRAM => { + STAGE1_PAGE_DESCRIPTOR::SH::InnerShareable + + STAGE1_PAGE_DESCRIPTOR::AttrIndx.val(mair::NORMAL) + } + MemAttributes::Device => { + STAGE1_PAGE_DESCRIPTOR::SH::OuterShareable + + STAGE1_PAGE_DESCRIPTOR::AttrIndx.val(mair::DEVICE) + } + }; + + // Access Permissions. + desc += match attribute_fields.acc_perms { + AccessPermissions::ReadOnly => STAGE1_PAGE_DESCRIPTOR::AP::RO_EL1, + AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, + }; + + // The execute-never attribute is mapped to PXN in AArch64. + desc += if attribute_fields.execute_never { + STAGE1_PAGE_DESCRIPTOR::PXN::True + } else { + STAGE1_PAGE_DESCRIPTOR::PXN::False + }; + + // Always set unprivileged exectue-never as long as userspace is not implemented yet. + desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; + + desc + } +} + +impl PageDescriptor { + /// Create an instance. + /// + /// Descriptor is invalid by default. + pub const fn new_zeroed() -> Self { + Self { value: 0 } + } + + /// Create an instance. + pub fn from_output_addr(phys_output_addr: usize, attribute_fields: &AttributeFields) -> Self { + let val = InMemoryRegister::::new(0); + + let shifted = phys_output_addr as u64 >> Granule64KiB::SHIFT; + val.write( + STAGE1_PAGE_DESCRIPTOR::OUTPUT_ADDR_64KiB.val(shifted) + + STAGE1_PAGE_DESCRIPTOR::AF::True + + STAGE1_PAGE_DESCRIPTOR::TYPE::Page + + STAGE1_PAGE_DESCRIPTOR::VALID::True + + (*attribute_fields).into(), + ); + + Self { value: val.get() } + } +} + +impl FixedSizeTranslationTable { + /// Create an instance. + pub const fn new() -> Self { + // Can't have a zero-sized address space. + assert!(NUM_TABLES > 0); + + Self { + lvl3: [[PageDescriptor::new_zeroed(); 8192]; NUM_TABLES], + lvl2: [TableDescriptor::new_zeroed(); NUM_TABLES], + } + } + + /// Iterates over all static translation table entries and fills them at once. + /// + /// # Safety + /// + /// - Modifies a `static mut`. Ensure it only happens from here. + pub unsafe fn populate_tt_entries(&mut self) -> Result<(), &'static str> { + for (l2_nr, l2_entry) in self.lvl2.iter_mut().enumerate() { + *l2_entry = + TableDescriptor::from_next_lvl_table_addr(self.lvl3[l2_nr].phys_start_addr_usize()); + + for (l3_nr, l3_entry) in self.lvl3[l2_nr].iter_mut().enumerate() { + let virt_addr = (l2_nr << Granule512MiB::SHIFT) + (l3_nr << Granule64KiB::SHIFT); + + let (phys_output_addr, attribute_fields) = + layout::virt_mem_layout().virt_addr_properties(virt_addr)?; + + *l3_entry = PageDescriptor::from_output_addr(phys_output_addr, &attribute_fields); + } + } + + Ok(()) + } + + /// The translation table's base address to be used for programming the MMU. + pub fn phys_base_address(&self) -> u64 { + self.lvl2.phys_start_addr_u64() + } +} diff --git a/src/main.rs b/src/main.rs index b1c474e5..9a6d7f0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![feature(asm_const)] #![feature(alloc_error_handler)] #![feature(stmt_expr_attributes)] #![feature(slice_take)]