xref: /haiku/src/system/boot/platform/efi/arch/arm/arch_start.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2019-2022 Haiku, Inc. All rights reserved.
3  * Released under the terms of the MIT License.
4  */
5 
6 
7 #include <kernel.h>
8 #include <boot/arch/arm/arch_cpu.h>
9 #include <boot/platform.h>
10 #include <boot/stage2.h>
11 #include <boot/stdio.h>
12 
13 #include "efi_platform.h"
14 #include "generic_mmu.h"
15 #include "mmu.h"
16 #include "serial.h"
17 #include "smp.h"
18 
19 //#define TRACE_ARCH_START
20 #ifdef TRACE_ARCH_START
21 #	define TRACE(x...) dprintf(x)
22 #else
23 #	define TRACE(x...) ;
24 #endif
25 
26 
27 #define ALIGN_MEMORY_MAP	4
28 
29 
30 extern "C" void clean_dcache_all(void);
31 extern "C" void invalidate_icache_all(void);
32 
33 extern "C" typedef void (*arch_enter_kernel_t)(uint32_t, addr_t, addr_t, addr_t);
34 
35 
36 // From entry.S
37 extern "C" void arch_enter_kernel(uint32_t ttbr, addr_t kernelArgs,
38 	addr_t kernelEntry, addr_t kernelStackTop);
39 
40 // From arch_mmu.cpp
41 extern void arch_mmu_post_efi_setup(size_t memoryMapSize,
42 	efi_memory_descriptor *memoryMap, size_t descriptorSize,
43 	uint32_t descriptorVersion);
44 
45 extern uint32_t arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize,
46 	efi_memory_descriptor *memoryMap, size_t descriptorSize,
47 	uint32_t descriptorVersion);
48 
49 
50 void
51 arch_convert_kernel_args(void)
52 {
53 	fix_address(gKernelArgs.arch_args.fdt);
54 }
55 
56 
57 static void *
58 allocate_trampoline_page(void)
59 {
60 	void *trampolinePage = NULL;
61 	if (platform_allocate_lomem(&trampolinePage, B_PAGE_SIZE) == B_OK)
62 		return trampolinePage;
63 
64 	trampolinePage = (void *)get_next_virtual_address(B_PAGE_SIZE);
65 	if (platform_allocate_region(&trampolinePage, B_PAGE_SIZE, 0, true) == B_OK)
66 		return trampolinePage;
67 
68 	trampolinePage = NULL;
69 	if (platform_allocate_region(&trampolinePage, B_PAGE_SIZE, 0, false) != B_OK)
70 		return NULL;
71 
72 	if (platform_free_region(trampolinePage, B_PAGE_SIZE) != B_OK)
73 		return NULL;
74 
75 	if (platform_allocate_region(&trampolinePage, B_PAGE_SIZE, 0, true) != B_OK)
76 		return NULL;
77 
78 	ASSERT_ALWAYS((uint32_t)trampolinePage >= 0x88000000);
79 	return trampolinePage;
80 }
81 
82 
83 void
84 arch_start_kernel(addr_t kernelEntry)
85 {
86 	// Allocate virtual memory for kernel args
87 	struct kernel_args *kernelArgs = NULL;
88 	if (platform_allocate_region((void **)&kernelArgs,
89 			sizeof(struct kernel_args), 0, false) != B_OK)
90 		panic("Failed to allocate kernel args.");
91 
92 	addr_t virtKernelArgs;
93 	platform_bootloader_address_to_kernel_address((void*)kernelArgs,
94 		&virtKernelArgs);
95 
96 	// Allocate identity mapped region for entry.S trampoline
97 	void *trampolinePage = allocate_trampoline_page();
98 	if (trampolinePage == NULL)
99 		panic("Failed to allocate trampoline page.");
100 
101 	memcpy(trampolinePage, (void *)arch_enter_kernel, B_PAGE_SIZE);
102 	arch_enter_kernel_t enter_kernel = (arch_enter_kernel_t)trampolinePage;
103 
104 	// Prepare to exit EFI boot services.
105 	// Read the memory map.
106 	// First call is to determine the buffer size.
107 	size_t memoryMapSize = 0;
108 	efi_memory_descriptor dummy;
109 	size_t mapKey;
110 	size_t descriptorSize;
111 	uint32_t descriptorVersion;
112 	if (kBootServices->GetMemoryMap(&memoryMapSize, &dummy, &mapKey,
113 			&descriptorSize, &descriptorVersion) != EFI_BUFFER_TOO_SMALL) {
114 		panic("Unable to determine size of system memory map");
115 	}
116 
117 	// Allocate a buffer twice as large as needed just in case it gets bigger
118 	// between calls to ExitBootServices.
119 	size_t actualMemoryMapSize = memoryMapSize * 2;
120 	efi_memory_descriptor *memoryMap
121 		= (efi_memory_descriptor *)kernel_args_malloc(actualMemoryMapSize +
122 			ALIGN_MEMORY_MAP);
123 
124 	// align memory_map to 4-byte boundary
125 	// otherwise we get alignment exception when calling GetMemoryMap below
126 	memoryMap = (efi_memory_descriptor *)ROUNDUP((uint32_t)memoryMap, ALIGN_MEMORY_MAP);
127 
128 	if (memoryMap == NULL)
129 		panic("Unable to allocate memory map.");
130 
131 	// Read (and print) the memory map.
132 	memoryMapSize = actualMemoryMapSize;
133 	if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey,
134 			&descriptorSize, &descriptorVersion) != EFI_SUCCESS) {
135 		panic("Unable to fetch system memory map.");
136 	}
137 
138 	addr_t addr = (addr_t)memoryMap;
139 	dprintf("System provided memory map:\n");
140 	for (size_t i = 0; i < memoryMapSize / descriptorSize; ++i) {
141 		efi_memory_descriptor *entry
142 			= (efi_memory_descriptor *)(addr + i * descriptorSize);
143 		dprintf("  phys: 0x%08" PRIx64 "-0x%08" PRIx64
144 			", virt: 0x%08" PRIx64 "-0x%08" PRIx64
145 			", type: %s (%#x), attr: %#" PRIx64 "\n",
146 			entry->PhysicalStart,
147 			entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE,
148 			entry->VirtualStart,
149 			entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE,
150 			memory_region_type_str(entry->Type), entry->Type,
151 			entry->Attribute);
152 	}
153 
154 	// Generate page tables for use after ExitBootServices.
155 	uint32_t final_ttbr0 = arch_mmu_generate_post_efi_page_tables(
156 		memoryMapSize, memoryMap, descriptorSize, descriptorVersion);
157 
158 	// Attempt to fetch the memory map and exit boot services.
159 	// This needs to be done in a loop, as ExitBootServices can change the
160 	// memory map.
161 	// Even better: Only GetMemoryMap and ExitBootServices can be called after
162 	// the first call to ExitBootServices, as the firmware is permitted to
163 	// partially exit. This is why twice as much space was allocated for the
164 	// memory map, as it's impossible to allocate more now.
165 	// A changing memory map shouldn't affect the generated page tables, as
166 	// they only needed to know about the maximum address, not any specific
167 	// entry.
168 	dprintf("Calling ExitBootServices. So long, EFI!\n");
169 	while (true) {
170 		if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) {
171 			// If the console was provided by boot services, disable it.
172 			stdout = NULL;
173 			stderr = NULL;
174 			// Also switch to legacy serial output
175 			// (may not work on all systems)
176 			serial_switch_to_legacy();
177 			dprintf("Switched to legacy serial output\n");
178 			break;
179 		}
180 
181 		memoryMapSize = actualMemoryMapSize;
182 		if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey,
183 				&descriptorSize, &descriptorVersion) != EFI_SUCCESS) {
184 			panic("Unable to fetch system memory map.");
185 		}
186 	}
187 
188 	// Update EFI, generate final kernel physical memory map, etc.
189 	arch_mmu_post_efi_setup(memoryMapSize, memoryMap,
190 		descriptorSize, descriptorVersion);
191 
192 	// Copy final kernel args
193 	// This should be the last step before jumping to the kernel
194 	// as there are some fixups happening to kernel_args even in the last minute
195 	memcpy(kernelArgs, &gKernelArgs, sizeof(struct kernel_args));
196 
197 	//smp_boot_other_cpus(final_ttbr0, kernelEntry, (addr_t)&gKernelArgs);
198 
199 	TRACE("CPSR = 0x%08" B_PRIx32 "\n", cpu_read_CPSR());
200 	TRACE("SCTLR = 0x%08" B_PRIx32 "\n", mmu_read_SCTLR());
201 	TRACE("TTBR0 = 0x%08" B_PRIx32 ", TTBR1 = 0x%08" B_PRIx32 ", TTBCR = 0x%08" B_PRIx32 "\n",
202 		mmu_read_TTBR0(), mmu_read_TTBR1(), mmu_read_TTBCR());
203 	TRACE("DACR = 0x%08" B_PRIx32 "\n",
204 		mmu_read_DACR());
205 
206 	clean_dcache_all();
207 	invalidate_icache_all();
208 
209 	// Enter the kernel!
210 	dprintf("enter_kernel(ttbr0: 0x%08x, kernelArgs: 0x%08x, "
211 		"kernelEntry: 0x%08x, sp: 0x%08x)\n",
212 		final_ttbr0, (uint32_t)virtKernelArgs, (uint32_t)kernelEntry,
213 		(uint32_t)(gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size));
214 
215 	enter_kernel(final_ttbr0, virtKernelArgs, kernelEntry,
216 		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
217 }
218