diff options
author | James Mintram <me@jamesrm.com> | 2021-11-24 20:09:43 +0000 |
---|---|---|
committer | Brian Gianforcaro <b.gianfo@gmail.com> | 2021-11-28 22:01:21 -0800 |
commit | 271b9b8da3a5c05fe03df4869a688b8ea33cf205 (patch) | |
tree | f302168aacafd13ab84b46f76ef580bdea751e05 | |
parent | c2d7e200eb1cc01567e635b2d84ab59505521feb (diff) | |
download | serenity-271b9b8da3a5c05fe03df4869a688b8ea33cf205.zip |
Kernel: Set up and activate the MMU in the aarch64 perkernel
-rw-r--r-- | Kernel/Arch/aarch64/Aarch64Asm.h | 16 | ||||
-rw-r--r-- | Kernel/Prekernel/Arch/aarch64/PrekernelMMU.cpp | 158 | ||||
-rw-r--r-- | Kernel/Prekernel/Arch/aarch64/init.cpp | 5 | ||||
-rw-r--r-- | Kernel/Prekernel/Arch/aarch64/linker.ld | 11 | ||||
-rw-r--r-- | Kernel/Prekernel/CMakeLists.txt | 1 |
5 files changed, 191 insertions, 0 deletions
diff --git a/Kernel/Arch/aarch64/Aarch64Asm.h b/Kernel/Arch/aarch64/Aarch64Asm.h index 41276949c5..ceeb362316 100644 --- a/Kernel/Arch/aarch64/Aarch64Asm.h +++ b/Kernel/Arch/aarch64/Aarch64Asm.h @@ -10,6 +10,22 @@ namespace Kernel { +inline void set_ttbr1_el1(FlatPtr ttbr1_el1) +{ + asm("msr ttbr1_el1, %[value]" ::[value] "r"(ttbr1_el1)); +} + +inline void set_ttbr0_el1(FlatPtr ttbr0_el1) +{ + asm("msr ttbr0_el1, %[value]" ::[value] "r"(ttbr0_el1)); +} + +inline void flush() +{ + asm("dsb ish"); + asm("isb"); +} + [[noreturn]] inline void halt() { for (;;) { diff --git a/Kernel/Prekernel/Arch/aarch64/PrekernelMMU.cpp b/Kernel/Prekernel/Arch/aarch64/PrekernelMMU.cpp new file mode 100644 index 0000000000..f3b7ef6d0f --- /dev/null +++ b/Kernel/Prekernel/Arch/aarch64/PrekernelMMU.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2021, James Mintram <me@jamesrm.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/Types.h> + +#include <Kernel/Prekernel/Arch/aarch64/Prekernel.h> + +#include <Kernel/Arch/aarch64/Aarch64Asm.h> +#include <Kernel/Arch/aarch64/Aarch64Registers.h> +#include <Kernel/Prekernel/Arch/aarch64/UART.h> + +// Documentation here for Aarch64 Address Translations +// https://documentation-service.arm.com/static/5efa1d23dbdee951c1ccdec5?token= + +using namespace Kernel; + +// These come from the linker script +extern u8 page_tables_phys_start[]; +extern u8 page_tables_phys_end[]; + +namespace Prekernel { + +// physical memory +constexpr u32 START_OF_NORMAL_MEMORY = 0x00000000; +constexpr u32 END_OF_NORMAL_MEMORY = 0x3EFFFFFF; +constexpr u32 START_OF_DEVICE_MEMORY = 0x3F000000; +constexpr u32 END_OF_DEVICE_MEMORY = 0x3FFFFFFF; + +// 4KiB page size was chosen for the prekernel to make this code slightly simpler +constexpr u32 GRANULE_SIZE = 0x1000; +constexpr u32 PAGE_TABLE_SIZE = 0x1000; + +// Documentation for translation table format +// https://developer.arm.com/documentation/101811/0101/Controlling-address-translation +constexpr u32 PAGE_DESCRIPTOR = 0b11; +constexpr u32 TABLE_DESCRIPTOR = 0b11; + +constexpr u32 ACCESS_FLAG = 1 << 10; + +// shareability +constexpr u32 OUTER_SHAREABLE = (2 << 8); +constexpr u32 INNER_SHAREABLE = (3 << 8); + +// these index into the MAIR attribute table +constexpr u32 NORMAL_MEMORY = (0 << 2); +constexpr u32 DEVICE_MEMORY = (1 << 2); + +using page_table_t = u8*; + +static void zero_identity_map(page_table_t page_table_start, page_table_t page_table_end) +{ + // Memset all page table memory to zero + for (uint64_t* p = (uint64_t*)page_table_start; + p < (uint64_t*)page_table_end; + p++) { + + *p = 0; + } +} + +static void build_identity_map(page_table_t page_table) +{ + // Set up first entry of level 1 + uint64_t* level1_entry = (uint64_t*)page_table; + *level1_entry = (uint64_t)&page_table[PAGE_TABLE_SIZE]; + *level1_entry |= TABLE_DESCRIPTOR; + + // Set up first entry of level 2 + uint64_t* level2_entry = (uint64_t*)&page_table[PAGE_TABLE_SIZE]; + *level2_entry = (uint64_t)&page_table[PAGE_TABLE_SIZE * 2]; + *level2_entry |= TABLE_DESCRIPTOR; + + // Set up L3 entries + for (uint32_t l3_idx = 0; l3_idx < 512; l3_idx++) { + uint64_t* l3_entry = (uint64_t*)&page_table[PAGE_TABLE_SIZE * 2 + (l3_idx * sizeof(uint64_t))]; + + *l3_entry = (uint64_t)(page_table + (PAGE_TABLE_SIZE * 3) + (l3_idx * PAGE_TABLE_SIZE)); + *l3_entry |= TABLE_DESCRIPTOR; + } + + // Set up L4 entries + size_t page_index = 0; + + for (size_t addr = START_OF_NORMAL_MEMORY; addr < END_OF_NORMAL_MEMORY; addr += GRANULE_SIZE, page_index++) { + uint64_t* l4_entry = (uint64_t*)&page_table[PAGE_TABLE_SIZE * 3 + (page_index * sizeof(uint64_t))]; + + *l4_entry = addr; + *l4_entry |= ACCESS_FLAG; + *l4_entry |= PAGE_DESCRIPTOR; + *l4_entry |= INNER_SHAREABLE; + *l4_entry |= NORMAL_MEMORY; + } + + // Set up entries for last 16MB of memory (MMIO) + for (size_t addr = START_OF_DEVICE_MEMORY; addr < END_OF_DEVICE_MEMORY; addr += GRANULE_SIZE, page_index++) { + uint64_t* l4_entry = (uint64_t*)&page_table[PAGE_TABLE_SIZE * 3 + (page_index * sizeof(uint64_t))]; + + *l4_entry = addr; + *l4_entry |= ACCESS_FLAG; + *l4_entry |= PAGE_DESCRIPTOR; + *l4_entry |= OUTER_SHAREABLE; + *l4_entry |= DEVICE_MEMORY; + } +} + +static void switch_to_page_table(u8* page_table) +{ + set_ttbr0_el1((FlatPtr)page_table); + set_ttbr1_el1((FlatPtr)page_table); +} + +static void activate_mmu() +{ + Aarch64_MAIR_EL1 mair_el1 = {}; + mair_el1.Attr[0] = 0xFF; // Normal memory + mair_el1.Attr[1] = 0b00000100; // Device-nGnRE memory (non-cacheble) + Aarch64_MAIR_EL1::write(mair_el1); + + // Configure cacheability attributes for memory associated with translation table walks + Aarch64_TCR_EL1 tcr_el1 = {}; + + tcr_el1.SH1 = Aarch64_TCR_EL1::InnerShareable; + tcr_el1.ORGN1 = Aarch64_TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; + tcr_el1.IRGN1 = Aarch64_TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; + + tcr_el1.SH0 = Aarch64_TCR_EL1::InnerShareable; + tcr_el1.ORGN0 = Aarch64_TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; + tcr_el1.IRGN0 = Aarch64_TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; + + tcr_el1.TG1 = Aarch64_TCR_EL1::TG1GranuleSize::Size_4KB; + tcr_el1.TG0 = Aarch64_TCR_EL1::TG0GranuleSize::Size_4KB; + + // Auto detect the Intermediate Physical Address Size + Aarch64_ID_AA64MMFR0_EL1 feature_register = Aarch64_ID_AA64MMFR0_EL1::read(); + tcr_el1.IPS = feature_register.PARange; + + Aarch64_TCR_EL1::write(tcr_el1); + + // Enable MMU in the system control register + Aarch64_SCTLR_EL1 sctlr_el1 = Aarch64_SCTLR_EL1::read(); + sctlr_el1.M = 1; //Enable MMU + Aarch64_SCTLR_EL1::write(sctlr_el1); + + flush(); +} + +void init_prekernel_page_tables() +{ + zero_identity_map(page_tables_phys_start, page_tables_phys_end); + build_identity_map(page_tables_phys_start); + switch_to_page_table(page_tables_phys_start); + activate_mmu(); +} + +} diff --git a/Kernel/Prekernel/Arch/aarch64/init.cpp b/Kernel/Prekernel/Arch/aarch64/init.cpp index 254ee5936b..3bef14c64d 100644 --- a/Kernel/Prekernel/Arch/aarch64/init.cpp +++ b/Kernel/Prekernel/Arch/aarch64/init.cpp @@ -43,11 +43,16 @@ extern "C" [[noreturn]] void init() uart.print_str("Drop CPU to EL1\r\n"); Prekernel::drop_to_exception_level_1(); + uart.print_str("Initialize MMU\r\n"); + Prekernel::init_prekernel_page_tables(); + auto& framebuffer = Prekernel::Framebuffer::the(); if (framebuffer.initialized()) { draw_logo(); } + uart.print_str("Enter loop\r\n"); + auto& timer = Prekernel::Timer::the(); u64 start_musec = 0; for (;;) { diff --git a/Kernel/Prekernel/Arch/aarch64/linker.ld b/Kernel/Prekernel/Arch/aarch64/linker.ld index 6b093cbd89..e04e9290ab 100644 --- a/Kernel/Prekernel/Arch/aarch64/linker.ld +++ b/Kernel/Prekernel/Arch/aarch64/linker.ld @@ -33,6 +33,17 @@ SECTIONS *(.bss) end_of_bss = .; } :bss + + /* + FIXME: 8MB is enough space for all of the tables required to identity map + physical memory. 8M is wasteful, so this should be properly calculated. + */ + + . = ALIGN(4K); + page_tables_phys_start = .; + + . += 8M; + page_tables_phys_end = .; } size_of_bss_divided_by_8 = (end_of_bss - start_of_bss) / 8; diff --git a/Kernel/Prekernel/CMakeLists.txt b/Kernel/Prekernel/CMakeLists.txt index de2bb6fa06..84bef8659d 100644 --- a/Kernel/Prekernel/CMakeLists.txt +++ b/Kernel/Prekernel/CMakeLists.txt @@ -17,6 +17,7 @@ if ("${SERENITY_ARCH}" STREQUAL "aarch64") # Preload specific Arch/aarch64/init.cpp + Arch/aarch64/PrekernelMMU.cpp Arch/aarch64/PrekernelExceptions.cpp Arch/aarch64/PrekernelCommon.cpp |