commit 591b6d61c5863ef341ffa65319de99f32aa0a39b Author: Rick Rongen Date: Fri Mar 17 22:13:42 2023 +0100 initial kernel diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ce05f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,109 @@ +# Created by https://www.toptal.com/developers/gitignore/api/cmake,clion+all +# Edit at https://www.toptal.com/developers/gitignore?templates=cmake,clion+all + +### CLion+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### CLion+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +# End of https://www.toptal.com/developers/gitignore/api/cmake,clion+all diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c2c81be --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +project(yak) +cmake_minimum_required() + +add_subdirectory(yak-kernel) + +add_custom_target(boot-cdrom.iso + COMMAND xorriso -follow on -as mkisofs + -b limine-cd.bin + -no-emul-boot + -boot-load-size 4 + -boot-info-table + --efi-boot + limine-cd-efi.bin + -efi-boot-part + --efi-boot-image + --protective-msdos-label + ${CMAKE_CURRENT_SOURCE_DIR}/boot-cdrom + -o boot-cdrom.iso + DEPENDS yak.elf) \ No newline at end of file diff --git a/boot-cdrom/limine-cd-efi.bin b/boot-cdrom/limine-cd-efi.bin new file mode 120000 index 0000000..d333f7d --- /dev/null +++ b/boot-cdrom/limine-cd-efi.bin @@ -0,0 +1 @@ +../limine/limine/limine-cd-efi.bin \ No newline at end of file diff --git a/boot-cdrom/limine-cd.bin b/boot-cdrom/limine-cd.bin new file mode 120000 index 0000000..65d1d7a --- /dev/null +++ b/boot-cdrom/limine-cd.bin @@ -0,0 +1 @@ +../limine/limine/limine-cd.bin \ No newline at end of file diff --git a/boot-cdrom/limine.cfg b/boot-cdrom/limine.cfg new file mode 100644 index 0000000..d4e5bd2 --- /dev/null +++ b/boot-cdrom/limine.cfg @@ -0,0 +1,10 @@ +# Timeout in seconds that Limine will use before automatically booting. +TIMEOUT=3 + +# The entry name that will be displayed in the boot menu +:YAK + # Change the protocol line depending on the used protocol. + PROTOCOL=limine + + # Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located. + KERNEL_PATH=boot:///yak.elf \ No newline at end of file diff --git a/boot-cdrom/limine.sys b/boot-cdrom/limine.sys new file mode 120000 index 0000000..0c8c7e3 --- /dev/null +++ b/boot-cdrom/limine.sys @@ -0,0 +1 @@ +../limine/limine/limine.sys \ No newline at end of file diff --git a/boot-cdrom/yak.elf b/boot-cdrom/yak.elf new file mode 120000 index 0000000..387d5d9 --- /dev/null +++ b/boot-cdrom/yak.elf @@ -0,0 +1 @@ +../cmake-build-debug-system-llvm/yak-kernel/yak.elf \ No newline at end of file diff --git a/limine/.gitignore b/limine/.gitignore new file mode 100644 index 0000000..235b999 --- /dev/null +++ b/limine/.gitignore @@ -0,0 +1 @@ +limine/ \ No newline at end of file diff --git a/limine/fetch.sh b/limine/fetch.sh new file mode 100644 index 0000000..e57b158 --- /dev/null +++ b/limine/fetch.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +LIMINE_VERSION=v4.20230315.0 + +git clone https://github.com/limine-bootloader/limine.git --branch="${LIMINE_VERSION}-binary" --depth=1 diff --git a/limine/include/limine-hdd.h b/limine/include/limine-hdd.h new file mode 120000 index 0000000..87b4147 --- /dev/null +++ b/limine/include/limine-hdd.h @@ -0,0 +1 @@ +../limine/limine-hdd.h \ No newline at end of file diff --git a/limine/include/limine.h b/limine/include/limine.h new file mode 120000 index 0000000..b93ff4c --- /dev/null +++ b/limine/include/limine.h @@ -0,0 +1 @@ +../limine/limine.h \ No newline at end of file diff --git a/run-qemu.sh b/run-qemu.sh new file mode 100755 index 0000000..420009e --- /dev/null +++ b/run-qemu.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +ISO=${1} + +qemu-system-x86_64 -bios /usr/share/ovmf/x64/OVMF.fd -cdrom "${ISO}" #-S -s -d guest_errors \ No newline at end of file diff --git a/yak-kernel/CMakeLists.txt b/yak-kernel/CMakeLists.txt new file mode 100644 index 0000000..91d49a5 --- /dev/null +++ b/yak-kernel/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/yak-kernel/linker.lds b/yak-kernel/linker.lds new file mode 100644 index 0000000..ccdd7fb --- /dev/null +++ b/yak-kernel/linker.lds @@ -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.*) + } +} \ No newline at end of file diff --git a/yak-kernel/main.c b/yak-kernel/main.c new file mode 100644 index 0000000..84d7f71 --- /dev/null +++ b/yak-kernel/main.c @@ -0,0 +1,107 @@ +#include +#include +#include + +// 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(); +} \ No newline at end of file