diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f07d25530..b0ab1a7eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get -y update - sudo apt-get -y install build-essential git xorriso qemu-system-x86 + sudo apt-get -y install build-essential curl xorriso qemu-system-x86 - name: Enable KVM Group Perms run: | diff --git a/.gitignore b/.gitignore index b44a7db6f..4419869ae 100644 --- a/.gitignore +++ b/.gitignore @@ -8,13 +8,17 @@ *.sys *.SYS /src/EFI -/build/limine -/build/ovmf +/build/limine-binary +/build/limine-binary.tar.gz +/build/edk2-ovmf-bins +/build/edk2-ovmf-bins.tar.gz *.iso *.raw *.hdd *.o *.d +/zealbooter/compile_commands.json +/zealbooter/.cache /zealbooter/src/limine.h /zealbooter/bin /zealbooter/obj diff --git a/build/build-iso.sh b/build/build-iso.sh index 058e42559..0551db701 100755 --- a/build/build-iso.sh +++ b/build/build-iso.sh @@ -79,33 +79,22 @@ umount_tempdisk echo "Rebuilding kernel headers, kernel, OS, and building Distro ISO ..." "$QEMU_BIN_PATH/qemu-system-x86_64" -machine q35 $KVM -drive format=raw,file="$TMPDISK" -m 1G -rtc base=localtime -smp 4 -device isa-debug-exit $QEMU_HEADLESS || true -LIMINE_BINARY_BRANCH="v10.x-binary" - -if [ -d "limine" ] +if [ ! -d "limine-binary" ] then - cd limine - git remote set-branches origin $LIMINE_BINARY_BRANCH - git fetch - git remote set-head origin $LIMINE_BINARY_BRANCH - git switch $LIMINE_BINARY_BRANCH - git config --local pull.ff true - git config --local pull.rebase true - git pull - rm limine - - cd .. -else - git clone https://github.com/limine-bootloader/limine.git --branch=$LIMINE_BINARY_BRANCH --depth=1 + echo "Downloading latest Limine binary release ..." + rm -f limine-binary.tar.gz + curl -fL -o limine-binary.tar.gz https://github.com/Limine-Bootloader/Limine/releases/latest/download/limine-binary.tar.gz + gunzip < limine-binary.tar.gz | tar -xf - + rm -f limine-binary.tar.gz fi -make -C limine -touch limine/Limine-BIOS-HDD.HH -echo "/*\$WW,1\$" > limine/Limine-BIOS-HDD.HH -cat limine/LICENSE >> limine/Limine-BIOS-HDD.HH -echo "*/\$WW,0\$" >> limine/Limine-BIOS-HDD.HH -cat limine/limine-bios-hdd.h >> limine/Limine-BIOS-HDD.HH -sed -i 's/const uint8_t/U8/g' limine/Limine-BIOS-HDD.HH -sed -i "s/\[\]/\[$(grep -o "0x" ./limine/limine-bios-hdd.h | wc -l)\]/g" limine/Limine-BIOS-HDD.HH +touch limine-binary/Limine-BIOS-HDD.HH +echo "/*\$WW,1\$" > limine-binary/Limine-BIOS-HDD.HH +cat limine-binary/LICENSE >> limine-binary/Limine-BIOS-HDD.HH +echo "*/\$WW,0\$" >> limine-binary/Limine-BIOS-HDD.HH +cat limine-binary/limine-bios-hdd.h >> limine-binary/Limine-BIOS-HDD.HH +sed -i 's/const uint8_t/U8/g' limine-binary/Limine-BIOS-HDD.HH +sed -i "s/\[\]/\[$(grep -o "0x" ./limine-binary/limine-bios-hdd.h | wc -l)\]/g" limine-binary/Limine-BIOS-HDD.HH mount_tempdisk echo "Extracting MyDistro ISO from vdisk ..." @@ -116,11 +105,12 @@ sudo cp -rf "$TMPMOUNT"/* "$TMPISODIR/" sudo rm -f "$TMPISODIR/Boot/OldMBR.BIN" sudo rm -f "$TMPISODIR/Boot/BootMHD2.BIN" sudo mkdir -p "$TMPISODIR/EFI/BOOT" -sudo cp limine/Limine-BIOS-HDD.HH "$TMPISODIR/Boot/Limine-BIOS-HDD.HH" -sudo cp limine/BOOTX64.EFI "$TMPISODIR/EFI/BOOT/BOOTX64.EFI" -sudo cp limine/limine-uefi-cd.bin "$TMPISODIR/Boot/Limine-UEFI-CD.BIN" -sudo cp limine/limine-bios-cd.bin "$TMPISODIR/Boot/Limine-BIOS-CD.BIN" -sudo cp limine/limine-bios.sys "$TMPISODIR/Boot/Limine-BIOS.SYS" +sudo cp limine-binary/Limine-BIOS-HDD.HH "$TMPISODIR/Boot/Limine-BIOS-HDD.HH" +sudo cp limine-binary/BOOTX64.EFI "$TMPISODIR/EFI/BOOT/BOOTX64.EFI" +sudo cp limine-binary/BOOTIA32.EFI "$TMPISODIR/EFI/BOOT/BOOTIA32.EFI" +sudo cp limine-binary/limine-uefi-cd.bin "$TMPISODIR/Boot/Limine-UEFI-CD.BIN" +sudo cp limine-binary/limine-bios-cd.bin "$TMPISODIR/Boot/Limine-BIOS-CD.BIN" +sudo cp limine-binary/limine-bios.sys "$TMPISODIR/Boot/Limine-BIOS.SYS" sudo cp ../zealbooter/bin/kernel "$TMPISODIR/Boot/ZealBooter.ELF" sudo cp ../zealbooter/limine.conf "$TMPISODIR/Boot/Limine.CONF" echo "Copying DVDKernel.ZXE over ISO Boot/Kernel.ZXE ..." @@ -128,31 +118,23 @@ sudo mv "$TMPMOUNT/Tmp/DVDKernel.ZXE" "$TMPISODIR/Boot/Kernel.ZXE" sudo rm -f "$TMPISODIR/Tmp/DVDKernel.ZXE" umount_tempdisk -truncate -s 32K bios_boot.img - +# ZealOS only boots off optical media, but the GPT stays, as the kernel +# finds the disc it booted from by its disk GUID. xorriso -as mkisofs -R -r -J -b Boot/Limine-BIOS-CD.BIN \ - -no-emul-boot -boot-load-size 4 -boot-info-table \ - --efi-boot Boot/Limine-UEFI-CD.BIN \ + -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \ + -apm-block-size 2048 --efi-boot Boot/Limine-UEFI-CD.BIN \ -efi-boot-part --efi-boot-image --protective-msdos-label \ - -append_partition 4 21686148-6449-6E6F-744E-656564454649 bios_boot.img \ - -appended_part_as_gpt \ "$TMPISODIR" -o ZealOS-limine.iso -rm bios_boot.img - -./limine/limine bios-install ZealOS-limine.iso --no-gpt-to-mbr-isohybrid-conversion - if [ "$TESTING" = true ]; then - if [ ! -d "ovmf" ]; then + if [ ! -d "edk2-ovmf-bins" ]; 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 .. + curl -fL -o edk2-ovmf-bins.tar.gz https://github.com/osdev0/edk2-ovmf-stable-bins/releases/latest/download/edk2-ovmf-bins.tar.gz + gunzip < edk2-ovmf-bins.tar.gz | tar -xf - + rm -f edk2-ovmf-bins.tar.gz fi echo "Testing limine-zealbooter-xorriso isohybrid boot in UEFI mode ..." - "$QEMU_BIN_PATH/qemu-system-x86_64" -machine q35 $KVM -m 1G -rtc base=localtime -bios ovmf/OVMF.fd -smp 4 -cdrom ZealOS-limine.iso $QEMU_HEADLESS + "$QEMU_BIN_PATH/qemu-system-x86_64" -machine q35 $KVM -m 1G -rtc base=localtime -drive if=pflash,unit=0,format=raw,file=edk2-ovmf-bins/ovmf-code-x86_64.fd,readonly=on -smp 4 -cdrom ZealOS-limine.iso $QEMU_HEADLESS echo "Testing limine-zealbooter-xorriso isohybrid boot in BIOS mode ..." "$QEMU_BIN_PATH/qemu-system-x86_64" -machine q35 $KVM -m 1G -rtc base=localtime -smp 4 -cdrom ZealOS-limine.iso $QEMU_HEADLESS echo "Testing native ZealC MyDistro legacy ISO in BIOS mode ..." diff --git a/src/System/Boot/LimineMHDIns.ZC b/src/System/Boot/LimineMHDIns.ZC index 7f18236b4..aeb2efcb9 100755 --- a/src/System/Boot/LimineMHDIns.ZC +++ b/src/System/Boot/LimineMHDIns.ZC @@ -67,13 +67,51 @@ Bool LimineCFGMake(U8 drv_let) return TRUE; } +#define LIMINE_MBR_GAP_BLKS 63 // MBR + post-MBR gap, limine bios-install refuses partitions starting inside it +#define LIMINE_STAGE2_LOC 0x1000 // byte offset limine bios-install puts stage 2 at, a multiple of any sector size + +Bool LimineMBRCheck(CMasterBoot *mbr) +{ + I64 i; + Bool any_active = FALSE; + + for (i = 0; i < 4; i++) + if (mbr->p[i].type && mbr->p[i].offset < LIMINE_MBR_GAP_BLKS) + { + "\n" ST_ERR_ST "A partition's start sector is less than %d.\n", LIMINE_MBR_GAP_BLKS; + return FALSE; + } + + for (i = 0; i < 4; i++) + { + if (mbr->p[i].active != 0x00 && mbr->p[i].active != 0x80) + break; + if (mbr->p[i].active) + any_active = TRUE; + } + + if (i < 4 || mbr->signature != 0xAA55) + { + "\n" ST_ERR_ST "Could not determine if the disk has a valid partition table.\n"; + return FALSE; + } + + if (!any_active) + { + "No active partition found, setting partition 1 as active.\n"; + mbr->p[0].active = 0x80; + } + + return TRUE; +} + Bool LimineMHDIns(U8 drv_let) { - U8 *limine = binary_limine_hdd_bin_data; - U16 limine_size = sizeof(binary_limine_hdd_bin_data); - U64 *limine_stage2 = limine + 0x1A4; - CBlkDev *bd = Letter2BlkDev(drv_let); - CMasterBoot old_mbr, *new_mbr = limine; + U8 *limine = binary_limine_hdd_bin_data, *blks; + U16 limine_size = sizeof(binary_limine_hdd_bin_data); + U64 *limine_stage2; + CBlkDev *bd = Letter2BlkDev(drv_let); + CMasterBoot old_mbr, *new_mbr; if (limine_size == 0) { @@ -81,22 +119,49 @@ Bool LimineMHDIns(U8 drv_let) return FALSE; } + if (limine_size - BLK_SIZE > LIMINE_MBR_GAP_BLKS * BLK_SIZE - LIMINE_STAGE2_LOC) + { + "\n" ST_ERR_ST "Limine HDD stage 2 does not fit in the post-MBR gap.\n"; + return FALSE; + } + '\n'; - *limine_stage2 = 0x200; + blks = MAlloc(LIMINE_MBR_GAP_BLKS * BLK_SIZE); + new_mbr = blks; + limine_stage2 = blks + 0x1A4; // where the Limine MBR code looks for stage 2 BlkDevLock(bd); + AHCIAtaBlksRead(bd, blks, 0, LIMINE_MBR_GAP_BLKS); // read old MBR and post-MBR gap currently on disk + BlkDevUnlock(bd); - AHCIAtaBlksRead(bd, &old_mbr, 0, 1); // read old MBR currently on disk - MemCopy(new_mbr->p, old_mbr.p, sizeof(CMasterBoot.p)); // copy old MBR partition table to new Limine MBR + if (!LimineMBRCheck(new_mbr)) + { + Free(blks); + return FALSE; + } // read the whole MBR + post-MBR-gap that Limine wants to overwrite, save all of that as an OldMBR.BIN - BootMHDOldRead(drv_let, drv_let, 63); + BootMHDOldRead(drv_let, drv_let, LIMINE_MBR_GAP_BLKS); - AHCIAtaBlksWrite(bd, limine, 0, 63); // write Limine to MBR and post-MBR gap + MemCopy(&old_mbr, blks, BLK_SIZE); + MemCopy(new_mbr, limine, BLK_SIZE); + MemCopy(blks + LIMINE_STAGE2_LOC, limine + BLK_SIZE, limine_size - BLK_SIZE); + *limine_stage2 = LIMINE_STAGE2_LOC; + MemCopy(new_mbr->code + 218, old_mbr.code + 218, 6); // keep the disk timestamp + new_mbr->media_id = old_mbr.media_id; + new_mbr->zero = old_mbr.zero; + MemCopy(new_mbr->p, old_mbr.p, sizeof(CMasterBoot.p)); // copy old MBR partition table to new Limine MBR + + BlkDevLock(bd); + AHCIAtaBlksWrite(bd, blks, 0, 1); // write Limine to MBR + AHCIAtaBlksWrite(bd, blks + LIMINE_STAGE2_LOC, LIMINE_STAGE2_LOC / BLK_SIZE, + CeilU64(limine_size - BLK_SIZE, BLK_SIZE) / BLK_SIZE); // write stage 2 to post-MBR gap BlkDevUnlock(bd); + Free(blks); + LimineCFGMake(drv_let); "\nLimine BSD2-licensed HDD MBR Boot Loader installed.\n\n"; diff --git a/zealbooter/GNUmakefile b/zealbooter/GNUmakefile index 1c5c6fca2..81450a240 100644 --- a/zealbooter/GNUmakefile +++ b/zealbooter/GNUmakefile @@ -1,6 +1,9 @@ # Nuke built-in rules. .SUFFIXES: +# Delete the target of a failed recipe. +.DELETE_ON_ERROR: + # This is the name that our final executable will have. # Change as needed. override OUTPUT := kernel @@ -82,9 +85,10 @@ override CPPFLAGS := \ -MP # Internal nasm flags that should not be changed by the user. -override NASMFLAGS += \ +override NASMFLAGS := \ -f elf64 \ $(patsubst -g,-g -F dwarf,$(NASMFLAGS)) \ + -i src/ \ -Wall # Internal linker flags that should not be changed by the user. @@ -93,9 +97,18 @@ override LDFLAGS += \ -nostdlib \ -static \ -z max-page-size=0x1000 \ + -z noexecstack \ --gc-sections \ -T linker.ld +# Check if the linker supports -no-pie and enable it if it does. +override LD_HAS_NO_PIE := $(shell ! $(LD) --help 2>/dev/null | grep -qE '(^|[[:space:]])--?no-pie([[:space:]]|$$)'; echo $$?) + +ifeq ($(LD_HAS_NO_PIE),1) + override LDFLAGS += \ + -no-pie +endif + # Use "find" to glob all *.c, *.S, and *.asm files in the tree and obtain the # object and header dependency file names. override SRCFILES := $(shell find -L src -type f 2>/dev/null | LC_ALL=C sort) @@ -103,7 +116,7 @@ override CFILES := $(filter %.c,$(SRCFILES)) override ASFILES := $(filter %.S,$(SRCFILES)) override NASMFILES := $(filter %.asm,$(SRCFILES)) override OBJ := $(addprefix obj/,$(CFILES:.c=.c.o) $(ASFILES:.S=.S.o) $(NASMFILES:.asm=.asm.o)) -override HEADER_DEPS := $(addprefix obj/,$(CFILES:.c=.c.d) $(ASFILES:.S=.S.d)) +override HEADER_DEPS := $(addprefix obj/,$(CFILES:.c=.c.d) $(ASFILES:.S=.S.d) $(NASMFILES:.asm=.asm.d)) # Default target. This must come first, before header dependencies. .PHONY: all @@ -113,7 +126,7 @@ all: bin/$(OUTPUT) -include $(HEADER_DEPS) src/limine.h: - curl -Lo $@ https://github.com/limine-bootloader/limine-protocol/raw/c4616df2572d77c60020bdefa617dd9bdcc6566a/include/limine.h || cp ../build/limine/limine.h src/limine.h || echo "ERROR" + curl -fL -o $@ https://github.com/Limine-Bootloader/limine-protocol/raw/da65184e91f80fcb397270121b1e2515a11e01ee/include/limine.h # Link rules for the final executable. bin/$(OUTPUT): GNUmakefile linker.ld $(OBJ) @@ -133,7 +146,7 @@ obj/%.S.o: %.S GNUmakefile src/limine.h # Compilation rules for *.asm (nasm) files. obj/%.asm.o: %.asm GNUmakefile mkdir -p "$(dir $@)" - nasm $(NASMFLAGS) $< -o $@ + nasm $(NASMFLAGS) -MD $(@:.o=.d) -MP $< -o $@ # Remove object files and the final executable. .PHONY: clean @@ -143,4 +156,4 @@ clean: # Remove everything built and generated including downloaded dependencies. .PHONY: distclean distclean: clean - rm -f src/limine.h + rm -rf .cache compile_commands.json src/limine.h diff --git a/zealbooter/src/lib.h b/zealbooter/src/lib.h index b8b407fb5..c06129ff7 100644 --- a/zealbooter/src/lib.h +++ b/zealbooter/src/lib.h @@ -2,15 +2,7 @@ #define __LIB_H__ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include uint64_t div_roundup_u64(uint64_t a, uint64_t b); diff --git a/zealbooter/src/lib/memcmp.c b/zealbooter/src/lib/memcmp.c deleted file mode 100644 index dd406ae22..000000000 --- a/zealbooter/src/lib/memcmp.c +++ /dev/null @@ -1,26 +0,0 @@ -/* memcmp( const void *, const void *, size_t ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -int memcmp(const void *s1, const void *s2, size_t n) -{ - const unsigned char *p1 = (const unsigned char *)s1; - const unsigned char *p2 = (const unsigned char *)s2; - - while (n--) - { - if (*p1 != *p2) - { - return *p1 - *p2; - } - - ++p1; - ++p2; - } - - return 0; -} - diff --git a/zealbooter/src/lib/memcmp.h b/zealbooter/src/lib/memcmp.h deleted file mode 100644 index 3bbc4ca76..000000000 --- a/zealbooter/src/lib/memcmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MEMCMP_H__ -#define __MEMCMP_H__ - -int memcmp(const void *s1, const void *s2, size_t n); - -#endif // __MEMCMP_H__ diff --git a/zealbooter/src/lib/memcpy.c b/zealbooter/src/lib/memcpy.c deleted file mode 100644 index 6584eb0ae..000000000 --- a/zealbooter/src/lib/memcpy.c +++ /dev/null @@ -1,21 +0,0 @@ -/* memcpy( void *, const void *, size_t ) - - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -void *memcpy(void *s1, const void *s2, size_t n) -{ - char *dest = (char *)s1; - const char *src = (const char *)s2; - - while (n--) - { - *dest++ = *src++; - } - - return s1; -} - diff --git a/zealbooter/src/lib/memcpy.h b/zealbooter/src/lib/memcpy.h deleted file mode 100644 index c0600149c..000000000 --- a/zealbooter/src/lib/memcpy.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __MEMCPY_H__ -#define __MEMCPY_H__ - -#include - -void *memcpy(void *s1, const void *s2, size_t n); - -#endif // __MEMCPY_H__ diff --git a/zealbooter/src/lib/memmove.c b/zealbooter/src/lib/memmove.c deleted file mode 100644 index 040402b4a..000000000 --- a/zealbooter/src/lib/memmove.c +++ /dev/null @@ -1,33 +0,0 @@ -/* memmove( void *, const void *, size_t ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -void *memmove(void *s1, const void *s2, size_t n) -{ - char *dest = (char *)s1; - const char *src = (const char *)s2; - - if (dest <= src) - { - while (n--) - { - *dest++ = *src++; - } - } - else - { - src += n; - dest += n; - - while (n--) - { - *--dest = *--src; - } - } - - return s1; -} - diff --git a/zealbooter/src/lib/memmove.h b/zealbooter/src/lib/memmove.h deleted file mode 100644 index 80d5cba61..000000000 --- a/zealbooter/src/lib/memmove.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MEMMOVE_H__ -#define __MEMMOVE_H__ - -void *memmove(void *s1, const void *s2, size_t n); - -#endif // __MEMMOVE_H__ diff --git a/zealbooter/src/lib/memory.c b/zealbooter/src/lib/memory.c new file mode 100644 index 000000000..986119a25 --- /dev/null +++ b/zealbooter/src/lib/memory.c @@ -0,0 +1,71 @@ +#include +#include +#include + +// GCC and Clang reserve the right to generate calls to the following +// 4 functions even if they are not directly called. +// They must be implemented as the C specification mandates. +// DO NOT remove or rename these functions, or stuff will eventually break! + +#ifdef memcpy +# undef memcpy +#endif +void *memcpy(void *restrict dest, const void *restrict src, size_t n) { + uint8_t *pdest = dest; + const uint8_t *psrc = src; + + for (size_t i = 0; i < n; i++) { + pdest[i] = psrc[i]; + } + + return dest; +} + +#ifdef memset +# undef memset +#endif +void *memset(void *s, int c, size_t n) { + uint8_t *p = s; + + for (size_t i = 0; i < n; i++) { + p[i] = (uint8_t)c; + } + + return s; +} + +#ifdef memmove +# undef memmove +#endif +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *pdest = dest; + const uint8_t *psrc = src; + + if ((uintptr_t)src > (uintptr_t)dest) { + for (size_t i = 0; i < n; i++) { + pdest[i] = psrc[i]; + } + } else if ((uintptr_t)src < (uintptr_t)dest) { + for (size_t i = n; i > 0; i--) { + pdest[i-1] = psrc[i-1]; + } + } + + return dest; +} + +#ifdef memcmp +# undef memcmp +#endif +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t *p1 = s1; + const uint8_t *p2 = s2; + + for (size_t i = 0; i < n; i++) { + if (p1[i] != p2[i]) { + return p1[i] < p2[i] ? -1 : 1; + } + } + + return 0; +} diff --git a/zealbooter/src/lib/memory.h b/zealbooter/src/lib/memory.h new file mode 100644 index 000000000..2ebfc7d05 --- /dev/null +++ b/zealbooter/src/lib/memory.h @@ -0,0 +1,20 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n); +void *memset(void *s, int c, size_t n); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +// Make uses of these functions go through the compiler builtins, which the +// compiler can optimise (e.g. inline) even when building freestanding code, +// falling back to calling the implementations in memory.c otherwise. +#define memcpy __builtin_memcpy +#define memset __builtin_memset +#define memmove __builtin_memmove +#define memcmp __builtin_memcmp + +#endif diff --git a/zealbooter/src/lib/memset.c b/zealbooter/src/lib/memset.c deleted file mode 100644 index cf951f094..000000000 --- a/zealbooter/src/lib/memset.c +++ /dev/null @@ -1,20 +0,0 @@ -/* memset( void *, int, size_t ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -void *memset(void *s, int c, size_t n) -{ - unsigned char *p = (unsigned char *)s; - - while (n--) - { - *p++ = (unsigned char)c; - } - - return s; -} - - diff --git a/zealbooter/src/lib/memset.h b/zealbooter/src/lib/memset.h deleted file mode 100644 index 78fcb5884..000000000 --- a/zealbooter/src/lib/memset.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MEMSET_H__ -#define __MEMSET_H__ - -void *memset(void *s, int c, size_t n); - -#endif // __MEMSET_H__ diff --git a/zealbooter/src/lib/strcmp.c b/zealbooter/src/lib/strcmp.c deleted file mode 100644 index 6451e1d53..000000000 --- a/zealbooter/src/lib/strcmp.c +++ /dev/null @@ -1,19 +0,0 @@ -/* strcmp( const char *, const char * ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -int strcmp(const char *s1, const char *s2) -{ - while ((*s1) && (*s1 == *s2)) - { - ++s1; - ++s2; - } - - return (*(unsigned char *)s1 - *(unsigned char *)s2); -} - - diff --git a/zealbooter/src/lib/strcmp.h b/zealbooter/src/lib/strcmp.h deleted file mode 100644 index 8343f792e..000000000 --- a/zealbooter/src/lib/strcmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __STRCMP_H__ -#define __STRCMP_H__ - -int strcmp(const char *s1, const char *s2); - -#endif // __STRCMP_H__ diff --git a/zealbooter/src/lib/strcpy.c b/zealbooter/src/lib/strcpy.c deleted file mode 100644 index 1f2d59ac5..000000000 --- a/zealbooter/src/lib/strcpy.c +++ /dev/null @@ -1,19 +0,0 @@ -/* strcpy( char *, const char * ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -char *strcpy(char *s1, const char *s2) -{ - char *rc = s1; - - while ((*s1++ = *s2++)) - { - /* EMPTY */ - } - - return rc; -} - diff --git a/zealbooter/src/lib/strcpy.h b/zealbooter/src/lib/strcpy.h deleted file mode 100644 index 6ae7ea87f..000000000 --- a/zealbooter/src/lib/strcpy.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __STRCPY_H__ -#define __STRCPY_H__ - -char *strcpy(char *s1, const char *s2); - -#endif // __STRCPY_H__ diff --git a/zealbooter/src/lib/strlen.c b/zealbooter/src/lib/strlen.c deleted file mode 100644 index 142a435d2..000000000 --- a/zealbooter/src/lib/strlen.c +++ /dev/null @@ -1,19 +0,0 @@ -/* strlen( const char * ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -size_t strlen(const char *s) -{ - size_t rc = 0; - - while (s[rc]) - { - ++rc; - } - - return rc; -} - diff --git a/zealbooter/src/lib/strlen.h b/zealbooter/src/lib/strlen.h deleted file mode 100644 index 859589bd7..000000000 --- a/zealbooter/src/lib/strlen.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __STRLEN_H__ -#define __STRLEN_H__ - -size_t strlen(const char *s); - -#endif // __STRLEN_H__ diff --git a/zealbooter/src/lib/strncmp.c b/zealbooter/src/lib/strncmp.c deleted file mode 100644 index e330938e2..000000000 --- a/zealbooter/src/lib/strncmp.c +++ /dev/null @@ -1,26 +0,0 @@ -/* strncmp( const char *, const char *, size_t ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - -int strncmp(const char *s1, const char *s2, size_t n) -{ - while (n && *s1 && (*s1 == *s2)) - { - ++s1; - ++s2; - --n; - } - - if (n == 0) - { - return 0; - } - else - { - return (*(unsigned char *)s1 - *(unsigned char *)s2); - } -} - diff --git a/zealbooter/src/lib/strncmp.h b/zealbooter/src/lib/strncmp.h deleted file mode 100644 index 8c37ccacc..000000000 --- a/zealbooter/src/lib/strncmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __STRNCMP_H__ -#define __STRNCMP_H__ - -int strncmp(const char *s1, const char *s2, size_t n); - -#endif // __STRNCMP_H__ diff --git a/zealbooter/src/lib/strncpy.c b/zealbooter/src/lib/strncpy.c deleted file mode 100644 index 452482897..000000000 --- a/zealbooter/src/lib/strncpy.c +++ /dev/null @@ -1,31 +0,0 @@ -/* strncpy( char *, const char *, size_t ) - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include - - -char *strncpy(char *s1, const char *s2, size_t n) -{ - char *rc = s1; - - while (n && (*s1++ = *s2++)) - { - /* Cannot do "n--" in the conditional as size_t is unsigned and we have - to check it again for >0 in the next loop below, so we must not risk - underflow. - */ - --n; - } - - /* Checking against 1 as we missed the last --n in the loop above. */ - while (n-- > 1) - { - *s1++ = '\0'; - } - - return rc; -} - - diff --git a/zealbooter/src/lib/strncpy.h b/zealbooter/src/lib/strncpy.h deleted file mode 100644 index fbaf449f0..000000000 --- a/zealbooter/src/lib/strncpy.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __STRNCPY_H__ -#define __STRNCPY_H__ - -char *strncpy(char *s1, const char *s2, size_t n); - -#endif // __STRNCPY_H__ diff --git a/zealbooter/src/trampoline.S b/zealbooter/src/trampoline.S index 62398b056..45d4b2580 100644 --- a/zealbooter/src/trampoline.S +++ b/zealbooter/src/trampoline.S @@ -1,16 +1,29 @@ +// Offsets into struct trampoline_args +.set ARGS_STACK, 0 +.set ARGS_KERNEL, 4 +.set ARGS_KERNEL_SIZE, 8 +.set ARGS_KERNEL_BASE, 12 +.set ARGS_GDT_PTR, 16 +.set ARGS_ENTRY_POINT, 20 +.set ARGS_PATCH_TABLE_BASE, 24 +.set ARGS_BOOT_BASE, 28 +.set ARGS_BOOT_STACK, 32 + .section .text .global trampoline trampoline: // Set new stack - mov %rdx, %rsp + movl ARGS_STACK(%rbx), %esp - // Load GDTR - lgdt (%rcx) + // Clear PCIDE, CR0.PG cannot be cleared with it set + mov %cr4, %rcx + btr $17, %rcx + mov %rcx, %cr4 // Far jump to 32-bit compatibility mode - // AKA (set CS to CGDT.cs32) - pushq $0x30 + // AKA (set CS to Limine's 32-bit code descriptor) + pushq $0x18 addq $(1f - trampoline), %rax pushq %rax lretq @@ -18,8 +31,8 @@ trampoline: .code32 1: - // Set all data segments to CGDT.ds - mov $0x10, %eax + // Set all data segments to Limine's 32-bit data descriptor + mov $0x20, %eax mov %eax, %ds mov %eax, %es mov %eax, %fs @@ -37,8 +50,47 @@ trampoline: xor %edx, %edx wrmsr + // Clear CR4 + mov %eax, %cr4 + + // Copy the kernel to BOOT_RAM_BASE, nothing of Limine's is in use anymore + mov ARGS_KERNEL(%ebx), %esi + mov ARGS_KERNEL_BASE(%ebx), %edi + mov ARGS_KERNEL_SIZE(%ebx), %ecx + cld + rep movsb + + // Load GDTR + mov ARGS_GDT_PTR(%ebx), %eax + lgdt (%eax) + + // Far jump to set CS to CGDT.cs32 + call 2f + 2: + popl %eax + addl $(3f - 2b), %eax + pushl $0x30 + pushl %eax + lretl + + 3: + + // Set all data segments to CGDT.ds + mov $0x60, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss + + // Set what CORE0_32BIT_INIT sets for the kernel + mov ARGS_BOOT_STACK(%ebx), %esp + mov ARGS_PATCH_TABLE_BASE(%ebx), %esi + mov ARGS_BOOT_BASE(%ebx), %edi + mov ARGS_ENTRY_POINT(%ebx), %ebx + // Set CR0 to (PE | NE) AKA SYS_START_CR0 - mov $0x11, %eax + mov $0x21, %eax mov %eax, %cr0 // Set flags to 0x02 diff --git a/zealbooter/src/zealbooter.c b/zealbooter/src/zealbooter.c index 66fa857d6..9ef01b72d 100644 --- a/zealbooter/src/zealbooter.c +++ b/zealbooter/src/zealbooter.c @@ -149,6 +149,54 @@ struct CKernel { #define RLF_VESA 0b010 #define RLF_32BIT 0b100 +#define BOOT_RAM_BASE 0x7c00 // KernelA.HH, where the native boot loaders load the kernel +#define BOOT_RAM_LIMIT 0x97000 // KernelA.HH, top of the native boot stack +#define MP_VECT_ADDR 0x97000 // KernelA.HH, the kernel copies its AP startup code to this page + +// the offsets in trampoline.S must match +struct trampoline_args { + uint32_t stack; + uint32_t kernel; + uint32_t kernel_size; + uint32_t kernel_base; + uint32_t gdt_ptr; + uint32_t entry_point; + uint32_t patch_table_base; + uint32_t boot_base; + uint32_t boot_stack; +} __attribute__((packed)); + +static void hcf(void) { + for (;;) { + asm("hlt"); + } +} + +// the types handed to the kernel as usable +static bool is_usable(uint64_t type) { + return type == LIMINE_MEMMAP_USABLE || type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE || type == LIMINE_MEMMAP_EXECUTABLE_AND_MODULES; +} + +// end of the usable memory contiguous from base, entries other than usable ones may overlap +static uint64_t usable_top(uint64_t base) { + uint64_t top = base; + bool progress; + + do { + progress = false; + for (size_t i = 0; i < memmap_request.response->entry_count; i++) { + struct limine_memmap_entry *entry = memmap_request.response->entries[i]; + + if (is_usable(entry->type) && entry->base <= top && entry->base + entry->length > top) { + top = entry->base + entry->length; + progress = true; + } + } + } while (progress); + + return top; +} + extern symbol trampoline, trampoline_end; struct E801 { @@ -183,14 +231,28 @@ void kmain(void) { printf("ZealBooter prekernel\n"); printf("____________________\n\n"); + if (module_request.response == NULL || module_request.response->module_count < 1 + || memmap_request.response == NULL || hhdm_request.response == NULL + || framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) { + printf("ERROR: missing Limine responses"); + hcf(); + } + struct limine_file *module_kernel = module_request.response->modules[0]; struct CKernel *kernel = module_kernel->address; + // the kernel runs where the native boot loaders load it and uses the memory up to its MP vector + if (BOOT_RAM_BASE + module_kernel->size > BOOT_RAM_LIMIT || usable_top(BOOT_RAM_BASE) < MP_VECT_ADDR + 0x1000) { + printf("ERROR: no memory for the kernel at BOOT_RAM_BASE"); + hcf(); + } + + // the trampoline leaves Limine's page tables, stack and GDT from usable memory before copying the kernel const size_t trampoline_size = (uintptr_t)trampoline_end - (uintptr_t)trampoline; - const size_t boot_stack_size = 32768; - const size_t final_size = align_up_u64(module_kernel->size + trampoline_size, 16) + boot_stack_size; + const size_t staging_stack_size = 4096; + const size_t staging_size = align_up_u64(trampoline_size + sizeof(struct trampoline_args), 16) + align_up_u64(module_kernel->size, 16) + staging_stack_size; - uintptr_t final_address = (uintptr_t)-1; + uintptr_t staging = (uintptr_t)-1; for (size_t i = 0; i < memmap_request.response->entry_count; i++) { struct limine_memmap_entry *entry = memmap_request.response->entries[i]; @@ -198,17 +260,21 @@ void kmain(void) { continue; } - if (entry->length >= final_size) { - final_address = entry->base; + // above 1MiB to stay clear of the kernel, below 4GiB for the 32-bit trampoline + uint64_t base = entry->base > 0x100000 ? entry->base : 0x100000; + uint64_t top = entry->base + entry->length < 0x100000000 ? entry->base + entry->length : 0x100000000; + + if (base < top && top - base >= staging_size) { + staging = base; break; } } - if (final_address == (uintptr_t)-1) { - printf("ERROR: could not find valid final address"); - for (;;) { asm("hlt"); } + if (staging == (uintptr_t)-1) { + printf("ERROR: could not find memory for the trampoline"); + hcf(); } - printf("final_address: 0x%X\n", final_address); + printf("staging: 0x%X\n", staging); struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0]; kernel->sys_framebuffer_pitch = fb->pitch; @@ -218,7 +284,7 @@ void kmain(void) { kernel->sys_framebuffer_addr = (uintptr_t)fb->address - hhdm_request.response->offset; struct limine_video_mode *mode; - for (size_t i = 0, j = 0; i < fb->mode_count && i < VBE_MODES_NUM; i++) + for (size_t i = 0, j = 0; framebuffer_request.response->revision >= 1 && i < fb->mode_count && j < VBE_MODES_NUM; i++) { mode = fb->modes[i]; if (mode->bpp == 32) @@ -244,7 +310,7 @@ void kmain(void) { } entry_point -= (uintptr_t)module_kernel->address; - entry_point += final_address; + entry_point += BOOT_RAM_BASE; printf("entry_point: 0x%X\n", entry_point); @@ -257,20 +323,24 @@ void kmain(void) { kernel->boot_blk = 0; kernel->boot_patch_table_base = (uintptr_t)kernel + kernel->h.patch_table_offset; kernel->boot_patch_table_base -= (uintptr_t)module_kernel->address; - kernel->boot_patch_table_base += final_address; + kernel->boot_patch_table_base += BOOT_RAM_BASE; printf("kernel->boot_patch_table_base: 0x%X\n", kernel->boot_patch_table_base); kernel->sys_run_level = RLF_VESA | RLF_16BIT | RLF_32BIT; kernel->boot_base = (uintptr_t)&kernel->jmp - (uintptr_t)module_kernel->address; - kernel->boot_base += final_address; + kernel->boot_base += BOOT_RAM_BASE; printf("kernel->boot_base: 0x%X\n", kernel->boot_base); kernel->sys_gdt_ptr.limit = sizeof(kernel->sys_gdt) - 1; kernel->sys_gdt_ptr.base = (void *)&kernel->sys_gdt - (uintptr_t)module_kernel->address; - kernel->sys_gdt_ptr.base += final_address; + kernel->sys_gdt_ptr.base += BOOT_RAM_BASE; + + // based at boot_base as KStart16 does, CORE0_32BIT_INIT relies on it when BootRAM reenters it + kernel->sys_gdt.boot_ds.lo += (uint64_t)kernel->boot_base << 16; + kernel->sys_gdt.boot_cs.lo += (uint64_t)kernel->boot_base << 16; printf("kernel->sys_gdt_ptr.limit: 0x%X\n", kernel->sys_gdt_ptr.limit); printf("kernel->sys_gdt_ptr.base: 0x%X\n", kernel->sys_gdt_ptr.base); @@ -284,7 +354,10 @@ void kmain(void) { kernel->mem_physical_space = 0; printf("memory map:\n"); - size_t mem_count = memmap_request.response->entry_count; // MEM_E820_ENTRIES_NUM now == 256, which is also Limine's memmap entry count max + size_t mem_count = memmap_request.response->entry_count; + if (mem_count > MEM_E820_ENTRIES_NUM - 1) { + mem_count = MEM_E820_ENTRIES_NUM - 1; // leave one to terminate, like KStart16 + } for (size_t i = 0; i < mem_count; i++) { struct limine_memmap_entry *entry = memmap_request.response->entries[i]; int zeal_mem_type; @@ -339,26 +412,18 @@ void kmain(void) { kernel->mem_physical_space = align_up_u64(kernel->mem_physical_space, 0x200000); void *sys_gdt_ptr = (void *)&kernel->sys_gdt_ptr - (uintptr_t)module_kernel->address; - sys_gdt_ptr += final_address; + sys_gdt_ptr += BOOT_RAM_BASE; printf("sys_gdt_ptr: 0x%X\n", sys_gdt_ptr); - void *sys_smbios_entry = (void *)smbios_request.response->entry_32; + void *sys_smbios_entry = smbios_request.response != NULL ? smbios_request.response->entry_32 : NULL; if (sys_smbios_entry != NULL) { kernel->sys_smbios_entry = (uintptr_t)sys_smbios_entry - hhdm_request.response->offset; } memcpy(kernel->sys_disk_uuid, &module_kernel->gpt_disk_uuid, sizeof(kernel->sys_disk_uuid)); - void *const trampoline_phys = (void *)final_address + module_kernel->size; - - printf("trampoline_phys: 0x%X\n", trampoline_phys); - - const uintptr_t boot_stack = final_address + final_size; - - printf("boot_stack: 0x%X\n", boot_stack); - - kernel->sys_boot_stack = boot_stack; + kernel->sys_boot_stack = BOOT_RAM_LIMIT; if (efi_request.response) { @@ -367,16 +432,27 @@ void kmain(void) { kernel->sys_bootloader_id = BL_LIMINE; - memcpy(trampoline_phys, trampoline, trampoline_size); - memcpy((void *)final_address, kernel, module_kernel->size); + struct trampoline_args *const args = (void *)staging + trampoline_size; + void *const staging_kernel = (void *)staging + align_up_u64(trampoline_size + sizeof(struct trampoline_args), 16); + + memcpy((void *)staging, trampoline, trampoline_size); + memcpy(staging_kernel, kernel, module_kernel->size); + + args->stack = staging + staging_size; + args->kernel = (uintptr_t)staging_kernel; + args->kernel_size = module_kernel->size; + args->kernel_base = BOOT_RAM_BASE; + args->gdt_ptr = (uintptr_t)sys_gdt_ptr; + args->entry_point = (uintptr_t)entry_point; + args->patch_table_base = kernel->boot_patch_table_base; + args->boot_base = kernel->boot_base; + args->boot_stack = kernel->sys_boot_stack; // printf("\nDEBUG: halting."); for (;;); asm volatile ( "jmp *%0" : - : "a"(trampoline_phys), "b"(entry_point), - "c"(sys_gdt_ptr), "d"(boot_stack), - "S"(kernel->boot_patch_table_base), "D"(kernel->boot_base) + : "a"(staging), "b"(args) : "memory"); __builtin_unreachable();