[haiku-commits] Change in haiku[master]: efi/arm: WIP mmu for kernel handoff and stuff.

  • From: Gerrit <review@xxxxxxxxxxxxxxxxxxx>
  • To: waddlesplash <waddlesplash@xxxxxxxxx>, haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 21 Dec 2020 17:42:02 +0000

From Alex von Gluck IV <kallisti5@xxxxxxxxxxx>:

Alex von Gluck IV has uploaded this change for review. ( 
https://review.haiku-os.org/c/haiku/+/3542 ;)


Change subject: efi/arm: WIP mmu for kernel handoff and stuff.
......................................................................

efi/arm: WIP mmu for kernel handoff and stuff.

Change-Id: I88bf416ee9f1dafd5da3e4a3876a4d3f7e38b88c
---
M headers/private/kernel/arch/arm/arch_kernel_args.h
M src/system/boot/platform/efi/arch/arm/Jamfile
M src/system/boot/platform/efi/arch/arm/arch_mmu.cpp
M src/system/boot/platform/efi/arch/arm/arch_smp.cpp
M src/system/boot/platform/efi/arch/arm/arch_start.cpp
M src/system/kernel/arch/arm/arch_debug_console.cpp
6 files changed, 479 insertions(+), 7 deletions(-)



  git pull ssh://git.haiku-os.org:22/haiku refs/changes/42/3542/1

diff --git a/headers/private/kernel/arch/arm/arch_kernel_args.h 
b/headers/private/kernel/arch/arm/arch_kernel_args.h
index e19183d..b441d26 100644
--- a/headers/private/kernel/arch/arm/arch_kernel_args.h
+++ b/headers/private/kernel/arch/arm/arch_kernel_args.h
@@ -27,6 +27,7 @@
        uint32  phys_pgdir;
        uint32  vir_pgdir;
        uint32  next_pagetable;
+       uint32  virtual_end;

        // needed for UEFI, otherwise kernel acpi support can't find ACPI root
        FixedWidthPointer<void> acpi_root;
diff --git a/src/system/boot/platform/efi/arch/arm/Jamfile 
b/src/system/boot/platform/efi/arch/arm/Jamfile
index 8cf1e95..2d2bbd1 100644
--- a/src/system/boot/platform/efi/arch/arm/Jamfile
+++ b/src/system/boot/platform/efi/arch/arm/Jamfile
@@ -1,6 +1,9 @@
 SubDir HAIKU_TOP src system boot platform efi arch arm ;

 SubDirHdrs $(HAIKU_TOP) src system boot platform efi ;
+SubDirHdrs $(HAIKU_TOP) src add-ons kernel bus_managers fdt ;
+
+UseLibraryHeaders [ FDirName libfdt ] ;

 UsePrivateHeaders [ FDirName kernel platform ] ;
 UsePrivateHeaders [ FDirName kernel boot platform efi ] ;
@@ -27,6 +30,8 @@
 
        BootMergeObject boot_platform_efi_arm.o :
                $(arch_src)
+               : :
+               boot_fdt.a
                ;
        }
 }
diff --git a/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp 
b/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp
index 9a86a85..485c107 100644
--- a/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp
+++ b/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp
@@ -1,10 +1,437 @@
 /*
- * Copyright 2019-2020 Haiku, Inc. All rights reserved.
- * Released under the terms of the MIT License.
+ * Copyright 2004-2008, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx.
+ * Based on code written by Travis Geiselbrecht for NewOS.
+ *
+ * Distributed under the terms of the MIT License.
  */

+
+#include "arch_mmu.h"
+
+#include <algorithm>
+#include <boot/platform.h>
+#include <boot/stdio.h>
+#include <boot/kernel_args.h>
+#include <boot/stage2.h>
+#include <arch/cpu.h>
+#include <arch_kernel.h>
+#include <arm_mmu.h>
+#include <kernel.h>
+
+#include <OS.h>
+
+#include <string.h>
+
+#include "fdt_support.h"
+
+extern "C" {
+#include <fdt.h>
+#include <libfdt.h>
+#include <libfdt_env.h>
+};
+
+#include "mmu.h"
+
+
+#define TRACE_MMU
+#ifdef TRACE_MMU
+#      define TRACE(x) dprintf x
+#else
+#      define TRACE(x) ;
+#endif
+
+
+/*
+ * Set translation table base
+ */
+void
+mmu_set_TTBR(uint32 ttb)
+{
+       ttb &= 0xffffc000;
+       asm volatile("MCR p15, 0, %[adr], c2, c0, 0"::[adr] "r" (ttb));
+}
+
+
+/*
+ * Flush the TLB
+ */
+void
+mmu_flush_TLB()
+{
+       uint32 value = 0;
+       asm volatile("MCR p15, 0, %[c8format], c8, c7, 0"::[c8format] "r" 
(value));
+}
+
+
+/*
+ * Read MMU Control Register
+ */
+uint32
+mmu_read_C1()
+{
+       uint32 controlReg = 0;
+       asm volatile("MRC p15, 0, %[c1out], c1, c0, 0":[c1out] "=r" 
(controlReg));
+       return controlReg;
+}
+
+
+/*
+ * Write MMU Control Register
+ */
+void
+mmu_write_C1(uint32 value)
+{
+       asm volatile("MCR p15, 0, %[c1in], c1, c0, 0"::[c1in] "r" (value));
+}
+
+
+/*
+ * Dump current MMU Control Register state
+ * For debugging, can be added to loader temporarly post-serial-init
+ */
+void
+mmu_dump_C1()
+{
+       uint32 cpValue = mmu_read_C1();
+
+       dprintf("MMU CP15:c1 State:\n");
+
+       if ((cpValue & (1 << 0)) != 0)
+               dprintf(" - MMU Enabled\n");
+       else
+               dprintf(" - MMU Disabled\n");
+
+       if ((cpValue & (1 << 2)) != 0)
+               dprintf(" - Data Cache Enabled\n");
+       else
+               dprintf(" - Data Cache Disabled\n");
+
+       if ((cpValue & (1 << 3)) != 0)
+               dprintf(" - Write Buffer Enabled\n");
+       else
+               dprintf(" - Write Buffer Disabled\n");
+
+       if ((cpValue & (1 << 12)) != 0)
+               dprintf(" - Instruction Cache Enabled\n");
+       else
+               dprintf(" - Instruction Cache Disabled\n");
+
+       if ((cpValue & (1 << 13)) != 0)
+               dprintf(" - Vector Table @ 0xFFFF0000\n");
+       else
+               dprintf(" - Vector Table @ 0x00000000\n");
+}
+
+
+void
+mmu_write_DACR(uint32 value)
+{
+       asm volatile("MCR p15, 0, %[c1in], c3, c0, 0"::[c1in] "r" (value));
+}
+
+
+//TODO:move this to generic/ ?
+static status_t
+fdt_map_memory_ranges(void* fdt, const char* path, bool physical = false)
+{
+       int node;
+       const void *prop;
+       int len;
+       uint64 total;
+
+       if (fdt == NULL) {
+               dprintf("%s: fdt missing! Unable to map %s.\n", __func__, path);
+               return B_ERROR;
+       }
+
+       dprintf("checking FDT for %s...\n", path);
+       node = fdt_path_offset(fdt, path);
+
+       total = 0;
+
+       int32 regAddressCells = 1;
+       int32 regSizeCells = 1;
+       fdt_get_cell_count(fdt, node, regAddressCells, regSizeCells);
+
+       prop = fdt_getprop(fdt, node, "reg", &len);
+       if (prop == NULL) {
+               dprintf("Unable to locate %s in FDT!\n", path);
+               return B_ERROR;
+       }
+
+       const uint32 *p = (const uint32 *)prop;
+       for (int32 i = 0; len; i++) {
+               uint64 base;
+               uint64 size;
+               if (regAddressCells == 2)
+                       base = fdt64_to_cpu(*(uint64_t *)p);
+               else
+                       base = fdt32_to_cpu(*(uint32_t *)p);
+               p += regAddressCells;
+               if (regSizeCells == 2)
+                       size = fdt64_to_cpu(*(uint64_t *)p);
+               else
+                       size = fdt32_to_cpu(*(uint32_t *)p);
+               p += regSizeCells;
+               len -= sizeof(uint32) * (regAddressCells + regSizeCells);
+
+               if (size <= 0) {
+                       dprintf("%ld: empty region\n", i);
+                       continue;
+               }
+               dprintf("%" B_PRIu32 ": base = %" B_PRIu64 ","
+                       "size = %" B_PRIu64 "\n", i, base, size);
+
+               total += size;
+
+               if (physical) {
+                       if (mmu_map_physical_memory(base, size,
+                                       kDefaultPageFlags) < 0) {
+                               dprintf("cannot map physical memory range "
+                                       "(num ranges = %" B_PRIu32 ")!\n",
+                                       gKernelArgs.num_physical_memory_ranges);
+                               return B_ERROR;
+                       }
+               } else {
+                       // TODO: virtual valid here?
+               }
+       }
+
+       dprintf("total '%s' physical memory = %" B_PRId64 "MB\n", path,
+               total / (1024 * 1024));
+
+       return B_OK;
+}
+
+
+static void
+fdt_map_peripheral(void* fdt)
+{
+       // map peripheral devices (such as uart) from fdt
+
+       #warning Map peripherals from the fdt we want to use in the bootloader!
+       // this assumes /pl011@9000000 which is the qemu virt uart
+       fdt_map_memory_ranges(fdt, "/pl011@9000000");
+       // this assumes /axi which is broadcom!
+       fdt_map_memory_ranges(fdt, "/axi");
+}
+
+
 void
 arch_mmu_init()
 {
-       // Stub
+       TRACE(("arch_mmu_init\n"));
+
+       mmu_dump_C1();
+}
+
+
+uint64_t
+arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
+       efi_memory_descriptor *memory_map, size_t descriptor_size,
+       uint32_t descriptor_version)
+{
+       // Generate page tables
+
+       // Page Global Directory
+       uint32_t *pgd;
+
+       // Page Table Entry
+       uint32_t *pte;
+
+       //uint64_t *pdpt;
+       //uint64_t *pageDir;
+       //uint64_t *pageTable;
+
+       // Allocate the top level page global directory.
+       pgd = NULL;
+       if (platform_allocate_region((void**)&pgd, B_PAGE_SIZE, 0, false) != 
B_OK)
+               panic("Failed to allocate page global directory!");
+       gKernelArgs.arch_args.phys_pgdir = (uint32_t)(addr_t)pgd;
+       memset(pgd, 0, B_PAGE_SIZE);
+
+       // Not really needed since 32-bit?
+       //platform_bootloader_address_to_kernel_address(pgd,
+       //      &gKernelArgs.arch_args.vir_pgdir);
+
+       // Store the virtual memory usage information.
+       gKernelArgs.virtual_allocated_range[0].start = KERNEL_LOAD_BASE;
+       gKernelArgs.virtual_allocated_range[0].size
+               = get_current_virtual_address() - KERNEL_LOAD_BASE;
+       gKernelArgs.num_virtual_allocated_ranges = 1;
+       gKernelArgs.arch_args.virtual_end = ROUNDUP(KERNEL_LOAD_BASE
+               + gKernelArgs.virtual_allocated_range[0].size, 0x200000);
+
+       // Find the highest physical memory address. We map all physical memory
+       // into the kernel address space, so we want to make sure we map 
everything
+       // we have available.
+       uint32 maxAddress = 0;
+       for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
+               efi_memory_descriptor *entry
+                       = (efi_memory_descriptor *)((addr_t)memory_map + i * 
descriptor_size);
+               maxAddress = std::max(maxAddress,
+                                     (uint32)(entry->PhysicalStart + 
entry->NumberOfPages * 4096));
+       }
+
+       // Want to map at least 4GB, there may be stuff other than usable RAM 
that
+       // could be in the first 4GB of physical address space.
+       maxAddress = std::max(maxAddress, (uint32)0x100000000);
+       maxAddress = ROUNDUP(maxAddress, 0x40000000);
+
+       // Currently only use 1 PDPT (512GB). This will need to change if 
someone
+       // wants to use Haiku on a box with more than 512GB of RAM but that's
+       // probably not going to happen any time soon.
+       if (maxAddress / 0x40000000 > 512)
+               panic("Can't currently support more than 512GB of RAM!");
+
+       // Create page tables for the physical map area. Also map this PDPT
+       // temporarily at the bottom of the address space so that we are 
identity
+       // mapped.
+
+       pte = (uint32*)mmu_allocate_page();
+       memset(pte, 0, B_PAGE_SIZE);
+       pgd[510] = (addr_t)pte | kTableMappingFlags;
+       pgd[0] = (addr_t)pte | kTableMappingFlags;
+
+       #if 0
+       for (uint64 i = 0; i < maxAddress; i += 0x40000000) {
+               pageDir = (uint64*)mmu_allocate_page();
+               memset(pageDir, 0, B_PAGE_SIZE);
+               pte[i / 0x40000000] = (addr_t)pageDir | kTableMappingFlags;
+
+               for (uint64 j = 0; j < 0x40000000; j += 0x200000) {
+                       pageDir[j / 0x200000] = (i + j) | 
kLargePageMappingFlags;
+               }
+       }
+       // Allocate tables for the kernel mappings.
+
+       pdpt = (uint64*)mmu_allocate_page();
+       memset(pdpt, 0, B_PAGE_SIZE);
+       pml4[511] = (addr_t)pdpt | kTableMappingFlags;
+
+       pageDir = (uint64*)mmu_allocate_page();
+       memset(pageDir, 0, B_PAGE_SIZE);
+       pdpt[510] = (addr_t)pageDir | kTableMappingFlags;
+
+       // We can now allocate page tables and duplicate the mappings across 
from
+       // the 32-bit address space to them.
+       pageTable = NULL; // shush, compiler.
+       for (uint32 i = 0; i < gKernelArgs.virtual_allocated_range[0].size
+                       / B_PAGE_SIZE; i++) {
+               if ((i % 512) == 0) {
+                       pageTable = (uint64*)mmu_allocate_page();
+                       memset(pageTable, 0, B_PAGE_SIZE);
+                       pageDir[i / 512] = (addr_t)pageTable | 
kTableMappingFlags;
+               }
+
+               // Get the physical address to map.
+               void *phys;
+               if (platform_kernel_address_to_bootloader_address(
+                       KERNEL_LOAD_BASE + (i * B_PAGE_SIZE), &phys) != B_OK) {
+                       continue;
+               }
+
+               pageTable[i % 512] = (addr_t)phys | kPageMappingFlags;
+       }
+       #endif
+
+       return (uint64)pgd;
+}
+
+
+// Called after EFI boot services exit.
+// Currently assumes that the memory map is sane... Sorted and no overlapping
+// regions.
+void
+arch_mmu_post_efi_setup(size_t memory_map_size,
+       efi_memory_descriptor *memory_map, size_t descriptor_size,
+       uint32_t descriptor_version)
+{
+       // Add physical memory to the kernel args and update virtual addresses 
for
+       // EFI regions.
+       addr_t addr = (addr_t)memory_map;
+       gKernelArgs.num_physical_memory_ranges = 0;
+
+       // First scan: Add all usable ranges
+       for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
+               efi_memory_descriptor *entry
+                       = (efi_memory_descriptor *)(addr + i * descriptor_size);
+               switch (entry->Type) {
+               case EfiLoaderCode:
+               case EfiLoaderData:
+               case EfiBootServicesCode:
+               case EfiBootServicesData:
+               case EfiConventionalMemory: {
+                       // Usable memory.
+                       // Ignore memory below 1MB and above 512GB.
+                       uint64_t base = entry->PhysicalStart;
+                       uint64_t end = entry->PhysicalStart + 
entry->NumberOfPages * 4096;
+                       uint64_t originalSize = end - base;
+                       if (base < 0x100000)
+                               base = 0x100000;
+                       if (end > (512ull * 1024 * 1024 * 1024))
+                               end = 512ull * 1024 * 1024 * 1024;
+
+                       gKernelArgs.ignored_physical_memory
+                               += originalSize - (max_c(end, base) - base);
+
+                       if (base >= end)
+                               break;
+                       uint64_t size = end - base;
+
+                       insert_physical_memory_range(base, size);
+                       // LoaderData memory is bootloader allocated memory, 
possibly
+                       // containing the kernel or loaded drivers.
+                       if (entry->Type == EfiLoaderData)
+                               insert_physical_allocated_range(base, size);
+                       break;
+               }
+               case EfiACPIReclaimMemory:
+                       // ACPI reclaim -- physical memory we could actually 
use later
+                       break;
+               case EfiRuntimeServicesCode:
+               case EfiRuntimeServicesData:
+                       entry->VirtualStart = entry->PhysicalStart;
+                       break;
+               }
+       }
+
+       uint64_t initialPhysicalMemory = total_physical_memory();
+
+       // Second scan: Remove everything reserved that may overlap
+       for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
+               efi_memory_descriptor *entry
+                       = (efi_memory_descriptor *)(addr + i * descriptor_size);
+               switch (entry->Type) {
+               case EfiLoaderCode:
+               case EfiLoaderData:
+               case EfiBootServicesCode:
+               case EfiBootServicesData:
+               case EfiConventionalMemory:
+                       break;
+               default:
+                       uint64_t base = entry->PhysicalStart;
+                       uint64_t end = entry->PhysicalStart + 
entry->NumberOfPages * 4096;
+                       remove_physical_memory_range(base, end - base);
+               }
+       }
+
+       gKernelArgs.ignored_physical_memory
+               += initialPhysicalMemory - total_physical_memory();
+
+       // Sort the address ranges.
+       sort_address_ranges(gKernelArgs.physical_memory_range,
+               gKernelArgs.num_physical_memory_ranges);
+       sort_address_ranges(gKernelArgs.physical_allocated_range,
+               gKernelArgs.num_physical_allocated_ranges);
+       sort_address_ranges(gKernelArgs.virtual_allocated_range,
+               gKernelArgs.num_virtual_allocated_ranges);
+
+       // Switch EFI to virtual mode, using the kernel pmap.
+       // Something involving ConvertPointer might need to be done after this?
+       // http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES
+       kRuntimeServices->SetVirtualAddressMap(memory_map_size, descriptor_size,
+               descriptor_version, memory_map);
+
+       // Make sure supervisor threads can fault on read only pages on ARM?
 }
diff --git a/src/system/boot/platform/efi/arch/arm/arch_smp.cpp 
b/src/system/boot/platform/efi/arch/arm/arch_smp.cpp
index 84e25b5..ddb0201 100644
--- a/src/system/boot/platform/efi/arch/arm/arch_smp.cpp
+++ b/src/system/boot/platform/efi/arch/arm/arch_smp.cpp
@@ -16,6 +16,7 @@
 #include <boot/stage2.h>
 #include <boot/menu.h>

+#include "mmu.h"

 //#define TRACE_SMP
 #ifdef TRACE_SMP
@@ -36,9 +37,30 @@
 void
 arch_smp_init_other_cpus(void)
 {
-       // One cpu for now.
+       // XXX: One cpu for now.
        gKernelArgs.num_cpus = 1;
-       return;
+
+       if (get_safemode_boolean(B_SAFEMODE_DISABLE_SMP, false)) {
+               // SMP has been disabled!
+               TRACE(("smp disabled per safemode setting\n"));
+               gKernelArgs.num_cpus = 1;
+       }
+
+       TRACE(("smp: found %" B_PRId32 " cpu%s\n", gKernelArgs.num_cpus,
+               gKernelArgs.num_cpus != 1 ? "s" : ""));
+
+       for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) {
+               // create a final stack the trampoline code will put the ap 
processor on
+               void* stack = NULL;
+               const size_t size = KERNEL_STACK_SIZE + 
KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
+               if (platform_allocate_region(&stack, size, 0, false) != B_OK) {
+                       panic("Unable to allocate AP stack");
+               }
+               memset(stack, 0, size);
+               gKernelArgs.cpu_kstack[i].start = fix_address((uint64_t)stack);
+               gKernelArgs.cpu_kstack[i].size = size;
+       }
+
 }


diff --git a/src/system/boot/platform/efi/arch/arm/arch_start.cpp 
b/src/system/boot/platform/efi/arch/arm/arch_start.cpp
index 17a2504..1484094 100644
--- a/src/system/boot/platform/efi/arch/arm/arch_start.cpp
+++ b/src/system/boot/platform/efi/arch/arm/arch_start.cpp
@@ -11,9 +11,20 @@
 #include "efi_platform.h"


+// From entry.S
 extern "C" void arch_enter_kernel(struct kernel_args *kernelArgs,
        addr_t kernelEntry, addr_t kernelStackTop);

+// From arch_mmu.cpp
+extern void arch_mmu_post_efi_setup(size_t memory_map_size,
+       efi_memory_descriptor *memory_map, size_t descriptor_size,
+       uint32_t descriptor_version);
+
+extern uint64_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
+       efi_memory_descriptor *memory_map, size_t descriptor_size,
+       uint32_t descriptor_version);
+
+
 void
 arch_start_kernel(addr_t kernelEntry)
 {
@@ -86,8 +97,13 @@
        }

        // Update EFI, generate final kernel physical memory map, etc.
-       //arch_mmu_post_efi_setup(memory_map_size, memory_map,
-       //              descriptor_size, descriptor_version);
+       arch_mmu_post_efi_setup(memory_map_size, memory_map,
+               descriptor_size, descriptor_version);
+
+       // Generate page tables for use after ExitBootServices.
+       uint64_t final_pml4 = arch_mmu_generate_post_efi_page_tables(
+               memory_map_size, memory_map, descriptor_size, 
descriptor_version);
+       dprintf("Final PML4 at %#lx\n", final_pml4);

        //smp_boot_other_cpus(final_pml4, kernelEntry);

diff --git a/src/system/kernel/arch/arm/arch_debug_console.cpp 
b/src/system/kernel/arch/arm/arch_debug_console.cpp
index bd5c39e..f5803c1 100644
--- a/src/system/kernel/arch/arm/arch_debug_console.cpp
+++ b/src/system/kernel/arch/arm/arch_debug_console.cpp
@@ -114,6 +114,7 @@
                return B_ERROR;

        gArchDebugUART->InitEarly();
+       gArchDebugUART->InitPort(115200);

        return B_OK;
 }

--
To view, visit https://review.haiku-os.org/c/haiku/+/3542
To unsubscribe, or for help writing mail filters, visit 
https://review.haiku-os.org/settings

Gerrit-Project: haiku
Gerrit-Branch: master
Gerrit-Change-Id: I88bf416ee9f1dafd5da3e4a3876a4d3f7e38b88c
Gerrit-Change-Number: 3542
Gerrit-PatchSet: 1
Gerrit-Owner: Alex von Gluck IV <kallisti5@xxxxxxxxxxx>
Gerrit-MessageType: newchange

Other related posts:

  • » [haiku-commits] Change in haiku[master]: efi/arm: WIP mmu for kernel handoff and stuff. - Gerrit