xref: /haiku/src/system/boot/platform/efi/arch/riscv64/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 <boot/platform.h>
8 #include <boot/stage2.h>
9 #include <boot/stdio.h>
10 
11 #include <arch_cpu_defs.h>
12 
13 #include "mmu.h"
14 #include "serial.h"
15 #include "smp.h"
16 #include "efi_platform.h"
17 #include "arch_traps.h"
18 
19 
20 // From entry.S
21 extern "C" void arch_enter_kernel(uint64 satp, addr_t kernelArgs,
22         addr_t kernelEntry, addr_t kernelStackTop);
23 
24 // From arch_mmu.cpp
25 extern void arch_mmu_post_efi_setup(size_t memory_map_size,
26     efi_memory_descriptor *memory_map, size_t descriptor_size,
27     uint32_t descriptor_version);
28 
29 extern uint64_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
30     efi_memory_descriptor *memory_map, size_t descriptor_size,
31     uint32_t descriptor_version);
32 
33 
34 #include <arch/riscv64/arch_uart_sifive.h>
35 
36 
37 void
38 arch_convert_kernel_args(void)
39 {
40 	fix_address(gKernelArgs.arch_args.fdt);
41 }
42 
43 
44 void
45 arch_start_kernel(addr_t kernelEntry)
46 {
47 	// Allocate virtual memory for kernel args
48 	struct kernel_args *kernelArgs = NULL;
49 	if (platform_allocate_region((void **)&kernelArgs,
50 			sizeof(struct kernel_args), 0, false) != B_OK)
51 		panic("Failed to allocate kernel args.");
52 
53 	addr_t virtKernelArgs;
54 	platform_bootloader_address_to_kernel_address((void*)kernelArgs,
55 		&virtKernelArgs);
56 
57 	// EFI assumed to be SBI booted
58 	gKernelArgs.arch_args.machine_platform = kPlatformSbi;
59 
60 	// Prepare to exit EFI boot services.
61 	// Read the memory map.
62 	// First call is to determine the buffer size.
63 	size_t memory_map_size = 0;
64 	efi_memory_descriptor dummy;
65 	efi_memory_descriptor *memory_map;
66 	size_t map_key;
67 	size_t descriptor_size;
68 	uint32_t descriptor_version;
69 	if (kBootServices->GetMemoryMap(&memory_map_size, &dummy, &map_key,
70 		&descriptor_size, &descriptor_version) != EFI_BUFFER_TOO_SMALL) {
71 		panic("Unable to determine size of system memory map");
72 	}
73 
74 	// Allocate a buffer twice as large as needed just in case it gets bigger
75 	// between calls to ExitBootServices.
76 	size_t actual_memory_map_size = memory_map_size * 2;
77 	memory_map
78 		= (efi_memory_descriptor *)kernel_args_malloc(actual_memory_map_size);
79 
80 	if (memory_map == NULL)
81 		panic("Unable to allocate memory map.");
82 
83 	// Read (and print) the memory map.
84 	memory_map_size = actual_memory_map_size;
85 	if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key,
86 		&descriptor_size, &descriptor_version) != EFI_SUCCESS) {
87 		panic("Unable to fetch system memory map.");
88 	}
89 
90 	addr_t addr = (addr_t)memory_map;
91 	dprintf("System provided memory map:\n");
92 	for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
93 		efi_memory_descriptor *entry
94 			= (efi_memory_descriptor *)(addr + i * descriptor_size);
95 		dprintf("  phys: %#lx, virt: %#lx, size: %#lx, ",
96 			entry->PhysicalStart, entry->VirtualStart,
97 			entry->NumberOfPages * B_PAGE_SIZE);
98 		switch (entry->Type) {
99 		case EfiReservedMemoryType:  dprintf("reservedMemoryType"); break;
100 		case EfiLoaderCode:          dprintf("loaderCode"); break;
101 		case EfiLoaderData:          dprintf("loaderData"); break;
102 		case EfiBootServicesCode:    dprintf("bootServicesCode"); break;
103 		case EfiBootServicesData:    dprintf("bootServicesData"); break;
104 		case EfiConventionalMemory:  dprintf("conventionalMemory"); break;
105 		case EfiACPIReclaimMemory:   dprintf("ACPIReclaimMemory"); break;
106 		case EfiRuntimeServicesCode: dprintf("runtimeServicesCode"); break;
107 		case EfiRuntimeServicesData: dprintf("runtimeServicesData"); break;
108 		default: dprintf("?(%d)", entry->Type);
109 		}
110 		dprintf(", attrs: %#lx\n", entry->Attribute);
111 	}
112 
113 	// Generate page tables for use after ExitBootServices.
114 	uint64_t satp = arch_mmu_generate_post_efi_page_tables(
115 		memory_map_size, memory_map, descriptor_size, descriptor_version);
116 	dprintf("SATP: 0x%016" B_PRIx64 "\n", satp);
117 
118 	// Attempt to fetch the memory map and exit boot services.
119 	// This needs to be done in a loop, as ExitBootServices can change the
120 	// memory map.
121 	// Even better: Only GetMemoryMap and ExitBootServices can be called after
122 	// the first call to ExitBootServices, as the firmware is permitted to
123 	// partially exit. This is why twice as much space was allocated for the
124 	// memory map, as it's impossible to allocate more now.
125 	// A changing memory map shouldn't affect the generated page tables, as
126 	// they only needed to know about the maximum address, not any specific
127 	// entry.
128 	dprintf("Calling ExitBootServices. So long, EFI!\n");
129 	while (true) {
130 		if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
131 			// The console was provided by boot services, disable it.
132 			stdout = NULL;
133 			stderr = NULL;
134 			// Also switch to legacy serial output
135 			// (may not work on all systems)
136 			serial_switch_to_legacy();
137 			dprintf("Switched to legacy serial output\n");
138 			break;
139 		}
140 
141 		memory_map_size = actual_memory_map_size;
142 		if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key,
143 				&descriptor_size, &descriptor_version) != EFI_SUCCESS) {
144 			panic("Unable to fetch system memory map.");
145 		}
146 	}
147 
148 	arch_traps_init();
149 
150 	// Update EFI, generate final kernel physical memory map, etc.
151 	arch_mmu_post_efi_setup(memory_map_size, memory_map,
152 			descriptor_size, descriptor_version);
153 
154 	dprintf("[PRE] SetSatp()\n");
155 	SetSatp(satp);
156 	dprintf("[POST] SetSatp()\n");
157 	FlushTlbAll();
158 	dprintf("[POST] FlushTlbAll()\n");
159 
160 	// Copy final kernel args
161 	// This should be the last step before jumping to the kernel
162 	// as there are some fixups happening to kernel_args even in the last minute
163 	memcpy(kernelArgs, &gKernelArgs, sizeof(struct kernel_args));
164 
165 	smp_boot_other_cpus(satp, kernelEntry, virtKernelArgs);
166 
167 	// Enter the kernel!
168 	dprintf("arch_enter_kernel(satp: %#" B_PRIxADDR ", kernelArgs: %#" B_PRIxADDR
169 		", kernelEntry: %#" B_PRIxADDR ", sp: %#" B_PRIxADDR ")\n",	satp,
170 		(addr_t)&kernelArgs, (addr_t)kernelEntry, kernelArgs->cpu_kstack[0].start
171 			+ kernelArgs->cpu_kstack[0].size);
172 
173 	arch_enter_kernel(satp, virtKernelArgs, kernelEntry,
174 		kernelArgs->cpu_kstack[0].start + kernelArgs->cpu_kstack[0].size);
175 }
176