1/*
2 * Copyright 2014-2022 Haiku, Inc. All rights reserved.
3 * Copyright 2013-2014, Fredrik Holmqvist, fredrik.holmqvist@gmail.com.
4 * Copyright 2014, Henry Harrington, henry.harrington@gmail.com.
5 * All rights reserved.
6 * Distributed under the terms of the MIT License.
7 *
8 * Authors:
9 * 	Alexander von Gluck IV <kallisti5@unixzen.com>
10 */
11
12
13#include <boot/platform.h>
14#include <boot/stage2.h>
15#include <boot/stdio.h>
16
17#include "mmu.h"
18#include "serial.h"
19#include "smp.h"
20#include "efi_platform.h"
21
22
23// From entry.S
24extern "C" void arch_enter_kernel(uint64 pml4, uint64 entry_point,
25	uint64 stackTop);
26
27// From arch_mmu.cpp
28extern void arch_mmu_post_efi_setup(size_t memory_map_size,
29    efi_memory_descriptor *memory_map, size_t descriptor_size,
30    uint32_t descriptor_version);
31
32extern uint64_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
33    efi_memory_descriptor *memory_map, size_t descriptor_size,
34    uint32_t descriptor_version);
35
36
37void
38arch_convert_kernel_args(void)
39{
40	fix_address(gKernelArgs.ucode_data);
41	fix_address(gKernelArgs.arch_args.apic);
42	fix_address(gKernelArgs.arch_args.hpet);
43}
44
45
46static const char*
47memory_region_type_str(int type)
48{
49	switch (type)	{
50		case EfiReservedMemoryType:
51			return "EfiReservedMemoryType";
52		case EfiLoaderCode:
53			return "EfiLoaderCode";
54		case EfiLoaderData:
55			return "EfiLoaderData";
56		case EfiBootServicesCode:
57			return "EfiBootServicesCode";
58		case EfiBootServicesData:
59			return "EfiBootServicesData";
60		case EfiRuntimeServicesCode:
61			return "EfiRuntimeServicesCode";
62		case EfiRuntimeServicesData:
63			return "EfiRuntimeServicesData";
64		case EfiConventionalMemory:
65			return "EfiConventionalMemory";
66		case EfiUnusableMemory:
67			return "EfiUnusableMemory";
68		case EfiACPIReclaimMemory:
69			return "EfiACPIReclaimMemory";
70		case EfiACPIMemoryNVS:
71			return "EfiACPIMemoryNVS";
72		case EfiMemoryMappedIO:
73			return "EfiMemoryMappedIO";
74		case EfiMemoryMappedIOPortSpace:
75			return "EfiMemoryMappedIOPortSpace";
76		case EfiPalCode:
77			return "EfiPalCode";
78		case EfiPersistentMemory:
79			return "EfiPersistentMemory";
80		default:
81			return "unknown";
82	}
83}
84
85
86void
87arch_start_kernel(addr_t kernelEntry)
88{
89	// Prepare to exit EFI boot services.
90	// Read the memory map.
91	// First call is to determine the buffer size.
92	size_t memory_map_size = 0;
93	efi_memory_descriptor dummy;
94	size_t map_key;
95	size_t descriptor_size;
96	uint32_t descriptor_version;
97	if (kBootServices->GetMemoryMap(&memory_map_size, &dummy, &map_key,
98		&descriptor_size, &descriptor_version) != EFI_BUFFER_TOO_SMALL) {
99		panic("Unable to determine size of system memory map");
100	}
101
102	// Allocate a buffer twice as large as needed just in case it gets bigger
103	// between calls to ExitBootServices.
104	size_t actual_memory_map_size = memory_map_size * 2;
105	efi_memory_descriptor *memory_map
106		= (efi_memory_descriptor *)kernel_args_malloc(actual_memory_map_size);
107
108	if (memory_map == NULL)
109		panic("Unable to allocate memory map.");
110
111	// Read (and print) the memory map.
112	memory_map_size = actual_memory_map_size;
113	if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key,
114		&descriptor_size, &descriptor_version) != EFI_SUCCESS) {
115		panic("Unable to fetch system memory map.");
116	}
117
118	addr_t addr = (addr_t)memory_map;
119	dprintf("System provided memory map:\n");
120	for (size_t i = 0; i < memory_map_size / descriptor_size; i++) {
121		efi_memory_descriptor *entry
122			= (efi_memory_descriptor *)(addr + i * descriptor_size);
123		dprintf("  phys: 0x%08lx-0x%08lx, virt: 0x%08lx-0x%08lx, type: %s (%#x), attr: %#lx\n",
124			entry->PhysicalStart,
125			entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE,
126			entry->VirtualStart,
127			entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE,
128			memory_region_type_str(entry->Type), entry->Type,
129			entry->Attribute);
130	}
131
132	// Generate page tables for use after ExitBootServices.
133	uint64_t final_pml4 = arch_mmu_generate_post_efi_page_tables(
134		memory_map_size, memory_map, descriptor_size, descriptor_version);
135
136	// Attempt to fetch the memory map and exit boot services.
137	// This needs to be done in a loop, as ExitBootServices can change the
138	// memory map.
139	// Even better: Only GetMemoryMap and ExitBootServices can be called after
140	// the first call to ExitBootServices, as the firmware is permitted to
141	// partially exit. This is why twice as much space was allocated for the
142	// memory map, as it's impossible to allocate more now.
143	// A changing memory map shouldn't affect the generated page tables, as
144	// they only needed to know about the maximum address, not any specific
145	// entry.
146
147	dprintf("Calling ExitBootServices. So long, EFI!\n");
148
149	// Re-init and activate serial in a horrific post-EFI landscape. Clowns roam the land freely.
150	serial_init();
151	serial_disable();
152
153	while (true) {
154		if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
155			// Disconnect from EFI serial_io / stdio services
156			serial_kernel_handoff();
157			dprintf("Unhooked from EFI serial services\n");
158			break;
159		}
160
161		memory_map_size = actual_memory_map_size;
162		if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key,
163				&descriptor_size, &descriptor_version) != EFI_SUCCESS) {
164			panic("Unable to fetch system memory map.");
165		}
166	}
167
168	// Update EFI, generate final kernel physical memory map, etc.
169	arch_mmu_post_efi_setup(memory_map_size, memory_map,
170		descriptor_size, descriptor_version);
171
172	// Restart serial. gUART only until we get into the kernel
173	serial_enable();
174
175	smp_boot_other_cpus(final_pml4, kernelEntry, (addr_t)&gKernelArgs);
176
177	// Enter the kernel!
178	dprintf("arch_enter_kernel(final_pml4: 0x%08" PRIx64 ", kernelArgs: %p, "
179		"kernelEntry: 0x%08" B_PRIxADDR ", sp: 0x%08" B_PRIx64 ")\n",
180		final_pml4, &gKernelArgs, kernelEntry,
181		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
182
183	arch_enter_kernel(final_pml4, kernelEntry,
184		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
185}
186