193 lines
6.9 KiB
C
193 lines
6.9 KiB
C
// SPDX-License-Identifier: ISC
|
|
|
|
#include <zinnia/compiler.h>
|
|
#include <string.h>
|
|
#include "limine.h"
|
|
#include "loader.h"
|
|
|
|
[[__used, __section(".limine_requests_start")]]
|
|
static volatile uint64_t limine_start[] = LIMINE_REQUESTS_START_MARKER;
|
|
[[__used, __section(".limine_requests")]]
|
|
static volatile uint64_t limine_revision[] = LIMINE_BASE_REVISION(6);
|
|
[[__used, __section(".limine_requests_end")]]
|
|
static volatile uint64_t limine_end[] = LIMINE_REQUESTS_END_MARKER;
|
|
|
|
#define LIMINE_REQUEST(request, tag, rev) \
|
|
[[__used, __section(".limine_requests")]] \
|
|
static volatile struct limine_##request request = { \
|
|
.id = tag, \
|
|
.revision = rev, \
|
|
.response = nullptr, \
|
|
}
|
|
|
|
LIMINE_REQUEST(bootloader_info_request, LIMINE_BOOTLOADER_INFO_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(executable_address_request, LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(executable_file_request, LIMINE_EXECUTABLE_FILE_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST_ID, 1);
|
|
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(dtb_request, LIMINE_DTB_REQUEST_ID, 0);
|
|
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST_ID, 0);
|
|
|
|
static uint64_t hhdm_offset;
|
|
static const struct limine_memmap_entry* pool;
|
|
static uint64_t pool_static_end;
|
|
static uint64_t pool_reclaimable_start;
|
|
|
|
void* loader_ptr(uint64_t phys) {
|
|
return (void*)(phys + hhdm_offset);
|
|
}
|
|
|
|
static uint64_t to_phys(const void* ptr) {
|
|
const uint64_t addr = (uint64_t)ptr;
|
|
return addr >= hhdm_offset ? addr - hhdm_offset : addr;
|
|
}
|
|
|
|
bool loader_alloc(enum phys_mem_usage usage, size_t size, uint64_t* out) {
|
|
size = ALIGN_UP(size, PAGE_SIZE);
|
|
if (pool_reclaimable_start - pool_static_end < size)
|
|
return false;
|
|
|
|
if (usage == PHYS_STATIC) {
|
|
*out = pool_static_end;
|
|
pool_static_end += size;
|
|
} else {
|
|
pool_reclaimable_start -= size;
|
|
*out = pool_reclaimable_start;
|
|
}
|
|
memset(loader_ptr(*out), 0, size);
|
|
return true;
|
|
}
|
|
|
|
static enum phys_mem_usage convert_mem_type(uint64_t type) {
|
|
switch (type) {
|
|
case LIMINE_MEMMAP_USABLE:
|
|
return PHYS_USABLE;
|
|
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
|
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
|
return PHYS_RECLAIMABLE;
|
|
case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES:
|
|
return PHYS_STATIC;
|
|
default:
|
|
return PHYS_RESERVED;
|
|
}
|
|
}
|
|
|
|
static bool init_pool(const struct limine_memmap_response* memmap) {
|
|
for (size_t i = 0; i < memmap->entry_count; i++) {
|
|
const struct limine_memmap_entry* entry = memmap->entries[i];
|
|
if (entry->type == LIMINE_MEMMAP_USABLE && (!pool || entry->length > pool->length))
|
|
pool = entry;
|
|
}
|
|
if (!pool)
|
|
return false;
|
|
|
|
pool_static_end = pool->base;
|
|
pool_reclaimable_start = pool->base + pool->length;
|
|
return true;
|
|
}
|
|
|
|
static uint64_t mem_map_top(const struct limine_memmap_response* memmap) {
|
|
uint64_t top = 0;
|
|
for (size_t i = 0; i < memmap->entry_count; i++) {
|
|
const struct limine_memmap_entry* entry = memmap->entries[i];
|
|
if (convert_mem_type(entry->type) != PHYS_RESERVED)
|
|
top = MAX(top, entry->base + entry->length);
|
|
}
|
|
return top;
|
|
}
|
|
|
|
static size_t convert_mem_map(const struct limine_memmap_response* memmap, struct phys_mem* out) {
|
|
size_t n = 0;
|
|
for (size_t i = 0; i < memmap->entry_count; i++) {
|
|
const struct limine_memmap_entry* entry = memmap->entries[i];
|
|
if (entry != pool) {
|
|
out[n++] = (struct phys_mem){entry->base, entry->length, convert_mem_type(entry->type)};
|
|
continue;
|
|
}
|
|
|
|
const uint64_t end = entry->base + entry->length;
|
|
out[n++] = (struct phys_mem){entry->base, pool_static_end - entry->base, PHYS_STATIC};
|
|
out[n++] = (struct phys_mem){pool_static_end, pool_reclaimable_start - pool_static_end, PHYS_USABLE};
|
|
out[n++] = (struct phys_mem){pool_reclaimable_start, end - pool_reclaimable_start, PHYS_RECLAIMABLE};
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static void find_framebuffer(struct boot_framebuffer* out) {
|
|
const struct limine_framebuffer_response* res = framebuffer_request.response;
|
|
if (!res || !res->framebuffer_count)
|
|
return;
|
|
|
|
const struct limine_framebuffer* fb = res->framebuffers[0];
|
|
out->base = to_phys(fb->address);
|
|
out->width = fb->width;
|
|
out->height = fb->height;
|
|
out->pitch = fb->pitch;
|
|
out->bytes_per_pixel = fb->bpp / 8;
|
|
out->red = (struct boot_color){fb->red_mask_shift, fb->red_mask_size};
|
|
out->green = (struct boot_color){fb->green_mask_shift, fb->green_mask_size};
|
|
out->blue = (struct boot_color){fb->blue_mask_shift, fb->blue_mask_size};
|
|
}
|
|
|
|
static bool load() {
|
|
const struct limine_memmap_response* memmap = memmap_request.response;
|
|
const struct limine_executable_address_response* exec = executable_address_request.response;
|
|
if (!LIMINE_BASE_REVISION_SUPPORTED(limine_revision) || !memmap || !hhdm_request.response || !exec)
|
|
return false;
|
|
hhdm_offset = hhdm_request.response->offset;
|
|
|
|
if (!arch_check_cpu() || !init_pool(memmap))
|
|
return false;
|
|
|
|
const struct limine_bootloader_info_response* info = bootloader_info_request.response;
|
|
struct kernel_image kernel;
|
|
if (!loader_load_kernel(&kernel, info ? info->name : "Limine"))
|
|
return false;
|
|
|
|
uint64_t mem;
|
|
if (!loader_alloc(PHYS_RECLAIMABLE, (memmap->entry_count + 2) * sizeof(struct phys_mem), &mem))
|
|
return false;
|
|
|
|
const struct limine_executable_file_response* file = executable_file_request.response;
|
|
if (file && file->executable_file->string) {
|
|
const char* cmdline = file->executable_file->string;
|
|
memcpy(kernel.info->cmdline, cmdline, MIN(strlen(cmdline), sizeof(kernel.info->cmdline) - 1));
|
|
}
|
|
|
|
const struct limine_module_response* modules = module_request.response;
|
|
for (size_t i = 0; modules && i < modules->module_count; i++) {
|
|
const struct limine_file* module = modules->modules[i];
|
|
if (!loader_add_file(&kernel, module->path, to_phys(module->address), module->size))
|
|
return false;
|
|
}
|
|
|
|
find_framebuffer(&kernel.info->fb);
|
|
if (rsdp_request.response)
|
|
kernel.info->rsdp = to_phys(rsdp_request.response->address);
|
|
if (dtb_request.response && dtb_request.response->dtb_ptr)
|
|
kernel.info->dtb = to_phys(dtb_request.response->dtb_ptr);
|
|
|
|
const uint64_t trampoline = (uint64_t)arch_enter_kernel;
|
|
uint64_t root;
|
|
if (!loader_page_tables(
|
|
&kernel,
|
|
mem_map_top(memmap),
|
|
exec->physical_base + (trampoline - exec->virtual_base),
|
|
&root
|
|
))
|
|
return false;
|
|
|
|
struct phys_mem* mem_map = loader_ptr(mem);
|
|
loader_set_mem_map(&kernel, mem_map, convert_mem_map(memmap, mem_map));
|
|
arch_enter_kernel(root, kernel.entry);
|
|
}
|
|
|
|
[[noreturn]]
|
|
void loader_main() {
|
|
load();
|
|
for (;;)
|
|
asm volatile("");
|
|
}
|