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

109
.gitignore vendored Normal file
View File

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

19
CMakeLists.txt Normal file
View File

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

View File

@@ -0,0 +1 @@
../limine/limine/limine-cd-efi.bin

1
boot-cdrom/limine-cd.bin Symbolic link
View File

@@ -0,0 +1 @@
../limine/limine/limine-cd.bin

10
boot-cdrom/limine.cfg Normal file
View File

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

1
boot-cdrom/limine.sys Symbolic link
View File

@@ -0,0 +1 @@
../limine/limine/limine.sys

1
boot-cdrom/yak.elf Symbolic link
View File

@@ -0,0 +1 @@
../cmake-build-debug-system-llvm/yak-kernel/yak.elf

1
limine/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
limine/

5
limine/fetch.sh Normal file
View File

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

1
limine/include/limine-hdd.h Symbolic link
View File

@@ -0,0 +1 @@
../limine/limine-hdd.h

1
limine/include/limine.h Symbolic link
View File

@@ -0,0 +1 @@
../limine/limine.h

5
run-qemu.sh Executable file
View File

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

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();
}