calcite/kernel/src/entry.c

195 lines
6 KiB
C

/* 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 <https://www.gnu.org/licenses/>.
*/
#include <framebuffer.h>
#include <interrupts.h>
#include <utility.h>
#include <initfs.h>
#include <limine.h>
#include <paging.h>
#include <panic.h>
#include <ps2.h>
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");
}