1817c6ca4SAlexander von Gluck IV /*
220c08874SDavid Karoly * Copyright 2019-2022 Haiku, Inc. All rights reserved.
3817c6ca4SAlexander von Gluck IV * Released under the terms of the MIT License.
4817c6ca4SAlexander von Gluck IV */
5817c6ca4SAlexander von Gluck IV
6817c6ca4SAlexander von Gluck IV
7817c6ca4SAlexander von Gluck IV #include <boot/platform.h>
8817c6ca4SAlexander von Gluck IV #include <boot/stage2.h>
9817c6ca4SAlexander von Gluck IV #include <boot/stdio.h>
10817c6ca4SAlexander von Gluck IV
11817c6ca4SAlexander von Gluck IV #include "efi_platform.h"
12a3c9f71eSDavid Karoly #include "generic_mmu.h"
134561a199SDavid Karoly #include "mmu.h"
14955acf7eSurnenfeld #include "serial.h"
15817c6ca4SAlexander von Gluck IV
16f9412d9fSurnenfeld #include "aarch64.h"
17817c6ca4SAlexander von Gluck IV
18a25542e7Smilek7 extern "C" void arch_enter_kernel(
19a25542e7Smilek7 struct kernel_args* kernelArgs, addr_t kernelEntry, addr_t kernelStackTop);
20817c6ca4SAlexander von Gluck IV
21f9412d9fSurnenfeld extern const char* granule_type_str(int tg);
22f9412d9fSurnenfeld
23f9412d9fSurnenfeld extern uint32_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
24a25542e7Smilek7 efi_memory_descriptor* memory_map, size_t descriptor_size, uint32_t descriptor_version);
25f9412d9fSurnenfeld
26a25542e7Smilek7 extern void arch_mmu_post_efi_setup(size_t memory_map_size, efi_memory_descriptor* memory_map,
27a25542e7Smilek7 size_t descriptor_size, uint32_t descriptor_version);
28f9412d9fSurnenfeld
29955acf7eSurnenfeld extern void arch_mmu_setup_EL1(uint64 tcr);
30f9412d9fSurnenfeld
31f9412d9fSurnenfeld
32817c6ca4SAlexander von Gluck IV void
arch_convert_kernel_args(void)3320c08874SDavid Karoly arch_convert_kernel_args(void)
3420c08874SDavid Karoly {
354561a199SDavid Karoly fix_address(gKernelArgs.arch_args.fdt);
3620c08874SDavid Karoly }
3720c08874SDavid Karoly
3820c08874SDavid Karoly
3920c08874SDavid Karoly void
arch_start_kernel(addr_t kernelEntry)40817c6ca4SAlexander von Gluck IV arch_start_kernel(addr_t kernelEntry)
41817c6ca4SAlexander von Gluck IV {
42817c6ca4SAlexander von Gluck IV // Prepare to exit EFI boot services.
43817c6ca4SAlexander von Gluck IV // Read the memory map.
44817c6ca4SAlexander von Gluck IV // First call is to determine the buffer size.
45817c6ca4SAlexander von Gluck IV size_t memory_map_size = 0;
46817c6ca4SAlexander von Gluck IV efi_memory_descriptor dummy;
47817c6ca4SAlexander von Gluck IV efi_memory_descriptor* memory_map;
48817c6ca4SAlexander von Gluck IV size_t map_key;
49817c6ca4SAlexander von Gluck IV size_t descriptor_size;
50817c6ca4SAlexander von Gluck IV uint32_t descriptor_version;
51a25542e7Smilek7 if (kBootServices->GetMemoryMap(
52a25542e7Smilek7 &memory_map_size, &dummy, &map_key, &descriptor_size, &descriptor_version)
53a25542e7Smilek7 != EFI_BUFFER_TOO_SMALL) {
54817c6ca4SAlexander von Gluck IV panic("Unable to determine size of system memory map");
55817c6ca4SAlexander von Gluck IV }
56817c6ca4SAlexander von Gluck IV
57817c6ca4SAlexander von Gluck IV // Allocate a buffer twice as large as needed just in case it gets bigger
58817c6ca4SAlexander von Gluck IV // between calls to ExitBootServices.
59817c6ca4SAlexander von Gluck IV size_t actual_memory_map_size = memory_map_size * 2;
60a25542e7Smilek7 memory_map = (efi_memory_descriptor*) kernel_args_malloc(actual_memory_map_size);
61817c6ca4SAlexander von Gluck IV
62817c6ca4SAlexander von Gluck IV if (memory_map == NULL)
63817c6ca4SAlexander von Gluck IV panic("Unable to allocate memory map.");
64817c6ca4SAlexander von Gluck IV
65817c6ca4SAlexander von Gluck IV // Read (and print) the memory map.
66817c6ca4SAlexander von Gluck IV memory_map_size = actual_memory_map_size;
67a25542e7Smilek7 if (kBootServices->GetMemoryMap(
68a25542e7Smilek7 &memory_map_size, memory_map, &map_key, &descriptor_size, &descriptor_version)
69a25542e7Smilek7 != EFI_SUCCESS) {
70817c6ca4SAlexander von Gluck IV panic("Unable to fetch system memory map.");
71817c6ca4SAlexander von Gluck IV }
72817c6ca4SAlexander von Gluck IV
73817c6ca4SAlexander von Gluck IV addr_t addr = (addr_t) memory_map;
74f9412d9fSurnenfeld efi_physical_addr loaderCode = 0LL;
75817c6ca4SAlexander von Gluck IV dprintf("System provided memory map:\n");
76817c6ca4SAlexander von Gluck IV for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
77a25542e7Smilek7 efi_memory_descriptor* entry = (efi_memory_descriptor*) (addr + i * descriptor_size);
78a25542e7Smilek7 dprintf(" phys: 0x%0lx-0x%0lx, virt: 0x%0lx-0x%0lx, size = 0x%0lx, type: %s (%#x), attr: "
79a25542e7Smilek7 "%#lx\n",
80a25542e7Smilek7 entry->PhysicalStart, entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE,
81a25542e7Smilek7 entry->VirtualStart, entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE,
82a25542e7Smilek7 entry->NumberOfPages * B_PAGE_SIZE, memory_region_type_str(entry->Type), entry->Type,
83f9412d9fSurnenfeld entry->Attribute);
84f9412d9fSurnenfeld if (entry->Type == EfiLoaderCode)
85f9412d9fSurnenfeld loaderCode = entry->PhysicalStart;
86817c6ca4SAlexander von Gluck IV }
87f9412d9fSurnenfeld // This is where our efi loader got relocated, therefore we need to use this
88f9412d9fSurnenfeld // offset for properly align symbols
89f9412d9fSurnenfeld dprintf("Efi loader symbols offset: 0x%0lx:\n", loaderCode);
90f9412d9fSurnenfeld
91f9412d9fSurnenfeld /*
92f9412d9fSurnenfeld * "The AArch64 exception model is made up of a number of exception levels
93f9412d9fSurnenfeld * (EL0 - EL3), with EL0 and EL1 having a secure and a non-secure
94f9412d9fSurnenfeld * counterpart. EL2 is the hypervisor level and exists only in non-secure
95f9412d9fSurnenfeld * mode. EL3 is the highest priority level and exists only in secure mode."
96f9412d9fSurnenfeld *
97f9412d9fSurnenfeld * "2.3 UEFI System Environment and Configuration
98f9412d9fSurnenfeld * The resident UEFI boot-time environment shall use the highest non-secure
99f9412d9fSurnenfeld * privilege level available. The exact meaning of this is architecture
100f9412d9fSurnenfeld * dependent, as detailed below."
101f9412d9fSurnenfeld
102f9412d9fSurnenfeld * "2.3.1 AArch64 Exception Levels
103f9412d9fSurnenfeld * On AArch64 UEFI shall execute as 64-bit code at either EL1 or EL2,
104f9412d9fSurnenfeld * depending on whether or not virtualization is available at OS load time."
105f9412d9fSurnenfeld */
106955acf7eSurnenfeld uint64 el = arch_exception_level();
107955acf7eSurnenfeld dprintf("Current Exception Level EL%1lx\n", el);
108c1c3f981Surnenfeld dprintf("TTBR0: %" B_PRIx64 " TTBRx: %" B_PRIx64 " SCTLR: %" B_PRIx64 " TCR: %" B_PRIx64 "\n",
109a25542e7Smilek7 arch_mmu_base_register(), arch_mmu_base_register(true), _arch_mmu_get_sctlr(),
110c1c3f981Surnenfeld _arch_mmu_get_tcr());
111c1c3f981Surnenfeld
112c1c3f981Surnenfeld if (arch_mmu_enabled()) {
113a25542e7Smilek7 dprintf("MMU Enabled, Granularity %s, bits %d\n", granule_type_str(arch_mmu_user_granule()),
114f9412d9fSurnenfeld arch_mmu_user_address_bits());
115f9412d9fSurnenfeld
116a25542e7Smilek7 dprintf("Kernel entry accessibility W: %x R: %x\n", arch_mmu_write_access(kernelEntry),
117f9412d9fSurnenfeld arch_mmu_read_access(kernelEntry));
118c1c3f981Surnenfeld }
119c1c3f981Surnenfeld
120955acf7eSurnenfeld // Generate page tables for use after ExitBootServices.
121955acf7eSurnenfeld arch_mmu_generate_post_efi_page_tables(
122955acf7eSurnenfeld memory_map_size, memory_map, descriptor_size, descriptor_version);
123817c6ca4SAlexander von Gluck IV
124817c6ca4SAlexander von Gluck IV // Attempt to fetch the memory map and exit boot services.
125817c6ca4SAlexander von Gluck IV // This needs to be done in a loop, as ExitBootServices can change the
126817c6ca4SAlexander von Gluck IV // memory map.
127817c6ca4SAlexander von Gluck IV // Even better: Only GetMemoryMap and ExitBootServices can be called after
128817c6ca4SAlexander von Gluck IV // the first call to ExitBootServices, as the firmware is permitted to
129817c6ca4SAlexander von Gluck IV // partially exit. This is why twice as much space was allocated for the
130817c6ca4SAlexander von Gluck IV // memory map, as it's impossible to allocate more now.
131817c6ca4SAlexander von Gluck IV // A changing memory map shouldn't affect the generated page tables, as
132817c6ca4SAlexander von Gluck IV // they only needed to know about the maximum address, not any specific
133817c6ca4SAlexander von Gluck IV // entry.
13481a892edSAlexander von Gluck IV
135817c6ca4SAlexander von Gluck IV dprintf("Calling ExitBootServices. So long, EFI!\n");
13681a892edSAlexander von Gluck IV serial_disable();
137*b965b55dSAlexander von Gluck IV
138817c6ca4SAlexander von Gluck IV while (true) {
139817c6ca4SAlexander von Gluck IV if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
14081a892edSAlexander von Gluck IV // Disconnect from EFI serial_io / stdio services
14181a892edSAlexander von Gluck IV serial_kernel_handoff();
14281a892edSAlexander von Gluck IV dprintf("Unhooked from EFI serial services\n");
143817c6ca4SAlexander von Gluck IV break;
144817c6ca4SAlexander von Gluck IV }
145817c6ca4SAlexander von Gluck IV
146817c6ca4SAlexander von Gluck IV memory_map_size = actual_memory_map_size;
147a25542e7Smilek7 if (kBootServices->GetMemoryMap(
148a25542e7Smilek7 &memory_map_size, memory_map, &map_key, &descriptor_size, &descriptor_version)
149a25542e7Smilek7 != EFI_SUCCESS) {
150817c6ca4SAlexander von Gluck IV panic("Unable to fetch system memory map.");
151817c6ca4SAlexander von Gluck IV }
152817c6ca4SAlexander von Gluck IV }
153817c6ca4SAlexander von Gluck IV
154817c6ca4SAlexander von Gluck IV // Update EFI, generate final kernel physical memory map, etc.
155a25542e7Smilek7 arch_mmu_post_efi_setup(memory_map_size, memory_map, descriptor_size, descriptor_version);
156*b965b55dSAlexander von Gluck IV
157*b965b55dSAlexander von Gluck IV // Re-init and activate serial in a horrific post-EFI landscape. Clowns roam the land freely.
158*b965b55dSAlexander von Gluck IV serial_init();
15981a892edSAlexander von Gluck IV serial_enable();
160817c6ca4SAlexander von Gluck IV
161955acf7eSurnenfeld switch (el) {
162955acf7eSurnenfeld case 1:
163955acf7eSurnenfeld arch_mmu_setup_EL1(READ_SPECIALREG(TCR_EL1));
164955acf7eSurnenfeld break;
165955acf7eSurnenfeld case 2:
166955acf7eSurnenfeld arch_mmu_setup_EL1(READ_SPECIALREG(TCR_EL2));
167f9412d9fSurnenfeld arch_cache_disable();
168f9412d9fSurnenfeld _arch_transition_EL2_EL1();
169955acf7eSurnenfeld break;
170955acf7eSurnenfeld default:
171955acf7eSurnenfeld panic("Unexpected Exception Level\n");
172955acf7eSurnenfeld break;
173f9412d9fSurnenfeld }
174f9412d9fSurnenfeld
175955acf7eSurnenfeld arch_cache_enable();
176955acf7eSurnenfeld
177fdb45913SDavid Karoly // smp_boot_other_cpus(final_pml4, kernelEntry, (addr_t)&gKernelArgs);
178817c6ca4SAlexander von Gluck IV
179a25542e7Smilek7 if (arch_mmu_read_access(kernelEntry)
180a25542e7Smilek7 && arch_mmu_read_access(gKernelArgs.cpu_kstack[0].start)) {
181817c6ca4SAlexander von Gluck IV // Enter the kernel!
182817c6ca4SAlexander von Gluck IV arch_enter_kernel(&gKernelArgs, kernelEntry,
18384174204Smilek7 gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
184f9412d9fSurnenfeld } else {
185955acf7eSurnenfeld // _arch_exception_panic("Kernel or Stack memory not accessible\n", __LINE__);
186955acf7eSurnenfeld panic("Kernel or Stack memory not accessible\n");
187f9412d9fSurnenfeld }
188817c6ca4SAlexander von Gluck IV }
189