summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Mintram <me@jamesrm.com>2021-11-24 20:09:43 +0000
committerBrian Gianforcaro <b.gianfo@gmail.com>2021-11-28 22:01:21 -0800
commit271b9b8da3a5c05fe03df4869a688b8ea33cf205 (patch)
treef302168aacafd13ab84b46f76ef580bdea751e05
parentc2d7e200eb1cc01567e635b2d84ab59505521feb (diff)
downloadserenity-271b9b8da3a5c05fe03df4869a688b8ea33cf205.zip
Kernel: Set up and activate the MMU in the aarch64 perkernel
-rw-r--r--Kernel/Arch/aarch64/Aarch64Asm.h16
-rw-r--r--Kernel/Prekernel/Arch/aarch64/PrekernelMMU.cpp158
-rw-r--r--Kernel/Prekernel/Arch/aarch64/init.cpp5
-rw-r--r--Kernel/Prekernel/Arch/aarch64/linker.ld11
-rw-r--r--Kernel/Prekernel/CMakeLists.txt1
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