1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#define pr_fmt(fmt) "limine: " fmt
4#include "limine.hpp"
5
6#include "mos/device/console.hpp"
7#include "mos/misc/cmdline.hpp"
8#include "mos/misc/setup.hpp"
9#include "mos/mm/mm.hpp"
10#include "mos/syslog/printk.hpp"
11
12#include <algorithm>
13#include <mos/list.hpp>
14#include <mos_stdlib.hpp>
15
16#define limine_request __section(".limine.requests") __used static volatile
17
18MOS_WARNING_PUSH
19MOS_WARNING_DISABLE("-Wextra-semi")
20MOS_WARNING_DISABLE("-Wpedantic")
21__used __section(".limine.markers.requests_start") static volatile LIMINE_REQUESTS_START_MARKER;
22__used __section(".limine.markers.requests_end") static volatile LIMINE_REQUESTS_END_MARKER;
23limine_request LIMINE_BASE_REVISION(2);
24MOS_WARNING_POP
25
26MOS_WARNING_PUSH
27MOS_WARNING_DISABLE("-Wmissing-field-initializers")
28limine_request struct limine_bootloader_info_request bootloader_info = { .id = LIMINE_BOOTLOADER_INFO_REQUEST, .revision = 0 };
29limine_request struct limine_dtb_request dtb = { .id = LIMINE_DTB_REQUEST, .revision = 0 };
30limine_request struct limine_efi_system_table_request efi_system_table = { .id = LIMINE_EFI_SYSTEM_TABLE_REQUEST, .revision = 0 };
31limine_request struct limine_framebuffer_request framebuffer = { .id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0 };
32limine_request struct limine_hhdm_request hhdm = { .id = LIMINE_HHDM_REQUEST, .revision = 0 };
33limine_request struct limine_kernel_address_request kernel_address = { .id = LIMINE_KERNEL_ADDRESS_REQUEST, .revision = 0 };
34limine_request struct limine_kernel_file_request kernel_file = { .id = LIMINE_KERNEL_FILE_REQUEST, .revision = 0 };
35limine_request struct limine_memmap_request memmap = { .id = LIMINE_MEMMAP_REQUEST, .revision = 0 };
36limine_request struct limine_module_request module = { .id = LIMINE_MODULE_REQUEST, .revision = 0 };
37limine_request struct limine_paging_mode_request paging_mode = { .id = LIMINE_PAGING_MODE_REQUEST, .revision = 0, .mode = LIMINE_PAGING_MODE_DEFAULT };
38limine_request struct limine_rsdp_request rsdp = { .id = LIMINE_RSDP_REQUEST, .revision = 0 };
39limine_request struct limine_smp_request smp = { .id = LIMINE_SMP_REQUEST, .revision = 0, .flags = 0 };
40limine_request struct limine_stack_size_request stack_size = { .id = LIMINE_STACK_SIZE_REQUEST, .revision = 0, .stack_size = 16 MB };
41MOS_WARNING_POP
42
43static void add_to_memmap(pfn_t start, size_t npages, bool reserved, u32 type, const char *typestr)
44{
45 if (start + npages < 1 MB / MOS_PAGE_SIZE)
46 type = LIMINE_MEMMAP_RESERVED, reserved = true;
47
48 if (npages == 0)
49 return;
50
51 pmm_region_t *entry = &platform_info->pmm_regions[platform_info->num_pmm_regions++];
52 entry->reserved = reserved;
53 entry->nframes = npages;
54 entry->pfn_start = start;
55 entry->type = type;
56 pr_dinfo2(limine, "%25s: " PFNADDR_RANGE " (%zu pages)", typestr, PFNADDR(entry->pfn_start, entry->pfn_start + entry->nframes), entry->nframes);
57
58 if (entry->type == LIMINE_MEMMAP_USABLE || entry->type == LIMINE_MEMMAP_KERNEL_AND_MODULES || entry->type == LIMINE_MEMMAP_FRAMEBUFFER ||
59 entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE || entry->type == LIMINE_MEMMAP_ACPI_RECLAIMABLE || entry->type == LIMINE_MEMMAP_ACPI_NVS)
60 platform_info->max_pfn = std::max(a: platform_info->max_pfn, b: entry->pfn_start + entry->nframes);
61}
62
63static void ap_entry(struct limine_smp_info *info)
64{
65 u64 processor_id = info->processor_id;
66 pr_dinfo(limine, "AP started: #%llu", processor_id);
67 platform_ap_entry(arg: info->extra_argument);
68}
69
70static void invoke_constructors(void)
71{
72 typedef void (*init_function_t)(void);
73 extern const init_function_t __init_array_start[], __init_array_end;
74
75 for (const init_function_t *func = __init_array_start; func != &__init_array_end; func++)
76 {
77 (*func)();
78 }
79}
80
81extern "C" void limine_entry(void)
82{
83 invoke_constructors();
84 const auto hhdm_response = hhdm.response;
85 if (hhdm_response && hhdm_response->offset)
86 platform_info->direct_map_base = hhdm_response->offset; // early-populate direct_map_base for pa_va
87
88 if (platform_info->boot_console)
89 console_register(con: platform_info->boot_console);
90
91#if MOS_DEBUG_FEATURE(limine)
92 pr_cont("bootloader: %s, version %s", bootloader_info.response ? bootloader_info.response->name : "unknown",
93 bootloader_info.response ? bootloader_info.response->version : "unknown");
94 pr_info2("stack size: %zu KB", stack_size.stack_size / (1 KB));
95#endif
96
97 if (!LIMINE_BASE_REVISION_SUPPORTED)
98 mos_panic("Unsupported Limine base revision");
99
100 if (paging_mode.response == NULL)
101 mos_panic("No paging mode found");
102
103 const auto paging_mode_response = paging_mode.response;
104 if (paging_mode_response->mode != LIMINE_PAGING_MODE_DEFAULT)
105 mos_panic("non-default paging mode not supported");
106
107 if (smp.response == NULL)
108 mos_panic("No SMP info found");
109
110 const auto smp_response = smp.response;
111 for (size_t i = 0; i < smp_response->cpu_count; i++)
112 {
113 struct limine_smp_info *info = smp_response->cpus[i];
114 if (info->processor_id == 0)
115 continue; // skip BSP
116
117 __atomic_store_n(&info->goto_address, &ap_entry, __ATOMIC_SEQ_CST);
118 }
119
120 const auto kernel_file_response = kernel_file.response;
121 if (kernel_file_response == NULL)
122 mos_panic("No kernel file found");
123
124 mos_cmdline_init(bootloader_cmdline: kernel_file_response->kernel_file->cmdline);
125 startup_invoke_early_cmdline_hooks();
126
127 if (hhdm_response == NULL)
128 mos_panic("No HHDM found");
129
130 platform_info->direct_map_base = hhdm_response->offset;
131 pr_dinfo2(limine, "Direct map base: " PTR_FMT, platform_info->direct_map_base);
132
133 if (memmap.response == NULL)
134 mos_panic("No memory map found"); // are we able to panic at this early stage?
135
136 pfn_t last_end_pfn = 0;
137
138 const auto memmap_response = memmap.response;
139 for (size_t i = 0; i < memmap_response->entry_count; i++)
140 {
141 const struct limine_memmap_entry *entry = memmap_response->entries[i];
142
143 const pfn_t start_pfn = entry->base / MOS_PAGE_SIZE;
144 const size_t npages = entry->length / MOS_PAGE_SIZE;
145
146 // there's a gap between the last region and this one
147 // we fake a reserved region to fill the gap
148 if (last_end_pfn != start_pfn)
149 add_to_memmap(start: last_end_pfn, npages: start_pfn - last_end_pfn, reserved: true, LIMINE_MEMMAP_RESERVED, typestr: "<hole>");
150 last_end_pfn = start_pfn + npages;
151
152 const char *typestr = NULL;
153 switch (entry->type)
154 {
155 case LIMINE_MEMMAP_USABLE: typestr = "usable"; break;
156 case LIMINE_MEMMAP_RESERVED: typestr = "reserved"; break;
157 case LIMINE_MEMMAP_ACPI_RECLAIMABLE: typestr = "ACPI reclaimable"; break;
158 case LIMINE_MEMMAP_ACPI_NVS: typestr = "ACPI NVS"; break;
159 case LIMINE_MEMMAP_BAD_MEMORY: typestr = "bad memory"; break;
160 case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: typestr = "bootloader reclaimable"; break;
161 case LIMINE_MEMMAP_KERNEL_AND_MODULES: typestr = "kernel and modules"; break;
162 case LIMINE_MEMMAP_FRAMEBUFFER: typestr = "framebuffer"; break;
163 }
164
165 add_to_memmap(start: start_pfn, npages, reserved: entry->type != LIMINE_MEMMAP_USABLE, type: entry->type, typestr);
166 }
167
168 const auto module_response = module.response;
169 if (module_response == NULL)
170 mos_panic("No modules found");
171
172 if (module_response->module_count != 1)
173 mos_panic("Expected exactly one module, got %zu", module_response->module_count);
174
175 const auto module = module_response->modules[0];
176 pr_dinfo2(limine, "initrd: %s, " PTR_RANGE, module->path, (ptr_t) module->address, (ptr_t) module->address + module->size);
177 platform_info->initrd_pfn = va_pfn(module->address);
178 platform_info->initrd_npages = ALIGN_UP_TO_PAGE(module->size) / MOS_PAGE_SIZE;
179 pr_dinfo2(limine, "initrd at " PFN_FMT ", size %zu pages", platform_info->initrd_pfn, platform_info->initrd_npages);
180
181 const auto kernel_address_response = kernel_address.response;
182 if (kernel_address.response == NULL)
183 mos_panic("No kernel address found");
184
185 platform_info->k_basepfn = kernel_address_response->physical_base / MOS_PAGE_SIZE;
186 platform_info->k_basevaddr = kernel_address_response->virtual_base;
187
188 if (rsdp.response)
189 {
190 platform_info->arch_info.rsdp_addr = (ptr_t) rsdp.response->address;
191 platform_info->arch_info.rsdp_revision = rsdp.response->revision;
192 pr_dinfo2(limine, "RSDP at " PTR_FMT ", revision %u", platform_info->arch_info.rsdp_addr, platform_info->arch_info.rsdp_revision);
193 }
194 else
195 {
196 pr_dinfo2(limine, "No RSDP found from limine");
197 }
198
199 if (dtb.response)
200 {
201#if MOS_PLATFORM_HAS_FDT
202 platform_info->arch_info.fdt = dtb.response->dtb_ptr;
203 pr_dinfo2(limine, "DTB at " PTR_FMT, (ptr_t) platform_info->arch_info.fdt);
204#endif
205 }
206 else
207 {
208 pr_dinfo2(limine, "No DTB found from limine");
209 }
210
211 mos_start_kernel();
212}
213