/* Calcite, src/kernel/entry.c * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "framebuffer.h" #include "interrupts.h" #include "scheduler.h" #include "syscalls.h" #include "process.h" #include "utility.h" #include "drives.h" #include "paging.h" #include "serial.h" #include "debug.h" #include "input.h" #include "timer.h" #include "heap.h" #include "pci.h" #include "ps2.h" #include "fs.h" #include #include LIMINE_BASE_REVISION(3) static volatile struct limine_framebuffer_request fb_request = { .id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0, .response = 0 }; static volatile struct limine_hhdm_request hhdm_request = { .id = LIMINE_HHDM_REQUEST, .revision = 0, .response = 0 }; static volatile struct limine_kernel_address_request ka_request = { .id = LIMINE_KERNEL_ADDRESS_REQUEST, .revision = 0, .response = 0 }; static volatile struct limine_memmap_request memmap_request = { .id = LIMINE_MEMMAP_REQUEST, .revision = 0, .response = 0 }; static volatile struct limine_executable_cmdline_request cmdline_request = { .id = LIMINE_EXECUTABLE_CMDLINE_REQUEST, .revision = 0, .response = 0 }; [[noreturn]] static void die() { while (1) __asm__ ("hlt"); } //these are defined in the linker script. they are page-aligned. extern uint8_t __kernel_rx_start; extern uint8_t __kernel_rx_end; extern uint8_t __kernel_ro_start; extern uint8_t __kernel_ro_end; extern uint8_t __kernel_rw_start; extern uint8_t __kernel_rw_end; static void map_kernel_region( uint64_t physical_start, void *virtual_start, uint64_t length, int writable, int executable) { for (uint64_t i = 0; i < length; i += 4096) map_in_kernel_page_table( physical_start + i, (uint8_t *)virtual_start + i, writable, executable); } #define MAX_CMDLINE_LENGTH 1000 static char cmdline_copy[MAX_CMDLINE_LENGTH + 1]; [[noreturn]] static void with_kernel_page_tables(); [[noreturn]] void kernel_entry() { //die if the bootloader hasn't given us something that we need. if (fb_request.response == 0 || hhdm_request.response == 0 || ka_request.response == 0 || memmap_request.response == 0 || fb_request.response->framebuffer_count == 0) die(); struct limine_framebuffer *fb = fb_request.response->framebuffers[0]; if (fb->memory_model != LIMINE_FRAMEBUFFER_RGB || fb->bpp != 32 || fb->red_mask_shift != 16 || fb->red_mask_size != 8 || fb->green_mask_shift != 8 || fb->green_mask_size != 8 || fb->blue_mask_shift != 0 || fb->blue_mask_size != 8) die(); //set up page tables. we will mark the regions with bootloader structures as //usable, so we need to be careful not to allocate any physical pages until //after we are done using bootloader structures. we map the kernel into our //page tables, and we remap the framebuffer to somewhere in kernel memory. //we do not map the bootloader structures, so we also need to not switch //to the new tables until we are done using them. uint64_t kernel_physical_base = ka_request.response->physical_base; uint8_t *kernel_virtual_base = (uint8_t *)ka_request.response->virtual_base; init_paging(kernel_physical_base, kernel_virtual_base); struct limine_memmap_response *mm_response = memmap_request.response; for (uint64_t i = 0; i < mm_response->entry_count; ++i) { struct limine_memmap_entry *entry = mm_response->entries[i]; if (entry->type == LIMINE_MEMMAP_USABLE || entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE) //limine guarantees these two types are page-aligned already. mark_physical_memory_free(entry->base, entry->length); } map_kernel_region( &__kernel_rx_start - kernel_virtual_base + kernel_physical_base, &__kernel_rx_start, &__kernel_rx_end - &__kernel_rx_start, 0, 1); map_kernel_region( &__kernel_ro_start - kernel_virtual_base + kernel_physical_base, &__kernel_ro_start, &__kernel_ro_end - &__kernel_ro_start, 0, 0); map_kernel_region( &__kernel_rw_start - kernel_virtual_base + kernel_physical_base, &__kernel_rw_start, &__kernel_rw_end - &__kernel_rw_start, 1, 0); //we round up to a multiple of a page. uint64_t fb_length = ((fb->height * fb->pitch - 1) / 4096 + 1) * 4096; fb_physical_base = (uint64_t)fb->address - hhdm_request.response->offset; fb_base = find_free_kernel_region(fb_length); map_kernel_region( fb_physical_base, fb_base, fb_length, 1, 0); //store rest of framebuffer information fb_width = fb->width; fb_height = fb->height; fb_pitch = fb->pitch; //make a copy of the kernel cmdline const char *original_cmdline = cmdline_request.response->cmdline; int cmdline_length = 0; while (original_cmdline[cmdline_length] != 0) ++cmdline_length; if (cmdline_length > MAX_CMDLINE_LENGTH) die(); memcpy(cmdline_copy, original_cmdline, cmdline_length + 1); //switch to kernel page tables! switch_to_kernel_page_tables(&with_kernel_page_tables); } struct cmdline_pair { const char *key; const char *value; }; static const struct cmdline_pair *cmdline_pairs; static int cmdline_pair_count; //returns the corresponding value if the key exists, otherwise returns 0. static const char *cmdline_look_up(const char *key) { for (int i = 0; i < cmdline_pair_count; ++i) if (strequ(cmdline_pairs[i].key, key)) return cmdline_pairs[i].value; return 0; } [[noreturn]] static void with_kernel_page_tables() { init_serial(); //store cmdline as key-value pairs if (cmdline_copy[0] == 0) cmdline_pair_count = 0; else { int comma_count = 0; for (int i = 0; cmdline_copy[i] != 0; ++i) if (cmdline_copy[i] == ',') ++comma_count; cmdline_pair_count = comma_count + 1; struct cmdline_pair *pairs = heap_alloc(cmdline_pair_count * sizeof(struct cmdline_pair)); int key_start = 0; for (int i = 0; i < cmdline_pair_count; ++i) { int key_end = key_start; while (1) { if (cmdline_copy[key_end] == '=') break; if (cmdline_copy[key_end] == ',' || cmdline_copy[key_end] == 0) die(); ++key_end; } int value_start = key_end + 1; int value_end = value_start; while (1) { if (cmdline_copy[value_end] == ',' || cmdline_copy[value_end] == 0) break; if (cmdline_copy[value_end] == '=') die(); ++value_end; } char *key = heap_alloc(key_end - key_start + 1); memcpy(key, &cmdline_copy[key_start], key_end - key_start); key[key_end - key_start] = 0; pairs[i].key = key; char *value = heap_alloc(value_end - value_start + 1); memcpy(value, &cmdline_copy[value_start], value_end - value_start); value[value_end - value_start] = 0; pairs[i].value = value; key_start = value_end + 1; } cmdline_pairs = pairs; } debug_log("command line:") for (int i = 0; i < cmdline_pair_count; ++i) debug_log(" %s = %s", cmdline_pairs[i].key, cmdline_pairs[i].value) //set up interrupts init_timer(); init_ps2(); set_irq_handler(0x00, &on_pit_irq); set_irq_handler(0x01, &on_keyboard_irq); set_irq_handler(0x0c, &on_mouse_irq); enable_interrupts(); //set up syscalls init_syscalls(); register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread); register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer); register_syscall(SYSCALL_OPEN_FILE, (void *)&syscall_open_file); register_syscall(SYSCALL_CLOSE_FILE, (void *)&syscall_close_file); register_syscall(SYSCALL_GET_FILE_SIZE, (void *)&syscall_get_file_size); register_syscall(SYSCALL_READ_FILE, (void *)&syscall_read_file); register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet); register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages); register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms); register_syscall(SYSCALL_START_ELF, (void *)&syscall_start_elf); register_syscall(SYSCALL_IPC_CREATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_dgram_receiver); register_syscall(SYSCALL_IPC_CREATE_DGRAM_SENDER, (void *)&syscall_ipc_create_dgram_sender); register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram); register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram); register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread); register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar); //probe pci devices probe_pci(); //load root file system const char *root_drive_name = cmdline_look_up("root-drive"); if (!root_drive_name) panic("root drive mising from cmdline") const struct drive_info *root_drive = look_up_drive(root_drive_name); if (!root_drive) panic("could not find root drive") const char *root_fs_type = cmdline_look_up("root-fs"); if (!root_fs_type) panic("root fs missing from cmdline") struct fs_info *root_fs = heap_alloc(sizeof(struct fs_info)); if (create_fs_info(root_drive, root_fs, root_fs_type) != FAR_SUCCESS) panic("could not create root file system info") set_fs("root", root_fs); //load init and start it init_scheduler(); if (start_elf("root://calcite/apps/init/init.elf") == 0) panic("could not start init.elf") debug_log("about to switch to init") debug_log(" free physical memory %B", count_free_pram()) debug_log(" free kernel virtual memory %B", count_free_kernel_vram()) resume_next_continuation(); }