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