initial kernel

This commit is contained in:
2023-03-17 22:13:42 +01:00
commit 591b6d61c5
15 changed files with 346 additions and 0 deletions

27
yak-kernel/CMakeLists.txt Normal file
View File

@@ -0,0 +1,27 @@
project(yak-kernel)
set(CMAKE_C_FLAGS "
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-lto \
-fno-pie \
-fno-pic \
-m64 \
-march=x86-64 \
-mabi=sysv \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-mcmodel=kernel")
set(CMAKE_EXE_LINKER_FLAGS "
-nostdlib \
-static \
-z max-page-size=0x1000 \
-T ${CMAKE_CURRENT_SOURCE_DIR}/linker.lds")
include_directories(../limine/include)
add_executable(yak.elf main.c)

57
yak-kernel/linker.lds Normal file
View File

@@ -0,0 +1,57 @@
/* Tell the linker that we want an x86_64 ELF64 output file */
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
/* We want the symbol _start to be our entry point */
ENTRY(_start)
/* Define the program headers we want so the bootloader gives us the right */
/* MMU permissions */
PHDRS
{
text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */
rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */
data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */
}
SECTIONS
{
/* We wanna be placed in the topmost 2GiB of the address space, for optimisations */
/* and because that is what the Limine spec mandates. */
/* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
/* that is the beginning of the region. */
. = 0xffffffff80000000;
.text : {
*(.text .text.*)
} :text
/* Move to the next memory page for .rodata */
. += CONSTANT(MAXPAGESIZE);
.rodata : {
*(.rodata .rodata.*)
} :rodata
/* Move to the next memory page for .data */
. += CONSTANT(MAXPAGESIZE);
.data : {
*(.data .data.*)
} :data
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
/* unnecessary zeros will be written to the binary. */
/* If you need, for example, .init_array and .fini_array, those should be placed */
/* above this. */
.bss : {
*(COMMON)
*(.bss .bss.*)
} :data
/* Discard .note.* and .eh_frame since they may cause issues on some hosts. */
/DISCARD/ : {
*(.eh_frame)
*(.note .note.*)
}
}

107
yak-kernel/main.c Normal file
View File

@@ -0,0 +1,107 @@
#include <stdint.h>
#include <stddef.h>
#include <limine.h>
// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, usually, they should
// be made volatile or equivalent.
static volatile struct limine_terminal_request terminal_request = {
.id = LIMINE_TERMINAL_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;
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
return dest;
}
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;
}
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;
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;
}
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;
}
}
return 0;
}
// Our quick and dirty strlen() implementation.
size_t strlen(const char *str) {
size_t ret = 0;
while (*str++) {
ret++;
}
return ret;
}
// Halt and catch fire function.
static void hcf(void) {
__asm__ ("cli");
for (;;) {
__asm__ ("hlt");
}
}
// 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 we got a terminal
if (terminal_request.response == NULL
|| terminal_request.response->terminal_count < 1) {
hcf();
}
// We should now be able to call the Limine terminal to print out
// a simple "Hello World" to screen.
const char *hello_msg = "Hello World";
struct limine_terminal *terminal = terminal_request.response->terminals[0];
terminal_request.response->write(terminal, hello_msg, strlen(hello_msg));
// We're done, just hang...
hcf();
}