feat: PoC higher-half kernel

This commit is contained in:
Mark Poliakov 2021-10-10 21:24:22 +03:00
parent 45055998bd
commit a8b7e88cfe
8 changed files with 166 additions and 64 deletions

View File

@ -40,6 +40,13 @@ QEMU_OPTS+=-kernel $(O)/kernel.bin \
endif
endif
ifeq ($(QEMU_DINT),1)
QEMU_OPTS+=-d int
endif
ifeq ($(QEMU_PAUSE),1)
QEMU_OPTS+=-S
endif
.PHONY: address error etc kernel src
all: kernel

View File

@ -9,7 +9,7 @@
"llvm-target": "aarch64-unknown-none",
"max-atomic-width": 128,
"panic-strategy": "abort",
"relocation-model": "static",
"relocation-model": "pic",
"target-pointer-width": "64",
"pre-link-args": {
"ld.lld": [ "-Tetc/aarch64-qemu.ld" ]

View File

@ -1,14 +1,22 @@
ENTRY(_entry);
KERNEL_OFFSET = 0; /* 0xFFFFFF8000000000; */
KERNEL_OFFSET = 0xFFFFFF8000000000;
BASE_OFFSET = 0x40080000;
SECTIONS {
. = 0x40080000 + KERNEL_OFFSET;
. = BASE_OFFSET;
.text.lower : {
*(.text._entry)
}
. = ALIGN(16);
. = . + KERNEL_OFFSET;
PROVIDE(__kernel_start = .);
.text : AT(. - KERNEL_OFFSET) {
*(.text._entry)
*(.text._entry_upper)
*(.text*)
}

View File

@ -5,6 +5,13 @@
add \reg, \reg, #:lo12:\sym
.endm
.macro ADR_ABS reg, sym
movz \reg, #:abs_g3:\sym
movk \reg, #:abs_g2_nc:\sym
movk \reg, #:abs_g1_nc:\sym
movk \reg, #:abs_g0_nc:\sym
.endm
.section .text._entry
.global _entry
_entry:
@ -19,22 +26,24 @@ _entry:
b 1b
2:
// TODO leave EL2 if running in it
// Zero .bss
ADR_REL x0, __bss_start
ADR_REL x1, __bss_end
1:
cmp x0, x1
beq 2f
stp xzr, xzr, [x0], #16
b 1b
2:
// TODO zero .bss
ADR_REL x0, bsp_stack_top
mov sp, x0
ADR_ABS x9, __aa64_entry_upper
b __aa64_enter_upper
mov x0, x8
b __aa64_bsp_main
.section .text._entry_upper
__aa64_entry_upper:
// x0 -- FDT base address
ADR_REL x1, bsp_stack_top
mov sp, x1
mov lr, xzr
bl __aa64_bsp_main
b .
.section .bss
.p2align 4

View File

@ -4,8 +4,8 @@ use crate::arch::{aarch64::asm::CPACR_EL1, machine};
use crate::dev::{Device, fdt::DeviceTree};
use crate::mem::virt;
use cortex_a::asm::barrier::{self, dsb, isb};
use cortex_a::registers::{DAIF, SCTLR_EL1, VBAR_EL1};
use tock_registers::interfaces::{ReadWriteable, Writeable};
use cortex_a::registers::{DAIF, SCTLR_EL1, VBAR_EL1, CurrentEL};
use tock_registers::interfaces::{ReadWriteable, Writeable, Readable};
#[no_mangle]
fn __aa64_bsp_main(fdt_base: usize) {
@ -34,7 +34,7 @@ fn __aa64_bsp_main(fdt_base: usize) {
machine::init_board().unwrap();
let fdt = DeviceTree::from_phys(fdt_base).expect("Failed to obtain a device tree");
let fdt = DeviceTree::from_phys(fdt_base + 0xFFFFFF8000000000).expect("Failed to obtain a device tree");
fdt.dump();
unsafe {
@ -43,6 +43,7 @@ fn __aa64_bsp_main(fdt_base: usize) {
loop {
DAIF.modify(DAIF::I::CLEAR);
cortex_a::asm::wfi();
}
}
@ -53,3 +54,5 @@ cfg_if! {
global_asm!(include_str!("entry.S"));
}
}
global_asm!(include_str!("upper.S"));

View File

@ -0,0 +1,95 @@
// vi:ft=a64asm:
.set PTE_BLOCK_AF, 1 << 10
.set PTE_BLOCK_ISH, 3 << 8
.set PTE_PRESENT, 1 << 0
.set MAIR_EL1_Attr0_Normal_Inner_NC, (4 << 0)
.set MAIR_EL1_Attr0_Normal_Outer_NC, (4 << 4)
.set MAIR_EL1_Attr1_Device, (0 << 4)
.set MAIR_EL1_Attr1_Device_nGnRnE, (0 << 4)
.set ID_AA64MMFR0_EL1_TGran4, (0xF << 28)
.set TCR_EL1_IPS_SHIFT, 32
.set TCR_EL1_TG1_4K, (2 << 30)
.set TCR_EL1_SH1_Outer, (2 << 28)
.set TCR_EL1_ORGN1_NC, (0 << 26)
.set TCR_EL1_IRGN1_NC, (0 << 24)
.set TCR_EL1_T1SZ_SHIFT, 16
.set TCR_EL1_TG0_4K, (0 << 14)
.set TCR_EL1_SH0_Outer, (2 << 12)
.set TCR_EL1_ORGN0_NC, (0 << 10)
.set TCR_EL1_IRGN0_NC, (0 << 8)
.set TCR_EL1_T0SZ_SHIFT, 0
.set TCR_EL1_ATTRS, (TCR_EL1_TG1_4K | TCR_EL1_SH1_Outer | TCR_EL1_TG0_4K | TCR_EL1_SH0_Outer | (25 << TCR_EL1_T1SZ_SHIFT) | (25 << TCR_EL1_T0SZ_SHIFT))
.set SCTLR_EL1_M, (1 << 0)
.macro MOV_L reg, value
mov \reg, #((\value) & 0xFFFF)
movk \reg, #((\value) >> 16), lsl #16
.endm
.section .text._entry
.global __aa64_enter_upper
.type __aa64_enter_upper, %function
__aa64_enter_upper:
// x8 -- FDT base
// x9 -- upper entry point
// Setup TTBR1_EL1
// TODO fix macros
ADR_ABS x5, KERNEL_TTBR1
ADR_ABS x6, KERNEL_OFFSET
// x5 = KERNEL_TTBR1 physical address
sub x5, x5, x6
// Fill KERNEL_TTBR1 table with upper-mapped Normal memory
.fill_ttbr1:
mov x2, #256
1:
sub x2, x2, #1
// x0 = (x2 << 30) | attrs...
lsl x1, x2, #30
mov x0, #(PTE_BLOCK_ISH | PTE_BLOCK_AF | PTE_PRESENT)
orr x0, x0, x1
str x0, [x5, x2, lsl #3]
cbnz x2, 1b
.init_mmu_regs:
mov x0, #(MAIR_EL1_Attr0_Normal_Outer_NC | MAIR_EL1_Attr0_Normal_Inner_NC | MAIR_EL1_Attr1_Device | MAIR_EL1_Attr1_Device_nGnRnE)
// Test for 4KiB page support
mrs x0, ID_AA64MMFR0_EL1
mov x1, ID_AA64MMFR0_EL1_TGran4
tst x0, x1
bne .no_4k_gran
// x0 = PARange
and x0, x0, #0xF
lsl x0, x0, #TCR_EL1_IPS_SHIFT
MOV_L x1, TCR_EL1_ATTRS
orr x0, x0, x1
msr tcr_el1, x0
msr mair_el1, x0
msr ttbr0_el1, x5
msr ttbr1_el1, x5
mrs x0, sctlr_el1
orr x0, x0, #SCTLR_EL1_M
msr sctlr_el1, x0
mov x0, x8
br x9
.no_4k_gran:
b .
.size __aa64_enter_upper, . - __aa64_enter_upper

View File

@ -2,6 +2,9 @@
pub mod virt;
/// Virtual offset applied to kernel address space
pub const KERNEL_OFFSET: usize = 0xFFFFFF8000000000;
/// See memcpy(3p).
///
/// # Safety

View File

@ -1,5 +1,6 @@
#![allow(missing_docs)]
use crate::mem::KERNEL_OFFSET;
use core::marker::PhantomData;
use core::ops::Deref;
use core::sync::atomic::{AtomicBool, Ordering};
@ -18,15 +19,6 @@ const PTE_PRESENT: u64 = 1 << 0;
struct Table([u64; 512]);
#[no_mangle]
static mut KERNEL_TTBR0: Table = {
let mut table = [0; 512];
// TODO fine-grained mapping
table[0] = (0 << 30) | PTE_BLOCK_AF | PTE_BLOCK_ISH | PTE_PRESENT;
table[1] = (1 << 30) | PTE_BLOCK_AF | PTE_BLOCK_ISH | PTE_PRESENT;
Table(table)
};
static mut KERNEL_TTBR1: Table = Table([0; 512]);
// 1GiB
static mut KERNEL_L1: Table = Table([0; 512]);
@ -36,6 +28,8 @@ static mut COUNT: usize = 0;
static mut BIG_COUNT: usize = 1;
static mut HUGE_COUNT: usize = 1;
const DEVICE_MAP_OFFSET: usize = KERNEL_OFFSET + (256 << 30);
pub struct DeviceMemory {
name: &'static str,
base: usize,
@ -64,10 +58,11 @@ impl DeviceMemory {
}
HUGE_COUNT += 1;
KERNEL_TTBR1.0[count + 256] = (phys as u64) | PTE_PRESENT | PTE_BLOCK_OSH | PTE_BLOCK_AF;
KERNEL_TTBR1.0[count + 256] =
(phys as u64) | PTE_PRESENT | PTE_BLOCK_OSH | PTE_BLOCK_AF;
0xFFFFFFC000000000 + (count << 30)
},
DEVICE_MAP_OFFSET + (count << 30)
}
512 => {
let count = BIG_COUNT;
if count == 512 {
@ -77,8 +72,8 @@ impl DeviceMemory {
KERNEL_L1.0[count] = (phys as u64) | PTE_PRESENT | PTE_BLOCK_OSH | PTE_BLOCK_AF;
0xFFFFFFC000000000 + (count << 21)
},
DEVICE_MAP_OFFSET + (count << 21)
}
1 => {
let count = COUNT;
if count == 512 {
@ -89,9 +84,9 @@ impl DeviceMemory {
KERNEL_L2.0[count] =
(phys as u64) | PTE_TABLE | PTE_PRESENT | PTE_BLOCK_OSH | PTE_BLOCK_AF;
0xFFFFFFC000000000 + count * 0x1000
},
_ => unimplemented!()
DEVICE_MAP_OFFSET + (count << 12)
}
_ => unimplemented!(),
}
};
@ -130,43 +125,25 @@ impl<T> Deref for DeviceMemoryIo<T> {
}
pub fn enable() -> Result<(), Errno> {
MAIR_EL1.write(
MAIR_EL1::Attr0_Normal_Outer::NonCacheable + MAIR_EL1::Attr0_Normal_Inner::NonCacheable,
);
unsafe {
KERNEL_L1.0[0] = (&KERNEL_L2 as *const _ as u64) | PTE_TABLE | PTE_PRESENT;
KERNEL_TTBR1.0[256] = (&KERNEL_L1 as *const _ as u64) | PTE_TABLE | PTE_PRESENT;
}
// TODO function to translate kernel addresses to physical ones
let l1_base = (&KERNEL_L1 as *const _ as u64) - KERNEL_OFFSET as u64;
let l2_base = (&KERNEL_L2 as *const _ as u64) - KERNEL_OFFSET as u64;
TTBR0_EL1.set(unsafe { &mut KERNEL_TTBR0 as *mut _ as u64 });
TTBR1_EL1.set(unsafe { &mut KERNEL_TTBR1 as *mut _ as u64 });
KERNEL_L1.0[0] = l2_base | PTE_TABLE | PTE_PRESENT;
KERNEL_TTBR1.0[256] = l1_base | PTE_TABLE | PTE_PRESENT;
if ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran4::NotSupported) {
return Err(Errno::InvalidArgument);
// NOTE don't think tlb needs to be invalidated when new entries are created
}
let parange = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange);
unsafe {
dsb(barrier::ISH);
isb(barrier::SY);
}
TCR_EL1.write(
TCR_EL1::IPS.val(parange)
+ TCR_EL1::T0SZ.val(25)
+ TCR_EL1::TG0::KiB_4
+ TCR_EL1::SH0::Outer
+ TCR_EL1::IRGN0::NonCacheable
+ TCR_EL1::ORGN0::NonCacheable
+ TCR_EL1::T1SZ.val(25)
+ TCR_EL1::TG1::KiB_4
+ TCR_EL1::SH1::Outer
+ TCR_EL1::IRGN1::NonCacheable
+ TCR_EL1::ORGN1::NonCacheable,
);
SCTLR_EL1.modify(SCTLR_EL1::M::SET);
// Disable lower-half translation
TTBR0_EL1.set(0);
TCR_EL1.modify(TCR_EL1::EPD0::SET);
Ok(())
}