summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob McDonnell <jacob@jacobmcdonnell.com>2025-06-08 15:03:33 -0400
committerJacob McDonnell <jacob@jacobmcdonnell.com>2025-06-08 15:03:33 -0400
commita4eef728064cfc3f14396d0ad91f105f12ef32ea (patch)
treefc4a4d2f6525705fc4e8ff3c262abcbde0ae6d19
Initial Commit
-rw-r--r--.gitignore4
m---------limine0
-rw-r--r--limine.conf6
-rw-r--r--linker.ld68
-rw-r--r--makefile85
-rw-r--r--source/LimineRequests.c45
-rw-r--r--source/LimineRequests.h15
-rw-r--r--source/main.c51
-rw-r--r--source/memory.c48
-rw-r--r--source/memory.h12
10 files changed, 334 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..05df4a3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+bin
+image.iso
+iso_root
+objects
diff --git a/limine b/limine
new file mode 160000
+Subproject 7d2bf778c27067ba421e95154208c8b0f3bbf10
diff --git a/limine.conf b/limine.conf
new file mode 100644
index 0000000..816d479
--- /dev/null
+++ b/limine.conf
@@ -0,0 +1,6 @@
+timeout: 5
+
+/kernel.bin
+ protocol: limine
+ path: boot():/boot/kernel.bin
+
diff --git a/linker.ld b/linker.ld
new file mode 100644
index 0000000..3b35556
--- /dev/null
+++ b/linker.ld
@@ -0,0 +1,68 @@
+/* Tell the linker that we want an x86_64 ELF64 output file */
+OUTPUT_FORMAT(elf64-x86-64)
+
+/* We want the symbol kmain to be our entry point */
+ENTRY(KernelMain)
+
+/* Define the program headers we want so the bootloader gives us the right */
+/* MMU permissions; this also allows us to exert more control over the linking */
+/* process. */
+PHDRS
+{
+ limine_requests PT_LOAD;
+ text PT_LOAD;
+ rodata PT_LOAD;
+ data PT_LOAD;
+}
+
+SECTIONS
+{
+ /* We want to 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;
+
+ /* Define a section to contain the Limine requests and assign it to its own PHDR */
+ .limine_requests : {
+ KEEP(*(.limine_requests_start))
+ KEEP(*(.limine_requests))
+ KEEP(*(.limine_requests_end))
+ } :limine_requests
+
+ /* Move to the next memory page for .text */
+ . = ALIGN(CONSTANT(MAXPAGESIZE));
+
+ .text : {
+ *(.text .text.*)
+ } :text
+
+ /* Move to the next memory page for .rodata */
+ . = ALIGN(CONSTANT(MAXPAGESIZE));
+
+ .rodata : {
+ *(.rodata .rodata.*)
+ } :rodata
+
+ /* Move to the next memory page for .data */
+ . = ALIGN(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 : {
+ *(.bss .bss.*)
+ *(COMMON)
+ } :data
+
+ /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
+ /DISCARD/ : {
+ *(.eh_frame*)
+ *(.note .note.*)
+ }
+}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..fef52a2
--- /dev/null
+++ b/makefile
@@ -0,0 +1,85 @@
+BIN_DIR = bin
+BIN = $(BIN_DIR)/kernel.bin
+ISO = image.iso
+ISO_ROOT = iso_root
+SRC_DIR = source
+SOURCES = $(SRC_DIR)/main.c \
+ $(SRC_DIR)/LimineRequests.c \
+ $(SRC_DIR)/memory.c
+CFILES = $(filter %.c,$(SOURCES))
+ASFILES = $(filter %.S,$(SOURCES))
+NASMFILES = $(filter %.asm,$(SOURCES))
+OBJ_DIR = objects
+OBJECTS = $(addprefix $(OBJ_DIR)/,$(notdir $(CFILES:.c=.c.o)) \
+ $(notdir $(ASFILES:.S=.S.o)) \
+ $(notdir $(NASMFILES:.asm=.asm.o)))
+CC = gcc
+CFLAGS = -Wall \
+ -Wextra \
+ -Werror \
+ -Wpedantic \
+ -std=gnu23 \
+ -ffreestanding \
+ -fno-stack-protector \
+ -fno-stack-check \
+ -fno-PIC \
+ -m64 \
+ -march=x86-64 \
+ -mno-80387 \
+ -mno-mmx \
+ -mno-sse \
+ -mno-sse2 \
+ -mno-red-zone \
+ -I src \
+ -DLIMINE_API_REVISION=3 \
+ -MMD \
+ -MP \
+ -mcmodel=kernel
+LDFLAGS = -Wl,-m,elf_x86_64 \
+ -Wl,--build-id=none \
+ -nostdlib \
+ -static \
+ -z max-page-size=0x1000 \
+ -T linker.ld
+NASMFLAGS = -Wall -f elf64
+
+.PHONY: all clean run
+all: $(ISO)
+
+
+$(BIN_DIR) $(OBJ_DIR) $(ISO_ROOT):
+ mkdir $@
+
+$(BIN): linker.ld $(OBJECTS) | $(BIN_DIR)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $@
+
+$(OBJ_DIR)/%.c.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+$(OBJ_DIR)/%.S.o: $(SRC_DIR)/%.S | $(OBJ_DIR)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+$(OBJ_DIR)/%.asm.o: $(SRC_DIR)/%.asm | $(OBJ_DIR)
+ nasm $(NASMFLAGS) $< -o $@
+
+$(ISO): $(BIN) | $(ISO_ROOT)
+ mkdir -p $(ISO_ROOT)/boot/limine
+ cp -v $(BIN) $(ISO_ROOT)/boot/
+ cp -v limine.conf limine/limine-bios.sys limine/limine-bios-cd.bin \
+ limine/limine-uefi-cd.bin $(ISO_ROOT)/boot/limine/
+ mkdir -p iso_root/EFI/BOOT
+ cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/
+ cp -v limine/BOOTIA32.EFI iso_root/EFI/BOOT/
+ xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \
+ -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
+ -apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \
+ -efi-boot-part --efi-boot-image --protective-msdos-label \
+ iso_root -o $@
+ ./limine/limine bios-install $@
+
+run: $(ISO)
+ qemu-system-x86_64 -hda $(ISO)
+
+clean:
+ rm -rf $(BIN_DIR) $(OBJ_DIR) $(ISO) $(ISO_ROOT)
+
diff --git a/source/LimineRequests.c b/source/LimineRequests.c
new file mode 100644
index 0000000..a3b9bd5
--- /dev/null
+++ b/source/LimineRequests.c
@@ -0,0 +1,45 @@
+#include <stdint.h>
+#include <limine.h>
+#include "LimineRequests.h"
+
+__attribute__((used, section(".limine_requests_start")))
+LIMINE_REQUESTS_START_MARKER
+
+__attribute__((used, section(".limine_requests")))
+static volatile LIMINE_BASE_REVISION(3)
+
+bool VerifyBaseRevision(void) {
+ return (limine_base_revision[2] == 0);
+}
+
+__attribute__((used, section(".limine_requests")))
+struct limine_stack_size_request stackSizeRequest = {
+ LIMINE_STACK_SIZE_REQUEST,
+ 0,
+ nullptr,
+ //0xFFFF
+ 0x100000
+};
+
+bool VerifyStackSizeRequest(void) {
+ return ((stackSizeRequest.response != nullptr) &&
+ (stackSizeRequest.response->revision >= stackSizeRequest.revision));
+}
+
+__attribute__((used, section(".limine_requests")))
+struct limine_framebuffer_request framebufferRequest = {
+ LIMINE_FRAMEBUFFER_REQUEST,
+ 0,
+ nullptr
+};
+
+bool VerifyFramebufferRequest(void) {
+ return ((framebufferRequest.response != nullptr) &&
+ (framebufferRequest.response->framebuffer_count > 0) &&
+ (framebufferRequest.response->revision >=
+ framebufferRequest.revision));
+}
+
+__attribute__((used, section(".limine_requests_end")))
+LIMINE_REQUESTS_END_MARKER
+
diff --git a/source/LimineRequests.h b/source/LimineRequests.h
new file mode 100644
index 0000000..45da613
--- /dev/null
+++ b/source/LimineRequests.h
@@ -0,0 +1,15 @@
+#ifndef LIMINEREQUESTS_H_
+#define LIMINEREQUESTS_H_
+
+#include <stdint.h>
+#include <limine.h>
+
+extern struct limine_stack_size_request stackSizeRequest;
+extern struct limine_framebuffer_request framebufferRequest;
+
+bool VerifyBaseRevision(void);
+bool VerifyStackSizeRequest(void);
+bool VerifyFramebufferRequest(void);
+
+#endif // LIMINEREQUESTS_H_
+
diff --git a/source/main.c b/source/main.c
new file mode 100644
index 0000000..316274f
--- /dev/null
+++ b/source/main.c
@@ -0,0 +1,51 @@
+#include <stddef.h>
+#include <limine.h>
+#include "LimineRequests.h"
+#include "memory.h"
+
+static void HaltForever(void) {
+ while (true) {
+ asm("hlt");
+ }
+}
+
+void KernelMain(void) {
+ if (!VerifyBaseRevision()) {
+ HaltForever();
+ }
+
+ if (!VerifyStackSizeRequest()) {
+ HaltForever();
+ }
+
+ if (!VerifyFramebufferRequest()) {
+ HaltForever();
+ }
+
+ const uint64_t framebufferCount =
+ framebufferRequest.response->framebuffer_count;
+
+ struct limine_framebuffer **framebuffers =
+ framebufferRequest.response->framebuffers;
+
+ struct limine_framebuffer *framebuffer = nullptr;
+
+ const uint32_t colors[] = {0xFF0000, 0x00FF00, 0x0000FF};
+ uint8_t count = 0;
+ uint8_t color = 0;
+
+ for (uint64_t i = 0; i < framebufferCount; i++) {
+ framebuffer = framebuffers[i];
+ volatile uint32_t *fb_ptr = (volatile uint32_t *)framebuffer->address;
+ for (uint64_t j = 0; j < framebuffer->height && j < framebuffer->width;
+ j++) {
+ fb_ptr[j * (framebuffer->pitch / 4) + j] = colors[color];
+ if ((count = (count + 1) % 16) == 0) {
+ color = (color + 1) % 3;
+ }
+ }
+ }
+
+ HaltForever();
+}
+
diff --git a/source/memory.c b/source/memory.c
new file mode 100644
index 0000000..b9d0405
--- /dev/null
+++ b/source/memory.c
@@ -0,0 +1,48 @@
+#include <stdint.h>
+#include "memory.h"
+
+void *memcpy(void *dest, const void *src, const size_t n) {
+ uint8_t *destB = (void *)dest;
+ const uint8_t *srcB = (void *)src;
+
+ for (size_t i = 0; i < n; i++) {
+ destB[i] = srcB[i];
+ }
+
+ return dest;
+}
+
+void *memset(void *s, int c, size_t n) {
+ uint8_t *sB = (uint8_t *)s;
+
+ for (size_t i = 0; i < n; i++) {
+ sB[i] = (uint8_t)c;
+ }
+
+ return s;
+}
+
+void *memmove(void *dest, const void *src, const size_t n) {
+ uint8_t *db = (uint8_t *)dest;
+ const uint8_t *sb = (const uint8_t *)src;
+
+ for (size_t i = n; i > 0; i--) {
+ db[i - 1] = sb[i - 1];
+ }
+
+ return dest;
+}
+
+int memcmp(const void *s1, const void *s2, const size_t n) {
+ const uint8_t *s1b = (const uint8_t *)s1;
+ const uint8_t *s2b = (const uint8_t *)s2;
+
+ for (size_t i = 0; i < n; i++) {
+ if (s1b[i] != s2b[i]) {
+ return ((s1b[i] > s2b[i]) ? 1 : -1);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/source/memory.h b/source/memory.h
new file mode 100644
index 0000000..0404144
--- /dev/null
+++ b/source/memory.h
@@ -0,0 +1,12 @@
+#ifndef MEMORY_H_
+#define MEMORY_H_
+
+#include <stddef.h>
+
+void *memcpy(void *dest, const void *src, const size_t n);
+void *memset(void *s, int c, size_t n);
+void *memmove(void *dest, const void *src, const size_t n);
+int memcmp(const void *s1, const void *s2, const size_t n);
+
+#endif // MEMORY_H_
+