Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bddef48

Browse files
committedOct 23, 2022
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] rust-lang/rust#93332 Signed-off-by: Akira Moroo <retrage01@gmail.com>
1 parent 9a9aa1e commit bddef48

File tree

9 files changed

+663
-3
lines changed

9 files changed

+663
-3
lines changed
 

‎Cargo.lock

+17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ atomic_refcell = "0.1.8"
3131
r-efi = "4.1.0"
3232
linked_list_allocator = "0.10.4"
3333

34+
[target.'cfg(target_arch = "aarch64")'.dependencies]
35+
tock-registers = "0.8.1"
36+
cortex-a = "8.0.0"
37+
3438
[target.'cfg(target_arch = "x86_64")'.dependencies]
3539
uart_16550 = "0.2.18"
3640
x86_64 = "0.14.10"

‎src/arch/aarch64/asm.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// Copyright (C) 2022 Akira Moroo
33

4+
use super::layout::map;
45
use core::arch::global_asm;
56

6-
global_asm!(include_str!("ram64.s"));
7+
global_asm!(include_str!("ram64.s"),
8+
FDT_START = const map::dram::FDT_START,
9+
STACK_END = const map::dram::STACK_END);

‎src/arch/aarch64/layout.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright (C) 2022 Akira Moroo
3+
// Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
4+
5+
use core::ops::RangeInclusive;
6+
7+
use super::paging::*;
8+
9+
pub mod map {
10+
pub const END: usize = 0x1_0000_0000;
11+
12+
pub mod fw {
13+
pub const START: usize = 0x0000_0000;
14+
pub const END: usize = 0x0040_0000;
15+
}
16+
pub mod mmio {
17+
pub const START: usize = super::fw::END;
18+
pub const PL011_START: usize = 0x0900_0000;
19+
pub const PL031_START: usize = 0x0901_0000;
20+
pub const END: usize = 0x4000_0000;
21+
}
22+
23+
pub mod dram {
24+
const FDT_SIZE: usize = 0x0020_0000;
25+
const ACPI_SIZE: usize = 0x0020_0000;
26+
pub const STACK_SIZE: usize = 0x0800_0000;
27+
28+
pub const START: usize = super::mmio::END;
29+
pub const FDT_START: usize = START;
30+
pub const ACPI_START: usize = FDT_START + FDT_SIZE;
31+
pub const KERNEL_START: usize = ACPI_START + ACPI_SIZE;
32+
pub const STACK_START: usize = STACK_END - STACK_SIZE;
33+
pub const STACK_END: usize = RESERVED_START;
34+
pub const RESERVED_START: usize = 0xfc00_0000;
35+
pub const END: usize = super::END;
36+
}
37+
}
38+
39+
pub type KernelAddrSpace = AddressSpace<{ map::END }>;
40+
41+
const NUM_MEM_RANGES: usize = 3;
42+
43+
pub static LAYOUT: KernelVirtualLayout<NUM_MEM_RANGES> = KernelVirtualLayout::new(
44+
map::END - 1,
45+
[
46+
TranslationDescriptor {
47+
name: "Firmware",
48+
virtual_range: RangeInclusive::new(map::fw::START, map::fw::END - 1),
49+
physical_range_translation: Translation::Identity,
50+
attribute_fields: AttributeFields {
51+
mem_attributes: MemAttributes::CacheableDRAM,
52+
acc_perms: AccessPermissions::ReadWrite,
53+
execute_never: false,
54+
},
55+
},
56+
TranslationDescriptor {
57+
name: "Device MMIO",
58+
virtual_range: RangeInclusive::new(map::mmio::START, map::mmio::END - 1),
59+
physical_range_translation: Translation::Identity,
60+
attribute_fields: AttributeFields {
61+
mem_attributes: MemAttributes::Device,
62+
acc_perms: AccessPermissions::ReadWrite,
63+
execute_never: true,
64+
},
65+
},
66+
TranslationDescriptor {
67+
name: "System Memory",
68+
virtual_range: RangeInclusive::new(map::dram::START, map::dram::END - 1),
69+
physical_range_translation: Translation::Identity,
70+
attribute_fields: AttributeFields {
71+
mem_attributes: MemAttributes::CacheableDRAM,
72+
acc_perms: AccessPermissions::ReadWrite, // FIXME
73+
execute_never: false,
74+
},
75+
},
76+
],
77+
);
78+
79+
pub fn virt_mem_layout() -> &'static KernelVirtualLayout<NUM_MEM_RANGES> {
80+
&LAYOUT
81+
}

‎src/arch/aarch64/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33

44
#[cfg(not(test))]
55
pub mod asm;
6+
pub mod layout;
7+
pub mod paging;
8+
mod translation;

‎src/arch/aarch64/paging.rs

+286
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright (C) 2022 Akira Moroo
3+
// Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
4+
5+
use core::ops::RangeInclusive;
6+
7+
use cortex_a::{asm::barrier, registers::*};
8+
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
9+
10+
use self::interface::Mmu;
11+
12+
use super::{layout::KernelAddrSpace, translation::TranslationTable};
13+
14+
/// MMU enable errors variants.
15+
#[derive(Debug)]
16+
pub enum MmuEnableError {
17+
AlreadyEnabled,
18+
Other(&'static str),
19+
}
20+
21+
/// Memory Management interfaces.
22+
pub mod interface {
23+
use super::*;
24+
25+
/// MMU functions.
26+
pub trait Mmu {
27+
unsafe fn enable_mmu_and_caching(&self) -> Result<(), MmuEnableError>;
28+
29+
fn is_enabled(&self) -> bool;
30+
}
31+
}
32+
33+
/// Describes the characteristics of a translation granule.
34+
pub struct TranslationGranule<const GRANULE_SIZE: usize>;
35+
36+
/// Describes properties of an address space.
37+
pub struct AddressSpace<const AS_SIZE: usize>;
38+
39+
/// Architecture agnostic translation types.
40+
#[allow(dead_code)]
41+
#[derive(Copy, Clone)]
42+
pub enum Translation {
43+
Identity,
44+
Offset(usize),
45+
}
46+
47+
/// Architecture agnostic memory attributes.
48+
#[derive(Copy, Clone)]
49+
pub enum MemAttributes {
50+
CacheableDRAM,
51+
Device,
52+
}
53+
54+
/// Architecture agnostic access permissions.
55+
#[derive(Copy, Clone)]
56+
pub enum AccessPermissions {
57+
ReadOnly,
58+
ReadWrite,
59+
}
60+
61+
/// Collection of memory attributes.
62+
#[derive(Copy, Clone)]
63+
pub struct AttributeFields {
64+
pub mem_attributes: MemAttributes,
65+
pub acc_perms: AccessPermissions,
66+
pub execute_never: bool,
67+
}
68+
69+
impl Default for AttributeFields {
70+
fn default() -> AttributeFields {
71+
AttributeFields {
72+
mem_attributes: MemAttributes::CacheableDRAM,
73+
acc_perms: AccessPermissions::ReadWrite,
74+
execute_never: true,
75+
}
76+
}
77+
}
78+
79+
/// Architecture agnostic descriptor for a memory range.
80+
pub struct TranslationDescriptor {
81+
pub name: &'static str,
82+
pub virtual_range: RangeInclusive<usize>,
83+
pub physical_range_translation: Translation,
84+
pub attribute_fields: AttributeFields,
85+
}
86+
87+
/// Type for expressing the kernel's virtual memory layout.
88+
pub struct KernelVirtualLayout<const NUM_SPECIAL_RANGES: usize> {
89+
/// The last (inclusive) address of the address space.
90+
max_virt_addr_inclusive: usize,
91+
92+
/// Array of descriptors for non-standard (normal cacheable DRAM) memory regions.
93+
inner: [TranslationDescriptor; NUM_SPECIAL_RANGES],
94+
}
95+
96+
impl<const GRANULE_SIZE: usize> TranslationGranule<GRANULE_SIZE> {
97+
/// The granule's size.
98+
pub const SIZE: usize = Self::size_checked();
99+
100+
/// The granule's shift, aka log2(size).
101+
pub const SHIFT: usize = Self::SIZE.trailing_zeros() as usize;
102+
103+
const fn size_checked() -> usize {
104+
assert!(GRANULE_SIZE.is_power_of_two());
105+
106+
GRANULE_SIZE
107+
}
108+
}
109+
110+
impl<const AS_SIZE: usize> AddressSpace<AS_SIZE> {
111+
/// The address space size.
112+
pub const SIZE: usize = Self::size_checked();
113+
114+
/// The address space shift, aka log2(size).
115+
pub const SIZE_SHIFT: usize = Self::SIZE.trailing_zeros() as usize;
116+
117+
const fn size_checked() -> usize {
118+
assert!(AS_SIZE.is_power_of_two());
119+
120+
// Check for architectural restrictions as well.
121+
Self::arch_address_space_size_sanity_checks();
122+
123+
AS_SIZE
124+
}
125+
}
126+
127+
impl<const NUM_SPECIAL_RANGES: usize> KernelVirtualLayout<{ NUM_SPECIAL_RANGES }> {
128+
/// Create a new instance.
129+
pub const fn new(max: usize, layout: [TranslationDescriptor; NUM_SPECIAL_RANGES]) -> Self {
130+
Self {
131+
max_virt_addr_inclusive: max,
132+
inner: layout,
133+
}
134+
}
135+
136+
/// For a virtual address, find and return the physical output address and corresponding
137+
/// attributes.
138+
///
139+
/// If the address is not found in `inner`, return an identity mapped default with normal
140+
/// cacheable DRAM attributes.
141+
pub fn virt_addr_properties(
142+
&self,
143+
virt_addr: usize,
144+
) -> Result<(usize, AttributeFields), &'static str> {
145+
if virt_addr > self.max_virt_addr_inclusive {
146+
return Err("Address out of range");
147+
}
148+
149+
for i in self.inner.iter() {
150+
if i.virtual_range.contains(&virt_addr) {
151+
let output_addr = match i.physical_range_translation {
152+
Translation::Identity => virt_addr,
153+
Translation::Offset(a) => a + (virt_addr - (i.virtual_range).start()),
154+
};
155+
156+
return Ok((output_addr, i.attribute_fields));
157+
}
158+
}
159+
160+
Ok((virt_addr, AttributeFields::default()))
161+
}
162+
}
163+
164+
/// Memory Management Unit type.
165+
struct MemoryManagementUnit;
166+
167+
pub type Granule512MiB = TranslationGranule<{ 512 * 1024 * 1024 }>;
168+
pub type Granule64KiB = TranslationGranule<{ 64 * 1024 }>;
169+
170+
/// Constants for indexing the MAIR_EL1.
171+
pub mod mair {
172+
pub const DEVICE: u64 = 0;
173+
pub const NORMAL: u64 = 1;
174+
}
175+
176+
/// The kernel translation tables.
177+
///
178+
/// # Safety
179+
///
180+
/// - Supposed to land in `.bss`. Therefore, ensure that all initial member values boil down to "0".
181+
static mut KERNEL_TABLES: TranslationTable = TranslationTable::new();
182+
183+
static MMU: MemoryManagementUnit = MemoryManagementUnit;
184+
185+
impl<const AS_SIZE: usize> AddressSpace<AS_SIZE> {
186+
/// Checks for architectural restrictions.
187+
pub const fn arch_address_space_size_sanity_checks() {
188+
// Size must be at least one full 512 MiB table.
189+
assert!((AS_SIZE % Granule512MiB::SIZE) == 0);
190+
191+
// Check for 48 bit virtual address size as maximum, which is supported by any ARMv8
192+
// version.
193+
assert!(AS_SIZE <= (1 << 48));
194+
}
195+
}
196+
197+
impl MemoryManagementUnit {
198+
/// Setup function for the MAIR_EL1 register.
199+
fn setup_mair(&self) {
200+
// Define the memory types being mapped.
201+
MAIR_EL1.write(
202+
// Attribute 1 - Cacheable normal DRAM.
203+
MAIR_EL1::Attr1_Normal_Outer::WriteBack_NonTransient_ReadWriteAlloc
204+
+ MAIR_EL1::Attr1_Normal_Inner::WriteBack_NonTransient_ReadWriteAlloc
205+
// Attribute 0 - Device.
206+
+ MAIR_EL1::Attr0_Device::nonGathering_nonReordering_EarlyWriteAck,
207+
);
208+
}
209+
210+
/// Configure various settings of stage 1 of the EL1 translation regime.
211+
fn configure_translation_control(&self) {
212+
let t0sz = (64 - KernelAddrSpace::SIZE_SHIFT) as u64;
213+
214+
TCR_EL1.write(
215+
TCR_EL1::TBI0::Used
216+
+ TCR_EL1::IPS::Bits_40
217+
+ TCR_EL1::TG0::KiB_64
218+
+ TCR_EL1::SH0::Inner
219+
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
220+
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
221+
+ TCR_EL1::EPD0::EnableTTBR0Walks
222+
+ TCR_EL1::A1::TTBR0
223+
+ TCR_EL1::T0SZ.val(t0sz)
224+
+ TCR_EL1::EPD1::DisableTTBR1Walks,
225+
);
226+
}
227+
}
228+
229+
/// Return a reference to the MMU instance.
230+
fn mmu() -> &'static impl interface::Mmu {
231+
&MMU
232+
}
233+
234+
impl interface::Mmu for MemoryManagementUnit {
235+
unsafe fn enable_mmu_and_caching(&self) -> Result<(), MmuEnableError> {
236+
if self.is_enabled() {
237+
return Err(MmuEnableError::AlreadyEnabled);
238+
}
239+
240+
// Fail early if translation granule is not supported.
241+
if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported) {
242+
return Err(MmuEnableError::Other(
243+
"Translation granule not supported in HW",
244+
));
245+
}
246+
247+
// Prepare the memory attribute indirection register.
248+
self.setup_mair();
249+
250+
// Populate translation tables.
251+
KERNEL_TABLES
252+
.populate_tt_entries()
253+
.map_err(MmuEnableError::Other)?;
254+
255+
// Set the "Translation Table Base Register".
256+
TTBR0_EL1.set_baddr(KERNEL_TABLES.phys_base_address());
257+
258+
self.configure_translation_control();
259+
260+
// Switch the MMU on.
261+
//
262+
// First, force all previous changes to be seen before the MMU is enabled.
263+
barrier::isb(barrier::SY);
264+
265+
// Enable the MMU and turn on data and instruction caching.
266+
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
267+
268+
// Force MMU init to complete before next instruction.
269+
barrier::isb(barrier::SY);
270+
271+
Ok(())
272+
}
273+
274+
#[inline(always)]
275+
fn is_enabled(&self) -> bool {
276+
SCTLR_EL1.matches_all(SCTLR_EL1::M::Enable)
277+
}
278+
}
279+
280+
pub fn setup() {
281+
unsafe {
282+
if let Err(e) = mmu().enable_mmu_and_caching() {
283+
panic!("Failed to setup paging: {:?}", e);
284+
}
285+
}
286+
}

‎src/arch/aarch64/ram64.s

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ ram64_start:
2222

2323
jump_to_rust:
2424
/* x0 typically points to device tree at entry */
25-
ldr x0, =0x40000000
25+
ldr x0, ={FDT_START}
2626

2727
/* setup stack */
28-
ldr x30, =0xfc000000
28+
ldr x30, ={STACK_END}
2929
mov sp, x30
3030

3131
/* x0: pointer to device tree */

‎src/arch/aarch64/translation.rs

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright (C) 2022 Akira Moroo
3+
// Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
4+
5+
//! Architectural translation table.
6+
//!
7+
//! Only 64 KiB granule is supported.
8+
9+
use core::convert;
10+
11+
use tock_registers::{
12+
interfaces::{Readable, Writeable},
13+
register_bitfields,
14+
registers::InMemoryRegister,
15+
};
16+
17+
use super::{layout, paging::*};
18+
19+
// A table descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-15.
20+
register_bitfields! {u64,
21+
STAGE1_TABLE_DESCRIPTOR [
22+
/// Physical address of the next descriptor.
23+
NEXT_LEVEL_TABLE_ADDR_64KiB OFFSET(16) NUMBITS(32) [], // [47:16]
24+
25+
TYPE OFFSET(1) NUMBITS(1) [
26+
Block = 0,
27+
Table = 1
28+
],
29+
30+
VALID OFFSET(0) NUMBITS(1) [
31+
False = 0,
32+
True = 1
33+
]
34+
]
35+
}
36+
37+
// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
38+
register_bitfields! {u64,
39+
STAGE1_PAGE_DESCRIPTOR [
40+
/// Unprivileged execute-never.
41+
UXN OFFSET(54) NUMBITS(1) [
42+
False = 0,
43+
True = 1
44+
],
45+
46+
/// Privileged execute-never.
47+
PXN OFFSET(53) NUMBITS(1) [
48+
False = 0,
49+
True = 1
50+
],
51+
52+
/// Physical address of the next table descriptor (lvl2) or the page descriptor (lvl3).
53+
OUTPUT_ADDR_64KiB OFFSET(16) NUMBITS(32) [],
54+
55+
/// Access flag.
56+
AF OFFSET(10) NUMBITS(1) [
57+
False = 0,
58+
True = 1
59+
],
60+
61+
/// Shareability field.
62+
SH OFFSET(8) NUMBITS(2) [
63+
OuterShareable = 0b10,
64+
InnerShareable = 0b11
65+
],
66+
67+
/// Access Permissions.
68+
AP OFFSET(6) NUMBITS(2) [
69+
RW_EL1 = 0b00,
70+
RW_EL1_EL0 = 0b01,
71+
RO_EL1 = 0b10,
72+
RO_EL1_EL0 = 0b11
73+
],
74+
75+
/// Memory attributes index into the MAIR_EL1 register.
76+
AttrIndx OFFSET(2) NUMBITS(3) [],
77+
78+
TYPE OFFSET(1) NUMBITS(1) [
79+
Reserved_Invalid = 0,
80+
Page = 1
81+
],
82+
83+
VALID OFFSET(0) NUMBITS(1) [
84+
False = 0,
85+
True = 1
86+
]
87+
]
88+
}
89+
90+
/// A table descriptor for 64 KiB aperture.
91+
///
92+
/// The output points to the next table.
93+
#[derive(Copy, Clone)]
94+
#[repr(C)]
95+
struct TableDescriptor {
96+
value: u64,
97+
}
98+
99+
/// A page descriptor with 64 KiB aperture.
100+
///
101+
/// The output points to physical memory.
102+
#[derive(Copy, Clone)]
103+
#[repr(C)]
104+
struct PageDescriptor {
105+
value: u64,
106+
}
107+
108+
trait StartAddr {
109+
fn phys_start_addr_u64(&self) -> u64;
110+
fn phys_start_addr_usize(&self) -> usize;
111+
}
112+
113+
const NUM_LVL2_TABLES: usize = layout::KernelAddrSpace::SIZE >> Granule512MiB::SHIFT;
114+
115+
/// Big monolithic struct for storing the translation tables. Individual levels must be 64 KiB
116+
/// aligned, so the lvl3 is put first.
117+
#[repr(C)]
118+
#[repr(align(65536))]
119+
pub struct FixedSizeTranslationTable<const NUM_TABLES: usize> {
120+
/// Page descriptors, covering 64 KiB windows per entry.
121+
lvl3: [[PageDescriptor; 8192]; NUM_TABLES],
122+
123+
/// Table descriptors, covering 512 MiB windows.
124+
lvl2: [TableDescriptor; NUM_TABLES],
125+
}
126+
127+
/// A translation table type for the kernel space.
128+
pub type TranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
129+
130+
// The binary is still identity mapped, so we don't need to convert here.
131+
impl<T, const N: usize> StartAddr for [T; N] {
132+
fn phys_start_addr_u64(&self) -> u64 {
133+
self as *const T as u64
134+
}
135+
136+
fn phys_start_addr_usize(&self) -> usize {
137+
self as *const _ as usize
138+
}
139+
}
140+
141+
impl TableDescriptor {
142+
/// Create an instance.
143+
///
144+
/// Descriptor is invalid by default.
145+
pub const fn new_zeroed() -> Self {
146+
Self { value: 0 }
147+
}
148+
149+
/// Create an instance pointing to the supplied address.
150+
pub fn from_next_lvl_table_addr(phys_next_lvl_table_addr: usize) -> Self {
151+
let val = InMemoryRegister::<u64, STAGE1_TABLE_DESCRIPTOR::Register>::new(0);
152+
153+
let shifted = phys_next_lvl_table_addr >> Granule64KiB::SHIFT;
154+
val.write(
155+
STAGE1_TABLE_DESCRIPTOR::NEXT_LEVEL_TABLE_ADDR_64KiB.val(shifted as u64)
156+
+ STAGE1_TABLE_DESCRIPTOR::TYPE::Table
157+
+ STAGE1_TABLE_DESCRIPTOR::VALID::True,
158+
);
159+
160+
Self { value: val.get() }
161+
}
162+
}
163+
164+
/// Convert the kernel's generic memory attributes to HW-specific attributes of the MMU.
165+
impl convert::From<AttributeFields>
166+
for tock_registers::fields::FieldValue<u64, STAGE1_PAGE_DESCRIPTOR::Register>
167+
{
168+
fn from(attribute_fields: AttributeFields) -> Self {
169+
// Memory attributes.
170+
let mut desc = match attribute_fields.mem_attributes {
171+
MemAttributes::CacheableDRAM => {
172+
STAGE1_PAGE_DESCRIPTOR::SH::InnerShareable
173+
+ STAGE1_PAGE_DESCRIPTOR::AttrIndx.val(mair::NORMAL)
174+
}
175+
MemAttributes::Device => {
176+
STAGE1_PAGE_DESCRIPTOR::SH::OuterShareable
177+
+ STAGE1_PAGE_DESCRIPTOR::AttrIndx.val(mair::DEVICE)
178+
}
179+
};
180+
181+
// Access Permissions.
182+
desc += match attribute_fields.acc_perms {
183+
AccessPermissions::ReadOnly => STAGE1_PAGE_DESCRIPTOR::AP::RO_EL1,
184+
AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1,
185+
};
186+
187+
// The execute-never attribute is mapped to PXN in AArch64.
188+
desc += if attribute_fields.execute_never {
189+
STAGE1_PAGE_DESCRIPTOR::PXN::True
190+
} else {
191+
STAGE1_PAGE_DESCRIPTOR::PXN::False
192+
};
193+
194+
// Always set unprivileged exectue-never as long as userspace is not implemented yet.
195+
desc += STAGE1_PAGE_DESCRIPTOR::UXN::True;
196+
197+
desc
198+
}
199+
}
200+
201+
impl PageDescriptor {
202+
/// Create an instance.
203+
///
204+
/// Descriptor is invalid by default.
205+
pub const fn new_zeroed() -> Self {
206+
Self { value: 0 }
207+
}
208+
209+
/// Create an instance.
210+
pub fn from_output_addr(phys_output_addr: usize, attribute_fields: &AttributeFields) -> Self {
211+
let val = InMemoryRegister::<u64, STAGE1_PAGE_DESCRIPTOR::Register>::new(0);
212+
213+
let shifted = phys_output_addr as u64 >> Granule64KiB::SHIFT;
214+
val.write(
215+
STAGE1_PAGE_DESCRIPTOR::OUTPUT_ADDR_64KiB.val(shifted)
216+
+ STAGE1_PAGE_DESCRIPTOR::AF::True
217+
+ STAGE1_PAGE_DESCRIPTOR::TYPE::Page
218+
+ STAGE1_PAGE_DESCRIPTOR::VALID::True
219+
+ (*attribute_fields).into(),
220+
);
221+
222+
Self { value: val.get() }
223+
}
224+
}
225+
226+
impl<const NUM_TABLES: usize> FixedSizeTranslationTable<NUM_TABLES> {
227+
/// Create an instance.
228+
pub const fn new() -> Self {
229+
// Can't have a zero-sized address space.
230+
assert!(NUM_TABLES > 0);
231+
232+
Self {
233+
lvl3: [[PageDescriptor::new_zeroed(); 8192]; NUM_TABLES],
234+
lvl2: [TableDescriptor::new_zeroed(); NUM_TABLES],
235+
}
236+
}
237+
238+
/// Iterates over all static translation table entries and fills them at once.
239+
///
240+
/// # Safety
241+
///
242+
/// - Modifies a `static mut`. Ensure it only happens from here.
243+
pub unsafe fn populate_tt_entries(&mut self) -> Result<(), &'static str> {
244+
for (l2_nr, l2_entry) in self.lvl2.iter_mut().enumerate() {
245+
*l2_entry =
246+
TableDescriptor::from_next_lvl_table_addr(self.lvl3[l2_nr].phys_start_addr_usize());
247+
248+
for (l3_nr, l3_entry) in self.lvl3[l2_nr].iter_mut().enumerate() {
249+
let virt_addr = (l2_nr << Granule512MiB::SHIFT) + (l3_nr << Granule64KiB::SHIFT);
250+
251+
let (phys_output_addr, attribute_fields) =
252+
layout::virt_mem_layout().virt_addr_properties(virt_addr)?;
253+
254+
*l3_entry = PageDescriptor::from_output_addr(phys_output_addr, &attribute_fields);
255+
}
256+
}
257+
258+
Ok(())
259+
}
260+
261+
/// The translation table's base address to be used for programming the MMU.
262+
pub fn phys_base_address(&self) -> u64 {
263+
self.lvl2.phys_start_addr_u64()
264+
}
265+
}

‎src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#![feature(asm_const)]
1516
#![feature(alloc_error_handler)]
1617
#![feature(stmt_expr_attributes)]
1718
#![feature(slice_take)]

0 commit comments

Comments
 (0)
Please sign in to comment.