Compare commits
6 commits
weblog-hel
...
alpha
Author | SHA1 | Date | |
---|---|---|---|
ce37be750d | |||
c7ed7a2508 | |||
125707d5bc | |||
9a4c59ecfd | |||
746218052e | |||
c7c65593d3 |
13 changed files with 580 additions and 70 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
||||||
build/
|
|
||||||
dependencies/
|
dependencies/
|
||||||
|
build.ninja
|
||||||
|
.ninja_log
|
||||||
|
build/
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
|
-std=c23
|
||||||
-I
|
-I
|
||||||
dependencies/limine
|
dependencies/limine
|
||||||
|
-I
|
||||||
|
kernel/include
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
timeout: 0
|
timeout: 0
|
||||||
quiet: yes
|
quiet: yes
|
||||||
|
|
||||||
/Calcite
|
/Calcite
|
||||||
protocol: limine
|
protocol: limine
|
||||||
path: boot():/calcite/kernel.elf
|
path: boot():/calcite/kernel.elf
|
||||||
|
module_path: boot():/calcite/initfs.tar
|
||||||
|
|
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
LIMINE_VERSION="9.3.4"
|
LIMINE_VERSION="9.3.4"
|
||||||
|
|
||||||
if [ -e dependencies/.got-all ]; then
|
|
||||||
echo already got dependencies.
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -e dependencies ]; then
|
if [ -e dependencies ]; then
|
||||||
echo it looks like we started getting the dependencies, and did not finish. delete the dependencies directory and run this again to try again.
|
echo dependencies directory already exists.
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -21,5 +16,3 @@ curl -L https://github.com/limine-bootloader/limine/archive/refs/tags/v${LIMINE_
|
||||||
mv limine-${LIMINE_VERSION}-binary limine
|
mv limine-${LIMINE_VERSION}-binary limine
|
||||||
make -C limine
|
make -C limine
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
touch dependencies/.got-all
|
|
||||||
|
|
26
kernel/include/initfs.h
Normal file
26
kernel/include/initfs.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/* Calcite, kernel/include/initfs.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 set_initfs(const uint8_t *start, uint64_t length);
|
||||||
|
|
||||||
|
//if the file does not exist, *start_out is set to a null pointer.
|
||||||
|
void look_up_initfs_file(
|
||||||
|
const char *path, const uint8_t **start_out, uint64_t *length_out);
|
39
kernel/include/paging.h
Normal file
39
kernel/include/paging.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/* Calcite, kernel/include/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.
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//base and length should be page-aligned
|
||||||
|
void mark_physical_memory_free(uint64_t base, uint64_t length);
|
||||||
|
|
||||||
|
//maps one page. physical and virtual bases should be page-aligned.
|
||||||
|
//virtual address should be within kernel range.
|
||||||
|
void map_in_kernel_page_table(
|
||||||
|
uint64_t physical_base, void *virtual_base, int writable, int executable);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//implemented in paging.asm. the continuation should be noreturn.
|
||||||
|
[[noreturn]] void switch_to_kernel_page_tables(void (*continuation)());
|
|
@ -15,7 +15,9 @@
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <initfs.h>
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
|
#include <paging.h>
|
||||||
|
|
||||||
LIMINE_BASE_REVISION(3)
|
LIMINE_BASE_REVISION(3)
|
||||||
|
|
||||||
|
@ -25,24 +27,166 @@ static volatile struct limine_framebuffer_request fb_request = {
|
||||||
.response = 0
|
.response = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static volatile struct limine_hhdm_request hhdm_request = {
|
||||||
|
.id = LIMINE_HHDM_REQUEST,
|
||||||
|
.revision = 0,
|
||||||
|
.response = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static volatile struct limine_kernel_address_request ka_request = {
|
||||||
|
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
||||||
|
.revision = 0,
|
||||||
|
.response = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static volatile struct limine_memmap_request memmap_request = {
|
||||||
|
.id = LIMINE_MEMMAP_REQUEST,
|
||||||
|
.revision = 0,
|
||||||
|
.response = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static volatile struct limine_module_request module_request = {
|
||||||
|
.id = LIMINE_MODULE_REQUEST,
|
||||||
|
.revision = 0,
|
||||||
|
.response = 0
|
||||||
|
};
|
||||||
|
|
||||||
[[noreturn]] static void die() {
|
[[noreturn]] static void die() {
|
||||||
while (1)
|
while (1)
|
||||||
__asm__ ("hlt");
|
__asm__ ("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//these are defined in the linker script. they are page-aligned.
|
||||||
|
extern uint8_t __kernel_rx_start;
|
||||||
|
extern uint8_t __kernel_rx_end;
|
||||||
|
extern uint8_t __kernel_ro_start;
|
||||||
|
extern uint8_t __kernel_ro_end;
|
||||||
|
extern uint8_t __kernel_rw_start;
|
||||||
|
extern uint8_t __kernel_rw_end;
|
||||||
|
|
||||||
|
static void map_kernel_region(
|
||||||
|
uint64_t physical_start, void *virtual_start,
|
||||||
|
uint64_t length, int writable, int executable) {
|
||||||
|
for (uint64_t i = 0; i < length; i += 4096)
|
||||||
|
map_in_kernel_page_table(
|
||||||
|
physical_start + i, (uint8_t *)virtual_start + i, writable, executable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *fb_base;
|
||||||
|
static int fb_width;
|
||||||
|
static int fb_height;
|
||||||
|
static int fb_pitch;
|
||||||
|
|
||||||
|
static uint8_t *initfs_start;
|
||||||
|
static uint64_t initfs_length;
|
||||||
|
|
||||||
|
[[noreturn]] static void with_kernel_page_tables();
|
||||||
|
|
||||||
[[noreturn]] void kernel_entry() {
|
[[noreturn]] void kernel_entry() {
|
||||||
|
|
||||||
if (fb_request.response == 0 || fb_request.response->framebuffer_count == 0)
|
//die if the bootloader hasn't given us something that we need.
|
||||||
|
|
||||||
|
if (fb_request.response == 0 || hhdm_request.response == 0 ||
|
||||||
|
ka_request.response == 0 || memmap_request.response == 0 ||
|
||||||
|
module_request.response == 0 ||
|
||||||
|
fb_request.response->framebuffer_count == 0 ||
|
||||||
|
module_request.response->module_count == 0)
|
||||||
die();
|
die();
|
||||||
|
|
||||||
struct limine_framebuffer *fb = fb_request.response->framebuffers[0];
|
struct limine_framebuffer *fb = fb_request.response->framebuffers[0];
|
||||||
|
if (fb->memory_model != LIMINE_FRAMEBUFFER_RGB || fb->bpp != 32 ||
|
||||||
|
fb->red_mask_shift != 16 || fb->red_mask_size != 8 ||
|
||||||
|
fb->green_mask_shift != 8 || fb->green_mask_size != 8 ||
|
||||||
|
fb->blue_mask_shift != 0 || fb->blue_mask_size != 8)
|
||||||
|
die();
|
||||||
|
|
||||||
for (uint64_t y = 0; y < fb->height; ++y)
|
struct limine_file *initfs = module_request.response->modules[0];
|
||||||
for (uint64_t x = 0; x < fb->width; ++x) {
|
|
||||||
uint8_t *pixel = (uint8_t *)fb->address + y * fb->pitch + x * 4;
|
//set up page tables. we will mark the regions with bootloader structures as
|
||||||
pixel[0] = y * 256 / fb->height;
|
//usable, so we need to be careful not to allocate any physical pages until
|
||||||
pixel[1] = x * 256 / fb->width;
|
//after we are done using bootloader structures. we map the kernel into our
|
||||||
pixel[2] = 0;
|
//page tables, and we remap the framebuffer to somewhere in kernel memory.
|
||||||
|
//we do not map the bootloader structures, so we also need to not switch
|
||||||
|
//to the new tables until we are done using them.
|
||||||
|
|
||||||
|
uint64_t kernel_physical_base = ka_request.response->physical_base;
|
||||||
|
uint8_t *kernel_virtual_base = (uint8_t *)ka_request.response->virtual_base;
|
||||||
|
init_paging(kernel_physical_base, kernel_virtual_base);
|
||||||
|
|
||||||
|
struct limine_memmap_response *mm_response = memmap_request.response;
|
||||||
|
for (uint64_t i = 0; i < mm_response->entry_count; ++i) {
|
||||||
|
struct limine_memmap_entry *entry = mm_response->entries[i];
|
||||||
|
if (entry->type == LIMINE_MEMMAP_USABLE ||
|
||||||
|
entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE)
|
||||||
|
//limine guarantees these two types are page-aligned already.
|
||||||
|
mark_physical_memory_free(entry->base, entry->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
map_kernel_region(
|
||||||
|
&__kernel_rx_start - kernel_virtual_base + kernel_physical_base,
|
||||||
|
&__kernel_rx_start, &__kernel_rx_end - &__kernel_rx_start, 0, 1);
|
||||||
|
map_kernel_region(
|
||||||
|
&__kernel_ro_start - kernel_virtual_base + kernel_physical_base,
|
||||||
|
&__kernel_ro_start, &__kernel_ro_end - &__kernel_ro_start, 0, 0);
|
||||||
|
map_kernel_region(
|
||||||
|
&__kernel_rw_start - kernel_virtual_base + kernel_physical_base,
|
||||||
|
&__kernel_rw_start, &__kernel_rw_end - &__kernel_rw_start, 1, 0);
|
||||||
|
|
||||||
|
//we round up to a multiple of a page.
|
||||||
|
uint64_t fb_length = ((fb->height * fb->pitch - 1) / 4096 + 1) * 4096;
|
||||||
|
fb_base = find_free_kernel_region(fb_length);
|
||||||
|
map_kernel_region(
|
||||||
|
(uint64_t)fb->address - hhdm_request.response->offset,
|
||||||
|
fb_base, fb_length, 1, 0);
|
||||||
|
|
||||||
|
//store rest of framebuffer information
|
||||||
|
|
||||||
|
fb_width = fb->width;
|
||||||
|
fb_height = fb->height;
|
||||||
|
fb_pitch = fb->pitch;
|
||||||
|
|
||||||
|
//remap initfs module and store information.
|
||||||
|
|
||||||
|
uint64_t initfs_physical_start =
|
||||||
|
(uint64_t)initfs->address - hhdm_request.response->offset;
|
||||||
|
|
||||||
|
initfs_length = initfs->size;
|
||||||
|
uint64_t initfs_length_rounded = ((initfs_length - 1) / 4096 + 1) * 4096;
|
||||||
|
|
||||||
|
initfs_start = find_free_kernel_region(initfs_length_rounded);
|
||||||
|
map_kernel_region(
|
||||||
|
initfs_physical_start, initfs_start, initfs_length_rounded, 0, 0);
|
||||||
|
|
||||||
|
//switch to kernel page tables!
|
||||||
|
|
||||||
|
switch_to_kernel_page_tables(&with_kernel_page_tables);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] static void with_kernel_page_tables() {
|
||||||
|
|
||||||
|
//test initfs
|
||||||
|
|
||||||
|
set_initfs(initfs_start, initfs_length);
|
||||||
|
|
||||||
|
const uint8_t *hello_start;
|
||||||
|
uint64_t hello_length;
|
||||||
|
look_up_initfs_file("resx/hello.txt", &hello_start, &hello_length);
|
||||||
|
|
||||||
|
if (hello_length != 7)
|
||||||
|
die();
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; ++i)
|
||||||
|
if (hello_start[i] != "Hello!\n"[i])
|
||||||
|
die();
|
||||||
|
|
||||||
|
//test framebuffer
|
||||||
|
|
||||||
|
for (int y = 0; y < fb_height; ++y)
|
||||||
|
for (int x = 0; x < fb_width; ++x) {
|
||||||
|
fb_base[y * fb_pitch + x * 4] = y * 256 / fb_height;
|
||||||
|
fb_base[y * fb_pitch + x * 4 + 1] = x * 256 / fb_width;
|
||||||
|
fb_base[y * fb_pitch + x * 4 + 2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
die();
|
die();
|
||||||
|
|
74
kernel/src/initfs.c
Normal file
74
kernel/src/initfs.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/* Calcite, kernel/src/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.
|
||||||
|
*
|
||||||
|
* 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 <initfs.h>
|
||||||
|
|
||||||
|
static const uint8_t *initfs_start;
|
||||||
|
static uint64_t initfs_length;
|
||||||
|
|
||||||
|
void set_initfs(const uint8_t *start, uint64_t length) {
|
||||||
|
initfs_start = start;
|
||||||
|
initfs_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t decode_octal(const char *from) {
|
||||||
|
uint64_t value = 0;
|
||||||
|
while (*from != '\0') {
|
||||||
|
value = value * 8 + *from - '0';
|
||||||
|
++from;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void look_up_initfs_file(
|
||||||
|
const char *path, const uint8_t **start_out, uint64_t *length_out) {
|
||||||
|
|
||||||
|
int path_len = 0;
|
||||||
|
while (path[path_len] != '\0')
|
||||||
|
++path_len;
|
||||||
|
|
||||||
|
uint64_t offset = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
if (offset + 512 > initfs_length) {
|
||||||
|
*start_out = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t length =
|
||||||
|
decode_octal((const char *)(initfs_start + offset + 0x7c));
|
||||||
|
|
||||||
|
int found_it = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < path_len; ++i)
|
||||||
|
if (initfs_start[offset + i] != path[i]) {
|
||||||
|
found_it = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_it) {
|
||||||
|
*start_out = initfs_start + offset + 512;
|
||||||
|
*length_out = length;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 512 + ((length - 1) / 512 + 1) * 512;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
kernel/src/paging.asm
Normal file
42
kernel/src/paging.asm
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
; Calcite, kernel/src/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.
|
||||||
|
;
|
||||||
|
; 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
|
||||||
|
|
||||||
|
;both defined in paging.c
|
||||||
|
extern kernel_p4_physical_address
|
||||||
|
extern init_stack
|
||||||
|
|
||||||
|
init_stack_length equ 16384
|
||||||
|
|
||||||
|
section .text
|
||||||
|
|
||||||
|
extern switch_to_kernel_page_tables
|
||||||
|
switch_to_kernel_page_tables:
|
||||||
|
|
||||||
|
;switch the page table
|
||||||
|
mov rax, qword [kernel_p4_physical_address]
|
||||||
|
mov cr3, rax
|
||||||
|
|
||||||
|
;set the stack
|
||||||
|
mov rsp, init_stack + init_stack_length
|
||||||
|
|
||||||
|
;push 0 before jumping to continuation so that if the continuation
|
||||||
|
;does return (which it shouldn't), it returns to null instead of
|
||||||
|
;popping garbage and returning there
|
||||||
|
push qword 0
|
||||||
|
jmp rdi
|
147
kernel/src/paging.c
Normal file
147
kernel/src/paging.c
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/* Calcite, kernel/src/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.
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#define MAX_PHYSICAL_GB 64ULL
|
||||||
|
|
||||||
|
//this is a simple bitmap. physical_map[i / 8] & (1 << (i % 8)) is nonzero if
|
||||||
|
//and only if the physical page with base i * 4096 is free.
|
||||||
|
static uint8_t physical_map[MAX_PHYSICAL_GB << 15];
|
||||||
|
|
||||||
|
//kernel virtual memory is the top gigabyte, i.e. one "page directory" worth
|
||||||
|
//of pages. in the future i might make it more reactive, and allow new page
|
||||||
|
//directories to be allocated as needed, but for now there is just the one
|
||||||
|
//page directory, and all of the page tables in that directory are statically
|
||||||
|
//allocated. i do not like the intel/amd names for the paging structures, so
|
||||||
|
//i will use these names throughout the rest of this file:
|
||||||
|
// p4 = "page map level 4"
|
||||||
|
// p3 = "page directory pointer table"
|
||||||
|
// p2 = "page directory"
|
||||||
|
// p1 = "page table"
|
||||||
|
|
||||||
|
//referenced from paging.asm
|
||||||
|
uint64_t kernel_p4_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];
|
||||||
|
alignas(4096) static uint64_t kernel_p1s[512 * 512];
|
||||||
|
|
||||||
|
//referenced from paging.asm. at the end of init, this will either be marked
|
||||||
|
//free or reused as an interrupt / syscall stack. if the length of this is
|
||||||
|
//changed, it also needs to be changed in paging.asm. there is no guard page,
|
||||||
|
//but i don't expect init to need much stack.
|
||||||
|
alignas(4096) uint64_t init_stack[16384];
|
||||||
|
|
||||||
|
void init_paging(uint64_t kernel_physical_base, void *kernel_virtual_base) {
|
||||||
|
|
||||||
|
uint64_t kernel_load_offset =
|
||||||
|
(uint64_t)kernel_virtual_base - kernel_physical_base;
|
||||||
|
|
||||||
|
kernel_p4_physical_address = (uint64_t)kernel_p4 - kernel_load_offset;
|
||||||
|
|
||||||
|
kernel_p4[511] = ((uint64_t)kernel_p3 - kernel_load_offset) | 0x3;
|
||||||
|
for (int i = 0; i < 511; ++i)
|
||||||
|
kernel_p4[i] = 0;
|
||||||
|
|
||||||
|
kernel_p3[511] = ((uint64_t)kernel_p2 - kernel_load_offset) | 0x3;
|
||||||
|
for (int i = 0; i < 511; ++i)
|
||||||
|
kernel_p3[i] = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 512; ++i)
|
||||||
|
kernel_p2[i] =
|
||||||
|
((uint64_t)kernel_p1s - kernel_load_offset + i * 4096) | 0x03;
|
||||||
|
|
||||||
|
for (int i = 0; i < 512 * 512; ++i)
|
||||||
|
kernel_p1s[i] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_physical_memory_free(uint64_t base, uint64_t length) {
|
||||||
|
|
||||||
|
if (base + length > MAX_PHYSICAL_GB << 30)
|
||||||
|
length = (MAX_PHYSICAL_GB << 30) - base;
|
||||||
|
|
||||||
|
while (length && (base & (0x7 << 12))) {
|
||||||
|
physical_map[base >> 15] |= 1 << ((base >> 12) % 8);
|
||||||
|
base += 4096;
|
||||||
|
length -= 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (length >= 4096 * 8) {
|
||||||
|
physical_map[base >> 15] = 0xff;
|
||||||
|
base += 4096 * 8;
|
||||||
|
length -= 4096 * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (length) {
|
||||||
|
physical_map[base >> 15] |= 1 << ((base >> 12) % 8);
|
||||||
|
base += 4096;
|
||||||
|
length -= 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void map_in_kernel_page_table(
|
||||||
|
uint64_t physical_base, void *virtual_base,
|
||||||
|
int writable, int executable) {
|
||||||
|
|
||||||
|
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
|
||||||
|
|
||||||
|
//should probably die in this case
|
||||||
|
if (virtual_base_u64 < 0xffffffffc0000000)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
|
||||||
|
|
||||||
|
//should probably die in this case too
|
||||||
|
if (kernel_p1s[p1s_index] != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
kernel_p1s[p1s_index] =
|
||||||
|
physical_base | (writable ? 0x3 : 0x1) |
|
||||||
|
(executable ? 0 : 0x8000000000000000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void *find_free_kernel_region(uint64_t length) {
|
||||||
|
|
||||||
|
length /= 4096;
|
||||||
|
|
||||||
|
uint64_t next = 0;
|
||||||
|
|
||||||
|
uint64_t run_start = 0;
|
||||||
|
uint64_t run_length = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (run_length == length)
|
||||||
|
return (void *)(run_start * 4096 + 0xffffffffc0000000);
|
||||||
|
if (next == 512 * 512)
|
||||||
|
//die. TODO: handle this nicer.
|
||||||
|
while (1)
|
||||||
|
__asm__ ("hlt");
|
||||||
|
if (kernel_p1s[next] == 0)
|
||||||
|
++run_length;
|
||||||
|
else {
|
||||||
|
run_length = 0;
|
||||||
|
run_start = next + 1;
|
||||||
|
}
|
||||||
|
++next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
make-build.sh
Normal file
67
make-build.sh
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/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}"
|
||||||
|
|
||||||
|
if [ -e build.ninja ]; then
|
||||||
|
echo build.ninja already exists.
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "rule nasm" >> build.ninja
|
||||||
|
echo " command = nasm -f elf64 \$in -o \$out" >> build.ninja
|
||||||
|
|
||||||
|
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 kernel_ld" >> build.ninja
|
||||||
|
echo " command = ld -T kernel/link.ld \$in -o \$out" >> build.ninja
|
||||||
|
|
||||||
|
ALL_KERNEL_OBJECTS=""
|
||||||
|
|
||||||
|
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"
|
||||||
|
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
|
||||||
|
|
||||||
|
echo "build build/kernel/kernel.elf: kernel_ld ${ALL_KERNEL_OBJECTS}" >> build.ninja
|
||||||
|
|
||||||
|
echo "rule initfs" >> build.ninja
|
||||||
|
echo " command =" \
|
||||||
|
"rm -rf build/initfs &&" \
|
||||||
|
"mkdir -p build/initfs/resx &&" \
|
||||||
|
"echo Hello! > build/initfs/resx/hello.txt &&" \
|
||||||
|
"cd build/initfs &&" \
|
||||||
|
"tar cf ../initfs.tar *" >> build.ninja
|
||||||
|
|
||||||
|
echo "build build/initfs.tar: initfs" >> build.ninja
|
||||||
|
|
||||||
|
echo "rule disk" >> build.ninja
|
||||||
|
echo " command =" \
|
||||||
|
"rm -rf build/disk &&" \
|
||||||
|
"cp -r disk build/disk &&" \
|
||||||
|
"cp dependencies/limine/limine-uefi-cd.bin build/disk/limine/ &&" \
|
||||||
|
"cp dependencies/limine/limine-bios-cd.bin build/disk/limine/ &&" \
|
||||||
|
"cp dependencies/limine/limine-bios.sys build/disk/limine/ &&"\
|
||||||
|
"mkdir -p build/disk/EFI/BOOT &&" \
|
||||||
|
"cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \
|
||||||
|
"mkdir build/disk/calcite &&" \
|
||||||
|
"cp build/kernel/kernel.elf build/disk/calcite/ &&" \
|
||||||
|
"cp build/initfs.tar build/disk/calcite/ &&" \
|
||||||
|
"xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image --protective-msdos-label build/disk -o build/disk.iso &&" \
|
||||||
|
"dependencies/limine/limine bios-install build/disk.iso" >> build.ninja
|
||||||
|
|
||||||
|
echo "build build/disk.iso: disk | build/kernel/kernel.elf build/initfs.tar" >> build.ninja
|
||||||
|
|
||||||
|
echo "default build/disk.iso" >> build.ninja
|
42
makefile
42
makefile
|
@ -1,42 +0,0 @@
|
||||||
CC_EXTRA_FLAGS = -O3 -Wall -Wextra
|
|
||||||
CC_FLAGS = -std=c23 -mno-sse -ffreestanding ${CC_EXTRA_FLAGS}
|
|
||||||
|
|
||||||
.PHONY: default
|
|
||||||
default: build/disk.iso
|
|
||||||
|
|
||||||
debug: build/disk.iso
|
|
||||||
gdb -x qemu.gdb
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -rf build
|
|
||||||
|
|
||||||
# kernel
|
|
||||||
|
|
||||||
KERNEL_SOURCES = entry.c
|
|
||||||
|
|
||||||
build/kernel/%.c.o: kernel/src/%.c
|
|
||||||
@mkdir -p ${@D}
|
|
||||||
cc -c ${CC_FLAGS} -I dependencies/limine $^ -o $@
|
|
||||||
|
|
||||||
build/kernel/kernel.elf: ${KERNEL_SOURCES:%=build/kernel/%.o}
|
|
||||||
ld -T kernel/link.ld $^ -o $@
|
|
||||||
|
|
||||||
# disk
|
|
||||||
|
|
||||||
build/disk.iso: build/kernel/kernel.elf
|
|
||||||
rm -rf build/disk
|
|
||||||
cp -r disk build/disk
|
|
||||||
cp dependencies/limine/limine-uefi-cd.bin build/disk/limine/
|
|
||||||
cp dependencies/limine/limine-bios-cd.bin build/disk/limine/
|
|
||||||
cp dependencies/limine/limine-bios.sys build/disk/limine/
|
|
||||||
mkdir -p build/disk/EFI/BOOT
|
|
||||||
cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/
|
|
||||||
mkdir build/disk/calcite
|
|
||||||
cp build/kernel/kernel.elf build/disk/calcite/
|
|
||||||
xorriso \
|
|
||||||
-as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot \
|
|
||||||
-boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 \
|
|
||||||
--efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image \
|
|
||||||
--protective-msdos-label build/disk -o build/disk.iso
|
|
||||||
dependencies/limine/limine bios-install build/disk.iso
|
|
32
readme.txt
32
readme.txt
|
@ -11,31 +11,45 @@ Calcite is very much alpha software. Do not expect it to do anything useful.
|
||||||
|
|
||||||
=== Building
|
=== Building
|
||||||
|
|
||||||
Calcite requires some dependencies before it can be built:
|
Calcite requires some software to be installed before it can be built:
|
||||||
* curl
|
* curl
|
||||||
* GCC (Any C compiler supporting both C99 and C23 should work.)
|
* GCC (Any C compiler supporting both C99 and C23 should work.)
|
||||||
* GNU Binutils (Specifically, "ld" is used to link the kernel.)
|
* GNU Binutils (Specifically, "ld" is used to link the kernel.)
|
||||||
* GNU Make (I don't think I have used any GNU extensions.)
|
* GNU tar (Any POSIX tar should be fine.)
|
||||||
* GNU xorriso
|
* GNU xorriso
|
||||||
|
* NASM
|
||||||
|
* Ninja build system
|
||||||
|
|
||||||
On Debian, it is sufficient to run this command:
|
On Debian, it is sufficient to run this command:
|
||||||
apt install binutils curl gcc make xorriso
|
apt install binutils curl gcc nasm ninja-build tar xorriso
|
||||||
|
|
||||||
To build Calcite, first run "sh get-dependencies.sh", then run "make".
|
To build Calcite, run these commands:
|
||||||
This will build a disk image at "build/disk.iso" that can be booted
|
sh get-dependencies.sh
|
||||||
with either BIOS or UEFI.
|
sh make-build.sh
|
||||||
|
ninja
|
||||||
|
|
||||||
|
This will build a disk image at "build/disk.iso" that can be booted with
|
||||||
|
either BIOS or UEFI.
|
||||||
|
|
||||||
=== Debugging
|
=== Debugging
|
||||||
|
|
||||||
Once Calcite has been built, some more dependencies are required to debug it:
|
Once Calcite has been built, some more software is required to debug it:
|
||||||
* GNU's GDB
|
* GNU's GDB
|
||||||
* QEMU, including the x86_64 system
|
* QEMU, including the x86_64 system
|
||||||
|
|
||||||
On Debian, it is sufficient to run this command:
|
On Debian, it is sufficient to run this command:
|
||||||
apt install gdb qemu-system-x86
|
apt install gdb qemu-system-x86
|
||||||
|
|
||||||
Then, just run "make debug". Calcite will be booted in QEMU, and GDB will be
|
Then, just run "gdb -x qemu.gdb". Calcite will be booted in QEMU, and GDB will
|
||||||
attached to it. Use "c" or "continue" in GDB to start execution.
|
be attached to it. Use "c" or "continue" in GDB to start execution.
|
||||||
|
|
||||||
|
While debugging, it is useful to disable optimizations and enable debugging
|
||||||
|
information. In "make-build.sh", under COMMON_CC_EXTRA_FLAGS, you can change
|
||||||
|
"-O3" to "-Og -ggdb". Then, run these commands to rebuild:
|
||||||
|
rm -r build
|
||||||
|
rm build.ninja
|
||||||
|
sh make-build.sh
|
||||||
|
ninja
|
||||||
|
|
||||||
=== License
|
=== License
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue