ring 3, reorganization

This commit is contained in:
Benji Dial 2025-12-26 03:52:11 -05:00
parent 9fec34806e
commit 8e0c51ae5e
38 changed files with 1406 additions and 128 deletions

View file

@ -1,5 +1,6 @@
-std=c23
-ffreestanding
-I
dependencies/limine
-I
kernel/include
include

31
include/calcite/calcite.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
[[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);

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
enum {
SYSCALL_END_THREAD,
SYSCALL_MAP_FRAMEBUFFER
};

View file

@ -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
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"
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
echo "build build/kernel/kernel.elf: kernel_ld ${ALL_KERNEL_OBJECTS}" >> build.ninja
apps=""
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 =" \

View file

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

View file

@ -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 <https://www.gnu.org/licenses/>.
* 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 "framebuffer.h"
uint64_t fb_physical_base;
uint8_t *fb_base;
int fb_width;
int fb_height;

View file

@ -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 <https://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
extern uint64_t fb_physical_base;
extern uint8_t *fb_base;
extern int fb_width;
extern int fb_height;

104
src/kernel/heap.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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;
}

23
src/kernel/heap.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
void *heap_alloc(uint64_t length);
void heap_dealloc(void *start, uint64_t length);

View file

@ -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 <https://www.gnu.org/licenses/>.
* 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 <initfs.h>
#include "initfs.h"
static const uint8_t *initfs_start;
static uint64_t initfs_length;

View file

@ -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

View file

@ -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 <https://www.gnu.org/licenses/>.
; You should have received a copy of the GNU General Public License along
; with this program. If not, see <https://www.gnu.org/licenses/>.
bits 64

View file

@ -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 <interrupts.h>
#include <panic.h>
#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 |

View file

@ -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

View file

@ -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 <https://www.gnu.org/licenses/>.
; You should have received a copy of the GNU General Public License along
; with this program. If not, see <https://www.gnu.org/licenses/>.
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:

View file

@ -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 <https://www.gnu.org/licenses/>.
* 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 <paging.h>
#include <panic.h>
#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");
}

View file

@ -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 <https://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
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)());

View file

@ -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

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
[[noreturn]] void panic_core(
const char *file, const char *function, int line, const char *message);

357
src/kernel/process.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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;
}

88
src/kernel/process.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
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);

View file

@ -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 <https://www.gnu.org/licenses/>.
; You should have received a copy of the GNU General Public License along
; with this program. If not, see <https://www.gnu.org/licenses/>.
bits 64

View file

@ -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 <https://www.gnu.org/licenses/>.
* 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 <panic.h>
#include <ps2.h>
#include "framebuffer.h"
#include "panic.h"
#include "ps2.h"
//defined in ps2.asm
//returns -1 if no byte available

View file

@ -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

57
src/kernel/scheduler.asm Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
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

118
src/kernel/scheduler.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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);
}

27
src/kernel/scheduler.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
void init_scheduler();
void create_user_task(
uint64_t cr3, uint64_t rip, uint64_t rsp);
[[noreturn]] void resume_next_continuation();

71
src/kernel/syscalls.asm Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
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

46
src/kernel/syscalls.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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);
}

27
src/kernel/syscalls.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <kernel-public/syscall-numbers.h>
#include <stdint.h>
void init_syscalls();
void register_syscall(
int number,
uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3));

33
src/kernel/utility.asm Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
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

View file

@ -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 <https://www.gnu.org/licenses/>.
* 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 <utility.h>
#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;
}

View file

@ -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 <stdint.h>
//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);

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <calcite/calcite.h>
[[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();
}

View file

@ -0,0 +1 @@
calcite

View file

@ -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 <https://www.gnu.org/licenses/>.
bits 64
section .text
;referenced in syscalls.c
global do_syscall
do_syscall:
mov rax, rcx
syscall
ret

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <kernel-public/syscall-numbers.h>
#include <calcite/calcite.h>
//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);
}