Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions loader/aarch64.ld
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ SECTIONS
{
. = LINK_ADDRESS;

_loader_start = .;

.text :
{
_text = .;
Expand All @@ -36,4 +38,6 @@ SECTIONS
. = ALIGN(4);
_bss_end = .;
} :all

_loader_end = .;
}
1 change: 1 addition & 0 deletions loader/src/aarch64/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ uint64_t boot_lvl2_upper[1 << 9] ALIGN(1 << 12);
/* Paging structures for identity mapping */
uint64_t boot_lvl0_lower[1 << 9] ALIGN(1 << 12);
uint64_t boot_lvl1_lower[1 << 9] ALIGN(1 << 12);
uint64_t boot_lvl2_lower[1 << 9] ALIGN(1 << 12);

int arch_mmu_enable(int logical_cpu)
{
Expand Down
4 changes: 4 additions & 0 deletions loader/src/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ void putc(uint8_t ch)
#error Board not defined
#endif

#ifdef UART_BASE
uint32_t *uart_addr = (uint32_t *)UART_BASE;
#endif

void puts(const char *s)
{
while (*s) {
Expand Down
53 changes: 49 additions & 4 deletions tool/microkit/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,20 +485,64 @@ impl<'a> Loader<'a> {
let (boot_lvl0_upper_addr, boot_lvl0_upper_size) = elf
.find_symbol("boot_lvl0_upper")
.expect("Could not find 'boot_lvl0_upper' symbol");
let (boot_lvl2_lower_addr, boot_lvl2_lower_size) = elf
.find_symbol("boot_lvl2_lower")
.expect("Could not find 'boot_lvl2_lower' symbol");
let (start_addr, _) = elf
.find_symbol("_loader_start")
.expect("Could not find 'loader_start' symbol");
let (end_addr, _) = elf
.find_symbol("_loader_end")
.expect("Could not find 'loader_end' symbol");

if Aarch64::lvl1_index(start_addr) != Aarch64::lvl1_index(end_addr) {
panic!("We only map 1GiB, but elfloader paddr range covers multiple GiB");
}

let mut boot_lvl0_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE];
boot_lvl0_lower[..8].copy_from_slice(&(boot_lvl1_lower_addr | 3).to_le_bytes());

let mut boot_lvl1_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE];
for i in 0..512 {

// map optional UART MMIO in l1 1GB page, only available if CONFIG_PRINTING
if let Ok((uart_addr, _)) = elf.find_symbol("uart_addr") {
let data = elf
.get_data(uart_addr, 8)
.expect("uart_addr not initialized");
let uart_base = u64::from_le_bytes(data[0..8].try_into().unwrap());

let lvl1_idx = Aarch64::lvl1_index(uart_base);
#[allow(clippy::identity_op)] // keep the (0 << 2) for clarity
let pt_entry: u64 = ((i as u64) << AARCH64_1GB_BLOCK_BITS) |
let pt_entry: u64 = ((lvl1_idx as u64) << AARCH64_1GB_BLOCK_BITS) |
(1 << 10) | // access flag
(0 << 2) | // strongly ordered memory
(1); // 1G block
(1 << 0); // 1G block
let start = 8 * lvl1_idx;
let end = 8 * (lvl1_idx + 1);
boot_lvl1_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes());
}

let mut boot_lvl2_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE];

// 1GB lvl1 Table entry
let pt_entry = (boot_lvl2_lower_addr | 3).to_le_bytes();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we be consistent with the other examples and split out the meanings of the 3 flags into the individual bits?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This this is a 2 bit marker for the 1GB L1 table entry here, not individual bits

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, we do |3 in a bunch of places below. I'll change that at a later point.

Copy link
Copy Markdown
Author

@bruelc bruelc Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, better to handle it globally yes. I’ll add a comment since “3” means different things depending on the granule (page or table)

let lvl1_idx = Aarch64::lvl1_index(start_addr);
let start = 8 * lvl1_idx;
let end = 8 * (lvl1_idx + 1);
boot_lvl1_lower[start..end].copy_from_slice(&pt_entry);

// map the loader 1:1 access into 2MB lvl2 Block entries for a 4KB granule
let lvl2_idx = Aarch64::lvl2_index(start_addr);
for i in lvl2_idx ..= Aarch64::lvl2_index(end_addr) {
let entry_idx = (i - Aarch64::lvl2_index(start_addr)) << AARCH64_2MB_BLOCK_BITS;
let pt_entry: u64 = (entry_idx as u64 + start_addr) |
(1 << 10) | // Access flag
(3 << 8) | // Sharable
(4 << 2) | // MT_NORMAL memory
(1 << 0); // 2M block
let start = 8 * i;
let end = 8 * (i + 1);
boot_lvl1_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes());
boot_lvl2_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes());
}

let boot_lvl0_upper: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE];
Expand Down Expand Up @@ -536,6 +580,7 @@ impl<'a> Loader<'a> {
(boot_lvl0_upper_addr, boot_lvl0_upper_size, boot_lvl0_upper),
(boot_lvl1_upper_addr, boot_lvl1_upper_size, boot_lvl1_upper),
(boot_lvl2_upper_addr, boot_lvl2_upper_size, boot_lvl2_upper),
(boot_lvl2_lower_addr, boot_lvl2_lower_size, boot_lvl2_lower),
]
}
}