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