This commit is contained in:
sam 2024-03-02 16:30:50 +13:00
parent 4856134d39
commit b91de2f374
22 changed files with 403 additions and 92 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "kernel/vendor/flanterm"]
path = kernel/vendor/flanterm
url = https://github.com/mintsuki/flanterm

View file

@ -1,7 +1,7 @@
# Nuke built-in rules and variables.
override MAKEFLAGS += -rR
override IMAGE_NAME := template
override IMAGE_NAME := radix
# Convenience macro to reliably declare user overridable variables.
define DEFAULT_VAR =
@ -33,7 +33,7 @@ all-hdd: $(IMAGE_NAME).hdd
.PHONY: run
run: $(IMAGE_NAME).iso
qemu-system-x86_64 -M q35 -m 2G -cdrom $(IMAGE_NAME).iso -boot d
qemu-system-x86_64 -m 1G -cdrom $(IMAGE_NAME).iso -enable-kvm -cpu host -boot d
.PHONY: run-uefi
run-uefi: ovmf $(IMAGE_NAME).iso

View file

@ -44,6 +44,7 @@ $(eval $(call DEFAULT_VAR,LDFLAGS,$(DEFAULT_LDFLAGS)))
# Internal C flags that should not be changed by the user.
override CFLAGS += \
-Wall \
-Werror \
-Wextra \
-std=gnu11 \
-ffreestanding \
@ -62,6 +63,7 @@ override CFLAGS += \
# Internal C preprocessor flags that should not be changed by the user.
override CPPFLAGS := \
-I src \
-I vendor \
$(CPPFLAGS) \
-MMD \
-MP
@ -84,7 +86,7 @@ override NASMFLAGS += \
# Use "find" to glob all *.c, *.S, and *.asm files in the tree and obtain the
# object and header dependency file names.
override CFILES := $(shell cd src && find -L * -type f -name '*.c')
override CFILES := $(shell cd src && find -L * -type f -name '*.c') $(shell cd vendor && find -L * -type f -name '*.c')
override ASFILES := $(shell cd src && find -L * -type f -name '*.S')
override NASMFILES := $(shell cd src && find -L * -type f -name '*.asm')
override OBJ := $(addprefix obj/,$(CFILES:.c=.c.o) $(ASFILES:.S=.S.o) $(NASMFILES:.asm=.asm.o))
@ -98,7 +100,7 @@ src/limine.h:
curl -Lo $@ https://github.com/limine-bootloader/limine/raw/trunk/limine.h
# Link rules for the final kernel executable.
bin/$(KERNEL): GNUmakefile linker.ld $(OBJ)
bin/$(KERNEL): Makefile linker.ld $(OBJ)
mkdir -p "$$(dirname $@)"
$(LD) $(OBJ) $(LDFLAGS) -o $@
@ -106,17 +108,21 @@ bin/$(KERNEL): GNUmakefile linker.ld $(OBJ)
-include $(HEADER_DEPS)
# Compilation rules for *.c files.
obj/%.c.o: src/%.c GNUmakefile src/limine.h
obj/%.c.o: src/%.c Makefile src/limine.h
mkdir -p "$$(dirname $@)"
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
obj/%.c.o: vendor/%.c Makefile src/limine.h
mkdir -p "$$(dirname $@)"
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
# Compilation rules for *.S files.
obj/%.S.o: src/%.S GNUmakefile
obj/%.S.o: src/%.S Makefile
mkdir -p "$$(dirname $@)"
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
# Compilation rules for *.asm (nasm) files.
obj/%.asm.o: src/%.asm GNUmakefile
obj/%.asm.o: src/%.asm Makefile
mkdir -p "$$(dirname $@)"
nasm $(NASMFLAGS) $< -o $@

5
kernel/src/ctype.c Normal file
View file

@ -0,0 +1,5 @@
#include <ctype.h>
int isdigit(char c) {
return c >= '0' && c <= '9';
}

6
kernel/src/ctype.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef __CTYPE_H__
#define __CTYPE_H__
int isdigit(char c);
#endif

18
kernel/src/framebuffer.c Normal file
View file

@ -0,0 +1,18 @@
#include <framebuffer.h>
#include <hcf.h>
#include <stddef.h>
struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
struct limine_framebuffer* framebuffer = { 0 };
void fb_init() {
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
hcf();
}
framebuffer = framebuffer_request.response->framebuffers[0];
}

9
kernel/src/framebuffer.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __FRAMEBUFFER_H__
#define __FRAMEBUFFER_H__
#include <limine.h>
extern struct limine_framebuffer* framebuffer;
void fb_init();
#endif

7
kernel/src/hcf.asm Normal file
View file

@ -0,0 +1,7 @@
global hcf
hcf:
cli
loop:
hlt
jmp loop

6
kernel/src/hcf.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef __HCF_H__
#define __HCF_H__
extern void hcf();
#endif

View file

@ -1,112 +1,108 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <framebuffer.h>
#include <terminal.h>
#include <limine.h>
// Set the base revision to 1, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.
#include <hcf.h>
#include <memory.h>
LIMINE_BASE_REVISION(1)
// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, in C, they should
// NOT be made "static".
struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0
};
// GCC and Clang reserve the right to generate calls to the following
// 4 functions even if they are not directly called.
// Implement them as the C specification mandates.
// DO NOT remove or rename these functions, or stuff will eventually break!
// They CAN be moved to a different .c file.
void* memcpy(void* dest, const void* src, size_t n) {
uint8_t* pdest = (uint8_t*)dest;
const uint8_t* psrc = (const uint8_t*)src;
void *memcpy(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
for(size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
return dest;
return dest;
}
void *memset(void *s, int c, size_t n) {
uint8_t *p = (uint8_t *)s;
void* memset(void* s, int c, size_t n) {
uint8_t* p = (uint8_t*)s;
for (size_t i = 0; i < n; i++) {
p[i] = (uint8_t)c;
}
for(size_t i = 0; i < n; i++) {
p[i] = (uint8_t)c;
}
return s;
return s;
}
void *memmove(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
void* memmove(void* dest, const void* src, size_t n) {
uint8_t* pdest = (uint8_t*)dest;
const uint8_t* psrc = (const uint8_t*)src;
if (src > dest) {
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if (src < dest) {
for (size_t i = n; i > 0; i--) {
pdest[i-1] = psrc[i-1];
}
}
if(src > dest) {
for(size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if(src < dest) {
for(size_t i = n; i > 0; i--) {
pdest[i-1] = psrc[i-1];
}
}
return dest;
return dest;
}
int memcmp(const void *s1, const void *s2, size_t n) {
const uint8_t *p1 = (const uint8_t *)s1;
const uint8_t *p2 = (const uint8_t *)s2;
int memcmp(const void* s1, const void* s2, size_t n) {
const uint8_t* p1 = (const uint8_t*)s1;
const uint8_t* p2 = (const uint8_t*)s2;
for (size_t i = 0; i < n; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
for(size_t i = 0; i < n; i++) {
if(p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
return 0;
}
// Halt and catch fire function.
static void hcf(void) {
asm ("cli");
for (;;) {
asm ("hlt");
}
}
const char* types[] = {
"USABLE",
"RESERVED",
"ACPI_RECLAIMABLE",
"ACPI_NVS",
"BAD_MEMORY",
"BOOTLOADER_RECLAIMABLE",
"KERNEL_AND_MODULES",
"FRAMEBUFFER"
};
// The following will be our kernel's entry point.
// If renaming _start() to something else, make sure to change the
// linker script accordingly.
void _start(void) {
// Ensure the bootloader actually understands our base revision (see spec).
if (LIMINE_BASE_REVISION_SUPPORTED == false) {
hcf();
}
if(LIMINE_BASE_REVISION_SUPPORTED == false) {
hcf();
}
fb_init();
term_init();
mem_init();
// Ensure we got a framebuffer.
if (framebuffer_request.response == NULL
|| framebuffer_request.response->framebuffer_count < 1) {
hcf();
}
printf("running at %dx%d\n", framebuffer->width, framebuffer->height);
// Fetch the first framebuffer.
struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0];
struct limine_memmap_response* memmap = memmap_request.response;
// Note: we assume the framebuffer model is RGB with 32-bit pixels.
for (size_t i = 0; i < 100; i++) {
volatile uint32_t *fb_ptr = framebuffer->address;
fb_ptr[i * (framebuffer->pitch / 4) + i] = 0xffffff;
}
printf("%d memmap entries present.\n", memmap->entry_count);
// We're done, just hang...
hcf();
for(uint64_t i = 0; i < memmap->entry_count; i++) {
struct limine_memmap_entry* entry = memmap->entries[i];
if(entry->type == LIMINE_MEMMAP_USABLE)
mem_add(entry->base, entry->length);
printf("0x%010x - 0x%010x (type: % 22s, size: % 10u)\n",
entry->base, entry->base + entry->length, types[entry->type], entry->length);
}
void* a = mem_alloc(10);
printf("memory allocated at %x", a);
hcf();
}

35
kernel/src/memory.c Normal file
View file

@ -0,0 +1,35 @@
#include <memory.h>
void* mem_start;
void* mem_ptr;
int blocks = 0;
struct limine_kernel_address_request kernel_address_request = {
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
.revision = 0
};
struct limine_kernel_address_response* kernel_address = { 0 };
void mem_init() {
kernel_address = kernel_address_request.response;
}
void mem_add(uint64_t base, uint64_t len) {
if(!mem_start) {
mem_start = mem_ptr = (void*)kernel_address->virtual_base + base;
*((struct mem_desc*)mem_start) = (struct mem_desc) {
.base = base,
.len = len
};
mem_ptr += sizeof(struct mem_desc);
blocks++;
}
}
void* mem_alloc(size_t sz) {
void* start = mem_ptr;
*((size_t*)start) = sz;
mem_ptr += sz + sizeof(size_t);
return start + sizeof(size_t);
}

18
kernel/src/memory.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef __MEMORY_H__
#define __MEMORY_H__
#include <limine.h>
#include <stddef.h>
void mem_init();
void mem_add(uint64_t base, uint64_t len);
void* mem_alloc(size_t sz);
struct mem_desc {
uint64_t base;
uint64_t len;
};
extern struct limine_kernel_address_response* kernel_address;
#endif

73
kernel/src/stdio.c Normal file
View file

@ -0,0 +1,73 @@
#include <printf.h>
#include <string.h>
#include <stdlib.h>
#include <terminal.h>
#include <flanterm/flanterm.h>
#include <ctype.h>
void putc(char ch) {
flanterm_write(ft_ctx, &ch, 1);
}
void puts(const char* str) {
flanterm_write(ft_ctx, str, strlen(str));
}
void printf(const char *str, ...) {
va_list list;
va_start(list, str);
char buf[128];
for(int i = 0; str[i] != '\0'; i++)
{
switch(str[i])
{
case '%':
i++;
size_t width = 0;
char padding_char = str[i];
if(isdigit(str[i]) || str[i] == ' ') {
i++;
int pos = 0;
while(isdigit(str[i])) {
buf[pos++] = str[i++];
}
buf[pos] = '\0';
width = atoi(buf);
}
const char* out = 0;
if(str[i] == 's')
out = va_arg(list, const char*);
if(str[i] == 'd')
out = itoa(va_arg(list, int), buf, 10);
if(str[i] == 'u')
out = ultoa(va_arg(list, uint64_t), buf, 10);
if(str[i] == 'x')
out = ultoa(va_arg(list, uint64_t), buf, 16);
if(str[i] == 'c')
putc(va_arg(list, int));
size_t len = strlen(out);
if(width > len) {
char padding[256];
size_t j = 0;
for(; j < width - len; j++)
padding[j] = padding_char;
padding[j] = '\0';
puts(padding);
}
puts(out);
break;
default:
putc(str[i]);
break;
}
}
va_end(list);
}

8
kernel/src/stdio.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __STDIO_H__
#define __STDIO_H__
void putc(char ch);
void puts(const char* str);
void printf(const char *str, ...);
#endif

77
kernel/src/stdlib.c Normal file
View file

@ -0,0 +1,77 @@
#include <stdlib.h>
#include <ctype.h>
const char* itoa(int value, char* str, int base)
{
char* rc;
char* ptr;
char* low;
if (base < 2 || base > 36)
{
*str = '\0';
return str;
}
rc = ptr = str;
if (value < 0 && base == 10)
{
*ptr++ = '-';
}
low = ptr;
do
{
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + value % base];
value /= base;
} while (value);
*ptr-- = '\0';
while (low < ptr)
{
char tmp = *low;
*low++ = *ptr;
*ptr-- = tmp;
}
return rc;
}
const char* ultoa(uint64_t value, char* str, int base)
{
char* rc;
char* ptr;
char* low;
if (base < 2 || base > 36)
{
*str = '\0';
return str;
}
rc = ptr = str;
low = ptr;
do
{
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + value % base];
value /= base;
} while (value);
*ptr-- = '\0';
while (low < ptr)
{
char tmp = *low;
*low++ = *ptr;
*ptr-- = tmp;
}
return rc;
}
int atoi(const char* str) {
int value = 0;
while(isdigit(*str)) {
value *= 10;
value += (*str)-'0';
str++;
}
return value;
}

9
kernel/src/stdlib.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __STDLIB_H__
#define __STDLIB_H__
#include <stdint.h>
const char* itoa(int value, char* str, int base);
const char* ultoa(uint64_t value, char* str, int base);
int atoi(const char* str);
#endif

8
kernel/src/string.c Normal file
View file

@ -0,0 +1,8 @@
#include <string.h>
size_t strlen(const char* str) {
size_t len = 0;
while (str[len])
len++;
return len;
}

8
kernel/src/string.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __STRING_H__
#define __STRING_H__
#include <stddef.h>
size_t strlen(const char* str);
#endif

11
kernel/src/terminal.c Normal file
View file

@ -0,0 +1,11 @@
#include <terminal.h>
#include <flanterm/backends/fb.h>
#include <framebuffer.h>
struct flanterm_context* ft_ctx = { 0 };
void term_init() {
ft_ctx = flanterm_fb_simple_init(
framebuffer->address, framebuffer->width, framebuffer->height, framebuffer->pitch
);
}

7
kernel/src/terminal.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __TERMINAL_H__
#define __TERMINAL_H__
void term_init();
extern struct flanterm_context* ft_ctx;
#endif

1
kernel/vendor/flanterm vendored Submodule

@ -0,0 +1 @@
Subproject commit 545ab1faa8ba7493ae7a43064d520e402ad308b5

View file

@ -1,16 +1,16 @@
# Timeout in seconds that Limine will use before automatically booting.
TIMEOUT=3
TIMEOUT=0
# The entry name that will be displayed in the boot menu.
:Limine Template (KASLR on)
#:Limine Template (KASLR on)
# We use the Limine boot protocol.
PROTOCOL=limine
# PROTOCOL=limine
# Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located.
KERNEL_PATH=boot:///kernel
# KERNEL_PATH=boot:///kernel
# Same thing, but without KASLR.
:Limine Template (KASLR off)
:radix
PROTOCOL=limine
# Disable KASLR (it is enabled by default for relocatable kernels)