GDT, page frame allocator

This commit is contained in:
Daniel Hammer
2025-02-27 19:48:03 +04:00
commit 6f1c6f1316
31 changed files with 2016 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
/kernel-deps
/limine
/ovmf
*.iso
*.hdd
+67
View File
@@ -0,0 +1,67 @@
{
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdlib": "cpp",
"exception": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"limits": "cpp",
"memory": "cpp",
"new": "cpp",
"numbers": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ratio": "cpp",
"span": "cpp",
"string_view": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"utility": "cpp",
"variant": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"cstdio": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"algorithm": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"deque": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"iterator": "cpp",
"memory_resource": "cpp",
"random": "cpp",
"system_error": "cpp",
"format": "cpp",
"fstream": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"mutex": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stdfloat": "cpp",
"streambuf": "cpp",
"text_encoding": "cpp",
"thread": "cpp",
"cinttypes": "cpp"
}
}
+249
View File
@@ -0,0 +1,249 @@
# Nuke built-in rules and variables.
MAKEFLAGS += -rR
.SUFFIXES:
# Target architecture to build for. Default to x86_64.
ARCH := x86_64
# Default user QEMU flags. These are appended to the QEMU command calls.
QEMUFLAGS := -m 2G
override IMAGE_NAME := template-$(ARCH)
# Toolchain for building the 'limine' executable for the host.
HOST_CC := cc
HOST_CFLAGS := -g -O2 -pipe
HOST_CPPFLAGS :=
HOST_LDFLAGS :=
HOST_LIBS :=
.PHONY: all
all: $(IMAGE_NAME).iso
.PHONY: all-hdd
all-hdd: $(IMAGE_NAME).hdd
.PHONY: run
run: run-$(ARCH)
.PHONY: run-hdd
run-hdd: run-hdd-$(ARCH)
.PHONY: run-x86_64
run-x86_64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).iso
qemu-system-$(ARCH) \
-M q35 \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-cdrom $(IMAGE_NAME).iso \
$(QEMUFLAGS)
.PHONY: run-hdd-x86_64
run-hdd-x86_64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).hdd
qemu-system-$(ARCH) \
-M q35 \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-hda $(IMAGE_NAME).hdd \
$(QEMUFLAGS)
.PHONY: run-aarch64
run-aarch64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).iso
qemu-system-$(ARCH) \
-M virt \
-cpu cortex-a72 \
-device ramfb \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-cdrom $(IMAGE_NAME).iso \
$(QEMUFLAGS)
.PHONY: run-hdd-aarch64
run-hdd-aarch64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).hdd
qemu-system-$(ARCH) \
-M virt \
-cpu cortex-a72 \
-device ramfb \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-hda $(IMAGE_NAME).hdd \
$(QEMUFLAGS)
.PHONY: run-riscv64
run-riscv64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).iso
qemu-system-$(ARCH) \
-M virt \
-cpu rv64 \
-device ramfb \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-cdrom $(IMAGE_NAME).iso \
$(QEMUFLAGS)
.PHONY: run-hdd-riscv64
run-hdd-riscv64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).hdd
qemu-system-$(ARCH) \
-M virt \
-cpu rv64 \
-device ramfb \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-hda $(IMAGE_NAME).hdd \
$(QEMUFLAGS)
.PHONY: run-loongarch64
run-loongarch64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).iso
qemu-system-$(ARCH) \
-M virt \
-cpu la464 \
-device ramfb \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-cdrom $(IMAGE_NAME).iso \
$(QEMUFLAGS)
.PHONY: run-hdd-loongarch64
run-hdd-loongarch64: ovmf/ovmf-code-$(ARCH).fd $(IMAGE_NAME).hdd
qemu-system-$(ARCH) \
-M virt \
-cpu la464 \
-device ramfb \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse \
-drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-$(ARCH).fd,readonly=on \
-hda $(IMAGE_NAME).hdd \
$(QEMUFLAGS)
.PHONY: run-bios
run-bios: $(IMAGE_NAME).iso
qemu-system-$(ARCH) \
-M q35 \
-cdrom $(IMAGE_NAME).iso \
-boot d \
$(QEMUFLAGS)
.PHONY: run-hdd-bios
run-hdd-bios: $(IMAGE_NAME).hdd
qemu-system-$(ARCH) \
-M q35 \
-hda $(IMAGE_NAME).hdd \
$(QEMUFLAGS)
ovmf/ovmf-code-$(ARCH).fd:
mkdir -p ovmf
curl -Lo $@ https://github.com/osdev0/edk2-ovmf-nightly/releases/latest/download/ovmf-code-$(ARCH).fd
case "$(ARCH)" in \
aarch64) dd if=/dev/zero of=$@ bs=1 count=0 seek=67108864 2>/dev/null;; \
riscv64) dd if=/dev/zero of=$@ bs=1 count=0 seek=33554432 2>/dev/null;; \
esac
limine/limine:
rm -rf limine
git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1
$(MAKE) -C limine \
CC="$(HOST_CC)" \
CFLAGS="$(HOST_CFLAGS)" \
CPPFLAGS="$(HOST_CPPFLAGS)" \
LDFLAGS="$(HOST_LDFLAGS)" \
LIBS="$(HOST_LIBS)"
kernel-deps:
./kernel/get-deps
touch kernel-deps
.PHONY: kernel
kernel: kernel-deps
$(MAKE) -C kernel
$(IMAGE_NAME).iso: limine/limine kernel
rm -rf iso_root
mkdir -p iso_root/boot
cp -v kernel/bin-$(ARCH)/kernel iso_root/boot/
mkdir -p iso_root/boot/limine
cp -v limine.conf iso_root/boot/limine/
mkdir -p iso_root/EFI/BOOT
ifeq ($(ARCH),x86_64)
cp -v limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin iso_root/boot/limine/
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 $(IMAGE_NAME).iso
./limine/limine bios-install $(IMAGE_NAME).iso
endif
ifeq ($(ARCH),aarch64)
cp -v limine/limine-uefi-cd.bin iso_root/boot/limine/
cp -v limine/BOOTAA64.EFI iso_root/EFI/BOOT/
xorriso -as mkisofs -R -r -J \
-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 $(IMAGE_NAME).iso
endif
ifeq ($(ARCH),riscv64)
cp -v limine/limine-uefi-cd.bin iso_root/boot/limine/
cp -v limine/BOOTRISCV64.EFI iso_root/EFI/BOOT/
xorriso -as mkisofs -R -r -J \
-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 $(IMAGE_NAME).iso
endif
ifeq ($(ARCH),loongarch64)
cp -v limine/limine-uefi-cd.bin iso_root/boot/limine/
cp -v limine/BOOTLOONGARCH64.EFI iso_root/EFI/BOOT/
xorriso -as mkisofs -R -r -J \
-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 $(IMAGE_NAME).iso
endif
rm -rf iso_root
$(IMAGE_NAME).hdd: limine/limine kernel
rm -f $(IMAGE_NAME).hdd
dd if=/dev/zero bs=1M count=0 seek=64 of=$(IMAGE_NAME).hdd
PATH=$$PATH:/usr/sbin:/sbin sgdisk $(IMAGE_NAME).hdd -n 1:2048 -t 1:ef00
ifeq ($(ARCH),x86_64)
./limine/limine bios-install $(IMAGE_NAME).hdd
endif
mformat -i $(IMAGE_NAME).hdd@@1M
mmd -i $(IMAGE_NAME).hdd@@1M ::/EFI ::/EFI/BOOT ::/boot ::/boot/limine
mcopy -i $(IMAGE_NAME).hdd@@1M kernel/bin-$(ARCH)/kernel ::/boot
mcopy -i $(IMAGE_NAME).hdd@@1M limine.conf ::/boot/limine
ifeq ($(ARCH),x86_64)
mcopy -i $(IMAGE_NAME).hdd@@1M limine/limine-bios.sys ::/boot/limine
mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTX64.EFI ::/EFI/BOOT
mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTIA32.EFI ::/EFI/BOOT
endif
ifeq ($(ARCH),aarch64)
mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTAA64.EFI ::/EFI/BOOT
endif
ifeq ($(ARCH),riscv64)
mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTRISCV64.EFI ::/EFI/BOOT
endif
ifeq ($(ARCH),loongarch64)
mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTLOONGARCH64.EFI ::/EFI/BOOT
endif
.PHONY: clean
clean:
$(MAKE) -C kernel clean
rm -rf iso_root $(IMAGE_NAME).iso $(IMAGE_NAME).hdd
.PHONY: distclean
distclean:
$(MAKE) -C kernel distclean
rm -rf iso_root *.iso *.hdd kernel-deps limine ovmf
+12
View File
@@ -0,0 +1,12 @@
Copyright (C) 2023-2025 mintsuki and contributors.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
+10
View File
@@ -0,0 +1,10 @@
# OS220
# Roadmap
[x] GDT
[ ] IDT/ISRs/Interrupts
[ ] Page frame allocator
[ ] Paging
[ ] Heap allocator
...
+6
View File
@@ -0,0 +1,6 @@
/freestnd-c-hdrs
/freestnd-cxx-hdrs
/cc-runtime*
/src/limine.h
/bin-*
/obj-*
+277
View File
@@ -0,0 +1,277 @@
# Nuke built-in rules and variables.
MAKEFLAGS += -rR
.SUFFIXES:
# This is the name that our final executable will have.
# Change as needed.
override OUTPUT := kernel
# Target architecture to build for. Default to x86_64.
ARCH := x86_64
# Install prefix; /usr/local is a good, standard default pick.
PREFIX := /usr/local
# Check if the architecture is supported.
ifeq ($(filter $(ARCH),aarch64 loongarch64 riscv64 x86_64),)
$(error Architecture $(ARCH) not supported)
endif
# User controllable C compiler command.
CC := cc
# User controllable C++ compiler command.
CXX := c++
# User controllable archiver command.
AR := ar
# User controllable C flags.
CFLAGS := -g -O2 -pipe
# User controllable C++ flags. We default to same as C flags.
CXXFLAGS := $(CFLAGS)
# User controllable C/C++ preprocessor flags. We set none by default.
CPPFLAGS :=
ifeq ($(ARCH),x86_64)
# User controllable nasm flags.
NASMFLAGS := -F dwarf -g
endif
# User controllable linker flags. We set none by default.
LDFLAGS :=
# Ensure the dependencies have been obtained.
ifneq ($(shell ( test '$(MAKECMDGOALS)' = clean || test '$(MAKECMDGOALS)' = distclean ); echo $$?),0)
ifeq ($(shell ( ! test -d freestnd-c-hdrs || ! test -d freestnd-cxx-hdrs || ! test -d cc-runtime || ! test -f src/limine.h ); echo $$?),0)
$(error Please run the ./get-deps script first)
endif
endif
# Check if CC is Clang.
override CC_IS_CLANG := $(shell ! $(CC) --version 2>/dev/null | grep 'clang' >/dev/null 2>&1; echo $$?)
# Check if CXX is Clang.
override CXX_IS_CLANG := $(shell ! $(CXX) --version 2>/dev/null | grep 'clang' >/dev/null 2>&1; echo $$?)
# Internal flags shared by both C and C++ compilers.
override SHARED_FLAGS := \
-Wall \
-Wextra \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-PIC \
-ffunction-sections \
-fdata-sections
# Internal C/C++ preprocessor flags that should not be changed by the user.
override CPPFLAGS_C := \
-I src \
-isystem freestnd-c-hdrs/$(ARCH)/include \
$(CPPFLAGS) \
-DLIMINE_API_REVISION=3 \
-MMD \
-MP
# Internal C++ only preprocessor flags.
override CPPFLAGS_CXX := \
-I src \
-isystem freestnd-c-hdrs/$(ARCH)/include \
-isystem freestnd-cxx-hdrs/$(ARCH)/include \
$(CPPFLAGS) \
-DLIMINE_API_REVISION=3 \
-MMD \
-MP
ifeq ($(ARCH),x86_64)
# Internal nasm flags that should not be changed by the user.
override NASMFLAGS += \
-Wall
endif
# Architecture specific internal flags.
ifeq ($(ARCH),x86_64)
ifeq ($(CC_IS_CLANG),1)
override CC += \
-target x86_64-unknown-none
endif
ifeq ($(CXX_IS_CLANG),1)
override CXX += \
-target x86_64-unknown-none
endif
override SHARED_FLAGS += \
-m64 \
-march=x86-64 \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-mcmodel=kernel
override LDFLAGS += \
-Wl,-m,elf_x86_64
override NASMFLAGS += \
-f elf64
endif
ifeq ($(ARCH),aarch64)
ifeq ($(CC_IS_CLANG),1)
override CC += \
-target aarch64-unknown-none
endif
ifeq ($(CXX_IS_CLANG),1)
override CXX += \
-target aarch64-unknown-none
endif
override SHARED_FLAGS += \
-mgeneral-regs-only
override LDFLAGS += \
-Wl,-m,aarch64elf
endif
ifeq ($(ARCH),riscv64)
ifeq ($(CC_IS_CLANG),1)
override CC += \
-target riscv64-unknown-none
override CFLAGS += \
-march=rv64imac
else
override CFLAGS += \
-march=rv64imac_zicsr_zifencei
endif
ifeq ($(CXX_IS_CLANG),1)
override CXX += \
-target riscv64-unknown-none
override CXXFLAGS += \
-march=rv64imac
else
override CXXFLAGS += \
-march=rv64imac_zicsr_zifencei
endif
override SHARED_FLAGS += \
-mabi=lp64 \
-mno-relax
override LDFLAGS += \
-Wl,-m,elf64lriscv \
-Wl,--no-relax
endif
ifeq ($(ARCH),loongarch64)
ifeq ($(CC_IS_CLANG),1)
override CC += \
-target loongarch64-unknown-none
endif
ifeq ($(CXX_IS_CLANG),1)
override CXX += \
-target loongarch64-unknown-none
endif
override SHARED_FLAGS += \
-march=loongarch64 \
-mabi=lp64s
override LDFLAGS += \
-Wl,-m,elf64loongarch \
-Wl,--no-relax
endif
# Internal C flags that should not be changed by the user.
override CFLAGS += \
-std=gnu11 \
$(SHARED_FLAGS)
# Internal C++ flags that should not be changed by the user.
override CXXFLAGS += \
-std=gnu++20 \
-fno-rtti \
-fno-exceptions \
$(SHARED_FLAGS)
# Internal linker flags that should not be changed by the user.
override LDFLAGS += \
-Wl,--build-id=none \
-nostdlib \
-static \
-z max-page-size=0x1000 \
-Wl,--gc-sections \
-T linker-$(ARCH).ld
# Use "find" to glob all *.c, *.cpp, *.S, and *.asm files in the tree and obtain the
# object and header dependency file names.
override SRCFILES := $(shell cd src && find -L * -type f | LC_ALL=C sort)
override CFILES := $(filter %.c,$(SRCFILES))
override CXXFILES := $(filter %.cpp,$(SRCFILES))
override ASFILES := $(filter %.S,$(SRCFILES))
ifeq ($(ARCH),x86_64)
override NASMFILES := $(filter %.asm,$(SRCFILES))
endif
override OBJ := $(addprefix obj-$(ARCH)/,$(CFILES:.c=.c.o) $(CXXFILES:.cpp=.cpp.o) $(ASFILES:.S=.S.o))
ifeq ($(ARCH),x86_64)
override OBJ += $(addprefix obj-$(ARCH)/,$(NASMFILES:.asm=.asm.o))
endif
override HEADER_DEPS := $(addprefix obj-$(ARCH)/,$(CFILES:.c=.c.d) $(CXXFILES:.cpp=.cpp.d) $(ASFILES:.S=.S.d))
# Default target. This must come first, before header dependencies.
.PHONY: all
all: bin-$(ARCH)/$(OUTPUT)
# Include header dependencies.
-include $(HEADER_DEPS)
# Link rules for building the C compiler runtime.
cc-runtime-$(ARCH)/cc-runtime.a: GNUmakefile cc-runtime/*
rm -rf cc-runtime-$(ARCH)
cp -r cc-runtime cc-runtime-$(ARCH)
$(MAKE) -C cc-runtime-$(ARCH) -f cc-runtime.mk \
CC="$(CC)" \
AR="$(AR)" \
CFLAGS="$(CFLAGS)" \
CPPFLAGS='-isystem ../freestnd-c-hdrs/$(ARCH)/include -DCC_RUNTIME_NO_FLOAT'
# Link rules for the final executable.
bin-$(ARCH)/$(OUTPUT): GNUmakefile linker-$(ARCH).ld $(OBJ) cc-runtime-$(ARCH)/cc-runtime.a
mkdir -p "$$(dirname $@)"
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJ) cc-runtime-$(ARCH)/cc-runtime.a -o $@
# Compilation rules for *.c files.
obj-$(ARCH)/%.c.o: src/%.c GNUmakefile
mkdir -p "$$(dirname $@)"
$(CC) $(CFLAGS) $(CPPFLAGS_C) -c $< -o $@
# Compilation rules for *.cpp files.
obj-$(ARCH)/%.cpp.o: src/%.cpp GNUmakefile
mkdir -p "$$(dirname $@)"
$(CXX) $(CXXFLAGS) $(CPPFLAGS_CXX) -c $< -o $@
# Compilation rules for *.S files.
obj-$(ARCH)/%.S.o: src/%.S GNUmakefile
mkdir -p "$$(dirname $@)"
$(CC) $(CFLAGS) $(CPPFLAGS_C) -c $< -o $@
ifeq ($(ARCH),x86_64)
# Compilation rules for *.asm (nasm) files.
obj-$(ARCH)/%.asm.o: src/%.asm GNUmakefile
mkdir -p "$$(dirname $@)"
nasm $(NASMFLAGS) $< -o $@
endif
# Remove object files and the final executable.
.PHONY: clean
clean:
rm -rf bin-$(ARCH) obj-$(ARCH) cc-runtime-$(ARCH)
# Remove everything built and generated including downloaded dependencies.
.PHONY: distclean
distclean:
rm -rf bin-* obj-* freestnd-c-hdrs freestnd-cxx-hdrs cc-runtime* src/limine.h
# Install the final built executable to its final on-root location.
.PHONY: install
install: all
install -d "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)"
install -m 644 bin-$(ARCH)/$(OUTPUT) "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)/$(OUTPUT)-$(ARCH)"
# Try to undo whatever the "install" target did.
.PHONY: uninstall
uninstall:
rm -f "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)/$(OUTPUT)-$(ARCH)"
-rmdir "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)"
+85
View File
@@ -0,0 +1,85 @@
#! /bin/sh
set -ex
srcdir="$(dirname "$0")"
test -z "$srcdir" && srcdir=.
cd "$srcdir"
clone_repo_commit() {
if test -d "$2/.git"; then
git -C "$2" reset --hard
git -C "$2" clean -fd
if ! git -C "$2" checkout $3; then
rm -rf "$2"
fi
else
if test -d "$2"; then
set +x
echo "error: '$2' is not a Git repository"
exit 1
fi
fi
if ! test -d "$2"; then
git clone $1 "$2"
if ! git -C "$2" checkout $3; then
rm -rf "$2"
exit 1
fi
fi
}
download_by_hash() {
DOWNLOAD_COMMAND="curl -Lo"
if ! command -v $DOWNLOAD_COMMAND >/dev/null 2>&1; then
DOWNLOAD_COMMAND="wget -O"
if ! command -v $DOWNLOAD_COMMAND >/dev/null 2>&1; then
set +x
echo "error: Neither curl nor wget found"
exit 1
fi
fi
SHA256_COMMAND="sha256sum"
if ! command -v $SHA256_COMMAND >/dev/null 2>&1; then
SHA256_COMMAND="sha256"
if ! command -v $SHA256_COMMAND >/dev/null 2>&1; then
set +x
echo "error: Cannot find sha256(sum) command"
exit 1
fi
fi
if ! test -f "$2" || ! $SHA256_COMMAND "$2" | grep $3 >/dev/null 2>&1; then
rm -f "$2"
mkdir -p "$2" && rm -rf "$2"
$DOWNLOAD_COMMAND "$2" $1
if ! $SHA256_COMMAND "$2" | grep $3 >/dev/null 2>&1; then
set +x
echo "error: Cannot download file '$2' by hash"
echo "incorrect hash:"
$SHA256_COMMAND "$2"
rm -f "$2"
exit 1
fi
fi
}
clone_repo_commit \
https://github.com/osdev0/freestnd-c-hdrs.git \
freestnd-c-hdrs \
21b59ecd6ef67bb32f893da8288ce08a324d1986
clone_repo_commit \
https://github.com/osdev0/freestnd-cxx-hdrs.git \
freestnd-cxx-hdrs \
d604a4c5033a7a4d610ba41daab3c541be96b43e
clone_repo_commit \
https://github.com/osdev0/cc-runtime.git \
cc-runtime \
576a01179f3298a4795b92f42c088f9f8800b56b
download_by_hash \
https://github.com/limine-bootloader/limine/raw/4687a182be23939c2d9f15db970382dc353ed956/limine.h \
src/limine.h \
6879e626f34c1be25ac2f72bf43b083fc2b53887280bb0fcdaee790e258c6974
+77
View File
@@ -0,0 +1,77 @@
/* Tell the linker that we want an aarch64 ELF64 output file */
OUTPUT_FORMAT(elf64-littleaarch64)
/* We want the symbol kmain to be our entry point */
ENTRY(kmain)
/* 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
/* C++ is a language that allows for global constructors. In order to obtain the */
/* address of the ".init_array" section we need to define a symbol for it. */
.init_array : {
__init_array = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.init_array .ctors))
__init_array_end = .;
} :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.*)
}
}
+77
View File
@@ -0,0 +1,77 @@
/* Tell the linker that we want a loongarch64 ELF64 output file */
OUTPUT_FORMAT(elf64-loongarch)
/* We want the symbol kmain to be our entry point */
ENTRY(kmain)
/* 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
/* C++ is a language that allows for global constructors. In order to obtain the */
/* address of the ".init_array" section we need to define a symbol for it. */
.init_array : {
__init_array = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.init_array .ctors))
__init_array_end = .;
} :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.*)
}
}
+79
View File
@@ -0,0 +1,79 @@
/* Tell the linker that we want a riscv64 ELF64 output file */
OUTPUT_FORMAT(elf64-littleriscv)
/* We want the symbol kmain to be our entry point */
ENTRY(kmain)
/* 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
/* C++ is a language that allows for global constructors. In order to obtain the */
/* address of the ".init_array" section we need to define a symbol for it. */
.init_array : {
__init_array = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.init_array .ctors))
__init_array_end = .;
} :rodata
/* Move to the next memory page for .data */
. = ALIGN(CONSTANT(MAXPAGESIZE));
.data : {
*(.data .data.*)
*(.sdata .sdata.*)
} :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 : {
*(.sbss .sbss.*)
*(.bss .bss.*)
*(COMMON)
} :data
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
/DISCARD/ : {
*(.eh_frame*)
*(.note .note.*)
}
}
+77
View File
@@ -0,0 +1,77 @@
/* 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(kmain)
/* 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
/* C++ is a language that allows for global constructors. In order to obtain the */
/* address of the ".init_array" section we need to define a symbol for it. */
.init_array : {
__init_array = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.init_array .ctors))
__init_array_end = .;
} :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.*)
}
}
+18
View File
@@ -0,0 +1,18 @@
#include "Panic.hpp"
void Panic(const char *meditationString, System::Registers registers) {
kerr << "=========== Kernel panic ===========" << Kt::newline;
kerr << meditationString << Kt::newline;
while (true) {
#if defined (__x86_64__)
asm ("cli");
asm ("hlt");
#elif defined (__aarch64__) || defined (__riscv)
asm ("wfi");
#elif defined (__loongarch64)
asm ("idle 0");
#endif
}
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <System/Registers.hpp>
#include <KernelTerminal/terminal.hpp>
void Panic(const char *meditationString, System::Registers registers);
+42
View File
@@ -0,0 +1,42 @@
/*
* UEFI.hpp
* Copyright (c) 2025 Daniel Hammer
*/
#pragma once
#include <cstdint>
namespace Efi {
typedef void* EFI_HANDLE;
struct TableHeader {
std::uint64_t Signature;
std::uint32_t Revision;
std::uint32_t HeaderSize;
std::uint32_t CRC32;
std::uint32_t Reserved;
}__attribute__((packed));
struct SystemTable {
TableHeader Header;
void* FirmwareVendor; // Pointer to a CHAR16 string of the fw vendor name string
std::uint32_t FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
void* ConIn;
EFI_HANDLE ConsoleOutHandle;
void* ConOut;
EFI_HANDLE StandardErrorHandle;
void* StdErr;
// Jackpot
void *RuntimeServices;
void *BootServices;
std::uint64_t NumberOfTableEntries;
void *ConfigurationTable;
};
};
+31
View File
@@ -0,0 +1,31 @@
;
; gdt.asm
; Copyright (c) 2025 Daniel Hammer
;
[bits 64]
section .text ; Text/code section
global ReloadSegments
global LoadGDT
LoadGDT:
lgdt [rdi] ; Run LGDT on the contents of 1st C parameter
ret
ReloadSegments:
push 0x08 ; CS descriptor
lea rax, [rel .reload_CS]
push rax
retfq
.reload_CS:
mov ax, 0x10 ; DS descriptor
; ds, es, fs, gs, ss are segment registers on x86_64
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
ret
+70
View File
@@ -0,0 +1,70 @@
/*
* gdt.hpp
*/
#include "GDT.hpp"
#include "../KernelTerminal/terminal.hpp"
// Limine loads a GDT of course, (CS = 0x28) but we will need to make a TSS someday... therefore we load our own now
namespace Hal {
using namespace Kt;
GDTPointer gdtPointer{};
BasicGDT kernelGDT{};
void PrepareGDT() {
kout << "[Hal] GDT at " << base::hex << (uint64_t)&kernelGDT << "\n";
kernelGDT = {
// Code segment offset 0x08
// Data segment offset 0x10
// Not sure if having LimitLow set to 0xFFFF for the Null segment is kosher
{0xFFFF, 0, 0, 0x00, 0x00, 0},
// Kernel code/data
{0xFFFF, 0, 0, 0x9A, 0xA0, 0},
{0xFFFF, 0, 0, 0x92, 0xA0, 0},
// User code/data
{0xFFFF, 0, 0, 0x9A, 0xA0, 0},
{0xFFFF, 0, 0, 0x92, 0xA0, 0},
// One day this will point to our actual TSS
{
// Limit = sizeof(TSS) - 1
0,
// Base = &TSS
0,
0,
// Access byte = 0xFA
0xFA,
// Granularity = 0x00
0x00,
0x0
}
};
gdtPointer = GDTPointer{
.Size = sizeof(kernelGDT) - 1,
.GDTAddress = (uint64_t)&kernelGDT
};
}
// Helpers implemented in gdt.asm
extern "C" void LoadGDT(GDTPointer *gdtPointer);
extern "C" void ReloadSegments();
void BridgeLoadGDT() {
// Puts the GDT pointer structure into the GDTR
kout << "[Hal] Setting GDTR" << Kt::newline;
LoadGDT(&gdtPointer);
kout << "[Hal] Reloading segments" << Kt::newline;
ReloadSegments();
}
};
+62
View File
@@ -0,0 +1,62 @@
/*
* gdt.hpp
* Copyright (c) 2025 Daniel Hammer
*/
#pragma once
#include <cstdint>
using namespace std;
// __attribute__((packed)) is the GCC extensions way of telling the compiler to ensure that it doesn't mess with these structures or add packing bytes
// for optimization because that would easily result in a triple fault.
namespace Hal {
class GDTEntry {
public:
// Base/Limit are obsolete in Long mode because segmentation is no longer used
// Sadly we must still build and load the GDT during kernel init
uint16_t LimitLow;
uint16_t BaseLow;
uint8_t BaseMiddle;
// Determine which processor rings this segment can be used in
uint8_t AccessByte;
// Lower 4 bits are the higher 4 bits of limit
uint8_t GranularityByte;
uint8_t BaseHigh;
// 16 + 16 + 8 + 8 = 48 bits
}__attribute__((packed));
struct BasicGDT {
// Conventionally the first entry of the GDT has all values zeroed out.
GDTEntry Null;
// Kernel code segment descriptor
GDTEntry KernelCode;
// Kernel data segment descriptor
GDTEntry KernelData;
// UM code segment descriptor
GDTEntry UserCode;
// UM data segment descriptor
GDTEntry UserData;
// Task State Segment
GDTEntry TSS;
}__attribute__((packed));
// Simple structure that tells the CPU the size of the GDT, and it's address
struct GDTPointer {
uint16_t Size;
uint64_t GDTAddress;
}__attribute__((packed));
void BridgeLoadGDT();
void PrepareGDT();
};
+41
View File
@@ -0,0 +1,41 @@
#include "terminal.hpp"
#include "../Libraries/flanterm/backends/fb.h"
#include "../Libraries/flanterm/flanterm.h"
#include "../Libraries/string.hpp"
namespace Kt {
flanterm_context *ctx;
void Initialize(std::uint32_t *framebuffer, std::size_t width, std::size_t height, std::size_t pitch,
std::uint8_t red_mask_size, std::uint8_t red_mask_shift,
std::uint8_t green_mask_size, std::uint8_t green_mask_shift,
std::uint8_t blue_mask_size, std::uint8_t blue_mask_shift
)
{
ctx = flanterm_fb_init(
NULL,
NULL,
framebuffer,
width, height, pitch,
red_mask_size, red_mask_shift,
green_mask_size, green_mask_shift,
blue_mask_size, blue_mask_shift,
NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, 0, 0, 1,
0, 0,
0
);
}
void Putchar(char c) {
flanterm_write(ctx, &c, 1);
}
void Print(const char *text) {
flanterm_write(ctx, text, Lib::strlen(text));
}
};
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <Libraries/string.hpp>
namespace Kt
{
constexpr char newline = '\n';
namespace screen
{
constexpr const char *clear = "\033[2J";
constexpr const char *cursor_reset = "\033[H";
};
void Initialize(std::uint32_t *framebuffer, std::size_t width, std::size_t height, std::size_t pitch,
std::uint8_t red_mask_size, std::uint8_t red_mask_shift,
std::uint8_t green_mask_size, std::uint8_t green_mask_shift,
std::uint8_t blue_mask_size, std::uint8_t blue_mask_shift);
void Putchar(char c);
void Print(const char *text);
enum base
{
oct = 8,
dec = 10,
hex = 16
};
inline base base_custom(int custom)
{
return (base)custom;
}
class KernelOutStream
{
public:
base streamBaseType = base::dec;
// C++ streaming operator like cout
friend KernelOutStream &operator<<(KernelOutStream &t, const char *string)
{
Print(string);
return t;
}
// C++ streaming operator like cout
friend KernelOutStream &operator<<(KernelOutStream &t, const char chr)
{
Putchar(chr);
return t;
}
friend KernelOutStream &operator<<(KernelOutStream &t, int number)
{
Print(Lib::int2basestr(number, t.streamBaseType));
return t;
}
friend KernelOutStream &operator<<(KernelOutStream &t, std::uint32_t number)
{
Print(Lib::uint2basestr(number, t.streamBaseType));
return t;
}
friend KernelOutStream &operator<<(KernelOutStream &t, std::uint64_t number)
{
Print(Lib::u64_2_basestr(number, t.streamBaseType));
return t;
}
friend KernelOutStream &operator<<(KernelOutStream &t, base newBase)
{
t.streamBaseType = newBase;
return t;
}
};
// This will be on the kernel entry point's stack. Which is totally fine since we don't ever exit from that function
};
extern Kt::KernelOutStream kout;
namespace Kt
{
class KernelErrorStream
{
public:
template <typename T>
friend KernelErrorStream &operator<<(KernelErrorStream &t, T value)
{
kout << "\e[0;31m" << value << "\e[0m";
return t;
}
};
};
extern Kt::KernelErrorStream kerr;
Submodule kernel/src/Libraries/flanterm added at 201100c968
+81
View File
@@ -0,0 +1,81 @@
#include "string.hpp"
#include <cstdint>
#include <cstddef>
using namespace std;
namespace Lib
{
char output[1024];
// TODO make this buffer safe
char *int2basestr(int num, size_t radix)
{
char * str = (char *)&output;
if (!num) {
return "0";
}
char base[] = "0123456789ABCDEFGH"; // IJKLMN ....
if (radix < 2 || radix > (sizeof(base) - 1))
{ // radix supported?
str[0] = '\0';
return str;
}
int si = 0;
if (num < 0)
{
str[si++] = '-';
num = -num;
}
char tmp[256];
int rdi = -1;
while (num)
{
tmp[++rdi] = base[num % radix];
num /= radix;
}
while (rdi >= 0)
str[si++] = tmp[rdi--];
str[si] = '\0';
return str;
}
char *u64_2_basestr(uint64_t num, size_t radix)
{
char * str = (char *)&output;
if (!num) {
return "0";
}
char base[] = "0123456789ABCDEFGH"; // IJKLMN ....
if (radix < 2 || radix > (sizeof(base) - 1))
{ // radix supported?
str[0] = '\0';
return str;
}
int si = 0;
char tmp[256];
int rdi = -1;
while (num)
{
tmp[++rdi] = base[num % radix];
num /= radix;
}
while (rdi >= 0)
str[si++] = tmp[rdi--];
str[si] = '\0';
return str;
}
char *uint2basestr(uint32_t num, size_t radix)
{
return u64_2_basestr((uint64_t)num, radix);
}
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
#include <cstddef>
namespace Lib {
inline int strlen(const char *string) {
int c = 0;
while (*string != '\0') {
string++;
c++;
}
return c;
}
char *int2basestr(int num, size_t radix);
char *u64_2_basestr(uint64_t num, size_t radix);
char *uint2basestr(uint32_t num, size_t radix);
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
namespace Memory {
extern std::uint64_t HHDMBase;
inline uint64_t HHDM(uint64_t address) {
return HHDMBase + address;
}
inline uint64_t HHDM(void* address) {
return HHDMBase + (uint64_t)address;
}
inline uint64_t SubHHDM(uint64_t address) {
return address - HHDMBase;
}
inline uint64_t SubHHDM(void* address) {
return SubHHDM((uint64_t)address);
}
inline uint64_t IsHDDMVirtAddr(uint64_t address) {
if (address > HHDMBase) {
return true;
}
return false;
}
};
+49
View File
@@ -0,0 +1,49 @@
#include "Memmap.hpp"
#include <KernelTerminal/terminal.hpp>
#include <Common/Panic.hpp>
#include "PageAllocator.hpp"
using namespace Kt;
namespace Memory {
LargestSection Scan(limine_memmap_response* mmap) {
LargestSection currentLargestSection{};
for (size_t i = 0; i < mmap->entry_count; i++) {
auto entry = mmap->entries[i];
if (entry->base == 0) {
continue;
}
if (entry->type == LIMINE_MEMMAP_USABLE) {
kout << "[Mem] Found conventional memory section (size = " << base::dec << entry->length << " bytes, address = 0x" << base::hex << (uint64_t)entry->base << ")" << newline;
if (entry->length > currentLargestSection.size) {
currentLargestSection = {
.address = (uint64_t)entry->base,
.size = entry->length
};
}
}
}
[[unlikely]] if (currentLargestSection.address == 0) {
Panic("Couldn't find a usable memory section.", System::Registers{});
}
return currentLargestSection;
// PageAllocator pa(currentLargestSection);
// uint64_t alloc1 = (uint64_t)pa.Allocate();
// uint64_t alloc2 = (uint64_t)pa.Allocate();
// pa.Free((void *)alloc2);
// uint64_t alloc3 = (uint64_t)pa.Allocate();
// kout << "0x" << base::hex << alloc1 << "\n";
// kout << "0x" << base::hex << alloc2 << "\n";
// kout << "0x" << base::hex << alloc3 << "\n";
}
};
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <limine.h>
#include <cstddef>
using namespace std;
namespace Memory {
// Shared
struct LargestSection {
uint64_t address;
size_t size;
};
LargestSection Scan(limine_memmap_response* mmap);
};
+131
View File
@@ -0,0 +1,131 @@
#include "PageAllocator.hpp"
#include "Memmap.hpp"
#include "HHDM.hpp"
#include <KernelTerminal/terminal.hpp>
namespace Memory
{
#if defined(__x86_64__) || defined(__aarch64__)
#define PAGE_SIZE 4096
#else
#error Sorry, Unimplemented!
#endif
PageAllocator::PageAllocator(LargestSection section)
{
g_section = section;
size_t pages = g_section.size / PAGE_SIZE;
Block *current_ref{&head};
Block *start_block_ptr {(Block *)HHDM(section.address)};
Block& start_block = *start_block_ptr;
start_block = Block {
.size = section.size,
.next = nullptr
};
*start_block_ptr = start_block;
current_ref->next = start_block_ptr;
kout << "[Mem] PageAllocator created for memory section starting at 0x" << Kt::base::hex << section.address << Kt::newline;
}
void* PageAllocator::RequestPage(bool phys) {
bool found_block = false;
Block* current_block_ptr{head.next};
Block* current_prev_ptr{(Block *)&head};
while (!found_block) {
if (current_block_ptr == nullptr) {
kerr << "A PageAllocator was forced to return a Null pointer. (1)" << Kt::newline;
return nullptr;
}
if (current_prev_ptr == nullptr) {
kerr << "A PageAllocator was forced to return a Null pointer. (2)" << Kt::newline;
return nullptr;
}
if (current_block_ptr->size >= 0x1000) { // 0x1000 == 4096
current_block_ptr->size -= 0x1000;
if (!current_block_ptr->size) {
current_prev_ptr->next = current_block_ptr->next;
return (void *)current_block_ptr;
}
Block block_copy = *current_block_ptr;
current_prev_ptr->next = (Block *)(uint64_t)current_block_ptr + 0x1000;
*current_prev_ptr->next = block_copy;
if (phys) {
return (void *)SubHHDM(current_block_ptr);
}
return (void *)current_block_ptr;
} else {
if (current_block_ptr->next != nullptr) {
current_prev_ptr = current_block_ptr;
current_block_ptr = current_block_ptr->next;
} else {
kerr << "A PageAllocator was forced to return a Null pointer. (3)" << Kt::newline;
return nullptr;
}
}
if (current_block_ptr != nullptr) {
if (current_block_ptr->next == nullptr) {
kerr << "A PageAllocator was forced to return a Null pointer. (4)" << Kt::newline;
return nullptr;
}
current_block_ptr = current_block_ptr->next;
} else
{
kerr << "A PageAllocator was forced to return a Null pointer. (5)" << Kt::newline;
return nullptr;
}
}
return nullptr;
}
void PageAllocator::Free(void *pagePtr) {
if (!IsHDDMVirtAddr((uint64_t)pagePtr)) {
pagePtr = (void *)HHDM(pagePtr);
}
Block* fmrNext = head.next;
Block* blockPtr = (Block *)pagePtr;
head.next = blockPtr;
head.next->size = 0x1000;
head.next->next = fmrNext;
}
void PageAllocator::Stress() {
// ~410 MiB
for (size_t i = 0; i < 100000; i++) {
kout << "[Mem] Stress allocation " << Kt::base::dec << i << ": " << "0x" << Kt::base::hex << (uint64_t)Memory::KernelPFA->RequestPage(false) << Kt::newline;
}
}
// Traverses the PageAllocator's linked list for debugging
void PageAllocator::Walk() {
Block* current = {head.next};
size_t i{0};
while (current != nullptr) {
kout << "Block " << Kt::base::dec << i << " {" << current->size << " bytes & address 0x" << Kt::base::hex << (uint64_t)current << "}" << Kt::newline;
current = current->next;
i++;
}
}
};
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "Memmap.hpp"
namespace Memory {
class PageAllocator {
LargestSection g_section;
struct Block {
size_t size;
Block* next;
};
Block head;
public:
PageAllocator(LargestSection section);
void* RequestPage(bool phys = false);
void Free(void *pagePtr);
void Stress();
void Walk();
};
extern PageAllocator* KernelPFA;
};
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
using namespace std;
#if defined (__x86_64__)
namespace System {
struct Registers {
};
};
#endif
+249
View File
@@ -0,0 +1,249 @@
#include <cstdint>
#include <cstddef>
#include <limine.h>
#include <Hal/GDT.hpp>
#include <KernelTerminal/terminal.hpp>
#include <Libraries/string.hpp>
#include <Efi/UEFI.hpp>
#include <Common/Panic.hpp>
#include <Memory/Memmap.hpp>
#include <Memory/PageAllocator.hpp>
using namespace Kt;
namespace Memory {
PageAllocator* KernelPFA;
uint64_t HHDMBase;
};
KernelOutStream kout;
KernelErrorStream kerr;
// Set the base revision to 3, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.
namespace {
__attribute__((used, section(".limine_requests")))
volatile LIMINE_BASE_REVISION(3);
}
// 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, _and_ they should be accessed at least
// once or marked as used with the "used" attribute as done here.
namespace {
__attribute__((used, section(".limine_requests")))
volatile limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_efi_system_table_request system_table_request = {
.id = LIMINE_EFI_SYSTEM_TABLE_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0,
.response = nullptr
};
}
// Finally, define the start and end markers for the Limine requests.
// These can also be moved anywhere, to any .cpp file, as seen fit.
namespace {
__attribute__((used, section(".limine_requests_start")))
volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
volatile LIMINE_REQUESTS_END_MARKER;
}
// 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 .cpp file.
extern "C" {
void *memcpy(void *dest, const void *src, std::size_t n) {
std::uint8_t *pdest = static_cast<std::uint8_t *>(dest);
const std::uint8_t *psrc = static_cast<const std::uint8_t *>(src);
for (std::size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
return dest;
}
void *memset(void *s, int c, std::size_t n) {
std::uint8_t *p = static_cast<std::uint8_t *>(s);
for (std::size_t i = 0; i < n; i++) {
p[i] = static_cast<uint8_t>(c);
}
return s;
}
void *memmove(void *dest, const void *src, std::size_t n) {
std::uint8_t *pdest = static_cast<std::uint8_t *>(dest);
const std::uint8_t *psrc = static_cast<const std::uint8_t *>(src);
if (src > dest) {
for (std::size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if (src < dest) {
for (std::size_t i = n; i > 0; i--) {
pdest[i-1] = psrc[i-1];
}
}
return dest;
}
int memcmp(const void *s1, const void *s2, std::size_t n) {
const std::uint8_t *p1 = static_cast<const std::uint8_t *>(s1);
const std::uint8_t *p2 = static_cast<const std::uint8_t *>(s2);
for (std::size_t i = 0; i < n; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
}
// Halt and catch fire function.
namespace {
void hcf() {
for (;;) {
#if defined (__x86_64__)
asm ("hlt");
#elif defined (__aarch64__) || defined (__riscv)
asm ("wfi");
#elif defined (__loongarch64)
asm ("idle 0");
#endif
}
}
}
// The following stubs are required by the Itanium C++ ABI (the one we use,
// regardless of the "Itanium" nomenclature).
// Like the memory functions above, these stubs can be moved to a different .cpp file,
// but should not be removed, unless you know what you are doing.
extern "C" {
int __cxa_atexit(void (*)(void *), void *, void *) { return 0; }
void __cxa_pure_virtual() { hcf(); }
void *__dso_handle;
}
// Extern declarations for global constructors array.
extern void (*__init_array[])();
extern void (*__init_array_end[])();
// The following will be our kernel's entry point.
// If renaming kmain() to something else, make sure to change the
// linker script accordingly.
extern "C" void kmain() {
// Ensure the bootloader actually understands our base revision (see spec).
if (LIMINE_BASE_REVISION_SUPPORTED == false) {
hcf();
}
// Call global constructors.
for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
__init_array[i]();
}
// Ensure we got a framebuffer.
if (framebuffer_request.response == nullptr
|| framebuffer_request.response->framebuffer_count < 1) {
hcf();
}
// Fetch the first framebuffer.
limine_framebuffer *framebuffer{framebuffer_request.response->framebuffers[0]};
// Initialize the terminal
Kt::Initialize((uint32_t*)framebuffer->address, framebuffer->width, framebuffer->height, framebuffer->pitch, framebuffer->red_mask_size, framebuffer->red_mask_shift,
framebuffer->green_mask_size, framebuffer->green_mask_shift,
framebuffer->blue_mask_size, framebuffer->blue_mask_shift);
#if defined (__x86_64__)
Hal::PrepareGDT();
Hal::BridgeLoadGDT();
#endif
// RGB lines
for (std::size_t i = 500; i < 800; i++) {
volatile std::uint32_t *fb_ptr = static_cast<volatile std::uint32_t *>(framebuffer->address);
fb_ptr[i * (framebuffer->pitch / 4) + (i - 5*5)] = 0xFF0000; // Red
fb_ptr[i * (framebuffer->pitch / 4) + (i - 10*5)] = 0x00FF00; // Green
fb_ptr[i * (framebuffer->pitch / 4) + (i - 15*5)] = 0x0000FF; // Blue
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 20*5)] = 0xFF0000; // Red
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 25*5)] = 0x00FF00; // Green
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 30*5)] = 0x0000FF; // Blue
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 35*5)] = 0xFF0000; // Red
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 40*5)] = 0x00FF00; // Green
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 45*5)] = 0x0000FF; // Blue
}
uint64_t hhdm_offset = hhdm_request.response->offset;
kout << "[Mem] HHDM offset: 0x" << base::hex << hhdm_offset << newline;
Memory::HHDMBase = hhdm_offset;
if (!system_table_request. response || !system_table_request.response->address) {
kerr << "[Efi] EFI System Table not supported" << newline;
} else {
kout << "[Efi] EFI system table at 0x" << base::hex << (uint64_t)system_table_request.response->address << newline;
}
if (memmap_request.response != nullptr) {
auto result = Memory::Scan(memmap_request.response);
auto allocator = Memory::PageAllocator(result);
kout << "[Mem] Creating PageAllocator for system conventional memory" << newline;
Memory::KernelPFA = &allocator;
} else {
Panic("Guru Meditation Error: System memory map missing!", System::Registers{});
}
hcf();
}
+10
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.
/Limine Template
# We use the Limine boot protocol.
protocol: limine
# Path to the kernel to boot. boot():/ represents the partition on which limine.conf is located.
path: boot():/boot/kernel