/* Calcite, kernel/src/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 #include #include #include #include #include #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_module_request module_request = { .id = LIMINE_MODULE_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); } static uint8_t *initfs_start; static uint64_t initfs_length; [[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 || module_request.response == 0 || fb_request.response->framebuffer_count == 0 || module_request.response->module_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(); //find the initfs, and die if we don't have one. struct limine_file *initfs = 0; struct limine_module_response *response = module_request.response; for (uint64_t i = 0; i < response->module_count; ++i) if (strequ(response->modules[i]->cmdline, "initfs")) { initfs = response->modules[i]; break; } if (!initfs) 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_base = find_free_kernel_region(fb_length); map_kernel_region( (uint64_t)fb->address - hhdm_request.response->offset, fb_base, fb_length, 1, 0); //store rest of framebuffer information fb_width = fb->width; fb_height = fb->height; fb_pitch = fb->pitch; //remap initfs module and store information. uint64_t initfs_physical_start = (uint64_t)initfs->address - hhdm_request.response->offset; initfs_length = initfs->size; uint64_t initfs_length_rounded = ((initfs_length - 1) / 4096 + 1) * 4096; initfs_start = find_free_kernel_region(initfs_length_rounded); map_kernel_region( initfs_physical_start, initfs_start, initfs_length_rounded, 0, 0); //switch to kernel page tables! switch_to_kernel_page_tables(&with_kernel_page_tables); } [[noreturn]] static void with_kernel_page_tables() { //set initfs set_initfs(initfs_start, initfs_length); //set up interrupts init_ps2(); set_irq_handler(0x01, &on_keyboard_irq); set_irq_handler(0x0c, &on_mouse_irq); enable_interrupts(); //halt repeatedly while (1) __asm__ ("hlt"); }