diff --git a/.gitignore b/.gitignore index d4faf943..25762c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ *.MAP src/Boot/ docs/Boot/ +build/limine +build/ovmf ZealOS-*.iso diff --git a/build/build-temp-vm-uefi-1.sh b/build/build-temp-vm-uefi-1.sh index 310ca64b..17df1095 100755 --- a/build/build-temp-vm-uefi-1.sh +++ b/build/build-temp-vm-uefi-1.sh @@ -35,6 +35,9 @@ umount_tempdisk() { [ ! -d $TMPMOUNT ] && mkdir -p $TMPMOUNT +echo "Building ZealBooter..." +( cd ../zealbooter && make clean all ) + echo "Making temp vdisk, running auto-install..." qemu-img create -f raw $TMPDISK 192M qemu-system-x86_64 -machine q35,accel=kvm -drive format=raw,file=$TMPDISK -m 1G -rtc base=localtime -cdrom AUTO-VM-1.ISO -device isa-debug-exit @@ -47,21 +50,25 @@ sudo cp -r ../src/* $TMPMOUNT [ ! -d "limine" ] && git clone https://github.com/limine-bootloader/limine.git --branch=v3.0-branch-binary --depth=1 sudo mkdir -p $TMPMOUNT/EFI/BOOT sudo cp limine/BOOTX64.EFI $TMPMOUNT/EFI/BOOT/BOOTX64.EFI +sudo cp ../zealbooter/zealbooter.elf $TMPMOUNT/Boot/ZealBooter.ELF umount_tempdisk -#echo "Generating ISO..." -echo "Running temporary VM" +echo "Rebuilding kernel..." qemu-system-x86_64 -machine q35,accel=kvm -drive format=raw,file=$TMPDISK -m 1G -rtc base=localtime -device isa-debug-exit -qemu-system-x86_64 -machine q35,accel=kvm -drive format=raw,file=$TMPDISK -m 1G -rtc base=localtime -bios /usr/share/ovmf/OVMF.fd -#echo "Extracting ISO from vdisk..." -#rm ./ZealOS-*.iso 2> /dev/null # comment this line if you want lingering old ISOs -#mount_tempdisk -#cp $TMPMOUNT/Tmp/MyDistro.ISO.C ./ZealOS-$(date +%Y-%m-%d-%H_%M_%S).iso -#umount_tempdisk +if [ ! -d "ovmf" ]; then + echo "Downloading OVMF..." + mkdir ovmf + cd ovmf + curl -o OVMF-X64.zip https://efi.akeo.ie/OVMF/OVMF-X64.zip + 7z x OVMF-X64.zip + cd .. +fi + +echo "Testing..." +qemu-system-x86_64 -machine q35,accel=kvm -drive format=raw,file=$TMPDISK -m 1G -rtc base=localtime -bios ovmf/OVMF.fd echo "Deleting temp folder..." rm -rf $TMPDIR echo "Finished." -#ls -lh ZealOS-*.iso diff --git a/src/limine.cfg b/src/limine.cfg new file mode 100644 index 00000000..6d6d2091 --- /dev/null +++ b/src/limine.cfg @@ -0,0 +1,6 @@ +TIMEOUT=2 + +:ZealOS +PROTOCOL=limine +KERNEL_PATH=boot:///Boot/ZealBooter.ELF +MODULE_PATH=boot:///Boot/Kernel.ZXE diff --git a/zealbooter/.gitignore b/zealbooter/.gitignore new file mode 100644 index 00000000..327ac05b --- /dev/null +++ b/zealbooter/.gitignore @@ -0,0 +1,6 @@ +limine.h +*.elf +*.iso +*.hdd +*.o +*.d diff --git a/zealbooter/GNUmakefile b/zealbooter/GNUmakefile new file mode 100644 index 00000000..4e7ba1fb --- /dev/null +++ b/zealbooter/GNUmakefile @@ -0,0 +1,106 @@ +# This is the name that our final kernel executable will have. +# Change as needed. +override KERNEL := zealbooter.elf + +# Convenience macro to reliably declare overridable command variables. +define DEFAULT_VAR = + ifeq ($(origin $1), default) + override $(1) := $(2) + endif + ifeq ($(origin $1), undefined) + override $(1) := $(2) + endif +endef + +# It is highly recommended to use a custom built cross toolchain to build a kernel. +# We are only using "cc" as a placeholder here. It may work by using +# the host system's toolchain, but this is not guaranteed. +$(eval $(call DEFAULT_VAR,CC,cc)) + +# Same thing for "ld" (the linker). +$(eval $(call DEFAULT_VAR,LD,ld)) + +# User controllable CFLAGS. +CFLAGS ?= -O2 -g -Wall -Wextra -Wpedantic -pipe + +# User controllable preprocessor flags. We set none by default. +CPPFLAGS ?= + +# User controllable nasm flags. +NASMFLAGS ?= -F dwarf -g + +# User controllable linker flags. We set none by default. +LDFLAGS ?= + +# Internal C flags that should not be changed by the user. +override CFLAGS += \ + -I. \ + -std=c11 \ + -ffreestanding \ + -fno-stack-protector \ + -fno-stack-check \ + -fno-pie \ + -fno-pic \ + -m64 \ + -march=x86-64 \ + -mabi=sysv \ + -mno-80387 \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 \ + -mno-red-zone \ + -mcmodel=kernel \ + -MMD + +# Internal linker flags that should not be changed by the user. +override LDFLAGS += \ + -nostdlib \ + -static \ + -z max-page-size=0x1000 \ + -T linker.ld + +# Internal nasm flags that should not be changed by the user. +override NASMFLAGS += \ + -f elf64 + +# Use find to glob all *.c, *.S, and *.asm files in the directory and extract the object names. +override CFILES := $(shell find ./ -type f -name '*.c') +override ASFILES := $(shell find ./ -type f -name '*.S') +override NASMFILES := $(shell find ./ -type f -name '*.asm') +override OBJ := $(CFILES:.c=.o) $(ASFILES:.S=.o) $(NASMFILES:.asm=.o) +override HEADER_DEPS := $(CFILES:.c=.d) $(ASFILES:.S=.d) + +# Default target. +.PHONY: all +all: $(KERNEL) + +limine.h: + curl https://raw.githubusercontent.com/limine-bootloader/limine/trunk/limine.h -o $@ + +# Link rules for the final kernel executable. +$(KERNEL): $(OBJ) + $(LD) $(OBJ) $(LDFLAGS) -o $@ + +# Include header dependencies. +-include $(HEADER_DEPS) + +# Compilation rules for *.c files. +%.o: %.c limine.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +# Compilation rules for *.S files. +%.o: %.S limine.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +# Compilation rules for *.asm (nasm) files. +%.o: %.asm + nasm $(NASMFLAGS) $< -o $@ + +# Remove object files and the final executable. +.PHONY: clean +clean: + rm -rf $(KERNEL) $(OBJ) $(HEADER_DEPS) + +.PHONY: distclean +distclean: clean + rm -f limine.h diff --git a/zealbooter/linker.ld b/zealbooter/linker.ld new file mode 100644 index 00000000..36452585 --- /dev/null +++ b/zealbooter/linker.ld @@ -0,0 +1,53 @@ +/* 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 + + .bss : { + *(COMMON) + *(.bss .bss.*) + } :data + + /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + } +} diff --git a/zealbooter/zealbooter.c b/zealbooter/zealbooter.c new file mode 100644 index 00000000..1b9b8e61 --- /dev/null +++ b/zealbooter/zealbooter.c @@ -0,0 +1,35 @@ +#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 +}; + +static void done(void) { + for (;;) { + __asm__("hlt"); + } +} + +// The following will be our kernel's entry point. +void _start(void) { + // Ensure we got a terminal + if (terminal_request.response == NULL + || terminal_request.response->terminal_count < 1) { + done(); + } + + // We should now be able to call the Limine terminal to print out + // a simple "Hello World" to screen. + struct limine_terminal *terminal = terminal_request.response->terminals[0]; + terminal_request.response->write(terminal, "Hello World", 11); + + // We're done, just hang... + done(); +}