Skip to main content

ax_cpu/aarch64/
init.rs

1//! Helper functions to initialize the CPU states on systems bootstrapping.
2
3use aarch64_cpu::{asm::barrier, registers::*};
4use ax_memory_addr::PhysAddr;
5
6/// Swtich current exception level to EL1.
7///
8/// It usually used in the system booting process, where the startup code is
9/// running in EL2 or EL3. Besides, the stack is not available and the MMU is
10/// not enabled.
11///
12/// # Safety
13///
14/// This function is unsafe as it changes the CPU mode.
15pub unsafe fn switch_to_el1() {
16    SPSel.write(SPSel::SP::ELx);
17    SP_EL0.set(0);
18    let current_el = CurrentEL.read(CurrentEL::EL);
19    if current_el >= 2 {
20        if current_el == 3 {
21            // Set EL2 to 64bit and enable the HVC instruction.
22            SCR_EL3.write(
23                SCR_EL3::NS::NonSecure + SCR_EL3::HCE::HvcEnabled + SCR_EL3::RW::NextELIsAarch64,
24            );
25            // Set the return address and exception level.
26            SPSR_EL3.write(
27                SPSR_EL3::M::EL1h
28                    + SPSR_EL3::D::Masked
29                    + SPSR_EL3::A::Masked
30                    + SPSR_EL3::I::Masked
31                    + SPSR_EL3::F::Masked,
32            );
33            ELR_EL3.set(LR.get());
34        }
35        // Disable EL1 timer traps and the timer offset.
36        CNTHCTL_EL2.modify(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET);
37        CNTVOFF_EL2.set(0);
38        // Set EL1 to 64bit.
39        HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64);
40        // Set the return address and exception level.
41        SPSR_EL2.write(
42            SPSR_EL2::M::EL1h
43                + SPSR_EL2::D::Masked
44                + SPSR_EL2::A::Masked
45                + SPSR_EL2::I::Masked
46                + SPSR_EL2::F::Masked,
47        );
48        SP_EL1.set(SP.get());
49        ELR_EL2.set(LR.get());
50        aarch64_cpu::asm::eret();
51    }
52}
53
54/// Configures and enables the MMU on the current CPU.
55///
56/// It first sets `MAIR_EL1`, `TCR_EL1`, `TTBR0_EL1`, `TTBR1_EL1` registers to
57/// the conventional values, and then enables the MMU and caches by setting
58/// `SCTLR_EL1`.
59///
60/// # Safety
61///
62/// This function is unsafe as it changes the address translation configuration.
63pub unsafe fn init_mmu(root_paddr: PhysAddr) {
64    use ax_page_table_entry::aarch64::MemAttr;
65
66    MAIR_EL1.set(MemAttr::MAIR_VALUE);
67
68    // Enable TTBR0 and TTBR1 walks, page size = 4K, vaddr size = 48 bits, paddr size = 48 bits.
69    let tcr_flags0 = TCR_EL1::EPD0::EnableTTBR0Walks
70        + TCR_EL1::TG0::KiB_4
71        + TCR_EL1::SH0::Inner
72        + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
73        + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
74        + TCR_EL1::T0SZ.val(16);
75    let tcr_flags1 = TCR_EL1::EPD1::EnableTTBR1Walks
76        + TCR_EL1::TG1::KiB_4
77        + TCR_EL1::SH1::Inner
78        + TCR_EL1::ORGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable
79        + TCR_EL1::IRGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable
80        + TCR_EL1::T1SZ.val(16);
81    TCR_EL1.write(TCR_EL1::IPS::Bits_48 + tcr_flags0 + tcr_flags1);
82    barrier::isb(barrier::SY);
83
84    // Set both TTBR0 and TTBR1
85    let root_paddr = root_paddr.as_usize() as u64;
86    TTBR0_EL1.set(root_paddr);
87    TTBR1_EL1.set(root_paddr);
88
89    // Flush the entire TLB
90    crate::asm::flush_tlb(None);
91
92    // Enable the MMU, I/D cache, and EL0-accessible cache instructions.
93    // UCT/DZE/UCI let userspace execute `MRS CTR_EL0`, `DC ZVA`, and
94    // `DC CVAU` / `IC IVAU`. musl, glibc, and Mesa emit these
95    // unconditionally during early process startup. Without them, the
96    // first userspace cache-line lookup traps as EC=0x18 and the process
97    // dies with SIGTRAP before reaching `main()`.
98    SCTLR_EL1.modify(
99        SCTLR_EL1::M::Enable
100            + SCTLR_EL1::C::Cacheable
101            + SCTLR_EL1::I::Cacheable
102            + SCTLR_EL1::UCT::DontTrap
103            + SCTLR_EL1::DZE::DontTrap
104            + SCTLR_EL1::UCI::DontTrap,
105    );
106    // Disable SPAN (bit 23; not exposed as a named field by aarch64-cpu).
107    SCTLR_EL1.set(SCTLR_EL1.get() | (1 << 23));
108    barrier::isb(barrier::SY);
109}
110
111/// Initializes trap handling on the current CPU.
112///
113/// In detail, it initializes the exception vector, and sets `TTBR0_EL1` to 0 to
114/// block low address access.
115pub fn init_trap() {
116    #[cfg(feature = "uspace")]
117    {
118        crate::uspace_common::init_exception_table();
119        CNTKCTL_EL1.modify(CNTKCTL_EL1::EL0VCTEN::TrappedNone + CNTKCTL_EL1::EL0PCTEN::TrappedNone);
120        barrier::isb(barrier::SY);
121    }
122    unsafe extern "C" {
123        fn exception_vector_base();
124    }
125    unsafe {
126        crate::asm::write_exception_vector_base(exception_vector_base as *const () as usize);
127        crate::asm::write_user_page_table(0.into());
128    }
129}