From 8e0c51ae5e3e0778ed66965b14b074482162aca4 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Fri, 26 Dec 2025 03:52:11 -0500 Subject: [PATCH] ring 3, reorganization --- compile_flags.txt | 3 +- include/calcite/calcite.h | 31 ++ include/kernel-public/syscall-numbers.h | 23 ++ make-build.sh | 83 ++++- {kernel/src => src/kernel}/entry.c | 71 ++-- {kernel/src => src/kernel}/framebuffer.c | 17 +- {kernel/include => src/kernel}/framebuffer.h | 17 +- src/kernel/heap.c | 104 ++++++ src/kernel/heap.h | 23 ++ {kernel/src => src/kernel}/initfs.c | 16 +- {kernel/include => src/kernel}/initfs.h | 2 +- {kernel/src => src/kernel}/interrupts.asm | 14 +- {kernel/src => src/kernel}/interrupts.c | 18 +- {kernel/include => src/kernel}/interrupts.h | 2 +- {kernel => src/kernel}/link.ld | 0 {kernel/src => src/kernel}/paging.asm | 20 +- {kernel/src => src/kernel}/paging.c | 71 +++- {kernel/include => src/kernel}/paging.h | 25 +- {kernel/src => src/kernel}/panic.c | 2 +- {kernel/include => src/kernel}/panic.h | 4 +- src/kernel/process.c | 357 +++++++++++++++++++ src/kernel/process.h | 88 +++++ {kernel/src => src/kernel}/ps2.asm | 14 +- {kernel/src => src/kernel}/ps2.c | 20 +- {kernel/include => src/kernel}/ps2.h | 2 +- src/kernel/scheduler.asm | 57 +++ src/kernel/scheduler.c | 118 ++++++ src/kernel/scheduler.h | 27 ++ src/kernel/syscalls.asm | 71 ++++ src/kernel/syscalls.c | 46 +++ src/kernel/syscalls.h | 27 ++ src/kernel/utility.asm | 33 ++ {kernel/src => src/kernel}/utility.c | 26 +- {kernel/include => src/kernel}/utility.h | 7 +- src/user-apps/hello/hello.c | 35 ++ src/user-apps/hello/libraries.txt | 1 + src/user-libs/calcite/syscalls.asm | 27 ++ src/user-libs/calcite/syscalls.c | 32 ++ 38 files changed, 1406 insertions(+), 128 deletions(-) create mode 100644 include/calcite/calcite.h create mode 100644 include/kernel-public/syscall-numbers.h rename {kernel/src => src/kernel}/entry.c (80%) rename {kernel/src => src/kernel}/framebuffer.c (70%) rename {kernel/include => src/kernel}/framebuffer.h (72%) create mode 100644 src/kernel/heap.c create mode 100644 src/kernel/heap.h rename {kernel/src => src/kernel}/initfs.c (88%) rename {kernel/include => src/kernel}/initfs.h (96%) rename {kernel/src => src/kernel}/interrupts.asm (92%) rename {kernel/src => src/kernel}/interrupts.c (91%) rename {kernel/include => src/kernel}/interrupts.h (95%) rename {kernel => src/kernel}/link.ld (100%) rename {kernel/src => src/kernel}/paging.asm (80%) rename {kernel/src => src/kernel}/paging.c (72%) rename {kernel/include => src/kernel}/paging.h (72%) rename {kernel/src => src/kernel}/panic.c (96%) rename {kernel/include => src/kernel}/panic.h (95%) create mode 100644 src/kernel/process.c create mode 100644 src/kernel/process.h rename {kernel/src => src/kernel}/ps2.asm (85%) rename {kernel/src => src/kernel}/ps2.c (88%) rename {kernel/include => src/kernel}/ps2.h (95%) create mode 100644 src/kernel/scheduler.asm create mode 100644 src/kernel/scheduler.c create mode 100644 src/kernel/scheduler.h create mode 100644 src/kernel/syscalls.asm create mode 100644 src/kernel/syscalls.c create mode 100644 src/kernel/syscalls.h create mode 100644 src/kernel/utility.asm rename {kernel/src => src/kernel}/utility.c (59%) rename {kernel/include => src/kernel}/utility.h (83%) create mode 100644 src/user-apps/hello/hello.c create mode 100644 src/user-apps/hello/libraries.txt create mode 100644 src/user-libs/calcite/syscalls.asm create mode 100644 src/user-libs/calcite/syscalls.c diff --git a/compile_flags.txt b/compile_flags.txt index 18399a5..fce5308 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -1,5 +1,6 @@ -std=c23 +-ffreestanding -I dependencies/limine -I -kernel/include +include diff --git a/include/calcite/calcite.h b/include/calcite/calcite.h new file mode 100644 index 0000000..7f9b4d7 --- /dev/null +++ b/include/calcite/calcite.h @@ -0,0 +1,31 @@ +/* Calcite, include/calcite/calcite.h + * 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 . + */ + +#pragma once + +#include + +[[noreturn]] void end_thread(); + +struct framebuffer_info { + uint8_t *fb_base; + int fb_width; + int fb_height; + int fb_pitch; +}; + +void map_framebuffer(struct framebuffer_info *info_out); diff --git a/include/kernel-public/syscall-numbers.h b/include/kernel-public/syscall-numbers.h new file mode 100644 index 0000000..05f7a6e --- /dev/null +++ b/include/kernel-public/syscall-numbers.h @@ -0,0 +1,23 @@ +/* Calcite, include/kernel-public/syscall-numbers.h + * 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 . + */ + +#pragma once + +enum { + SYSCALL_END_THREAD, + SYSCALL_MAP_FRAMEBUFFER +}; diff --git a/make-build.sh b/make-build.sh index 4bffb5f..17a1e28 100644 --- a/make-build.sh +++ b/make-build.sh @@ -1,8 +1,10 @@ #!/bin/sh COMMON_CC_EXTRA_FLAGS="-O3 -Wall -Wextra" -COMMON_CC_FLAGS="-std=c23 ${COMMON_CC_EXTRA_FLAGS}" -KERNEL_CC_FLAGS="-I dependencies/limine -I kernel/include -mno-sse -ffreestanding ${COMMON_CC_FLAGS}" +COMMON_CC_FLAGS="-std=c23 -ffreestanding -I include ${COMMON_CC_EXTRA_FLAGS}" +KERNEL_CC_FLAGS="-mno-sse -I dependencies/limine ${COMMON_CC_FLAGS}" +#in the future user code will be allowed to use sse +USER_CC_FLAGS="-mno-sse ${COMMON_CC_FLAGS}" if [ -e build.ninja ]; then echo build.ninja already exists. @@ -18,34 +20,81 @@ echo "rule kernel_cc" >> build.ninja echo " depfile = \$out.d" >> build.ninja echo " command = cc -c -MD -MF \$out.d ${KERNEL_CC_FLAGS} \$in -o \$out" >> build.ninja +echo "rule user_cc" >> build.ninja +echo " depfile = \$out.d" >> build.ninja +echo " command = cc -c -MD -MF \$out.d ${USER_CC_FLAGS} \$in -o \$out" >> build.ninja + echo "rule kernel_ld" >> build.ninja -echo " command = ld -T kernel/link.ld \$in -o \$out" >> build.ninja +echo " command = ld -T src/kernel/link.ld \$in -o \$out" >> build.ninja -ALL_KERNEL_OBJECTS="" +echo "rule user_lib_ld" >> build.ninja +echo " command = ld -r \$in -o \$out" >> build.ninja -for f in kernel/src/*.asm; do - f=$(echo $f | sed -e 's/^kernel\/src\///') - echo "build build/kernel/$f.o: nasm kernel/src/$f" >> build.ninja - ALL_KERNEL_OBJECTS="${ALL_KERNEL_OBJECTS} build/kernel/$f.o" +#eventually maybe a libc will be linked in +echo "rule user_app_ld" >> build.ninja +echo " command = ld \$in -o \$out" >> build.ninja + +#builds everything in a directory +# $1 - source directory +# $2 - cc rule +# $3 - ld rule +# $4 - linked object name +# $5 - extra objects to link in +build_all() { + + sources="$1/*.asm $1/*.c" + objects="$5" + + for src in $sources; do + if echo $src | grep -q -v '*'; then + build=$(echo $src | sed -e 's/^src/build/').o + if echo $src | grep -q '.c$'; then + echo "build $build: $2 $src" >> build.ninja + else + echo "build $build: nasm $src" >> build.ninja + fi + objects="$objects $build" + fi + done + + build_dir=$(echo $1 | sed -e 's/^src/build/') + echo "build $build_dir/$4: $3 $objects" >> build.ninja + +} + +build_all src/kernel kernel_cc kernel_ld kernel.elf + +for dir in src/user-libs/*; do + lib_name=$(echo $dir | sed -e 's/^src\/user-libs\///') + build_all $dir user_cc user_lib_ld lib$lib_name.o done -for f in kernel/src/*.c; do - f=$(echo $f | sed -e 's/^kernel\/src\///') - echo "build build/kernel/$f.o: kernel_cc kernel/src/$f" >> build.ninja - ALL_KERNEL_OBJECTS="${ALL_KERNEL_OBJECTS} build/kernel/$f.o" -done +apps="" -echo "build build/kernel/kernel.elf: kernel_ld ${ALL_KERNEL_OBJECTS}" >> build.ninja +for dir in src/user-apps/*; do + + if [ -e $dir/libraries.txt ]; then + lib_paths=$(cat $dir/libraries.txt | sed -e 's/^.*$/build\/user-libs\/\0\/lib\0.o/') + else + lib_paths="" + fi + + app_name=$(echo $dir | sed -e 's/^src\/user-apps\///') + build_all $dir user_cc user_app_ld $app_name.elf $lib_paths + build_dir=$(echo $dir | sed -e 's/^src/build/') + apps="$apps $build_dir/$app_name.elf" + +done echo "rule initfs" >> build.ninja echo " command =" \ "rm -rf build/initfs &&" \ - "mkdir -p build/initfs/resx &&" \ - "echo Hello! > build/initfs/resx/hello.txt &&" \ + "mkdir -p build/initfs/apps &&" \ + "cp build/user-apps/hello/hello.elf build/initfs/apps/ &&" \ "cd build/initfs &&" \ "tar cf ../initfs.tar *" >> build.ninja -echo "build build/initfs.tar: initfs" >> build.ninja +echo "build build/initfs.tar: initfs $apps" >> build.ninja echo "rule disk" >> build.ninja echo " command =" \ diff --git a/kernel/src/entry.c b/src/kernel/entry.c similarity index 80% rename from kernel/src/entry.c rename to src/kernel/entry.c index 952dba9..51fd941 100644 --- a/kernel/src/entry.c +++ b/src/kernel/entry.c @@ -1,28 +1,34 @@ -/* Calcite, kernel/src/entry.c +/* 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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ -#include -#include -#include -#include +#include "framebuffer.h" +#include "interrupts.h" +#include "kernel-public/syscall-numbers.h" +#include "scheduler.h" +#include "syscalls.h" +#include "process.h" +#include "utility.h" +#include "initfs.h" +#include "paging.h" +#include "panic.h" +#include "heap.h" +#include "ps2.h" + #include -#include -#include -#include LIMINE_BASE_REVISION(3) @@ -145,10 +151,9 @@ static uint64_t initfs_length; //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( - (uint64_t)fb->address - hhdm_request.response->offset, - fb_base, fb_length, 1, 0); + map_kernel_region(fb_physical_base, fb_base, fb_length, 1, 0); //store rest of framebuffer information @@ -187,9 +192,35 @@ static uint64_t initfs_length; set_irq_handler(0x0c, &on_mouse_irq); enable_interrupts(); - //halt repeatedly + //set up syscalls - while (1) - __asm__ ("hlt"); + init_syscalls(); + register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread); + register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer); + + //load hello and start it + + init_scheduler(); + + const uint8_t *hello_start; + uint64_t hello_length; + look_up_initfs_file("apps/hello.elf", &hello_start, &hello_length); + + struct process *hello = heap_alloc(sizeof(struct process)); + uint64_t hello_entry; + create_process(hello); + load_elf(hello, &hello_entry, hello_start, hello_length); + + struct thread *hello_thread = heap_alloc(sizeof(struct thread)); + create_thread(hello, hello_thread); + + create_user_task( + hello->p4_physical_base, + hello_entry, + (uint64_t)hello_thread->stack_top); + + running_thread = hello_thread; + + resume_next_continuation(); } diff --git a/kernel/src/framebuffer.c b/src/kernel/framebuffer.c similarity index 70% rename from kernel/src/framebuffer.c rename to src/kernel/framebuffer.c index e261248..a9e545d 100644 --- a/kernel/src/framebuffer.c +++ b/src/kernel/framebuffer.c @@ -1,22 +1,23 @@ -/* Calcite, kernel/src/framebuffer.c +/* Calcite, src/kernel/framebuffer.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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ -#include +#include "framebuffer.h" +uint64_t fb_physical_base; uint8_t *fb_base; int fb_width; int fb_height; diff --git a/kernel/include/framebuffer.h b/src/kernel/framebuffer.h similarity index 72% rename from kernel/include/framebuffer.h rename to src/kernel/framebuffer.h index dc26553..71bdb57 100644 --- a/kernel/include/framebuffer.h +++ b/src/kernel/framebuffer.h @@ -1,22 +1,25 @@ -/* Calcite, kernel/include/framebuffer.h +/* Calcite, src/kernel/framebuffer.h * 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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ +#pragma once + #include +extern uint64_t fb_physical_base; extern uint8_t *fb_base; extern int fb_width; extern int fb_height; diff --git a/src/kernel/heap.c b/src/kernel/heap.c new file mode 100644 index 0000000..4a9c1f9 --- /dev/null +++ b/src/kernel/heap.c @@ -0,0 +1,104 @@ +/* Calcite, src/kernel/heap.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 "paging.h" +#include "heap.h" + +struct dealloc_record { + //0 for unused + uint64_t length; + void *start; +}; + +struct dealloc_record_page { + struct dealloc_record records[255]; + struct dealloc_record_page *prev_page; +}; + +static struct dealloc_record_page *last_page; + +static void *map_pages(uint64_t page_count) { + + void *vma = find_free_kernel_region(page_count * 4096); + + for (uint64_t i = 0; i < page_count; ++i) { + uint64_t pma = take_free_physical_page(); + map_in_kernel_page_table(pma, vma + i * 4096, 1, 0); + } + + return vma; + +} + +static struct dealloc_record *get_unused_record() { + + for (struct dealloc_record_page *page = last_page; + page != 0; page = page->prev_page) + for (int i = 0; i < 255; ++i) + if (page->records[i].length == 0) + return &page->records[i]; + + struct dealloc_record_page *page = map_pages(1); + page->prev_page = last_page; + last_page = page; + + page->records[0].start = (void *)page + 4096 - 8; + page->records[0].length = 8; + + for (int i = 2; i < 255; ++i) + page->records[i].length = 0; + + return &page->records[1]; + +} + +void *heap_alloc(uint64_t length) { + + for (struct dealloc_record_page *page = last_page; + page != 0; page = page->prev_page) + for (int i = 0; i < 255; ++i) + if (page->records[i].length == length) { + page->records[i].length = 0; + return page->records[i].start; + } + + for (struct dealloc_record_page *page = last_page; + page != 0; page = page->prev_page) + for (int i = 0; i < 255; ++i) + if (page->records[i].length > length) { + page->records[i].length -= length; + return page->records[i].start + page->records[i].length; + } + + uint64_t pages = (length - 1) / 4096 + 1; + void *vma = map_pages(pages); + + if (pages * 4096 != length) { + struct dealloc_record *record = get_unused_record(); + record->start = vma + length; + record->length = pages * 4096 - length; + } + + return vma; + +} + +void heap_dealloc(void *start, uint64_t length) { + struct dealloc_record *record = get_unused_record(); + record->start = start; + record->length = length; +} diff --git a/src/kernel/heap.h b/src/kernel/heap.h new file mode 100644 index 0000000..a999f49 --- /dev/null +++ b/src/kernel/heap.h @@ -0,0 +1,23 @@ +/* Calcite, src/kernel/heap.h + * 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 . + */ + +#pragma once + +#include + +void *heap_alloc(uint64_t length); +void heap_dealloc(void *start, uint64_t length); diff --git a/kernel/src/initfs.c b/src/kernel/initfs.c similarity index 88% rename from kernel/src/initfs.c rename to src/kernel/initfs.c index a176b3e..9cf3fed 100644 --- a/kernel/src/initfs.c +++ b/src/kernel/initfs.c @@ -1,21 +1,21 @@ -/* Calcite, kernel/src/initfs.c +/* Calcite, src/kernel/initfs.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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ -#include +#include "initfs.h" static const uint8_t *initfs_start; static uint64_t initfs_length; diff --git a/kernel/include/initfs.h b/src/kernel/initfs.h similarity index 96% rename from kernel/include/initfs.h rename to src/kernel/initfs.h index 3a1a4d3..e03f7fa 100644 --- a/kernel/include/initfs.h +++ b/src/kernel/initfs.h @@ -1,4 +1,4 @@ -/* Calcite, kernel/include/initfs.h +/* Calcite, src/kernel/initfs.h * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify diff --git a/kernel/src/interrupts.asm b/src/kernel/interrupts.asm similarity index 92% rename from kernel/src/interrupts.asm rename to src/kernel/interrupts.asm index 4740556..8ea354e 100644 --- a/kernel/src/interrupts.asm +++ b/src/kernel/interrupts.asm @@ -1,18 +1,18 @@ - ; Calcite, kernel/src/interrupts.asm + ; Calcite, src/kernel/interrupts.asm ; 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. + ; 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. + ; 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 . + ; You should have received a copy of the GNU General Public License along + ; with this program. If not, see . bits 64 diff --git a/kernel/src/interrupts.c b/src/kernel/interrupts.c similarity index 91% rename from kernel/src/interrupts.c rename to src/kernel/interrupts.c index 94575aa..9792863 100644 --- a/kernel/src/interrupts.c +++ b/src/kernel/interrupts.c @@ -1,4 +1,4 @@ -/* Calcite, kernel/src/interrupts.c +/* Calcite, src/kernel/interrupts.c * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify @@ -21,8 +21,8 @@ // https://osdev.wiki/wiki/Interrupt_Descriptor_Table // https://osdev.wiki/wiki/Task_State_Segment -#include -#include +#include "interrupts.h" +#include "panic.h" struct [[gnu::packed]] exception_parameter { uint64_t r15; @@ -212,6 +212,18 @@ void enable_interrupts() { tss_entry->base_24 = (tss_base >> 24) & 0xff; tss_entry->base_32 = tss_base >> 32; + struct gdt_entry *user_data_entry = &the_gdt[3]; + user_data_entry->access_byte = + GDT_AB_CODE_DATA_ACCESSED | GDT_AB_DATA_WRITABLE | + GDT_AB_TYPE_DATA | GDT_AB_DPL_THREE | GDT_AB_PRESENT; + user_data_entry->flags = GDT_FLAG_LONG; + + struct gdt_entry *user_code_entry = &the_gdt[4]; + user_code_entry->access_byte = + GDT_AB_CODE_DATA_ACCESSED | GDT_AB_TYPE_CODE | + GDT_AB_DPL_THREE | GDT_AB_PRESENT; + user_code_entry->flags = GDT_FLAG_LONG; + struct gdt_entry *kernel_code_entry = &the_gdt[5]; kernel_code_entry->access_byte = GDT_AB_CODE_DATA_ACCESSED | GDT_AB_TYPE_CODE | diff --git a/kernel/include/interrupts.h b/src/kernel/interrupts.h similarity index 95% rename from kernel/include/interrupts.h rename to src/kernel/interrupts.h index d5b075e..b10bce4 100644 --- a/kernel/include/interrupts.h +++ b/src/kernel/interrupts.h @@ -1,4 +1,4 @@ -/* Calcite, kernel/include/interrupts.h +/* Calcite, src/kernel/interrupts.h * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify diff --git a/kernel/link.ld b/src/kernel/link.ld similarity index 100% rename from kernel/link.ld rename to src/kernel/link.ld diff --git a/kernel/src/paging.asm b/src/kernel/paging.asm similarity index 80% rename from kernel/src/paging.asm rename to src/kernel/paging.asm index b22f94e..1539875 100644 --- a/kernel/src/paging.asm +++ b/src/kernel/paging.asm @@ -1,18 +1,18 @@ - ; Calcite, kernel/src/paging.asm + ; Calcite, src/kernel/paging.asm ; 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. + ; 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. + ; 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 . + ; You should have received a copy of the GNU General Public License along + ; with this program. If not, see . bits 64 @@ -25,6 +25,12 @@ init_stack_length equ 16384 section .text +;referenced in paging.c +extern invlpg +invlpg: + invlpg byte [rdi] + ret + extern switch_to_kernel_page_tables switch_to_kernel_page_tables: diff --git a/kernel/src/paging.c b/src/kernel/paging.c similarity index 72% rename from kernel/src/paging.c rename to src/kernel/paging.c index f03bff4..ec664df 100644 --- a/kernel/src/paging.c +++ b/src/kernel/paging.c @@ -1,22 +1,22 @@ -/* Calcite, kernel/src/paging.c +/* Calcite, src/kernel/paging.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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ -#include -#include +#include "paging.h" +#include "panic.h" #define MAX_PHYSICAL_GB 64ULL @@ -38,6 +38,8 @@ static uint8_t physical_map[MAX_PHYSICAL_GB << 15]; //referenced from paging.asm uint64_t kernel_p4_physical_address; +uint64_t kernel_p3_physical_address; + alignas(4096) static uint64_t kernel_p4[512]; alignas(4096) static uint64_t kernel_p3[512]; alignas(4096) static uint64_t kernel_p2[512]; @@ -55,6 +57,7 @@ void init_paging(uint64_t kernel_physical_base, void *kernel_virtual_base) { (uint64_t)kernel_virtual_base - kernel_physical_base; kernel_p4_physical_address = (uint64_t)kernel_p4 - kernel_load_offset; + kernel_p3_physical_address = (uint64_t)kernel_p3 - kernel_load_offset; kernel_p4[511] = ((uint64_t)kernel_p3 - kernel_load_offset) | 0x3; for (int i = 0; i < 511; ++i) @@ -98,6 +101,9 @@ void mark_physical_memory_free(uint64_t base, uint64_t length) { } +//defined in paging.asm +void invlpg(void *address); + void map_in_kernel_page_table( uint64_t physical_base, void *virtual_base, int writable, int executable) { @@ -112,6 +118,39 @@ void map_in_kernel_page_table( physical_base | (writable ? 0x3 : 0x1) | (executable ? 0 : 0x8000000000000000); + invlpg(virtual_base); + +} + +void unmap_kernel_page(void *virtual_base) { + + uint64_t virtual_base_u64 = (uint64_t)virtual_base; + assert(virtual_base_u64 >= 0xffffffffc0000000); + + uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12; + assert(kernel_p1s[p1s_index] != 0); + + kernel_p1s[p1s_index] = 0; + + invlpg(virtual_base); + +} + +void unmap_and_free_kernel_page(void *virtual_base) { + + uint64_t virtual_base_u64 = (uint64_t)virtual_base; + assert(virtual_base_u64 >= 0xffffffffc0000000); + + uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12; + assert(kernel_p1s[p1s_index] != 0); + + uint64_t pma = kernel_p1s[p1s_index] & 0x7ffffffffffff000; + mark_physical_memory_free(pma, 4096); + + kernel_p1s[p1s_index] = 0; + + invlpg(virtual_base); + } void *find_free_kernel_region(uint64_t length) { @@ -127,9 +166,7 @@ void *find_free_kernel_region(uint64_t length) { if (run_length == length) return (void *)(run_start * 4096 + 0xffffffffc0000000); if (next == 512 * 512) - //die. TODO: handle this nicer. - while (1) - __asm__ ("hlt"); + panic("out of kernel virtual memory"); if (kernel_p1s[next] == 0) ++run_length; else { @@ -140,3 +177,13 @@ void *find_free_kernel_region(uint64_t length) { } } + +uint64_t take_free_physical_page() { + for (uint64_t i = 0; i < (MAX_PHYSICAL_GB << 15); ++i) + for (int j = 0; j < 8; ++j) + if (physical_map[i] & (1 << j)) { + physical_map[i] &= ~(1 << j); + return (i << 15) + (j << 12); + } + panic("out of physical memory"); +} diff --git a/kernel/include/paging.h b/src/kernel/paging.h similarity index 72% rename from kernel/include/paging.h rename to src/kernel/paging.h index 9f93ebe..a572f6b 100644 --- a/kernel/include/paging.h +++ b/src/kernel/paging.h @@ -1,24 +1,26 @@ -/* Calcite, kernel/include/paging.h +/* Calcite, src/kernel/paging.h * 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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ #pragma once #include +extern uint64_t kernel_p3_physical_address; + //kernel physical and virtual bases are passed so that we can compute the //physical addresses of the kernel's statically allocated paging structures. void init_paging(uint64_t kernel_physical_base, void *kernel_virtual_base); @@ -31,9 +33,18 @@ void mark_physical_memory_free(uint64_t base, uint64_t length); void map_in_kernel_page_table( uint64_t physical_base, void *virtual_base, int writable, int executable); +//unmaps one page. base should be page-aligned and within kernel range. +void unmap_kernel_page(void *virtual_base); + +//unmaps one page, and frees the corresponding physical page. +//base should be page-aligned and within kernel range. +void unmap_and_free_kernel_page(void *virtual_base); + //returns a region of contiguous pages in kernel virtual memory where nothing //is mapped. length should be page-aligned. void *find_free_kernel_region(uint64_t length); +uint64_t take_free_physical_page(); + //implemented in paging.asm. the continuation should be noreturn. [[noreturn]] void switch_to_kernel_page_tables(void (*continuation)()); diff --git a/kernel/src/panic.c b/src/kernel/panic.c similarity index 96% rename from kernel/src/panic.c rename to src/kernel/panic.c index ad9f0e6..74844dd 100644 --- a/kernel/src/panic.c +++ b/src/kernel/panic.c @@ -1,4 +1,4 @@ -/* Calcite, kernel/src/panic.c +/* Calcite, src/kernel/panic.c * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify diff --git a/kernel/include/panic.h b/src/kernel/panic.h similarity index 95% rename from kernel/include/panic.h rename to src/kernel/panic.h index 8555ab3..473415b 100644 --- a/kernel/include/panic.h +++ b/src/kernel/panic.h @@ -1,4 +1,4 @@ -/* Calcite, kernel/include/panic.h +/* Calcite, src/kernel/panic.h * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify @@ -15,6 +15,8 @@ * with this program. If not, see . */ +#pragma once + [[noreturn]] void panic_core( const char *file, const char *function, int line, const char *message); diff --git a/src/kernel/process.c b/src/kernel/process.c new file mode 100644 index 0000000..c149cdd --- /dev/null +++ b/src/kernel/process.c @@ -0,0 +1,357 @@ +/* Calcite, src/kernel/process.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 "scheduler.h" +#include "process.h" +#include "utility.h" +#include "paging.h" +#include "panic.h" +#include "heap.h" + +void create_process(struct process *process_out) { + + process_out->p4_physical_base = take_free_physical_page(); + process_out->p4_virtual_base = find_free_kernel_region(4096); + map_in_kernel_page_table( + process_out->p4_physical_base, process_out->p4_virtual_base, 1, 0); + + process_out->p3_physical_base = take_free_physical_page(); + process_out->p3_virtual_base = find_free_kernel_region(4096); + map_in_kernel_page_table( + process_out->p3_physical_base, process_out->p3_virtual_base, 1, 0); + + process_out->p4_virtual_base[0] = process_out->p3_physical_base | 0x7; + process_out->p4_virtual_base[511] = kernel_p3_physical_address | 0x3; + for (int i = 1; i < 511; ++i) + process_out->p4_virtual_base[i] = 0; + + for (int i = 0; i < 512; ++i) { + process_out->p3_virtual_base[i] = 0; + process_out->p2_virtual_bases[i] = 0; + process_out->p1_virtual_bases[i] = 0; + } + +process_out->n_threads = 0; + +} + +void map_page_for_process( + struct process *process, uint64_t physical_base, + void *virtual_base, int writable, int executable) { + + assert(physical_base % 4096 == 0) + + uint64_t vma = (uint64_t)virtual_base; + assert(vma % 4096 == 0) + assert(vma != 0) + assert(vma < 0x0000008000000000) + + int p1i = (vma >> 12) & 0x1ff; + int p2i = (vma >> 21) & 0x1ff; + int p3i = (vma >> 30) & 0x1ff; + + if (process->p3_virtual_base[p3i] == 0) { + uint64_t p2_pma = take_free_physical_page(); + uint64_t *p2_vma = find_free_kernel_region(4096); + map_in_kernel_page_table(p2_pma, p2_vma, 1, 0); + process->p3_virtual_base[p3i] = p2_pma | 0x7; + process->p2_virtual_bases[p3i] = p2_vma; + process->p1_virtual_bases[p3i] = heap_alloc(4096); + for (int i = 0; i < 512; ++i) { + p2_vma[i] = 0; + process->p1_virtual_bases[p3i][i] = 0; + } + } + + if (process->p2_virtual_bases[p3i][p2i] == 0) { + uint64_t p1_pma = take_free_physical_page(); + uint64_t *p1_vma = find_free_kernel_region(4096); + map_in_kernel_page_table(p1_pma, p1_vma, 1, 0); + process->p2_virtual_bases[p3i][p2i] = p1_pma | 0x7; + process->p1_virtual_bases[p3i][p2i] = p1_vma; + for (int i = 0; i < 512; ++i) + p1_vma[i] = 0; + } + + assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0) + + process->p1_virtual_bases[p3i][p2i][p1i] = + physical_base | 0x5 | (writable ? 0x2 : 0x0) | + (executable ? 0 : 0x8000000000000000); + +} + +void unmap_page_for_process( + struct process *process, void *virtual_base) { + + uint64_t vma = (uint64_t)virtual_base; + assert(vma % 4096 == 0) + assert(vma != 0) + assert(vma < 0x0000008000000000) + + int p1i = (vma >> 12) & 0x1ff; + int p2i = (vma >> 21) & 0x1ff; + int p3i = (vma >> 30) & 0x1ff; + + assert( + process->p1_virtual_bases[p3i] && + process->p1_virtual_bases[p3i][p2i] && + process->p1_virtual_bases[p3i][p2i][p1i]) + + process->p1_virtual_bases[p3i][p2i][p1i] = 0; + +} + +int is_mapped_writable(struct process *process, void *start, uint64_t length) { + + uint64_t vma_start = (uint64_t)start; + uint64_t vma_end = vma_start + length; + vma_start = (vma_start / 4096) * 4096; + vma_end = ((vma_end - 1) / 4096 + 1) * 4096; + + for (uint64_t vma = vma_start; vma < vma_end; vma += 4096) { + + if (vma == 0 || vma >= 0x0000008000000000) + return 0; + + int p1i = (vma >> 12) & 0x1ff; + int p2i = (vma >> 21) & 0x1ff; + int p3i = (vma >> 30) & 0x1ff; + + if (!process->p1_virtual_bases[p3i] || + !process->p1_virtual_bases[p3i][p2i] || + !process->p1_virtual_bases[p3i][p2i][p1i] || + !(process->p1_virtual_bases[p3i][p2i][p1i] & 0x2)) + return 0; + + } + + return 1; + +} + +void *find_free_process_region( + struct process *process, uint64_t page_count) { + + uint64_t start = 512 * 512 * 4096; + uint64_t length = 0; + + while (1) { + + if (length >= page_count * 4096) + break; + + uint64_t vma = start + length; + if (vma >= 0x0000008000000000) + goto no_mem; + + int p1i = (vma >> 12) & 0x1ff; + int p2i = (vma >> 21) & 0x1ff; + int p3i = (vma >> 30) & 0x1ff; + + if (p2i == 0 && p1i == 0 && process->p2_virtual_bases[p3i] == 0) { + length += 512 * 512 * 4096; + continue; + } + + if (p1i == 0 && process->p1_virtual_bases[p3i][p2i] == 0) { + length += 512 * 4096; + continue; + } + + if (process->p1_virtual_bases[p3i][p2i][p1i] == 0) { + length += 4096; + continue; + } + + start = start + length + 4096; + length = 0; + + } + + return (void *)start; + +no_mem: + panic("process out of virtual memory") + +} + +void load_elf( + struct process *process, uint64_t *entry_out, + const void *elf_start, uint64_t elf_length) { + + if (elf_length < 58) + panic("malformed elf") + + *entry_out = *(uint64_t *)(elf_start + 24); + + uint64_t phead_start = *(uint64_t *)(elf_start + 32); + uint16_t phead_entry_size = *(uint16_t *)(elf_start + 54); + uint16_t phead_entry_count = *(uint16_t *)(elf_start + 56); + + if (elf_length < phead_start + phead_entry_count * phead_entry_size) + panic("malformed elf") + + for (uint16_t i = 0; i < phead_entry_count; ++i) { + + uint64_t *entry = + (uint64_t *)(elf_start + phead_start + i * phead_entry_size); + + if ((entry[0] & 0xffffffff) != 1) + continue; + + int executable = (entry[0] >> 32) & 0x1; + int writable = (entry[0] >> 33) & 0x1; + + uint64_t file_start = entry[1]; + void *virtual_start = (void *)entry[2]; + uint64_t file_length = entry[4]; + uint64_t virtual_length = ((entry[5] - 1) / 4096 + 1) * 4096; + + if (elf_length < file_start + file_length) + panic("malformed elf") + if (file_length > virtual_length) + panic("malformed elf") + + if ((uint64_t)virtual_start % 4096 != 0 || virtual_length % 4096 != 0) + panic("unaligned elf") + + for (uint64_t i = 0; i < virtual_length; i += 4096) { + + uint64_t pma = take_free_physical_page(); + map_page_for_process( + process, pma, virtual_start + i, writable, executable); + + void *kvma = find_free_kernel_region(4096); + map_in_kernel_page_table(pma, kvma, 1, 0); + + if (i + 4096 <= file_length) + memcpy(kvma, elf_start + file_start + i, 4096); + else if (i >= file_length) + memzero(kvma, 4096); + else { + memcpy(kvma, elf_start + file_start + i, file_length & 0xfff); + memzero(kvma + (file_length & 0xfff), 4096 - (file_length & 0xfff)); + } + + unmap_kernel_page(kvma); + + } + + } + +} + +void destroy_process(struct process *process) { + for (int p3i = 0; p3i < 512; ++p3i) + if (process->p3_virtual_base[p3i]) { + for (int p2i = 0; p2i < 512; ++p2i) + if (process->p2_virtual_bases[p3i][p2i]) { + for (int p1i = 0; p1i < 512; ++p1i) + mark_physical_memory_free( + process->p1_virtual_bases[p3i][p2i][p1i] & 0x7ffffffffffff000, + 4096); + unmap_and_free_kernel_page(process->p1_virtual_bases[p3i][p2i]); + } + unmap_and_free_kernel_page(process->p2_virtual_bases[p3i]); + heap_dealloc(process->p1_virtual_bases[p3i], 4096); + } + unmap_and_free_kernel_page(process->p3_virtual_base); + unmap_and_free_kernel_page(process->p4_virtual_base); + heap_dealloc(process, sizeof(struct process)); +} + +#define INITIAL_STACK_SIZE (16 << 20) + +void create_thread(struct process *process, struct thread *thread_out) { + + //TODO: allocate stack as needed on page faults, have guard pages, etc. + + void *stack_bottom_vma = + find_free_process_region(process, INITIAL_STACK_SIZE / 4096); + + for (int i = 0; i < INITIAL_STACK_SIZE / 4096; ++i) { + + uint64_t pma = take_free_physical_page(); + map_page_for_process(process, pma, stack_bottom_vma + i * 4096, 1, 0); + + void *kvma = find_free_kernel_region(4096); + map_in_kernel_page_table(pma, kvma, 1, 0); + memzero(kvma, 4096); + unmap_kernel_page(kvma); + + } + + thread_out->process = process; + thread_out->stack_bottom = stack_bottom_vma; + thread_out->stack_top = stack_bottom_vma + INITIAL_STACK_SIZE; + + ++process->n_threads; + +} + +struct thread *running_thread = 0; + +[[noreturn]] void syscall_end_thread() { + + assert(running_thread != 0) + assert(running_thread->process->n_threads >= 1) + + if (running_thread->process->n_threads == 1) + destroy_process(running_thread->process); + + else { + + --running_thread->process->n_threads; + + for (void *p = running_thread->stack_bottom; + p < running_thread->stack_top; p += 4096) + unmap_page_for_process(running_thread->process, p); + + } + + heap_dealloc(running_thread, sizeof(struct thread)); + + running_thread = 0; + resume_next_continuation(); + +} + +void syscall_map_framebuffer(struct syscall_framebuffer_info *info_out) { + + if (!is_mapped_writable( + running_thread->process, info_out, + sizeof(struct syscall_framebuffer_info))) + panic("bad syscall"); + + uint64_t pages_needed = (fb_pitch * fb_height - 1) / 4096 + 1; + + void *base = find_free_process_region(running_thread->process, pages_needed); + + for (uint64_t i = 0; i < pages_needed; ++i) + map_page_for_process( + running_thread->process, + fb_physical_base + i * 4096, + base + i * 4096, 1, 0); + + info_out->fb_base = base; + info_out->fb_pitch = fb_pitch; + info_out->fb_height = fb_height; + info_out->fb_width = fb_width; + +} diff --git a/src/kernel/process.h b/src/kernel/process.h new file mode 100644 index 0000000..a623429 --- /dev/null +++ b/src/kernel/process.h @@ -0,0 +1,88 @@ +/* Calcite, src/kernel/process.h + * 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 . + */ + +#pragma once + +#include + +struct process { + + uint64_t p4_physical_base; + uint64_t *p4_virtual_base; + uint64_t p3_physical_base; + uint64_t *p3_virtual_base; + + int n_threads; + + //0 for missing levels. just bottom p3 of address space. + uint64_t *p2_virtual_bases[512]; + uint64_t **p1_virtual_bases[512]; + +}; + +struct thread { + + struct process *process; + + //both page-aligned + void *stack_bottom; + void *stack_top; + +}; + +extern struct thread *running_thread; + +void create_process(struct process *process_out); +void create_thread(struct process *process, struct thread *thread_out); + +//physical and virtual bases must be page-aligned. +//virtual base must be in bottom p3 and not zero. +void map_page_for_process( + struct process *process, uint64_t physical_base, + void *virtual_base, int writable, int executable); + +//virtual base must be page-aligned, in bottom p3, and not zero. +void unmap_page_for_process( + struct process *process, void *virtual_base); + +//finds a free memory region in the bottom p3 +//of the address space, and not the bottom p2. +void *find_free_process_region( + struct process *process, uint64_t page_count); + +//loaded sections are copied to new pages. once storage drivers are written, +//this should probably just take a file handle so it can load and parse the +//header and then load sections directly into user pages. +void load_elf( + struct process *process, uint64_t *entry_out, + const void *elf_start, uint64_t elf_length); + +void destroy_process(struct process *process); + +//returs 1 if [start, start + length) is writable by process, otherwise 0. +int is_mapped_writable(struct process *process, void *start, uint64_t length); + +[[noreturn]] void syscall_end_thread(); + +struct syscall_framebuffer_info { + uint8_t *fb_base; + int fb_width; + int fb_height; + int fb_pitch; +}; + +void syscall_map_framebuffer(struct syscall_framebuffer_info *info_out); diff --git a/kernel/src/ps2.asm b/src/kernel/ps2.asm similarity index 85% rename from kernel/src/ps2.asm rename to src/kernel/ps2.asm index b91ec81..21b0aad 100644 --- a/kernel/src/ps2.asm +++ b/src/kernel/ps2.asm @@ -1,18 +1,18 @@ - ; Calcite, kernel/src/ps2.asm + ; Calcite, src/kernel/ps2.asm ; 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. + ; 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. + ; 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 . + ; You should have received a copy of the GNU General Public License along + ; with this program. If not, see . bits 64 diff --git a/kernel/src/ps2.c b/src/kernel/ps2.c similarity index 88% rename from kernel/src/ps2.c rename to src/kernel/ps2.c index 6da5884..54506af 100644 --- a/kernel/src/ps2.c +++ b/src/kernel/ps2.c @@ -1,23 +1,23 @@ -/* Calcite, kernel/src/ps2.c +/* Calcite, src/kernel/ps2.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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ -#include -#include -#include +#include "framebuffer.h" +#include "panic.h" +#include "ps2.h" //defined in ps2.asm //returns -1 if no byte available diff --git a/kernel/include/ps2.h b/src/kernel/ps2.h similarity index 95% rename from kernel/include/ps2.h rename to src/kernel/ps2.h index 53364fb..235caf2 100644 --- a/kernel/include/ps2.h +++ b/src/kernel/ps2.h @@ -1,4 +1,4 @@ -/* Calcite, kernel/include/ps2.h +/* Calcite, src/kernel/ps2.h * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify diff --git a/src/kernel/scheduler.asm b/src/kernel/scheduler.asm new file mode 100644 index 0000000..ec2d14d --- /dev/null +++ b/src/kernel/scheduler.asm @@ -0,0 +1,57 @@ + ; Calcite, src/kernel/scheduler.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 . + + +bits 64 + +;referenced in scheduler.c +global user_task_start +user_task_start: + + mov cr3, rbx + mov rcx, rbp + mov r11, 0x200 + + xor rax, rax + xor rbx, rbx + xor rdx, rdx + xor rdi, rdi + xor rsi, rsi + xor rbp, rbp + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + o64 sysret + +;referenced in scheduler.c +global resume_continuation +resume_continuation: + + mov rax, qword [rdi] + mov rbx, qword [rdi + 8] + mov rbp, qword [rdi + 16] + mov rsp, qword [rdi + 24] + mov r12, qword [rdi + 32] + mov r13, qword [rdi + 40] + mov r14, qword [rdi + 48] + mov r15, qword [rdi + 56] + + jmp rax diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c new file mode 100644 index 0000000..f3d4595 --- /dev/null +++ b/src/kernel/scheduler.c @@ -0,0 +1,118 @@ +/* Calcite, src/kernel/scheduler.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 "scheduler.h" +#include "utility.h" +#include "heap.h" + +struct continuation_info { + uint64_t rip; + uint64_t rbx; + uint64_t rbp; + uint64_t rsp; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; +}; + +static struct continuation_info **ready_continuations = 0; +static int rc_buffer_length = 0; +static int rc_read_ptr = 0; +static int rc_count = 0; + +#define INITIAL_RC_BUFFER_LENGTH 128 + +void init_scheduler() { + ready_continuations = heap_alloc(INITIAL_RC_BUFFER_LENGTH * sizeof(void *)); + rc_buffer_length = INITIAL_RC_BUFFER_LENGTH; + for (int i = 0; i < INITIAL_RC_BUFFER_LENGTH; ++i) + ready_continuations[i] = 0; +} + +static void queue_continuation(struct continuation_info *info) { + + if (rc_count == rc_buffer_length) { + + struct continuation_info **new_rc_buffer = + heap_alloc(2 * rc_buffer_length * sizeof(void *)); + + memcpy( + new_rc_buffer, + ready_continuations + rc_read_ptr, + (rc_buffer_length - rc_read_ptr) * sizeof(void *)); + + memcpy( + new_rc_buffer + rc_buffer_length - rc_read_ptr, + ready_continuations, + rc_read_ptr * sizeof(void *)); + + heap_dealloc(ready_continuations, rc_buffer_length * sizeof(void *)); + + new_rc_buffer[rc_buffer_length] = info; + for (int i = rc_buffer_length + 1; i < 2 * rc_buffer_length; ++i) + new_rc_buffer[i] = 0; + + ready_continuations = new_rc_buffer; + rc_buffer_length *= 2; + rc_read_ptr = 0; + ++rc_count; + + } + + else { + ready_continuations[(rc_read_ptr + rc_count) % rc_buffer_length] = info; + ++rc_count; + } + +} + +//defined in scheduler.asm +void user_task_start(); + +void create_user_task( + uint64_t cr3, uint64_t rip, uint64_t rsp) { + + struct continuation_info *info = + heap_alloc(sizeof(struct continuation_info)); + + info->rip = (uint64_t)&user_task_start; + info->rsp = (uint64_t)rsp; + info->rbx = cr3; + info->rbp = rip; + + queue_continuation(info); + +} + +//defined in scheduler.asm +[[noreturn]] void resume_continuation(struct continuation_info *info); + +[[noreturn]] void resume_next_continuation() { + + while (rc_count == 0) + __asm__ ("hlt"); + + struct continuation_info *info = ready_continuations[rc_read_ptr]; + + ready_continuations[rc_read_ptr] = 0; + rc_read_ptr = (rc_read_ptr + 1) % rc_buffer_length; + --rc_count; + + resume_continuation(info); + +} diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h new file mode 100644 index 0000000..cf503f3 --- /dev/null +++ b/src/kernel/scheduler.h @@ -0,0 +1,27 @@ +/* Calcite, src/kernel/scheduler.h + * 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 . + */ + +#pragma once + +#include + +void init_scheduler(); + +void create_user_task( + uint64_t cr3, uint64_t rip, uint64_t rsp); + +[[noreturn]] void resume_next_continuation(); diff --git a/src/kernel/syscalls.asm b/src/kernel/syscalls.asm new file mode 100644 index 0000000..de55a1d --- /dev/null +++ b/src/kernel/syscalls.asm @@ -0,0 +1,71 @@ + ; Calcite, src/kernel/syscalls.asm + ; 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 . + + +bits 64 + +extern syscall_entry_c + +section .bss + +;this should have guard pages blah blah blah + resb 16 << 20 +syscall_stack_top: + +section .text + +;system call number is in rax. +;system call arguments are in rdi, rsi, rdx. +;system call returns a value in rax. +syscall_entry: + mov qword [syscall_stack_top - 8], rsp + mov rsp, syscall_stack_top - 8 + push r11 + push rcx + + mov rcx, rax + call syscall_entry_c + + pop rcx + pop r11 + pop rsp + o64 sysret + +global init_syscalls +init_syscalls: + + mov ecx, 0xc0000080 + rdmsr + or al, 0x01 + wrmsr + + mov edx, 0x00130028 + xor eax, eax + mov ecx, 0xc0000081 + wrmsr + + mov rdx, syscall_entry + mov eax, edx + shr rdx, 32 + mov ecx, 0xc0000082 + wrmsr + + xor edx, edx + xor eax, eax + mov ecx, 0xc0000084 + wrmsr + + ret diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c new file mode 100644 index 0000000..18e6124 --- /dev/null +++ b/src/kernel/syscalls.c @@ -0,0 +1,46 @@ +/* Calcite, src/kernel/syscalls.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 "syscalls.h" +#include "panic.h" + +#define MAX_SYSCALL_NUMBER 99 + +static uint64_t (*syscall_handlers[MAX_SYSCALL_NUMBER])( + uint64_t, uint64_t, uint64_t); + +void register_syscall( + int number, + uint64_t (*handler)(uint64_t, uint64_t, uint64_t)) { + + assert(number >= 0 && number <= MAX_SYSCALL_NUMBER) + assert(syscall_handlers[number] == 0) + + syscall_handlers[number] = handler; + +} + +//referenced in syscalls.asm +uint64_t syscall_entry_c( + uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t number) { + + if (number > MAX_SYSCALL_NUMBER || syscall_handlers[number] == 0) + panic("TODO: bad syscall"); + + return (*syscall_handlers[number])(arg1, arg2, arg3); + +} diff --git a/src/kernel/syscalls.h b/src/kernel/syscalls.h new file mode 100644 index 0000000..3171a7a --- /dev/null +++ b/src/kernel/syscalls.h @@ -0,0 +1,27 @@ +/* Calcite, src/kernel/syscalls.h + * 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 . + */ + +#pragma once + +#include +#include + +void init_syscalls(); + +void register_syscall( + int number, + uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3)); diff --git a/src/kernel/utility.asm b/src/kernel/utility.asm new file mode 100644 index 0000000..4922388 --- /dev/null +++ b/src/kernel/utility.asm @@ -0,0 +1,33 @@ + ; Calcite, src/kernel/utility.asm + ; 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 . + + +bits 64 + +section .text + +;global memcpy +;memcpy: +; mov rcx, rdx +; rep movsb +; ret + +;global memzero +;memzero: +; xor al, al +; mov rcx, rsi +; rep stosb +; ret diff --git a/kernel/src/utility.c b/src/kernel/utility.c similarity index 59% rename from kernel/src/utility.c rename to src/kernel/utility.c index a46a357..742ac5d 100644 --- a/kernel/src/utility.c +++ b/src/kernel/utility.c @@ -1,21 +1,21 @@ -/* Calcite, kernel/src/utility.c +/* Calcite, src/kernel/utility.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. + * 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. + * 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 . + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . */ -#include +#include "utility.h" int strequ(const char *str1, const char *str2) { while (1) { @@ -27,3 +27,13 @@ int strequ(const char *str1, const char *str2) { ++str2; } } + +void memcpy(void *to, const void *from, uint64_t bytes) { + for (uint64_t i = 0; i < bytes; ++i) + *(uint8_t *)(to + i) = *(const uint8_t *)(from + i); +} + +void memzero(void *start, uint64_t bytes) { + for (uint64_t i = 0; i < bytes; ++i) + *(uint8_t *)(start + i) = 0; +} diff --git a/kernel/include/utility.h b/src/kernel/utility.h similarity index 83% rename from kernel/include/utility.h rename to src/kernel/utility.h index 19d3995..cc679a6 100644 --- a/kernel/include/utility.h +++ b/src/kernel/utility.h @@ -1,4 +1,4 @@ -/* Calcite, kernel/include/utility.h +/* Calcite, src/kernel/utility.h * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify @@ -17,5 +17,10 @@ #pragma once +#include + //returns 1 if equal, 0 if not. int strequ(const char *str1, const char *str2); + +void memcpy(void *to, const void *from, uint64_t bytes); +void memzero(void *start, uint64_t bytes); diff --git a/src/user-apps/hello/hello.c b/src/user-apps/hello/hello.c new file mode 100644 index 0000000..20ea16a --- /dev/null +++ b/src/user-apps/hello/hello.c @@ -0,0 +1,35 @@ +/* Calcite, src/user-apps/hello/hello.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 + +[[noreturn]] +void _start() { + + struct framebuffer_info fb_info; + map_framebuffer(&fb_info); + + for (int y = 0; y < fb_info.fb_height; ++y) + for (int x = 0; x < fb_info.fb_width; ++x) { + uint8_t *pixel = fb_info.fb_base + y * fb_info.fb_pitch + x * 4; + pixel[0] = x * 256 / fb_info.fb_width; + pixel[1] = y * 256 / fb_info.fb_height; + } + + end_thread(); + +} diff --git a/src/user-apps/hello/libraries.txt b/src/user-apps/hello/libraries.txt new file mode 100644 index 0000000..8503661 --- /dev/null +++ b/src/user-apps/hello/libraries.txt @@ -0,0 +1 @@ +calcite diff --git a/src/user-libs/calcite/syscalls.asm b/src/user-libs/calcite/syscalls.asm new file mode 100644 index 0000000..4ded113 --- /dev/null +++ b/src/user-libs/calcite/syscalls.asm @@ -0,0 +1,27 @@ + ; Calcite, src/user-libs/calcite/syscalls.asm + ; 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 . + + +bits 64 + +section .text + +;referenced in syscalls.c +global do_syscall +do_syscall: + mov rax, rcx + syscall + ret diff --git a/src/user-libs/calcite/syscalls.c b/src/user-libs/calcite/syscalls.c new file mode 100644 index 0000000..d1584ce --- /dev/null +++ b/src/user-libs/calcite/syscalls.c @@ -0,0 +1,32 @@ +/* Calcite, src/user-libs/calcite/syscalls.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 + +//defined in syscalls.asm +uint64_t do_syscall + (uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t number); + +[[noreturn]] void end_thread() { + do_syscall(0, 0, 0, SYSCALL_END_THREAD); + __builtin_unreachable(); +} + +void map_framebuffer(struct framebuffer_info *info_out) { + do_syscall((uint64_t)info_out, 0, 0, SYSCALL_MAP_FRAMEBUFFER); +}