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