From 2bb7471dbbeabf26665a3abaa08ade7d1ae9b414 Mon Sep 17 00:00:00 2001 From: Zhirik1337 Date: Fri, 13 Mar 2026 14:19:46 +0000 Subject: [PATCH] Add limine sources --- limine/.editorconfig | 18 + limine/.forgejo/workflows/binary-release.yml | 91 + limine/.forgejo/workflows/check.yml | 51 + limine/.forgejo/workflows/pr_branch_check.yml | 15 + limine/.forgejo/workflows/qa.yml | 24 + limine/.forgejo/workflows/release.yml | 100 + limine/.gitignore | 75 + limine/.typos.toml | 24 + limine/3RDPARTY.md | 67 + limine/CONFIG.md | 364 ++++ limine/COPYING | 22 + limine/ChangeLog | 1882 +++++++++++++++++ limine/FAQ.md | 35 + limine/GNUmakefile.in | 401 ++++ limine/INSTALL.md | 50 + ...-scancode-bsd-no-disclaimer-unmodified.txt | 7 + limine/README.md | 102 + limine/USAGE.md | 107 + limine/bochsrc | 7 + limine/bootstrap | 170 ++ limine/common/common.mk | 675 ++++++ limine/common/crypt/blake2b.c | 220 ++ limine/common/crypt/blake2b.h | 10 + limine/common/drivers/disk.h | 26 + limine/common/drivers/disk.s2.c | 846 ++++++++ limine/common/drivers/edid.c | 85 + limine/common/drivers/edid.h | 43 + limine/common/drivers/gop.c | 380 ++++ limine/common/drivers/gop.h | 18 + limine/common/drivers/serial.c | 50 + limine/common/drivers/serial.h | 15 + limine/common/drivers/vbe.c | 364 ++++ limine/common/drivers/vbe.h | 14 + limine/common/drivers/vga_textmode.c | 369 ++++ limine/common/drivers/vga_textmode.h | 35 + limine/common/efi_thunk.asm_uefi_aarch64 | 12 + limine/common/efi_thunk.asm_uefi_ia32 | 10 + limine/common/efi_thunk.asm_uefi_loongarch64 | 10 + limine/common/efi_thunk.asm_uefi_riscv64 | 11 + limine/common/efi_thunk.asm_uefi_x86_64 | 10 + limine/common/entry.s2.c | 134 ++ limine/common/entry.s3.c | 202 ++ limine/common/entry_asm.s2.asm_bios_ia32 | 31 + limine/common/fs/fat32.h | 11 + limine/common/fs/fat32.s2.c | 733 +++++++ limine/common/fs/file.h | 45 + limine/common/fs/file.s2.c | 184 ++ limine/common/fs/iso9660.h | 10 + limine/common/fs/iso9660.s2.c | 544 +++++ limine/common/gensyms.sh | 51 + limine/common/lib/acpi.c | 475 +++++ limine/common/lib/acpi.h | 205 ++ limine/common/lib/bli.c | 90 + limine/common/lib/bli.h | 11 + limine/common/lib/config.c | 756 +++++++ limine/common/lib/config.h | 36 + limine/common/lib/elf.c | 1209 +++++++++++ limine/common/lib/elf.h | 87 + limine/common/lib/elsewhere.c | 100 + limine/common/lib/elsewhere.h | 19 + limine/common/lib/fb.c | 65 + limine/common/lib/fb.h | 43 + limine/common/lib/fdt.c | 44 + limine/common/lib/fdt.h | 15 + limine/common/lib/getchar.c | 365 ++++ limine/common/lib/getchar.h | 28 + limine/common/lib/gterm.c | 907 ++++++++ limine/common/lib/gterm.h | 11 + limine/common/lib/guid.c | 95 + limine/common/lib/guid.h | 20 + limine/common/lib/image.c | 65 + limine/common/lib/image.h | 32 + limine/common/lib/libc.c | 131 ++ limine/common/lib/libc.h | 36 + limine/common/lib/libc.s2.c | 149 ++ limine/common/lib/macros.aarch64_asm.h | 52 + limine/common/lib/memory.s2.c | 53 + limine/common/lib/misc.c | 417 ++++ limine/common/lib/misc.h | 130 ++ limine/common/lib/misc.s2.c | 129 ++ limine/common/lib/panic.s2.c | 89 + limine/common/lib/part.h | 110 + limine/common/lib/part.s2.c | 645 ++++++ limine/common/lib/pe.c | 496 +++++ limine/common/lib/pe.h | 12 + limine/common/lib/print.h | 16 + limine/common/lib/print.s2.c | 235 ++ limine/common/lib/rand.c | 89 + limine/common/lib/rand.h | 11 + limine/common/lib/real.h | 34 + limine/common/lib/real.s2.asm_bios_ia32 | 168 ++ limine/common/lib/sleep.asm_bios_ia32 | 157 ++ limine/common/lib/spinup.asm_aarch64 | 113 + limine/common/lib/spinup.asm_bios_ia32 | 49 + limine/common/lib/spinup.asm_loongarch64 | 110 + limine/common/lib/spinup.asm_riscv64 | 55 + limine/common/lib/spinup.asm_uefi_ia32 | 74 + limine/common/lib/spinup.asm_uefi_x86_64 | 111 + limine/common/lib/stb_image.c | 6 + limine/common/lib/term.c | 311 +++ limine/common/lib/term.h | 59 + limine/common/lib/time.c | 75 + limine/common/lib/time.h | 8 + limine/common/lib/trace.h | 8 + limine/common/lib/trace.s2.c | 87 + limine/common/lib/uri.c | 286 +++ limine/common/lib/uri.h | 10 + limine/common/libc-compat/stdlib.h | 6 + limine/common/libc-compat/string.h | 23 + limine/common/linker_bios.ld.in | 113 + limine/common/linker_uefi_aarch64.ld.in | 79 + limine/common/linker_uefi_ia32.ld.in | 79 + limine/common/linker_uefi_loongarch64.ld.in | 79 + limine/common/linker_uefi_riscv64.ld.in | 83 + limine/common/linker_uefi_x86_64.ld.in | 79 + limine/common/menu.c | 1312 ++++++++++++ limine/common/menu.h | 20 + limine/common/menu_thunk.asm_aarch64 | 27 + limine/common/menu_thunk.asm_ia32 | 29 + limine/common/menu_thunk.asm_loongarch64 | 25 + limine/common/menu_thunk.asm_riscv64 | 24 + limine/common/menu_thunk.asm_x86_64 | 23 + limine/common/mm/mtrr.c | 140 ++ limine/common/mm/mtrr.h | 11 + limine/common/mm/pmm.c | 87 + limine/common/mm/pmm.h | 79 + limine/common/mm/pmm.s2.c | 885 ++++++++ limine/common/mm/vmm.c | 555 +++++ limine/common/mm/vmm.h | 166 ++ limine/common/protos/chainload.c | 367 ++++ limine/common/protos/chainload.h | 8 + limine/common/protos/limine.c | 1746 +++++++++++++++ limine/common/protos/limine.h | 8 + limine/common/protos/limine_32.asm_x86 | 138 ++ limine/common/protos/limine_asm.asm_ia32 | 278 +++ limine/common/protos/linux.h | 73 + limine/common/protos/linux_32.asm_x86 | 69 + limine/common/protos/linux_64.asm_uefi_x86_64 | 62 + limine/common/protos/linux_risc.c | 473 +++++ limine/common/protos/linux_x86.c | 664 ++++++ limine/common/protos/multiboot.h | 17 + limine/common/protos/multiboot1.c | 509 +++++ limine/common/protos/multiboot1.h | 100 + limine/common/protos/multiboot2.c | 1024 +++++++++ limine/common/protos/multiboot2.h | 418 ++++ limine/common/protos/multiboot_32.asm_x86 | 23 + limine/common/protos/multiboot_reloc.asm_x86 | 66 + limine/common/pxe/pxe.h | 111 + limine/common/pxe/pxe.s2.c | 72 + limine/common/pxe/pxe_asm.s2.asm_bios_ia32 | 99 + limine/common/pxe/tftp.h | 46 + limine/common/pxe/tftp.s2.c | 267 +++ limine/common/stb_image.patch | 90 + limine/common/sys/a20.h | 9 + limine/common/sys/a20.s2.c | 92 + limine/common/sys/cpu.h | 432 ++++ limine/common/sys/cpu.s2.c | 26 + limine/common/sys/cpu_riscv.c | 427 ++++ limine/common/sys/dummy_isr.asm_ia32 | 12 + limine/common/sys/dummy_isr.asm_x86_64 | 28 + limine/common/sys/e820.h | 13 + limine/common/sys/e820.s2.c | 44 + limine/common/sys/exceptions.s2.c | 50 + limine/common/sys/gdt.h | 29 + limine/common/sys/gdt.s2.c | 95 + limine/common/sys/idt.c | 71 + limine/common/sys/idt.h | 59 + limine/common/sys/idt.s2.c | 35 + limine/common/sys/int_thunks.s2.asm_bios_ia32 | 41 + limine/common/sys/iommu.c | 144 ++ limine/common/sys/iommu.h | 6 + limine/common/sys/lapic.c | 507 +++++ limine/common/sys/lapic.h | 38 + limine/common/sys/pic.c | 48 + limine/common/sys/pic.h | 11 + limine/common/sys/sbi.asm_riscv64 | 19 + limine/common/sys/sbi.h | 32 + limine/common/sys/smp.c | 881 ++++++++ limine/common/sys/smp.h | 46 + limine/common/sys/smp_trampoline.asm_aarch64 | 149 ++ limine/common/sys/smp_trampoline.asm_riscv64 | 84 + limine/common/sys/smp_trampoline.asm_x86 | 271 +++ limine/config.h.in | 7 + limine/configure.ac | 348 +++ limine/decompressor/decompressor.mk | 84 + limine/decompressor/entry.asm | 20 + limine/decompressor/linker.ld | 39 + limine/decompressor/main.c | 26 + limine/decompressor/memory.c | 53 + limine/decompressor/tinf.patch | 264 +++ limine/host/.gitignore | 2 + limine/host/hgen.sh | 16 + limine/host/host.mk | 19 + limine/host/limine.c | 1444 +++++++++++++ limine/logo.png | Bin 0 -> 755 bytes limine/man/man1/limine.1.in | 13 + limine/screenshot.png | Bin 0 -> 860008 bytes limine/stage1/cd/bootsect.asm | 108 + limine/stage1/cd/read_2k_sectors.asm | 35 + limine/stage1/gdt.asm | 24 + limine/stage1/hdd/bootsect.asm | 153 ++ limine/stage1/hdd/disk.asm | 90 + limine/stage1/pxe/bootsect.asm | 60 + limine/test.mk | 278 +++ limine/test/.gitignore | 2 + limine/test/bg.jpg | Bin 0 -> 271346 bytes limine/test/device_tree.dts | 16 + limine/test/e9print.c | 98 + limine/test/e9print.h | 9 + limine/test/limine.c | 612 ++++++ limine/test/limine.conf | 59 + limine/test/linker.ld | 53 + limine/test/memory.c | 52 + limine/test/multiboot.c | 92 + limine/test/multiboot.ld | 35 + limine/test/multiboot2.c | 181 ++ limine/test/multiboot2.ld | 35 + limine/test/multiboot2_trampoline.asm | 45 + limine/test/multiboot_trampoline.asm | 37 + limine/test/test.mk | 163 ++ limine/version.sh | 29 + 221 files changed, 36911 insertions(+) create mode 100644 limine/.editorconfig create mode 100644 limine/.forgejo/workflows/binary-release.yml create mode 100644 limine/.forgejo/workflows/check.yml create mode 100644 limine/.forgejo/workflows/pr_branch_check.yml create mode 100644 limine/.forgejo/workflows/qa.yml create mode 100644 limine/.forgejo/workflows/release.yml create mode 100644 limine/.gitignore create mode 100644 limine/.typos.toml create mode 100644 limine/3RDPARTY.md create mode 100644 limine/CONFIG.md create mode 100644 limine/COPYING create mode 100644 limine/ChangeLog create mode 100644 limine/FAQ.md create mode 100644 limine/GNUmakefile.in create mode 100644 limine/INSTALL.md create mode 100644 limine/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt create mode 100644 limine/README.md create mode 100644 limine/USAGE.md create mode 100644 limine/bochsrc create mode 100755 limine/bootstrap create mode 100644 limine/common/common.mk create mode 100644 limine/common/crypt/blake2b.c create mode 100644 limine/common/crypt/blake2b.h create mode 100644 limine/common/drivers/disk.h create mode 100644 limine/common/drivers/disk.s2.c create mode 100644 limine/common/drivers/edid.c create mode 100644 limine/common/drivers/edid.h create mode 100644 limine/common/drivers/gop.c create mode 100644 limine/common/drivers/gop.h create mode 100644 limine/common/drivers/serial.c create mode 100644 limine/common/drivers/serial.h create mode 100644 limine/common/drivers/vbe.c create mode 100644 limine/common/drivers/vbe.h create mode 100644 limine/common/drivers/vga_textmode.c create mode 100644 limine/common/drivers/vga_textmode.h create mode 100644 limine/common/efi_thunk.asm_uefi_aarch64 create mode 100644 limine/common/efi_thunk.asm_uefi_ia32 create mode 100644 limine/common/efi_thunk.asm_uefi_loongarch64 create mode 100644 limine/common/efi_thunk.asm_uefi_riscv64 create mode 100644 limine/common/efi_thunk.asm_uefi_x86_64 create mode 100644 limine/common/entry.s2.c create mode 100644 limine/common/entry.s3.c create mode 100644 limine/common/entry_asm.s2.asm_bios_ia32 create mode 100644 limine/common/fs/fat32.h create mode 100644 limine/common/fs/fat32.s2.c create mode 100644 limine/common/fs/file.h create mode 100644 limine/common/fs/file.s2.c create mode 100644 limine/common/fs/iso9660.h create mode 100644 limine/common/fs/iso9660.s2.c create mode 100755 limine/common/gensyms.sh create mode 100644 limine/common/lib/acpi.c create mode 100644 limine/common/lib/acpi.h create mode 100644 limine/common/lib/bli.c create mode 100644 limine/common/lib/bli.h create mode 100644 limine/common/lib/config.c create mode 100644 limine/common/lib/config.h create mode 100644 limine/common/lib/elf.c create mode 100644 limine/common/lib/elf.h create mode 100644 limine/common/lib/elsewhere.c create mode 100644 limine/common/lib/elsewhere.h create mode 100644 limine/common/lib/fb.c create mode 100644 limine/common/lib/fb.h create mode 100644 limine/common/lib/fdt.c create mode 100644 limine/common/lib/fdt.h create mode 100644 limine/common/lib/getchar.c create mode 100644 limine/common/lib/getchar.h create mode 100644 limine/common/lib/gterm.c create mode 100644 limine/common/lib/gterm.h create mode 100644 limine/common/lib/guid.c create mode 100644 limine/common/lib/guid.h create mode 100644 limine/common/lib/image.c create mode 100644 limine/common/lib/image.h create mode 100644 limine/common/lib/libc.c create mode 100644 limine/common/lib/libc.h create mode 100644 limine/common/lib/libc.s2.c create mode 100644 limine/common/lib/macros.aarch64_asm.h create mode 100644 limine/common/lib/memory.s2.c create mode 100644 limine/common/lib/misc.c create mode 100644 limine/common/lib/misc.h create mode 100644 limine/common/lib/misc.s2.c create mode 100644 limine/common/lib/panic.s2.c create mode 100644 limine/common/lib/part.h create mode 100644 limine/common/lib/part.s2.c create mode 100644 limine/common/lib/pe.c create mode 100644 limine/common/lib/pe.h create mode 100644 limine/common/lib/print.h create mode 100644 limine/common/lib/print.s2.c create mode 100644 limine/common/lib/rand.c create mode 100644 limine/common/lib/rand.h create mode 100644 limine/common/lib/real.h create mode 100644 limine/common/lib/real.s2.asm_bios_ia32 create mode 100644 limine/common/lib/sleep.asm_bios_ia32 create mode 100644 limine/common/lib/spinup.asm_aarch64 create mode 100644 limine/common/lib/spinup.asm_bios_ia32 create mode 100644 limine/common/lib/spinup.asm_loongarch64 create mode 100644 limine/common/lib/spinup.asm_riscv64 create mode 100644 limine/common/lib/spinup.asm_uefi_ia32 create mode 100644 limine/common/lib/spinup.asm_uefi_x86_64 create mode 100644 limine/common/lib/stb_image.c create mode 100644 limine/common/lib/term.c create mode 100644 limine/common/lib/term.h create mode 100644 limine/common/lib/time.c create mode 100644 limine/common/lib/time.h create mode 100644 limine/common/lib/trace.h create mode 100644 limine/common/lib/trace.s2.c create mode 100644 limine/common/lib/uri.c create mode 100644 limine/common/lib/uri.h create mode 100644 limine/common/libc-compat/stdlib.h create mode 100644 limine/common/libc-compat/string.h create mode 100644 limine/common/linker_bios.ld.in create mode 100644 limine/common/linker_uefi_aarch64.ld.in create mode 100644 limine/common/linker_uefi_ia32.ld.in create mode 100644 limine/common/linker_uefi_loongarch64.ld.in create mode 100644 limine/common/linker_uefi_riscv64.ld.in create mode 100644 limine/common/linker_uefi_x86_64.ld.in create mode 100644 limine/common/menu.c create mode 100644 limine/common/menu.h create mode 100644 limine/common/menu_thunk.asm_aarch64 create mode 100644 limine/common/menu_thunk.asm_ia32 create mode 100644 limine/common/menu_thunk.asm_loongarch64 create mode 100644 limine/common/menu_thunk.asm_riscv64 create mode 100644 limine/common/menu_thunk.asm_x86_64 create mode 100644 limine/common/mm/mtrr.c create mode 100644 limine/common/mm/mtrr.h create mode 100644 limine/common/mm/pmm.c create mode 100644 limine/common/mm/pmm.h create mode 100644 limine/common/mm/pmm.s2.c create mode 100644 limine/common/mm/vmm.c create mode 100644 limine/common/mm/vmm.h create mode 100644 limine/common/protos/chainload.c create mode 100644 limine/common/protos/chainload.h create mode 100644 limine/common/protos/limine.c create mode 100644 limine/common/protos/limine.h create mode 100644 limine/common/protos/limine_32.asm_x86 create mode 100644 limine/common/protos/limine_asm.asm_ia32 create mode 100644 limine/common/protos/linux.h create mode 100644 limine/common/protos/linux_32.asm_x86 create mode 100644 limine/common/protos/linux_64.asm_uefi_x86_64 create mode 100644 limine/common/protos/linux_risc.c create mode 100644 limine/common/protos/linux_x86.c create mode 100644 limine/common/protos/multiboot.h create mode 100644 limine/common/protos/multiboot1.c create mode 100644 limine/common/protos/multiboot1.h create mode 100644 limine/common/protos/multiboot2.c create mode 100644 limine/common/protos/multiboot2.h create mode 100644 limine/common/protos/multiboot_32.asm_x86 create mode 100644 limine/common/protos/multiboot_reloc.asm_x86 create mode 100644 limine/common/pxe/pxe.h create mode 100644 limine/common/pxe/pxe.s2.c create mode 100644 limine/common/pxe/pxe_asm.s2.asm_bios_ia32 create mode 100644 limine/common/pxe/tftp.h create mode 100644 limine/common/pxe/tftp.s2.c create mode 100644 limine/common/stb_image.patch create mode 100644 limine/common/sys/a20.h create mode 100644 limine/common/sys/a20.s2.c create mode 100644 limine/common/sys/cpu.h create mode 100644 limine/common/sys/cpu.s2.c create mode 100644 limine/common/sys/cpu_riscv.c create mode 100644 limine/common/sys/dummy_isr.asm_ia32 create mode 100644 limine/common/sys/dummy_isr.asm_x86_64 create mode 100644 limine/common/sys/e820.h create mode 100644 limine/common/sys/e820.s2.c create mode 100644 limine/common/sys/exceptions.s2.c create mode 100644 limine/common/sys/gdt.h create mode 100644 limine/common/sys/gdt.s2.c create mode 100644 limine/common/sys/idt.c create mode 100644 limine/common/sys/idt.h create mode 100644 limine/common/sys/idt.s2.c create mode 100644 limine/common/sys/int_thunks.s2.asm_bios_ia32 create mode 100644 limine/common/sys/iommu.c create mode 100644 limine/common/sys/iommu.h create mode 100644 limine/common/sys/lapic.c create mode 100644 limine/common/sys/lapic.h create mode 100644 limine/common/sys/pic.c create mode 100644 limine/common/sys/pic.h create mode 100644 limine/common/sys/sbi.asm_riscv64 create mode 100644 limine/common/sys/sbi.h create mode 100644 limine/common/sys/smp.c create mode 100644 limine/common/sys/smp.h create mode 100644 limine/common/sys/smp_trampoline.asm_aarch64 create mode 100644 limine/common/sys/smp_trampoline.asm_riscv64 create mode 100644 limine/common/sys/smp_trampoline.asm_x86 create mode 100644 limine/config.h.in create mode 100644 limine/configure.ac create mode 100644 limine/decompressor/decompressor.mk create mode 100644 limine/decompressor/entry.asm create mode 100644 limine/decompressor/linker.ld create mode 100644 limine/decompressor/main.c create mode 100644 limine/decompressor/memory.c create mode 100644 limine/decompressor/tinf.patch create mode 100644 limine/host/.gitignore create mode 100755 limine/host/hgen.sh create mode 100644 limine/host/host.mk create mode 100644 limine/host/limine.c create mode 100644 limine/logo.png create mode 100644 limine/man/man1/limine.1.in create mode 100644 limine/screenshot.png create mode 100644 limine/stage1/cd/bootsect.asm create mode 100644 limine/stage1/cd/read_2k_sectors.asm create mode 100644 limine/stage1/gdt.asm create mode 100644 limine/stage1/hdd/bootsect.asm create mode 100644 limine/stage1/hdd/disk.asm create mode 100644 limine/stage1/pxe/bootsect.asm create mode 100644 limine/test.mk create mode 100644 limine/test/.gitignore create mode 100644 limine/test/bg.jpg create mode 100644 limine/test/device_tree.dts create mode 100644 limine/test/e9print.c create mode 100644 limine/test/e9print.h create mode 100644 limine/test/limine.c create mode 100644 limine/test/limine.conf create mode 100644 limine/test/linker.ld create mode 100644 limine/test/memory.c create mode 100644 limine/test/multiboot.c create mode 100644 limine/test/multiboot.ld create mode 100644 limine/test/multiboot2.c create mode 100644 limine/test/multiboot2.ld create mode 100644 limine/test/multiboot2_trampoline.asm create mode 100644 limine/test/multiboot_trampoline.asm create mode 100644 limine/test/test.mk create mode 100755 limine/version.sh diff --git a/limine/.editorconfig b/limine/.editorconfig new file mode 100644 index 0000000..5181c19 --- /dev/null +++ b/limine/.editorconfig @@ -0,0 +1,18 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +max_line_length = 80 + +[{Makefile,GNUmakefile*}] +indent_style = tab + +[{*.toml,*.yml}] +indent_size = 2 diff --git a/limine/.forgejo/workflows/binary-release.yml b/limine/.forgejo/workflows/binary-release.yml new file mode 100644 index 0000000..822b8b0 --- /dev/null +++ b/limine/.forgejo/workflows/binary-release.yml @@ -0,0 +1,91 @@ +name: Binary release + +on: + push: + tags: + - 'v*' + +jobs: + build: + name: Build and upload binary artifacts + runs-on: codeberg-medium + container: archlinux:latest + + steps: + - name: Create resolv.conf + run: | + set -ex + echo 'nameserver 8.8.8.8' >/etc/resolv.conf + echo 'nameserver 8.8.4.4' >>/etc/resolv.conf + + - name: Install dependencies + run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs base-devel gnupg git autoconf automake nasm curl mtools llvm clang lld mingw-w64-gcc + + - name: Import GPG public key + run: gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821 + + - name: Import GPG private key + run: echo "$MINTSUKI_PRIVATE_KEY" | gpg --batch --import + env: + MINTSUKI_PRIVATE_KEY: ${{ secrets.MINTSUKI_PRIVATE_KEY }} + + - name: Checkout code + uses: https://code.forgejo.org/actions/checkout@v6 + with: + fetch-depth: '0' + + - name: Git config + run: | + set -e + git config --global --add safe.directory "$FORGEJO_WORKSPACE" + git config --global user.name 'Mintsuki' + git config --global user.email 'mintsuki@protonmail.com' + git config --global user.signingkey 05D29860D0A0668AAEFB9D691F3C021BECA23821 + + - name: Get tag name + run: echo "TAG_NAME=$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" >> $FORGEJO_ENV + + - name: Get branch name + run: echo "BRANCH_NAME=$(echo "$TAG_NAME" | grep -o 'v[0-9]\+\.')x" >> $FORGEJO_ENV + + - name: Regenerate + run: ./bootstrap + + - name: Create build dir + run: mkdir -p build + + - name: Configure + run: cd build && ../configure --enable-all + + - name: Build the bootloader + run: make -C build -j$(nproc) + + - name: Clean limine + run: rm build/bin/limine + + - name: Build limine for Windows + run: make -C build/bin CC="i686-w64-mingw32-gcc" CFLAGS="-O2 -pipe" CPPFLAGS="-D__USE_MINGW_ANSI_STDIO" limine + + - name: Strip limine for Windows + run: i686-w64-mingw32-strip build/bin/limine.exe + + - name: Copy LICENSE to bin + run: cp COPYING build/bin/LICENSE + + - name: Remove limine-bios-hdd.bin + run: rm build/bin/limine-bios-hdd.bin + + - name: Push binaries to binary branch + run: | + set -e + git remote set-url origin https://x-access-token:${{ secrets.FORGEJO_TOKEN }}@codeberg.org/Limine/Limine.git + git fetch --all + git checkout $BRANCH_NAME-binary || git checkout --orphan $BRANCH_NAME-binary + rm -rf $(ls -a | grep -v '^\.git$' | grep -v '^\.\.$' | grep -v '^\.$' | grep -v '^build$') + cp -r build/bin/. ./ + rm -rf build + git add -f . + git commit -m "Binary release $TAG_NAME" -S + git push origin $BRANCH_NAME-binary + git tag $TAG_NAME-binary -s -m $TAG_NAME-binary + git push origin $TAG_NAME-binary diff --git a/limine/.forgejo/workflows/check.yml b/limine/.forgejo/workflows/check.yml new file mode 100644 index 0000000..161eb91 --- /dev/null +++ b/limine/.forgejo/workflows/check.yml @@ -0,0 +1,51 @@ +name: Check for compilation failures + +on: [push, pull_request] + +jobs: + build: + name: Check for compilation failures + runs-on: codeberg-small + container: archlinux:latest + + steps: + - name: Create resolv.conf + run: | + set -ex + echo 'nameserver 8.8.8.8' >/etc/resolv.conf + echo 'nameserver 8.8.4.4' >>/etc/resolv.conf + + - name: Install dependencies + run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs base-devel git autoconf automake nasm curl mtools llvm clang lld + + - name: Checkout code + uses: https://code.forgejo.org/actions/checkout@v6 + + - name: Build the bootloader (LLVM, default) + run: ./bootstrap && ./configure --enable-werror --enable-all && make all && make distclean + + - name: Set cross GCC version + run: echo "GCC_VERSION=15.2.0" >> $FORGEJO_ENV + + - name: Download GCC cross toolchains + run: | + set -e + for i in aarch64 loongarch64 riscv64 x86_64; do + ( curl -Lo x86_64-gcc-${{ env.GCC_VERSION }}-nolibc-$i-linux.tar.gz https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/${{ env.GCC_VERSION }}/x86_64-gcc-${{ env.GCC_VERSION }}-nolibc-$i-linux.tar.gz && tar -xf x86_64-gcc-${{ env.GCC_VERSION }}-nolibc-$i-linux.tar.gz ) & + WAITING_ON_PIDS="$WAITING_ON_PIDS $!" + done + for pid in $WAITING_ON_PIDS; do + wait $pid + done + + - name: Build the bootloader (GNU, x86) + run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/x86_64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=x86_64-linux- --enable-werror --enable-bios --enable-uefi-ia32 --enable-uefi-x86-64 && make all -j$(nproc) && make distclean + + - name: Build the bootloader (GNU, aarch64) + run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/aarch64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=aarch64-linux- --enable-werror --enable-uefi-aarch64 && make all -j$(nproc) && make distclean + + - name: Build the bootloader (GNU, riscv64) + run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/riscv64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=riscv64-linux- --enable-werror --enable-uefi-riscv64 && make all -j$(nproc) && make distclean + + - name: Build the bootloader (GNU, loongarch64) + run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/loongarch64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=loongarch64-linux- --enable-werror --enable-uefi-loongarch64 && make all -j$(nproc) && make distclean diff --git a/limine/.forgejo/workflows/pr_branch_check.yml b/limine/.forgejo/workflows/pr_branch_check.yml new file mode 100644 index 0000000..04645d9 --- /dev/null +++ b/limine/.forgejo/workflows/pr_branch_check.yml @@ -0,0 +1,15 @@ +name: Check that the PR is targetting trunk + +on: [ pull_request ] + +jobs: + pr_branch_check: + name: Check that the PR is targetting trunk + runs-on: codeberg-tiny + steps: + - name: Check that the PR is targetting trunk + if: ${{ forge.base_ref != 'trunk' }} + run: | + set -e + echo "The PR is not targetting the trunk branch, please fix that." + false diff --git a/limine/.forgejo/workflows/qa.yml b/limine/.forgejo/workflows/qa.yml new file mode 100644 index 0000000..4163bd5 --- /dev/null +++ b/limine/.forgejo/workflows/qa.yml @@ -0,0 +1,24 @@ +name: QA + +on: [ merge_group, push, pull_request ] + +jobs: + spellcheck: + name: Spellcheck + runs-on: codeberg-tiny + container: archlinux:latest + steps: + - name: Create resolv.conf + run: | + set -ex + echo 'nameserver 8.8.8.8' >/etc/resolv.conf + echo 'nameserver 8.8.4.4' >>/etc/resolv.conf + + - name: Install dependencies + run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs git typos + + - name: Checkout code + uses: https://code.forgejo.org/actions/checkout@v6 + + - name: Run spellchecker + run: typos diff --git a/limine/.forgejo/workflows/release.yml b/limine/.forgejo/workflows/release.yml new file mode 100644 index 0000000..deeab28 --- /dev/null +++ b/limine/.forgejo/workflows/release.yml @@ -0,0 +1,100 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + build: + name: Build and upload release tarball + runs-on: codeberg-small + container: archlinux:latest + + steps: + - name: Create resolv.conf + run: | + set -ex + echo 'nameserver 8.8.8.8' >/etc/resolv.conf + echo 'nameserver 8.8.4.4' >>/etc/resolv.conf + + - name: Install dependencies + run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs jq curl tea base-devel gnupg gzip bzip2 xz git autoconf automake nasm curl mtools llvm clang lld github-cli + + - name: Import GPG public key + run: gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821 + + - name: Import GPG private key + run: echo "$MINTSUKI_PRIVATE_KEY" | gpg --batch --import + env: + MINTSUKI_PRIVATE_KEY: ${{ secrets.MINTSUKI_PRIVATE_KEY }} + + - name: Checkout code + uses: https://code.forgejo.org/actions/checkout@v6 + + - name: Package release tarball + run: | + set -e + ./bootstrap + ./configure + make dist + mkdir dist-output + mv limine-*.tar.* dist-output/ + + - name: Sign release tarball + run: | + set -e + for f in dist-output/*; do + gpg --batch --default-key 05D29860D0A0668AAEFB9D691F3C021BECA23821 --detach-sign $f + done + + - name: Release + uses: https://code.forgejo.org/actions/forgejo-release@v2.7.3 + with: + direction: upload + hide-archive-link: true + release-dir: dist-output + release-notes: | + Changelog can be found [here](https://codeberg.org/Limine/Limine/src/tag/${{ forge.ref_name }}/ChangeLog). + + Binary release can be found [here](https://codeberg.org/Limine/Limine/src/tag/${{ forge.ref_name }}-binary). + + Tarballs are signed using key ID `05D29860D0A0668AAEFB9D691F3C021BECA23821` which can be obtained from a keyserver such as `keyserver.ubuntu.com`. + + Import the public key with: + ```bash + gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821 + ``` + + In order to verify the tarball with the given signature, do: + ```bash + gpg --verify + ``` + + - name: GitHub mirror release + env: + GITHUB_TOKEN: ${{ secrets.MINTSUKI_GH_TOKEN }} + run: | + set -e + cat >release_notes.txt <<'EOF' + Changelog can be found [here](https://github.com/limine-bootloader/limine/blob/${{ forge.ref_name }}/ChangeLog). + + Binary release can be found [here](https://github.com/limine-bootloader/limine/tree/${{ forge.ref_name }}-binary). + + Tarballs are signed using key ID `05D29860D0A0668AAEFB9D691F3C021BECA23821` which can be obtained from a keyserver such as `keyserver.ubuntu.com`. + + Import the public key with: + ```bash + gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821 + ``` + + In order to verify the tarball with the given signature, do: + ```bash + gpg --verify + ``` + EOF + gh release create "${{ forge.ref_name }}" \ + --repo "limine-bootloader/limine" \ + --title "${{ forge.ref_name }}" \ + --notes-file "release_notes.txt" \ + dist-output/* diff --git a/limine/.gitignore b/limine/.gitignore new file mode 100644 index 0000000..bbecc3f --- /dev/null +++ b/limine/.gitignore @@ -0,0 +1,75 @@ +# We don't want to ignore the following files +!.clang-format +!.editorconfig +!.gitattributes +!.gitignore +!.typos.toml + + +# Generated files +*.o +*.d +*.a +*.exe +*.EFI +*.bin +*.bin.gz +*.tar* +*.elf +*.hdd +*.iso +*.sys +*.dtb +*~ + +/bin +/build +/limine-protocol +/picoefi +/freestnd-c-hdrs +/flanterm +/common/lib/stb_image.h.nopatch +/common/lib/stb_image.h +/common/cc-runtime.s2.c +/cc-runtime +/decompressor/tinf +/decompressor/cc-runtime.c +/libfdt +/tinf +/edk2-ovmf +/bochsout.txt +/bx_enh_dbg.ini +/test_image +/configure +/configure.ac.save +/timestamps +/build-aux +/aclocal.m4 +/config.status +/config.log +/autom4te.cache +/man/man1/limine.1 +/GNUmakefile +/config.h +/common-bios +/common-uefi-ia32 +/common-uefi-x86-64 +/common-uefi-aarch64 +/common-uefi-riscv64 +/common-uefi-loongarch64 +/decompressor-build +/stage1.stamp + + +# For local development +/.vscode +/.gdb_history +/tags +/TAGS + +# Clang's compilation database file +compile_commands.json + +# clangd caches +/.clangd +/.cache/clangd diff --git a/limine/.typos.toml b/limine/.typos.toml new file mode 100644 index 0000000..a8a2073 --- /dev/null +++ b/limine/.typos.toml @@ -0,0 +1,24 @@ +# Configuration for https://github.com/crate-ci/typos + +[files] +extend-exclude = [ + # Typos already uses .gitignore. These excludes are additional: +] + +[default.extend-words] +hda = "hda" +Nd = "Nd" +sie = "sie" +SIE = "SIE" +ist = "ist" +rela = "rela" +RELA = "RELA" +guid = "guid" +GUID = "GUID" +pn = "pn" +hd = "hd" +htpt = "htpt" +CMO = "CMO" +extint = "extint" + +[default.extend-identifiers] diff --git a/limine/3RDPARTY.md b/limine/3RDPARTY.md new file mode 100644 index 0000000..eee4a16 --- /dev/null +++ b/limine/3RDPARTY.md @@ -0,0 +1,67 @@ +# 3rd Party Software Acknowledgments + +The Limine project depends on several other projects. + +(For readers with access to source code, know that these are pulled in by the +`./bootstrap` script, or, in the case of release tarballs, are shipped +alongside the core Limine code in the tarballs themselves, similar to +`./bootstrap` having already been run.) + +These additional projects are NOT covered by the License as contained inside +the `COPYING` file as present at the root of the source tree, or, for installed +copies, present at `${DOCDIR}/COPYING` (assuming the file has not been +otherwise removed by the packager). These are instead licensed as described by +each individual project's documentation present in each project's dedicated +subdirectory or license header(s) in the source tree. For readers without access +to the source code, one can read the following for a quick overview of licenses +that Limine is distributed under: + +A non-binding, informal summary of all projects Limine depends on, and the +licenses used by said projects, in SPDX format, is as follows: + +- [cc-runtime](https://codeberg.org/OSDev/cc-runtime) +(Apache-2.0 WITH LLVM-exception) is used to provide runtime libgcc-like +routines. + +- [0BSD Freestanding C Headers](https://codeberg.org/OSDev/freestnd-c-hdrs-0bsd) +(0BSD) provide GCC and Clang compatible freestanding C headers. + +- [Limine Boot Protocol](https://codeberg.org/Limine/limine-protocol) +(0BSD) has the C/C++ header and the specification text of the Limine Boot +Protocol. + +- [PicoEFI](https://codeberg.org/PicoEFI/PicoEFI) (multiple licenses, see list +below) provides headers and build-time support for UEFI. + - BSD-2-Clause + - BSD-2-Clause-Patent + - BSD-3-Clause + - LicenseRef-scancode-bsd-no-disclaimer-unmodified + - MIT + + For more information about the + LicenseRef-scancode-bsd-no-disclaimer-unmodified license used by parts of + PicoEFI, see + https://scancode-licensedb.aboutcode.org/bsd-no-disclaimer-unmodified.html + and the LicenseRef file + [here](LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt), + in case of viewing this file from inside the source tree, alternatively at + `${DOCDIR}/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt` + in case of installed copies, assuming the file has not been otherwise + removed by the packager. + +- [tinf](https://github.com/jibsen/tinf) (Zlib) is used in early x86 BIOS +stages for GZIP decompression of stage2. + +- [Flanterm](https://codeberg.org/Mintsuki/Flanterm) (BSD-2-Clause) is used for +text related screen drawing. + +- [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) (MIT) is +used for wallpaper image loading. + +- [libfdt](https://codeberg.org/OSDev/libfdt) (BSD-2-Clause) is used for +manipulating Flat Device Trees. + +Note that some of these projects, or parts of them, are provided under +dual-licensing, in which case, in the above list, the only license mentioned is +the one chosen by the Limine developers. Refer to each individual project's +documentation for details. diff --git a/limine/CONFIG.md b/limine/CONFIG.md new file mode 100644 index 0000000..db01899 --- /dev/null +++ b/limine/CONFIG.md @@ -0,0 +1,364 @@ +# Limine configuration file + +## Location of the config file + +For EFI-booted Limine, `/limine.conf` is taken into account +first. On BIOS, or on EFI if that file is not found, Limine scans for the +config file on *the boot drive*. Every partition of the boot drive is scanned +sequentially - first partition first (or, on EFI, the partition containing the +EFI executable of the booted Limine is scanned first), last partition last - +for the presence of either a `/boot/limine/limine.conf`, `/boot/limine.conf`, +`/limine/limine.conf`, or a `/limine.conf` file, in that order. + +Once the file is located, Limine will use it as its config file. Other possible +candidates in subsequent partitions or directories are ignored. + +It is thus imperative that the intended config file is placed in a location +that will not be shadowed by another candidate config file. + +### Config via SMBIOS + +Alternatively, if present, Limine considers first and foremost configurations +supplied to it as SMBIOS OEM String entries (Type 11). Such configurations are +accepted if the first string of such an entry starts with the prefix of +`limine:config:`. The rest of the string is taken as the config file. If such a +configuration is found, no further scanning for config files is done. As such, +in this SMBIOS-provided config file scenario, the `boot():` drive is undefined +on BIOS, and set to the boot device of Limine on UEFI. + +## Structure of the config file + +The Limine configuration file is comprised of *menu entries* and *options*. +Comments begin in '#' and can only be on their own lines. + +### Menu entries and sub-entries + +*Menu entries* describe *entries* which the user can select in the *boot menu*. + +A *menu entry* is opened by a line starting with `/` followed by a +newline-terminated string, that being the title of the entry which the user +will see. +Any *local option* that comes after it, and before another *menu entry*, or +the end of the file, will be part of that *menu entry*. + +A *menu entry* can be a directory, meaning it can hold sub-entries. In order +for an entry to become a directory, it needs to have a sub-entry following +right after it. +(A `comment` option may be present between the beginning of the directory entry +and the beginning of the sub-entry). + +A *sub-entry* is a menu entry started with a number of `/` greater than 1 +prepended to it. +Each `/` represents 1 level deeper down the tree hierarchy of directories and +entries. + +Directories can be expanded (meaning they will not show up as collapsed in the +menu) by default if a `+` is put between the `/`s and the beginning of the +entry's title. + +### Options + +*Options* are simple `option_name: string...` style "assignments". +The string can have spaces and other special characters, without requiring +quotations. New lines are delimiters. Option names are not case sensitive. + +Some *options* are part of an entry (*local*), some other options are *global*. +*Global options* can appear anywhere in the file and are not part of an entry, +although usually one would put them at the beginning of the config. +Some *local options* work the same between entries using any *protocol*, while +other *local options* are specific to a given *protocol*. + +Some options take *paths* as strings; these are described in the next section. + +*Global options* are: + +Miscellaneous: + +* `timeout` - Specifies the timeout in seconds before the first *entry* is + automatically booted. If set to `no`, disable automatic boot. If set to `0`, + boots default entry instantly (see `default_entry` option). +* `quiet` - If set to `yes`, enable quiet mode, where all screen output except + panics and important warnings is suppressed. If `timeout` is not 0, the + `timeout` still occurs, and pressing any key during the timeout will reveal + the menu and disable quiet mode. +* `serial` - If set to `yes`, enable serial I/O for the bootloader. +* `serial_baudrate` - If `serial` is set to `yes`, this specifies the baudrate + to use for serial I/O. Defaults to `115200`. BIOS only, ignored with Limine + UEFI. +* `global_dtb` - If set, use this DTB instead of the firmware-provided DTB for + Limine itself, as well as for any booted entry whose protocol supports DTBs + and the DTB is not locally overridden with `dtb_path`. +* `default_entry` - 1-based entry index of the entry which will be + automatically selected at startup. If unspecified, it is `1`. +* `remember_last_entry` - If set to `yes`, remember last booted entry. + (UEFI only). +* `graphics` - If set to `no`, force CGA text mode for the boot menu, else use + a video mode. Ignored with Limine UEFI. +* `wallpaper` - Path to a file to use as a wallpaper. BMP, PNG, and JPEG + formats are supported. There can be multiple of this option, in which case + the wallpaper will be randomly selected from the provided options. +* `wallpaper_style` - The style which will be used to display the wallpaper + image: `tiled`, `centered`, or `stretched`. Default is `stretched`. +* `backdrop` - When the background style is `centered`, this specifies the + colour of the backdrop for parts of the screen not covered by the background + image, in RRGGBB format. +* `verbose` - If set to `yes`, print additional information during boot. + Defaults to not verbose. +* `randomise_memory` - If set to `yes`, randomise the contents of RAM at bootup + in order to find bugs related to non zeroed memory or for security reasons. + This option will slow down boot time significantly. For the BIOS port of + Limine, this will only randomise memory below 4GiB. +* `randomize_memory` - Alias of `randomise_memory`. +* `hash_mismatch_panic` - If set to `no`, do not panic if there is a hash + mismatch for a file, but print a warning instead. + +Limine interface control options: + +* `interface_resolution` - Specify screen resolution to be used by the Limine + interface (menu, editor, console...) in the form `x`. This + will *only* affect the Limine interface, not any booted OS. If not specified, + Limine will pick a resolution automatically. If the resolution is not + available, Limine will pick another one automatically. Ignored if using text + mode. +* `interface_rotation` - Specifies the rotation of the Limine interface. + It can be any of the following values: `0`, `90`, `180`, `270`. Default is `0`. +* `interface_branding` - A string that will be displayed on top of the Limine + interface. +* `interface_branding_colour` - A value between 0 and 7 specifying the colour + of the branding string. See below for a table of colours. Default is `6`. +* `interface_branding_color` - Alias of `interface_branding_colour`. +* `interface_help_hidden` - Hides the help text located at the top of the + screen showing the key bindings. +* `interface_help_colour` - A value between 0 and 7 specifying the colour + of the help strings. See below for a table of colours. Default is `2`. +* `interface_help_color` - Alias of `interface_help_colour`. + + | Code | Color | + |------|---------| + | 0 | Black | + | 1 | Red | + | 2 | Green | + | 3 | Yellow | + | 4 | Blue | + | 5 | Magenta | + | 6 | Cyan | + | 7 | Gray | + +Limine graphical terminal control options: + +These are ignored if using text mode. + +* `term_font` - Path to a font file to be used instead of the default one for + the menu and terminal. The font file must be a code page 437 character set + comprised of 256 consecutive glyph bitmaps. Each glyph's bitmap must be + expressed left to right (1 byte per row), and top to bottom (16 bytes per + whole glyph by default; see `term_font_size`). See e.g. the + [VGA text mode font](https://github.com/viler-int10h/vga-text-mode-fonts) + collection for fonts. +* `term_font_size` - The size of each glyph of the font in dots, which must + correspond to the font file, or display will be garbled or loading issues + will occur. Since it is assumed that all fonts are of width 8, the first + value of the pair (AKA the `8` in `8x16`) is effectively ignored. To set + horizontal spacing between glyphs on screen, see `term_font_spacing`. + Defaults to `8x16`. Ignored if `term_font` not set or if the font fails to + load. +* `term_font_scale` - Scaling for the font in the x and y directions. `2x2` + would display the font in double size, which is useful on high-DPI displays + at native resolution. `2x1` only makes the font twice as wide, similar to the + VGA 40 column mode. `4x2` might be good for a narrow font on a high + resolution display. Values over 8 are disallowed. Default is no scaling, + i.e. `1x1`. +* `term_font_spacing` - Horizontal spacing, in pixels, between glyphs on + screen. Also applies to the built-in Limine font. Defaults to 1. 0 is + allowed. +* `term_palette` - Specifies the colour palette used by the terminal (RRGGBB). + It is a `;` separated array of 8 colours: black, red, green, brown, blue, + magenta, cyan, and gray. Ignored if not using a graphical terminal. +* `term_palette_bright` - Specifies the bright colour palette used by the + terminal (RRGGBB). It is a `;` separated array of 8 bright colours: + dark gray, bright red, bright green, yellow, bright blue, bright magenta, + bright cyan, and white. Ignored if not using a graphical terminal. +* `term_background` - Terminal text background colour (TTRRGGBB). TT stands for + transparency. +* `term_foreground` - Terminal text foreground colour (RRGGBB). +* `term_background_bright` - Terminal text background bright colour (RRGGBB). +* `term_foreground_bright` - Terminal text foreground bright colour (RRGGBB). +* `term_margin` - Set the amount of margin around the terminal. +* `term_margin_gradient` - Set the thickness in pixel for the gradient around + the terminal. + +Editor control options: + +* `editor_enabled` - If set to `no`, the editor will not be accessible. + Defaults to `yes` unless a config hash is enrolled. +* `editor_highlighting` - If set to `no`, syntax highlighting in the editor + will be disabled. Defaults to `yes`. +* `editor_validation` - If set to `no`, the editor will not alert you about + invalid options or syntax errors. Defaults to `yes`. + +*Locally assignable (non protocol specific) options* are: + +* `comment` - An optional comment string that will be displayed by the + bootloader on the menu when an entry is selected. +* `protocol` - The boot protocol that will be used to boot the + kernel/executable. Valid protocols are: `linux`, `limine`, `multiboot` + (or `multiboot1`), `multiboot2`, `efi`, and `bios`. +* `cmdline` - The command line string to be passed to the kernel/executable. + Can be omitted. +* `kernel_cmdline` - Alias of `cmdline`. + +> **NOTE:** `uefi` and `efi_chainload` are aliases of the `efi` protocol +> option. `bios_chainload` is an alias of the `bios` protocol option. + +> **NOTE:** BIOS chainloading entries will be hidden when booting using UEFI +> and vice-versa. + +*Locally assignable (protocol specific) options* are: + +* Linux protocol: + * `path` - The path of the kernel. + * `kernel_path` - Alias of `path`. + * `module_path` - The path to a module (such as initramfs). This option can + be specified multiple times to specify multiple modules. + * `resolution` - The resolution to be used. This setting takes the form of + `xx`. If the resolution is not available, Limine will + pick another one automatically. Omitting `` will default to 32. + * `textmode` - If set to `yes`, prefer text mode. (BIOS only) + * `dtb_path` - A device tree blob to pass instead of the one provided by the + firmware. + +* Limine protocol: + * `path` - The path of the executable. + * `kernel_path` - Alias of `path`. + * `module_path` - The path to a module. This option can be specified multiple + times to specify multiple modules. + * `module_string` - A string to be associated with a module. This option can + also be specified multiple times. It applies to the module described by the + last module option specified. + * `module_cmdline` - Alias of `module_string`. + * `resolution` - The resolution to be used. This setting takes the form of + `xx`. If the resolution is not available, Limine will + pick another one automatically. Omitting `` will default to 32. + * `kaslr` - For relocatable executables, if set to `yes`, enable kernel + address space layout randomisation. KASLR is disabled by default. + * `randomise_hhdm_base` - If set to `yes`, randomise the base address of the + higher half direct map. If set to `no`, do not. By default it is `yes` if + KASLR is supported and enabled, else it is `no`. + * `randomize_hhdm_base` - Alias of `randomise_hhdm_base`. + * `max_paging_mode`, `min_paging_mode` - Limit the maximum and minimum paging + modes to one of the following: + - x86-64 and aarch64: `4level`, `5level`. + - riscv64: `sv39`, `sv48`, `sv57`. + - loongarch64: `4level`. + * `paging_mode` - Equivalent to setting both `max_paging_mode` and + `min_paging_mode` to the same value. + * `dtb_path` - A device tree blob to pass instead of the one provided by the + firmware. + +* multiboot1 and multiboot2 protocols: + * `path` - The path of the executable. + * `kernel_path` - Alias of `path`. + * `module_path` - The path to a module. This option can be specified multiple + times to specify multiple modules. + * `module_string` - A string to be passed to a module. This option can also + be specified multiple times. It applies to the module described by the last + module option specified. + * `resolution` - The resolution to be used should the executable request a + graphical framebuffer. This setting takes the form of + `xx` and *overrides* any resolution requested by the + executable. If the resolution is not available, Limine will pick another + one automatically. Omitting `` will default to 32. + * `textmode` - If set to `yes`, prefer text mode. (BIOS only) + +* EFI Chainload protocol: + * `path` - Path of the EFI application to chainload. + * `image_path` - Alias of `path`. + * `resolution` - The resolution to be used. This setting takes the form of + `xx`. If the resolution is not available, Limine will + pick another one automatically. Omitting `` will default to 32. + +* BIOS Chainload protocol: + * `drive` - The 1-based drive to chainload, if omitted, assume boot drive. + * `partition` - The 1-based partition to chainload, if omitted, or set to 0, + chainload drive (MBR). + * `mbr_id` - Optional. If passed, use an MBR ID (32-bit hex value) to + identify the drive containing the volume to chainload. Overrides `drive`, + if present, but does *not* override `partition`. + * `gpt_uuid` or `gpt_guid` - Optional. If passed, use the GPT GUID to + identify the drive containing the volume to chainload. Overrides `drive` + and `mbr_id`, if present, but does *not* override `partition`. + +## Paths + +A Limine path is used to locate files in the whole system. It is comprised of +a *resource*, a *resource argument*, and a *path*. It takes the form of: +``` +resource(argument):/path +``` + +The format for `argument` changes depending on the resource used. + +A resource can be one of the following: +* `boot` - If booted off PXE this is an alias of `tftp`. Else the `argument` is + the 1-based decimal value representing the partition on the boot drive + (values of 5+ for MBR logical partitions). If omitted, the partition + containing the configuration file on the boot drive is used. + For example: `boot(2):/...` will use partition 2 of the boot drive and + `boot():/...` will use the partition containing the config file on the boot + drive. +* `hdd` - Hard disk drives. The `argument` takes the form of `drive:partition`; + for example: `hdd(3:1):/...` would use hard drive 3, partition 1. Partitions + and drives are both 1-based (partition values of 5+ for MBR logical + partitions). Omitting the partition is possible; + for example: `hdd(2:):/...`. Omitting the partition will access the entire + volume instead of a specific partition (useful for unpartitioned media). +* `odd` - Optical disk drives (CDs/DVDs/...). The `argument` takes the form of + `drive:partition`; for example: `odd(3:1):/...` would use optical drive 3, + partition 1. Partitions and drives are both 1-based (partition values of 5+ + for MBR logical partitions). Omitting the partition is possible; + for example: `odd(2:):/...`. Omitting the partition will access the entire + volume instead of a specific partition (useful for unpartitioned media, which + is often the case for optical media). +* `guid` - The `argument` takes the form of a GUID/UUID, such as + `guid(736b5698-5ae1-4dff-be2c-ef8f44a61c52):/...`. The GUID is that of either + a filesystem, when available, or a GPT partition GUID, when using GPT, in a + unified namespace. +* `uuid` - Alias of `guid`. +* `fslabel` - The `argument` is the name of the filesystem label of a + partition. +* `tftp` - The `argument` is the IP address of the tftp server to load the file + from. If the argument is left empty (`tftp():/...`) the file will be loaded + from the server Limine booted from. This resource is only available when + booting off PXE. + +A path can optionally be suffixed with a blake2b hash for the referenced file, +by appending a pound character (`#`) followed by the blake2b hash. +E.g.: `boot():/somemodule.tar#ca6914d2...446b470a`. + +## Macros + +Macros are strings that can be arbitrarily assigned to represent other strings. +For example: +``` +${MY_MACRO}=Some text +``` + +Now, whenever `${MY_MACRO}` is used in the config file (except for an +assignment as above), it will be replaced by the text `Some text`. For example: +``` +CMDLINE=something before ${MY_MACRO} something after +``` + +Macros must always be placed inside `${...}` where `...` is the arbitrary macro +name. + +### Built-in macros + +Limine automatically defines these macros: + +* `ARCH` - This built-in macro expands to the architecture of the machine. + Possible values are: `x86-64`, `ia-32`, `aarch64`, `riscv64`, `loongarch64`. + In the case of IA-32, BIOS or UEFI, the macro will always expand to `x86-64` + if the 64-bit extensions are available, else `ia-32`. +* `FW_TYPE` - This built-in macro expands to `UEFI` if booted using UEFI + firmware, or `BIOS` if booted using legacy x86 BIOS. diff --git a/limine/COPYING b/limine/COPYING new file mode 100644 index 0000000..a59751f --- /dev/null +++ b/limine/COPYING @@ -0,0 +1,22 @@ +Copyright (C) 2019-2026 Mintsuki and contributors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/limine/ChangeLog b/limine/ChangeLog new file mode 100644 index 0000000..cb95cbf --- /dev/null +++ b/limine/ChangeLog @@ -0,0 +1,1882 @@ +2026-03-12 Mintsuki + + *** Release 10.8.5 *** + + Noteworthy changes compared to the previous release, 10.8.4: + + Bug fixes: + - Update the Flanterm library to 3.0.2 to fix a regression introduced + in Flanterm 3.0.1 that would also cause misrenderings of the boot + menu's text colour for selected entries. + +2026-03-11 Mintsuki + + *** Release 10.8.4 *** + + Noteworthy changes compared to the previous release, 10.8.3: + + Bug fixes: + - Update the Flanterm library to 3.0.1 to fix a regression that would + cause misrenderings of the boot menu's text colour for selected + entries. + +2026-03-07 Mintsuki + + *** Release 10.8.3 *** + + Noteworthy changes compared to the previous release, 10.8.2: + + Bug fixes: + - Preserve LAPIC register state across x2APIC-to-xAPIC transition. + - SMP: Widen bsp_lapic_id to uint32_t to avoid x2APIC ID truncation. + - Linux boot protocol: Validate RISC-V kernel header before trusting + image_size for allocation. + - Host tool: Replace GCC/Clang __builtin overflow checks with portable + C99 helpers. + - Host tool: Fix ENDSWAP width mismatch in GPT-to-MBR partition entry + conversion on big-endian hosts. + - BIOS HDD Stage 1: Fix 64-bit LBA calculation and carry propagation in + disk read loop. + - BIOS HDD Stage 1: Fix stack imbalance on int 13h/AH=48h failure. + - Add NULL terminator check in config_get_entry header line scan. + - Guard editor window_size decrement to prevent underflow on long line + wrapping. + +2026-02-26 Mintsuki + + *** Release 10.8.2 *** + + Noteworthy changes compared to the previous release, 10.8.1: + + Bug fixes: + - Revert broken/pointless legacy PIC reinitialisation logic on flush + that accidentally made it into 10.8.1. + +2026-02-26 Mintsuki + + *** Release 10.8.1 *** + + Noteworthy changes compared to the previous release, 10.8.0: + + Bug fixes: + - Fix VT-d disable order to TE, IRE, QIE. + - Fix VT-d polling loop read order and remove polling timeout. + - SMP: Add xAPIC ICR delivery status checks to INIT-SIPI-SIPI sequence. + - Amend certain APIC-related issues of Limine boot protocol and + implement the corrected behaviour. + +2026-02-22 Mintsuki + + *** Release 10.8.0 *** + + Noteworthy changes compared to the previous release, 10.7.0: + + New features: + - Limine boot protocol: Implement base revision 5. + - Limine boot protocol: Implement x86-64 Keep IOMMU feature. + + Bug fixes: + - Fix font autoscaling not resetting per display in multi-display + configurations. + - Fix undefined behaviour in memmove() due to direct pointer + comparisons. + - Skip GOP and VBE modes with invalid pitch values. + - GOP: Work around firmware reporting incorrect pitch via QueryMode() + by fetching from gop->Mode->Info after SetMode(). + - GOP: Ensure SetMode() is called at least once per handle to work + around firmware not initialising state without that. + - Fall back to EFI/BIOS console instead of panicking on small + terminals. + + Miscellaneous: + - Disable Intel VT-d and AMD-Vi IOMMUs before kernel entry by default + for the Limine, multiboot1, and multiboot2 protocols. + - Try to gracefully disable x2APIC before handoff for the Limine + (unless kernel asks for it), multiboot1, and multiboot2 protocols. + - Replace cycle-based delay() with wall time calibrated stall(). + - Prefer physical memory allocations below 4 GiB on x86. + - Align ACPI table memory map entries to page size where safe. + - Undo several overzealous limits and bounds checks added in prior + releases. + - Updated limine-protocol subproject to latest revision. + +2026-02-10 Mintsuki + + *** Release 10.7.0 *** + + Noteworthy changes compared to the previous release, 10.6.6: + + New features: + - Multiboot2: Implement relocatable preference=2 (prefer highest + address). + - Multiboot2: Accept LOAD_BASE_ADDR and NETWORK info request tags. + - Multiboot2: Accept EFI system table and image handle info requests. + + Bug fixes: + - Extensive hardening of the ELF and PE loaders with thorough bounds, + overflow, and alignment validation. + - Extensive hardening of the Multiboot and Multiboot2 protocol + implementations with thorough validation of header fields, tag sizes, + and relocation parameters. + - Linux boot protocol: Honour kernel_alignment field from the x86 boot + protocol header for kernel load address alignment, and init_size for + allocation sizing. Use image_size from the kernel header for non-x86 + allocation. + - Fix several bugs in the physical memory manager's overlap detection, + sanitiser logic, and memory info reporting. + - SMP: Fix x86 AP temp stack pointer to pass top of allocation instead + of base. Fix several bugs in the AArch64 SMP trampoline and AP + enumeration paths. Skip APs with unrecognised PSCI method instead of + panicking. + - Host tool: Fix signature search algorithm to correctly handle partial + prefix overlaps. Bounds-check GPT partition entry offset arithmetic + for overflow. Guard secondary GPT nuke against alternate_lba + underflow. + - ACPI: Fall back to RSDT when XSDT is above 4 GiB on 32-bit. + - VMM: Preserve WC/FB cache attribute in x86 page table flags during + page splits. + + Miscellaneous: + - SMP: Send two SIPIs per Intel SDM recommendation for AP startup. + - Use rdtime.d and CPUCFG for LoongArch64 performance timing. + - Use rdtime and time_base_frequency for RISC-V performance timing. + - Updated Flanterm subproject to latest revision. + - Miscellaneous bug fixes and improvements. + +2026-02-05 Mintsuki + + *** Release 10.6.6 *** + + Noteworthy changes compared to the previous release, 10.6.5: + + Bug fixes: + - Re-add a mistakenly removed check for EFI volume responsiveness. Lack + of this check would indirectly cause hangs due to EFI volumes being + indexed despite their non-responsiveness. + + Miscellaneous: + - Replace suboptimal code to handle reads close to the end of a volume. + +2026-02-04 Mintsuki + + *** Release 10.6.5 *** + + Noteworthy changes compared to the previous release, 10.6.4: + + Bug fixes: + - Work around PicoEFI structure definition issue related to padding + that would cause volume_by_device_path() to always fail for + non-optical media. + - Fix issue that would cause unique sector detection on systems with 3+ + collisions to fail and misreport unique sectors. + - Add extra layer of verification for unique sector matches to minimise + the chance of volume mismatches. + + Miscellaneous: + - RISC-V: Implement Flanterm refresh path for SoCs not exposing Zicbom + in the device tree or ACPI (often the case with U-boot). + - Use volume_by_device_path() as primary disk_volume_from_efi_handle() + detection method. + - Lazily discover unique sectors only when the fallback requiring this + procedure is actually used. + +2026-02-02 Mintsuki + + *** Release 10.6.4 *** + + Noteworthy changes compared to the previous release, 10.6.3: + + Bug fixes: + - Strip out all code relying on disk writes from the codebase. This + gets rid of the (remote) possibility of disk corruption and the (even + more remote) possibility of firmware/flash corruption. This also + sorts out an issue on Apple Silicon where m1n1/U-boot appears to + leave the NVMe controller in an inconsistent state after write + operations are attempted. + +2026-01-14 Mintsuki + + *** Release 10.6.3 *** + + Noteworthy changes compared to the previous release, 10.6.2: + + Bug fixes: + - Fix an issue with the `limine` host tool where the files read by or + created by the `bios-install` command with the + `--uninstall-data-file=` flag were prefixed by `=` due to an + off-by-one error. + - Fix an issue with the `limine` host tool where the `--quiet` and + `--reset` flags could cause the next flag to be ignored due to an + off-by-one error. + - Fix an issue with the `limine` host tool that would allow a user to + provide a GPT BIOS boot partition to the `bios-install` command that + was 1 greater than the maximum number of partitions due to an + off-by-one error. + - Fix internal module path corruption in the Limine boot protocol code, + caused by a static buffer reuse bug. + - Add timeout for A20 address line initialisation in the keyboard + controller code path. Lack of this could cause hangs on certain older + "legacy-free" PCs. + - Fix a couple of minor memory leaks. + - Undo several overzealous "bug fixes" done since 10.5.0 for issues + that were impossible or highly unlikely to trigger in practice. + - Miscellaneous bug fixes. + +2026-01-07 Mintsuki + + *** Release 10.6.2 *** + + Noteworthy changes compared to the previous release, 10.6.1: + + Bug fixes: + - Fix a bug where a buffer holding a configuration file option's value + was erroneously overwritten. This affected x86 BIOS builds only, and + it would be triggered if the `serial_baudrate` configuration file + option was used. + +2026-01-05 Mintsuki + + *** Release 10.6.1 *** + + Noteworthy changes compared to the previous release, 10.6.0: + + Bug fixes: + - Fix an issue where there were no bound checks when pressing the + delete key in the menu entry editor, sometimes causing out-of-bounds + accesses. + - Add many checks for return values of disk-related functions that + could fail. + - Fix a typo in the physical memory manager code where a pointer was + checked instead of the value it was pointing to. This could have in + theory caused 0-length usable memory entries to appear in the memory + map. + - Add checks to avoid division by 0 if the user-specified baud rate is + 0. + - Fix broken calculation in the fb_clear() function that caused severe + out-of-bounds writes when the bits-per-pixel were not 32 or 16. + - Fix potential unaligned memory accesses in the BLAKE2B code. This + could have caused issues on RISC architectures like aarch64 or + riscv64. + - Fix missing continues in the riscv ISA string initialisation logic. + This could have caused HARTs with invalid ISA strings to be used + anyways instead of being ignored. + - More minor bug fixes for bugs discovered during static analysis. + + Miscellaneous: + - Use C versions of mem*() functions instead of assembly versions for + x86 and x86-64. + +2025-12-28 Mintsuki + + *** Release 10.6.0 *** + + Noteworthy changes compared to the previous release, 10.5.1: + + New features: + - ISO9660: Add support for large, multi-extent files. + + Bug fixes: + - Unbreak BIOS booting from optical media with ISO9660 filesystems on + many platforms including QEMU. This was a regression introduced in + 10.5.1. + - Fix an issue with large (>4GiB) physical memory allocations on 32-bit + platforms. Some values were truncated to size_t (aka uint32_t) in + many instances causing bugs. + - Fix a couple of memory leaks missed in 10.5.1. + - Add safeguard against the font file being too small for the expected + size. This avoids potential panics from the filesystem driver, and it + instead just prints a warning and falls back to using the default + font. + + Miscellaneous: + - Update description of the `term_font_size` and `term_font_spacing` + configuration options in CONFIG.md. + +2025-12-26 Mintsuki + + *** Release 10.5.1 *** + + Noteworthy changes compared to the previous release, 10.5.0: + + Bug fixes: + - Many bug fixes throughout the codebase, mainly tackling hardening + (especially of filesystem drivers). Most issues were caught with + static analysis. + - Fixage of some memory leaks. + - Fixage of major issue that could have caused UEFI PXE to not work. + - Multiboot1: fix issue that would cause the information buffer to be + underallocated. + +2025-12-11 Mintsuki + + *** Release 10.5.0 *** + + Noteworthy changes compared to the previous release, 10.4.0: + + New features: + - Add new `interface_help_colour` global configuration file option to + allow changing the colour of "helper" messages in the interface. + - Add new `interface_rotation` global configuration option to allow + rotating the output interface be 0, 90, 180, or 270 degrees. + +2025-11-30 Mintsuki + + *** Release 10.4.0 *** + + Noteworthy changes compared to the previous release, 10.3.2: + + New features: + - Add new configuration file option "global_dtb" to allow overriding + the device tree used by Limine itself as well as for all boot entries + (unless overridden by entry-local "dtb_path" option). + + Bug fixes: + - Flush entire I-Cache if it is not PIPT on aarch64. + - Fix use-after-free bug in riscv64 initialisation code related to + device tree handling. + + Miscellaneous: + - Framebuffer-related caching improvements for riscv64 and aarch64. + This should fix a long standing issue where, on a lot of riscv64 + boards, the Limine interface would look mangled due to missing cache + flushing. + - Prioritise using ACPI for riscv64 initialisation code if available + and if a DTB is not manually specified. + +2025-11-23 Mintsuki + + *** Release 10.3.2 *** + + Noteworthy changes compared to the previous release, 10.3.1: + + CI-related changes, otherwise, same as 10.3.1. + +2025-11-23 Mintsuki + + *** Release 10.3.1 *** + + Noteworthy changes compared to the previous release, 10.3.0: + + Bug fixes: + - Fix wrong virtual address related constants, for the loongarch64 + port, that made Limine-protocol kernels fail to boot. + - Fix issue that caused the MBR ID in the Limine boot protocol + limine_file structure to always be 0. + +2025-11-07 Mintsuki + + *** Release 10.3.0 *** + + Noteworthy changes compared to the previous release, 10.2.1: + + **Special notes:** + Despite there being breaking changes in this release, no major version + bump is done; this is because the changes are minor enough, there are + no big new features, and version 10.x was not released long enough ago. + This is technically in violation of Semantic Versioning, but considered + to be an acceptable exception. + + Breaking changes: + - Change default paths and default path ordering for configuration file + search. Mainly, search first for a configuration file in the same + directory as the EFI application file of Limine, on EFI ports. + - Change default path ordering for stage 3 file search on BIOS ports. + - No longer install the limine.h header file on the host. + - No longer install PROTOCOL.md (Limine Boot Protocol specification) on + the host. + + Bug fixes: + - Fix an issue where code assumed that the RSDT address in the RSDP was + always non-NULL, which could cause crashes or hangs when using base + revision 4 of the Limine Boot Protocol. + +2025-10-29 Mintsuki + + *** Release 10.2.1 *** + + Noteworthy changes compared to the previous release, 10.2.0: + + Bug fixes: + - Add workaround for skipping SPI flash devices exposed as EFI volumes. + This fixes the hanging issues on many ARM laptops, without breaking + U-Boot unlike the previous workaround. + +2025-10-26 Mintsuki + + *** Release 10.2.0 *** + + Noteworthy changes compared to the previous release, 10.1.1: + + New features: + - Implement base revision 4 of the Limine boot protocol as per + documentation (read documentation for specific details). + + Bug fixes: + - "Fix" multiboot1 framebuffer structure to match GRUB instead of the + multiboot1 specification. + +2025-10-13 Mintsuki + + *** Release 10.1.1 *** + + Noteworthy changes compared to the previous release, 10.1.0: + + Bug fixes: + - DTBs: Always reallocate DTBs to bootloader-reclaimable memory. This + is to prevent pointers to EFI/reserved memory from being leaked to + Limine protocol executables, which is against said protocol, which + mandates that DTBs have to be in bootloader-reclaimable memory. + +2025-10-05 Mintsuki + + *** Release 10.1.0 *** + + Noteworthy changes compared to the previous release, 10.0.1: + + New features: + - Expand safety checks of the `limine bios-install` command and replace + the `--force-mbr` flag with just `--force`. + - Add autodetection of the BIOS boot partition if no explicit partition + index is passed to `limine bios-install`. + + Bug fixes: + - Only use ASCII characters for terminal output when graphical output + fails to initialise and the EFI standard output console is used + instead. + + Miscellaneous: + - Remove left-over logic that was meant to supplement the removed GPT + embedding feature. + - Remove workaround to allow building the RISC-V port with LLVM <=16. + - Update a bunch of output messages from the `limine` host tool. + +2025-09-21 Mintsuki + + *** Release 10.0.1 *** + + Noteworthy changes compared to the previous release, 10.0.0: + + Bug fixes: + - Fix a potential use-after-free issue when flushing interrupts on x86 + UEFI systems. + - Prefer using the 32-bit Linux protocol unless it is strictly + necessary to use the 64-bit protocol due to allocation constraints. + This is due to reports of the 32-bit protocol being more reliable + on certain rare systems where the 64-bit protocol crashes or hangs. + +2025-09-15 Mintsuki + + *** Release 10.0.0 *** + + Noteworthy changes compared to the previous release, 9.6.7: + + Breaking changes: + - GPT embedding is no longer supported as a deployment method for + GPT-with-BIOS configurations. The rationale for this being that it is + far too fragile, especially with regards to growing or shrinking disk + images, and it may cause certain machines to refuse to boot such + setups as they are technically in violation of the GPT specification. + - The default baud rate for serial I/O on BIOS systems is now 115200, + instead of 9600. + - BIOS (chainloading) boot menu entries are now hidden when booting in + UEFI mode, and EFI (chainloading) entries are hidden when booting in + BIOS mode. + - For the Limine boot protocol, for relocatable executables, KASLR now + defaults to being disabled. The rationale for this being that KASLR + is a somewhat niche and fairly bypassable security technique, which + can cause headaches for kernels not wanting to support it, and make + troubleshooting harder. + - For the Limine boot protocol, for PE format kernels, do not discard + discardable sections, as code put to a discardable section like INIT + might be intended to only be discarded after early kernel init, not + before the kernel is even loaded. + +2025-09-13 Mintsuki + + *** Release 9.6.7 *** + + Noteworthy changes compared to the previous release, 9.6.6: + + Bug fixes: + - Relax EFI handle validation before running fallback volume detection. + This further addresses issues caused by 9.5.4. + +2025-09-06 Mintsuki + + *** Release 9.6.6 *** + + Noteworthy changes compared to the previous release, 9.6.5: + + Bug fixes: + - Fix regression introduced by 9.5.4 that caused Limine to fail to boot + with U-Boot. + +2025-08-30 Mintsuki + + *** Release 9.6.3 through 9.6.5 *** + + Noteworthy changes compared to the previous release, 9.6.2: + + These are special hotfix releases attempting to address issues with the + Forgejo release workflow after the migration to Codeberg. + + Other than that, they have no differences compared to 9.6.2. + +2025-08-30 Mintsuki + + *** Release 9.6.2 *** + + Noteworthy changes compared to the previous release, 9.6.1: + + Miscellaneous: + - The Limine project has moved its home to Codeberg. + - Nyu-EFI was rebranded to PicoEFI. + - Minor build system improvements and subproject bumps. + +2025-08-16 Mintsuki + + *** Release 9.6.1 *** + + Noteworthy changes compared to the previous release, 9.6.0: + + Bug fixes: + - EFI chainloading: Make EFI device path of loaded image relative to + the loaded image device handle. This now complies with the EFI + specification, and, amongst others, it fixes chainloading Shim. + + Miscellaneous: + - Miscellaneous build system changes and improvements. + - Do not disable linker relaxations for LoongArch64. + - `limine` host tool: Add check to ensure that a provided GPT BIOS + partition is at least 32KiB in size. + +2025-08-07 Mintsuki + + *** Release 9.6.0 *** + + Noteworthy changes compared to the previous release, 9.5.4: + + Miscellaneous: + - Get rid of the fragile and messy "freestanding-toolchain" build + helper tool. This allows for a simpler and more predictable way of + selecting the target toolchain components via ./configure. + +2025-08-06 Mintsuki + + *** Release 9.5.4 *** + + Noteworthy changes compared to the previous release, 9.5.3: + + Bug fixes: + - Exclude non-PCI devices from the volume discovery process on UEFI. + This is because on (especially, but not limited to) Snapdragon X + systems, firmware/flash devices, which are non-PCI, are exposed as + block I/O volumes too. This skips them, which makes the bootloader + more reliable and avoids long hangs at boot. + - Miscellaneous changes and fixes to the fallback volume discovery + path, to increase reliability and reduce the chance of accidental + damage or data loss. See GitHub issue #525. + - Minor assembly improvement that fixed Limine not building with old + NASM. + +2025-08-04 Mintsuki + + *** Release 9.5.3 *** + + Noteworthy changes compared to the previous release, 9.5.2: + + Bug fixes: + - Miscellaneous build system fixes, mainly to sort out possible issues + introduced by 9.5.2. + +2025-08-01 Mintsuki + + *** Release 9.5.2 *** + + Noteworthy changes compared to the previous release, 9.5.1: + + Bug fixes: + - Revert change that moved to `nm` from `objdump` for generating the + symbol table, as that broke section-specific symbol dumps and caused + BIOS stage 2 to be bloated with the full symbol table. + + Miscellaneous: + - Miscellaneous build system updates and improvements. + +2025-07-29 Mintsuki + + *** Release 9.5.1 *** + + Noteworthy changes compared to the previous release, 9.5.0: + + Bug fixes: + - Unbreak build using ld.gold as linker. + + Miscellaneous: + - Miscellaneous build system updates and improvements. + +2025-07-14 Mintsuki + + *** Release 9.5.0 *** + + Noteworthy changes compared to the previous release, 9.4.0: + + New features: + - protocols/efi: Hybridise loading mechanism with old memory-device + based approach. This may work around the redundant requirement that + EFI applications be signed, and it may fix certain issues created + when the chainload mechanism was changed in 9.0.1. + + Bug fixes: + - `limine` host tool: Fix some mismatched printf format specifiers. + + Miscellaneous: + - Miscellaneous build system updates and improvements. + - Many dependencies had their commits bumped, some new dependencies + were added (limine-protocol), some were changed (libfdt moved to a + different repository that only includes BSD-2-Clause licensable + code). + +2025-07-10 Mintsuki + + *** Release 9.4.0 *** + + Noteworthy changes compared to the previous release, 9.3.4: + + New features: + - BLI: Implement `LoaderTimeInitUSec` and `LoaderTimeExecUSec`. + These are variables used by `systemd-analyze` and are useful for boot + performance metrics. + - Limine boot protocol: Add bootloader performance feature. + This feature provides the same information as the new Boot Loader + Interface variables, but encoded in a nicer way for Limine protocol + executables. + + Miscellaneous: + - Use `nm` instead of `objdump` for generating the symbol table, which + results in a better, more consistent output, especially for non-x86 + architectures. + - Miscellaneous build system updates and improvements. + +2025-06-21 Mintsuki + + *** Release 9.3.4 *** + + Noteworthy changes compared to the previous release, 9.3.3: + + Bug fixes: + - ELF: Make an optimisation related to skipping scanning of .bss + regions for finding of structures in the loaded image (for Limine + protocol structures or otherwise) less overeager as that could break + in certain cases. + - SMP: On x86, ensure that the value of IA32_APIC_BASE is the same on + the APs as it is on the BSP. + - PMM: Mark EfiLoader{Code,Data} regions as bootloader reclaimable + rather than reserved memory, as for certain protocols, like the + Limine boot protocol, reserved memory is unmapped at runtime, while + these regions may contain hot data that is still needed, like Limine + bootloader memory stacks. + +2025-05-27 Mintsuki + + *** Release 9.3.3 *** + + Noteworthy changes compared to the previous release, 9.3.2: + + Bug fixes: + - Reinstate the limit for scanned drives to 0xf0. This is due to a + report of int 13h hangs when used on drives >=0xf0. + +2025-05-19 Mintsuki + + *** Release 9.3.2 *** + + Noteworthy changes compared to the previous release, 9.3.1: + + Bug fixes: + - PMM: Mark EfiBootServices{Code,Data} memory regions as bootloader + reclaimable instead of usable free memory as apparently the ACPI + and UEFI specifications make no guarantees about useful structures + being left in these areas after leaving boot services (namely BGRT + and ESRT). + +2025-05-15 Mintsuki + + *** Release 9.3.1 *** + + Noteworthy changes compared to the previous release, 9.3.0: + + Bug fixes: + - Remove .git directory mistakenly left over inside release tarballs. + - Fix bug that would lead to panics when booting Linux protocol kernels + without modules on x86. + +2025-05-07 Mintsuki + + *** Release 9.3.0 *** + + Noteworthy changes compared to the previous release, 9.2.3: + + New features: + - Add optional support for the 64-bit x86-64 boot protocol for Linux. + This is supported on the UEFI/x86-64 port of Limine, and it is + preferred over the 32-bit boot protocol if supported by the given + kernel. This helps with loading larger modules on certain systems + with low amounts of low memory which would otherwise panic. + + Bug fixes: + - Fix away some ungraceful handling of out-of-memory situations when + loading files using the Linux boot protocol. Make into panics instead + of crashes or hangs. + + Miscellaneous: + - Improve performance and memory usage of loading modules for the Linux + boot protocol. + +2025-04-12 Mintsuki + + *** Release 9.2.3 *** + + Noteworthy changes compared to the previous release, 9.2.2: + + Bug fixes: + - Fix issues introduced by the stb_image fix introduced in 9.2.1 which + could cause hangs or crashes when certain wallpaper images are used. + + Miscellaneous: + - Update cc-runtime to new, integer-only, packed version. + +2025-03-31 Mintsuki + + *** Release 9.2.2 *** + + Noteworthy changes compared to the previous release, 9.2.1: + + Bug fixes: + - Two quick bug fixes and workarounds in order to make multiboot2 more + resilient when booting specific kernels such as Xen. + - Avoid mistakenly allocating more memory than necessary when creating + the volume index. + +2025-03-25 Mintsuki + + *** Release 9.2.1 *** + + Noteworthy changes compared to the previous release, 9.2.0: + + Bug fixes: + - Fix potential buffer overflow bug with our stb_image support code. + + Miscellaneous: + - Dynamically allocate volume index instead of relying on a hard coded + limit and a fixed allocation. This fixes potential panics or failure + to access volumes on systems with a lot of drives/partitions. + - Limine boot protocol/ELF: Print the name of unresolved symbols on + panic. + +2025-03-16 Mintsuki + + *** Release 9.2.0 *** + + Noteworthy changes compared to the previous release, 9.1.3: + + New features: + - Introduce initial support for the Boot Loader Interface + specification. + - Add /EFI/limine/limine.conf to the paths tried for config file when + using EFI. + + Miscellaneous: + - Do not try config path /EFI/BOOT/limine.conf unless booted using EFI. + +2025-03-13 Mintsuki + + *** Release 9.1.3 *** + + Noteworthy changes compared to the previous release, 9.1.2: + + Bug fixes: + - Loongarch64: Fix alignment of TLB-refill handler. + + Miscellaneous: + - Many fixes and improvements to documentation, installed or otherwise, + including further licensing clarifications. + +2025-03-11 Mintsuki + + *** Release 9.1.2 *** + + Noteworthy changes compared to the previous release, 9.1.1: + + This release does not change anything code-wise, but rather it focuses on + sorting out the licensing mess of Nyu-EFI, by updating it to the latest + revision which tackles this problem, and furthermore it improves the + COPYING file to be more explicit about submodules and their licensing. + +2025-03-10 Mintsuki + + *** Release 9.1.1 *** + + Noteworthy changes compared to the previous release, 9.1.0: + + Bug fixes: + - Do not use ConOut for serial output exclusively unless serial is + verified to be present to the best of our abilities. This fixes an + issue where if `serial=yes` is used on some machines (including + several Apple Mac models) a black screen will be shown instead of the + Limine menu. + + Miscellaneous: + - Begin signing tarballs and (most) commits. + +2025-03-07 Mintsuki + + *** Release 9.1.0 *** + + Noteworthy changes compared to the previous release, 9.0.1: + + New features: + - `limine` host tool: When using the `bios-install` command on an + ISOHYBRID with a GPT, automatically convert it to MBR, if possible + and unless explicitly disabled, in order to enhance compatibility of + said ISOHYBRIDs with many systems (of note, Lenovo Thinkpads). + +2025-02-25 Mintsuki + + *** Release 9.0.1 *** + + Noteworthy changes compared to the previous release, 9.0.0: + + Miscellaneous: + - EFI chainload: Replace old chainload method with one that is more + friendly to other EFI bootloaders such as rEFInd. + - Limine boot protocol/PE: Map previously unmapped PE image headers. + +2025-02-17 Mintsuki + + *** Release 9.0.0 *** + + Noteworthy changes compared to the previous release, 8.7.0: + + Breaking changes: + - Support for the ext2/3/4 filesystems has been once again dropped due + to lack of maintenance and to avoid extra potential attack surface + during Secure Boot Limine usage. + - Drop support for the legacy config format (limine.cfg) and only keep + around the new one (limine.conf) introduced with Limine 8.x. + - Drop support for the (barely worked and seldom used) `chainload_next` + protocol. + + New features: + - Limine boot protocol: Introduce API revision 3, for the `limine.h` + file with several renamed identifiers for better clarity. + - Limine boot protocol: Specify and implement a new, convenience, + "Executable Command Line feature". + - Limine boot protocol: Use monitor/mwait CPU instructions, when + available, to reduce CPU usage and increase performance during + multiprocessor initialisation. + - Add preferred aliases for BIOS and EFI chainload (namely `bios` and + `efi`) protocol names for the `protocol` config option. + + Bug fixes: + - EFI (chainload) boot protocol: Fix `path` option alias not working. + + Miscellaneous: + - Update Flanterm submodule to latest. + +2025-01-10 Mintsuki + + *** Release 8.7.0 *** + + Noteworthy changes compared to the previous release, 8.6.1: + + New features: + - Limine boot protocol: Add support for PE executables. + - Linux boot protocol: Enable support for Loongarch64. + - Add support for selecting a random wallpaper every boot if multiple + are provided. + - Add `path` aliases for `kernel_path` and `image_path` for the Linux + and EFI Chainloading protocols respectively. + + Miscellaneous: + - Submodule version/commit bumps. + +2024-12-26 Mintsuki + + *** Release 8.6.1 *** + + Noteworthy changes compared to the previous release, 8.6.0: + + Bug fixes: + - Propagate EFI PXE device handle properly when chainloading. + - Ensure that, when specifying a `dtb_path`, extra space is properly + allocated just as is done for EFI-provided DTBs. + + Miscellaneous: + - Linux on RISCs: General code clean up; pass EFI memory map and initrd + information as EFI configuration tables alongside already passed DTB + parameters for the same, in preparation for loongarch64 support. + +2024-12-05 Mintsuki + + *** Release 8.6.0 *** + + Noteworthy changes compared to the previous release, 8.5.0: + + New features: + - Limine boot protocol: Specify and implement new "RISC-V BSP Hart ID" + feature. + - Limine boot protocol: Add a new "SBI" firmware type to the firmware + type feature + - Limine boot protocol: Add a new API revision 2 to the limine.h header + file. This replaces many mentions of 'kernel' with a more neutral + 'executable' instead. + - Config: add 'path' alias for 'kernel_path' for the Limine and + multiboot1 and 2 protocols. + +2024-11-30 Mintsuki + + *** Release 8.5.0 *** + + Noteworthy changes compared to the previous release, 8.4.1: + + New features: + - Limine boot protocol: Introduce limine.h API revisions to avoid API + breaks. + + Bug fixes: + - Limine boot protocol: Amend base revision 3 to keep EFI memmap + pointer as virtual. Making it physical made no sense and is + henceforth considered a bug. + + Miscellaneous: + - Limine boot protocol: Rename SMP feature to MP feature for limine.h + API revision 1+. + - Limine boot protocol: Make some types that were void pointers into + uint64_t's to better represent their physical nature; limine.h API + revision 1+. + +2024-11-25 Mintsuki + + *** Release 8.4.1 *** + + Noteworthy changes compared to the previous release, 8.4.0: + + Miscellaneous: + - Limine boot protocol: Ensure machines with pre-enabled x2APIC are + properly supported. + - Reuse a viable framebuffer for UEFI fallback terminal output (like on + panics) if boot services have been exited instead of bailing out. + - Miscellaneous build system updates. + +2024-11-08 Mintsuki + + *** Release 8.4.0 *** + + Noteworthy changes compared to the previous release, 8.3.2: + + New features: + - Support passing Limine its configuration via SMBIOS tables. + + Miscellaneous: + - Update cc-runtime to latest; use non-packed version. + +2024-10-31 Mintsuki + + *** Release 8.3.2 *** + + Noteworthy changes compared to the previous release, 8.3.1: + + Miscellaneous: + - Limine boot protocol: Amend base revision 3 to mandate reporting base + revision used to kernels. + +2024-10-28 Mintsuki + + *** Release 8.3.1 *** + + Noteworthy changes compared to the previous release, 8.3.0: + + Bug fixes: + - Limine boot protocol: Revert broken optimisation. + +2024-10-28 Mintsuki + + *** Release 8.3.0 *** + + Noteworthy changes compared to the previous release, 8.2.0: + + New features: + - Limine boot protocol: Specify and implement base revision 3. See the + specification for further info. + - Add a new `FW_TYPE` built-in config macro. + + Miscellaneous: + - Bump Flanterm revision to a6f6edd6631c01caab932a59dce97bbd5f0c72c8. + - Limine boot protocol: Misc optimisations to page tables usage. + +2024-10-23 Mintsuki + + *** Release 8.2.0 *** + + Noteworthy changes compared to the previous release, 8.1.2: + + New features: + - Add device tree blob override configuration options for the Limine + and Linux boot protocols. + - Limine boot protocol: Filter `memory@...` nodes out of device trees. + + Miscellaneous: + - Documentation: USAGE.md: Update `xorriso` command to produce + ISOHYBRIDs with Joliet info and an HFS+ filesystem for broader + compatibility. + - Increase the size of the `limine-cd-uefi.bin` image to 5760 sectors; + this allows more and/or larger EFI executables to fit (like for + example those generated when compiling with -O0). + - Bump cc-runtime revision to d5425655388977fa12ff9b903e554a20b20c426e. + +2024-10-15 Mintsuki + + *** Release 8.1.2 *** + + Noteworthy changes compared to the previous release, 8.1.1: + + Bug fixes: + - Add missing register saves and restores around call from assembly to + C code for the x86-64 UEFI port. This bug could have caused crashes + with certain compiler optimisation levels (like -O0). + - Adjust size of reserved area of VBE mode info struct to be the right + size. This bug could have caused crashes with certain compiler + optimisation levels (like -O0). + - Ignore UEFI memory map entries of length 0. This fixes crashes that + could have happened on quirky UEFI firmwares passing such entries. + +2024-10-12 Mintsuki + + *** Release 8.1.1 *** + + Noteworthy changes compared to the previous release, 8.1.0: + + Bug fixes: + - Limine boot protocol: Do not hard depend on the CPU supporting the + Page Attribute Table (PAT) *for SMP processors as well*. + +2024-10-12 Mintsuki + + *** Release 8.1.0 *** + + Noteworthy changes compared to the previous release, 8.0.14: + + Bug fixes: + - Limine boot protocol: Do not hard depend on the CPU supporting the + Page Attribute Table (PAT). + + New features: + - Limine boot protocol: Add a `randomise_hhdm_base` config file option. + + Miscellaneous: + - Update libfdt (from dtc) to 1.7.1. + +2024-09-29 Mintsuki + + *** Release 8.0.14 *** + + Noteworthy changes compared to the previous release, 8.0.13: + + Miscellaneous: + - Limine boot protocol: Increase maximum memory map entry count from + 256 to 1024 as it would otherwise cause issues on certain systems. + +2024-09-18 Mintsuki + + *** Release 8.0.13 *** + + Noteworthy changes compared to the previous release, 8.0.12: + + Bug fixes: + - multiboot2: Fix handling of relocatable kernels. + +2024-09-15 Mintsuki + + *** Release 8.0.12 *** + + Noteworthy changes compared to the previous release, 8.0.11: + + Bug fixes: + - Fix a build system problem that could cause configure failures if + STRIP variable set to an absolute path. + + Miscellaneous: + - Updates for "freestanding-headers" being rebranded to + "freestnd-c-hdrs-0bsd". + - Updates for "limine-efi" being rebranded to "nyu-efi". + +2024-09-10 Mintsuki + + *** Release 8.0.11 *** + + Noteworthy changes compared to the previous release, 8.0.10: + + Miscellaneous: + - Replace "freestnd-c-hdrs" back with "freestanding-headers" due to + GPLv3 with Runtime Exceptions licensing concerns and size. + +2024-09-09 Mintsuki + + *** Release 8.0.10 *** + + Noteworthy changes compared to the previous release, 8.0.9: + + Bug fixes: + - Fix bug where contents of loaded EFI image size variable could be off + significantly compared to actual size when compiled with GCC, causing + many issues. + - Fix memory manager bug introduced in 2021 during optimisation work. + + Miscellaneous: + - Replace "freestanding-headers" with GCC-provided "freestnd-c-hdrs". + +2024-09-04 Mintsuki + + *** Release 8.0.9 *** + + Noteworthy changes compared to the previous release, 8.0.8: + + Bug fixes: + - Fix an issue that could cause Limine to not boot on UEFI systems with + too many memory map entries. + +2024-09-03 Mintsuki + + *** Release 8.0.8 *** + + Noteworthy changes compared to the previous release, 8.0.7: + + Bug fixes: + - Work around makefile issues that caused GNU make versions 4.0 and 4.1 + as well as version 3.80 to fail to build Limine. + +2024-09-02 Mintsuki + + *** Release 8.0.7 *** + + Noteworthy changes compared to the previous release, 8.0.6: + + Miscellaneous: + - Remove GNU make version check as we now support building on older + make versions. + - Miscellaneous build system improvements. + +2024-08-25 Mintsuki + + *** Release 8.0.6 *** + + Noteworthy changes compared to the previous release, 8.0.5: + + Bug fixes: + - Fix issue with makefiles that would cause, among others, issues with + job control on older GNU make versions. + + Miscellaneous: + - Update limine-efi dependency to latest. + - Minor build system improvements. + +2024-08-12 Mintsuki + + *** Release 8.0.5 *** + + Noteworthy changes compared to the previous release, 8.0.4: + + Bug fixes: + - Limine boot protocol: Obtain LAPIC ID from LAPIC instead of CPUID. + This fixes crashes on some odd machines whose firmware remaps the + BSP's LAPIC ID from the default one. + +2024-08-10 Mintsuki + + *** Release 8.0.4 *** + + Noteworthy changes compared to the previous release, 8.0.3: + + Bug fixes: + - Limine boot protocol: Fix off-by-1 error in HHDM MAXPHYADDR bounds + check. + + Miscellaneous: + - Updated cc-runtime dependency to latest, packed version. + +2024-08-09 Mintsuki + + *** Release 8.0.3 *** + + Noteworthy changes compared to the previous release, 8.0.2: + + Bug fixes: + - AArch64, riscv64: SMP: Fix BSP potentially timing out while waiting + for APs due to missing delay. + +2024-08-08 Mintsuki + + *** Release 8.0.2 *** + + Noteworthy changes compared to the previous release, 8.0.1: + + Bug fixes: + - Menu: Miscellaneous cosmetic fixes. + - Editor: Fix input and displaying of tab characters. + + Miscellaneous: + - Config: Accept tab characters as whitespace after option colon. + +2024-08-05 Mintsuki + + *** Release 8.0.1 *** + + Noteworthy changes compared to the previous release, 8.0.0: + + Bug fixes: + - Assortment of menu fixes to issues that could cause things not to + render as they should. + + Miscellaneous: + - Documentation fixes for 8.x. + +2024-08-02 Mintsuki + + *** Release 8.0.0 *** + + Noteworthy changes compared to the previous release, 7.13.3: + + New features: + - Add loongarch64 support. + + Bug fixes: + - Limine boot protocol: Ensure higher half direct map size does not + exceed MAXPHYADDR on x86. + + Miscellaneous: + - Extensive changes to the config syntax. (See CONFIG.md). + - Rename term_wallpaper, term_wallpaper_style, and term_backdrop config + options to remove the term_ prefix. + - Removed support for GZ-compressed files (and internal Limine + protocol modules). + +2024-08-02 Mintsuki + + *** Release 7.13.3 *** + + Noteworthy changes compared to the previous release, 7.13.2: + + Bug fixes: + - Limine boot protocol: Fix broken comparison that could cause an HHDM + too large to not be properly detected. + - Limine boot protocol: aarch64: Fix incorrect macro value that would + cause the size of the higher half to be incorrectly detected as half. + + Miscellaneous: + - Remove Nix flake stuff from repository/tarball. + +2024-07-30 Mintsuki + + *** Release 7.13.2 *** + + Noteworthy changes compared to the previous release, 7.13.1: + + Bug fixes: + - Limine boot protocol: Fix a bug introduced in 7.13.0 that made + compressed modules not work on IA-32 ports. + +2024-07-29 Mintsuki + + *** Release 7.13.1 *** + + Noteworthy changes compared to the previous release, 7.13.0: + + Bug fixes: + - Limine boot protocol: Fix a bug introduced in 7.13.0 that made the + protocol unusable with the BIOS port when loading any modules, when + Limine was linked using ld.bfd. + +2024-07-29 Mintsuki + + *** Release 7.13.0 *** + + Noteworthy changes compared to the previous release, 7.12.1: + + New features: + - Limine boot protocol: Allow above-4GiB load of modules on IA-32 + ports. + + Miscellaneous: + - Build system improvements. + +2024-07-27 Mintsuki + + *** Release 7.12.1 *** + + Noteworthy changes compared to the previous release, 7.12.0: + + Miscellaneous: + - Limine boot protocol: limit the HHDM size to half of the higher half + in all conditions; limit HHDM KASLR wiggle room to a quarter rather + than half of the higher half. + - Build system improvements. + +2024-07-21 Mintsuki + + *** Release 7.12.0 *** + + Noteworthy changes compared to the previous release, 7.11.0: + + New features: + - Reinstate support for ext2/3/4, albeit in an unsupported and + unmaintained state, at least for the time being. + - Menu: Add vertical bars around entry title in entry editor. + - Menu: Add clearer indications of what is wrong when config invalid or + missing. + + Bug fixes: + - Terminal: Fix long standing wallpaper drawing related bug that under + certain circumstances could cause memory corruption. + - Miscellaneous VBE related bug fixes. + - Miscellaneous A20 line related improvements/fixes. + + Miscellaneous: + - Limine boot protocol: perform appropriate checks to ensure that the + higher half direct map fits in the higher half; enforce paging modes + as needed. + - Limine boot protocol: aarch64: Specify that the granule size for both + TTBR0_EL1 and TTBR1_EL1 is 4KiB. + - Limine boot protocol: aarch64: Specify the values of TCR_EL1.T0SZ and + TCR_EL1.T1SZ depending on the paging mode used. + - Limine boot protocol: aarch64: Specify the condition of TTBR0_EL1 and + TTBR1_EL1 at entry. + +2024-07-18 Mintsuki + + *** Release 7.11.0 *** + + Noteworthy changes compared to the previous release, 7.10.3: + + New features: + - Limine boot protocol: Specify and implement "firmware type" feature. + - Limine boot protocol: Specify and implement revision 1 "paging mode" + request extension. + +2024-07-16 Mintsuki + + *** Release 7.10.3 *** + + Noteworthy changes compared to the previous release, 7.10.2: + + Bug fixes: + - Menu: Fix high menu entry count support that was previously broken in + a regression. + - Linux boot protocol: Install "memory reservation" EFI configuration + table on aarch64/riscv64. + + Miscellaneous: + - Miscellaneous improvements and optimisations of code introduced in + version 7.10.2. + +2024-07-14 Mintsuki + + *** Release 7.10.2 *** + + Noteworthy changes compared to the previous release, 7.10.1: + + Bug fixes: + - Fix a long standing issue with volume detection on UEFI. + - Fix issue returning to editor after a panic when coming from a blank + entry without a valid config. + - Add missing "loading" prints when booting Linux on non-x86. + +2024-07-12 Mintsuki + + *** Release 7.10.1 *** + + Noteworthy changes compared to the previous release, 7.10.0: + + Bug fixes: + - DTB: Never pass UEFI DTB buffer directly; fix misuse of libfdt API. + +2024-07-12 Mintsuki + + *** Release 7.10.0 *** + + Noteworthy changes compared to the previous release, 7.9.2: + + New features: + - SMP: Support SMP on AArch64 without ACPI. + + Bug fixes: + - SMP: Do not allocate a useless stack for the BSP. + - `limine`: Protect against overwriting MBR partitions beginning at + exceedingly low sector values (outside de facto specification). + + Miscellaneous: + - Limine boot protocol: Do not pass responses if RSDP or SMBIOS not + available instead of returning responses with NULL pointers. + - Limine boot protocol: Do not pass a framebuffer response if no + framebuffers are available. + +2024-07-06 Mintsuki + + *** Release 7.9.2 *** + + Noteworthy changes compared to the previous release, 7.9.1: + + Bug fixes: + - Limine boot protocol: Return NULL response instead of a response with + erroneous CPU count of 0 for SMP on x86-64, if x2APIC support not + enabled and missing xAPIC fallback. + - Limine boot protocol: Do not ignore MAX_PAGING_MODE config setting if + paging mode request missing from loaded kernel. + + Miscellaneous: + - Limine boot protocol/ELF: Apply a slide of 0xffffffff80000000 minus + the ELF-reported base load address for relocatable kernels that have + lower half load addresses, instead of always applying a slide of + 0xffffffff80000000. + +2024-06-29 Mintsuki + + *** Release 7.9.1 *** + + Noteworthy changes compared to the previous release, 7.9.0: + + Bug fixes: + - `limine`: Revert change that marked protective MBR wrapper partition + as active on GPT. + +2024-06-28 Mintsuki + + *** Release 7.9.0 *** + + Noteworthy changes compared to the previous release, 7.8.0: + + New features: + - Linux boot protocol: Add support on aarch64 and riscv64. + + Bug fixes: + - multiboot1/2: Relax ELF file type requirements (mainly for multiboot2 + relocatable kernels). + - UEFI: Fix bug that caused delete key to behave as if it was backspace + even when the SERIAL config option was not "yes". + + Miscellaneous: + - Limine boot protocol/ELF: Do not consider ELFs relocatable unless the + ELF file type is ET_DYN. + - Some dead code removal. + +2024-06-24 Mintsuki + + *** Release 7.8.0 *** + + Noteworthy changes compared to the previous release, 7.7.2: + + New features: + - Return to booted edited entry on panic. + + Bug fixes: + - `limine`: Mark protective MBR wrapper partition as active on GPT. + + Miscellaneous: + - UEFI: Allocate most memory as EfiLoaderCode instead of EfiLoaderData. + - Smaller fixes and improvements. + +2024-06-09 Mintsuki + + *** Release 7.7.2 *** + + Noteworthy changes compared to the previous release, 7.7.1: + + Bug fixes: + - Limine boot protocol/ELF: Ignore PT_LOAD segments with 0 memory size. + - Limine boot protocol/ELF: Panic on non-weak unresolved symbols. + - Limine boot protocol/ELF: Do not sanity check DT_RELAENT size for non + DT_RELA relocations. This is because DT_RELAENT may not be present. + - multiboot 1 and 2 protocols: Reject relocatable ELFs. + +2024-06-06 Mintsuki + + *** Release 7.7.1 *** + + Noteworthy changes compared to the previous release, 7.7.0: + + Bug fixes: + - Limine boot protocol/ELF: Stop parsing PT_DYNAMIC segment once + DT_NULL is reached. + +2024-06-04 Mintsuki + + *** Release 7.7.0 *** + + Noteworthy changes compared to the previous release, 7.6.0: + + New features: + - Limine boot protocol/ELF: Add support for GLOB_DAT, JUMP_SLOT, 64, + and NONE relocation types. + - Limine boot protocol/ELF: Add RELR (packed relative relocations) + support. + + Bug fixes: + - Limine boot protocol/ELF: Reject ELFs that depend on external + dynamically linked libraries. + + Miscellaneous: + - Bring back .xz compressed release tarball. + +2024-06-01 Mintsuki + + *** Release 7.6.0 *** + + Noteworthy changes compared to the previous release, 7.5.3: + + New features: + - RISC-V: Add support for RISC-V systems without ACPI (using DTBs). + +2024-05-24 Mintsuki + + *** Release 7.5.3 *** + + Noteworthy changes compared to the previous release, 7.5.2: + + Bug fixes: + - Multiboot 2 protocol: Fix an issue where the 32-bit EFI Image Handle + tag would be generated under 64-bit EFI and vice versa. + - Do not set VirtualStart to PhysicalStart when generating the EFI + memory map; set it to 0 instead. + - Do not perform additional checks when loading relocatable ELF files + after verifying that they are of type ET_DYN and have a PT_DYNAMIC + segment. This ensures that certain relocatable ELF files that were + previously misidentified as non-relocatable are recognised as such. + +2024-05-22 Mintsuki + + *** Release 7.5.2 *** + + Noteworthy changes compared to the previous release, 7.5.1: + + Bug fixes: + - Fix missing checks in the FAT filesystem driver that could cause + divide-by-0 exceptions when parsing malformed/corrupted filesystems. + - Limine boot protocol: Do not reject relocatable kernels with ELF PHDR + load addresses at or around 0, instead slide them all the way to the + topmost 2GiB (0xffffffff80000000). This allows setting a base load + address of 0 in LD BFD/LLD linker scripts which makes LD BFD (the + default GNU LD) actually emit an ELF with ET_DYN type. + +2024-05-08 Mintsuki + + *** Release 7.5.1 *** + + Noteworthy changes compared to the previous release, 7.5.0: + + Bug fixes: + - Workarounds for issues related to drive iteration on BIOS. Discovered + on an HP Pavilion dv6-3152er. + +2024-05-03 Mintsuki + + *** Release 7.5.0 *** + + Noteworthy changes compared to the previous release, 7.4.1: + + New features: + - Limine boot protocol: Add support for requests start marker, to + complement the previously called delimiter, now renamed to "end + marker". + - Limine boot protocol: Add base revision 2. The sole difference + compared to base revision 1 being that it mandates bootloaders to + support request delimiters. + + Bug fixes: + - Fix an issue regarding commit date parsing in the bootstrap script. + + Miscellaneous: + - General documentation updates and improvements; split USAGE.md from + README.md and install the former only. + +2024-04-29 Mintsuki + + *** Release 7.4.1 *** + + Noteworthy changes compared to the previous release, 7.4.0: + + Miscellaneous: + - Bump max volume limit for UEFI from 64 to 256. + - Improve how ISA name and firmware type is reported as part of the + default branding. + +2024-04-20 Mintsuki + + *** Release 7.4.0 *** + + Noteworthy changes compared to the previous release, 7.3.1: + + New features: + - ISA name and firmware type is now printed as part of the default + bootloader branding. + - Add 'B' key shortcut to menu to open up a blank entry. + + Bug fixes: + - Fix missing NULL pointer check in configuration related function. + + Miscellaneous: + - Console has been removed. + +2024-04-11 Mintsuki + + *** Release 7.3.1 *** + + Noteworthy changes compared to the previous release, 7.3.0: + + Bug fixes: + - Fix a minor issue with scrolling when using the EFI console fallback. + + Miscellaneous: + - Bump the Flanterm revision to current latest. + - Some dead code removal. + +2024-03-31 Mintsuki + + *** Release 7.3.0 *** + + Noteworthy changes compared to the previous release, 7.2.1: + + New features: + - Limine boot protocol: Add a `MAX_PAGING_MODE` configuration option + to override kernel/bootloader selected paging mode. + + Bug fixes: + - Fix a couple of minor bugs with the 4GiB workaround introduced in + version 7.2.1. + + Miscellaneous: + - No longer produce xz compressed release tarballs. Add bzip2, lzip, + and zstd as alternatives in its place. + +2024-03-28 Mintsuki + + *** Release 7.2.1 *** + + Noteworthy changes compared to the previous release, 7.2.0: + + Bug fixes: + - Implement workaround to allow Limine loaded above 4GiB by UEFI on + x86-64 to work in some configurations, instead of panicking. + +2024-03-24 Mintsuki + + *** Release 7.2.0 *** + + Noteworthy changes compared to the previous release, 7.1.0: + + New features: + - Add support for remembering the last booted entry on UEFI by means + of the `REMEMBER_LAST_ENTRY` config option. + +2024-03-19 Mintsuki + + *** Release 7.1.0 *** + + Noteworthy changes compared to the previous release, 7.0.5: + + New features: + - Limine boot protocol: Add support for requests delimiter. + + Bug fixes: + - Fixed a potential security vulnerability in the ISO9660 driver. + +2024-02-15 Mintsuki + + *** Release 7.0.5 *** + + Noteworthy changes compared to the previous release, 7.0.4: + + Bug fixes: + - Build system related fixes and improvements. This fixes an issue that + caused Limine 7.0.4 to fail building on Busybox systems. + +2024-02-14 Mintsuki + + *** Release 7.0.4 *** + + Noteworthy changes compared to the previous release, 7.0.3: + + Bug fixes: + - Minor build system related fixes and improvements. + + Miscellaneous: + - Significantly improve reproducibility of builds. + +2024-02-08 Mintsuki + + *** Release 7.0.3 *** + + Noteworthy changes compared to the previous release, 7.0.2: + + Bug fixes: + - multiboot2 protocol: Do not panic if there is a ENTRY_ADDRESS_EFI64 + tag, but there is a valid alternative entry point. This further aligns + our behaviour with GRUB2. + + Miscellaneous: + - Terminal: Significantly improve serial and fallback UEFI console's + rendition (especially colour palettes). + - Serial: Add a baud rate config setting for Limine's BIOS version. + +2024-01-26 Mintsuki + + *** Release 7.0.2 *** + + Noteworthy changes compared to the previous release, 7.0.1: + + Bug fixes: + - Fix a cosmetic bug in the menu where, when the editor was disabled, + the controls showed "ENTER Expand" even for boot entries instead of + only for menu directories. + + Miscellaneous: + - Exclusively use git for obtaining dependencies in "bootstrap" script. + +2024-01-22 Mintsuki + + *** Release 7.0.1 *** + + Noteworthy changes compared to the previous release, 7.0.0: + + Bug fixes: + - Fix ARM64/aarch64 spinup code to properly support Apple Silicon + chips. + - Prevent users from building Limine with a GNU Make version older than + 4.2, as that is not supported, by gracefully erroring out with a + useful error message. + - Avoid usage of reserved C identifiers in certain cases. + + Miscellaneous: + - Drop usage of "libgcc-binaries" for "cc-runtime". This finally makes + us able to ship Limine tarballs without included binary blobs, and + should improve compatibility with more obscure toolchain set ups. + +2024-01-14 Mintsuki + + *** Release 7.0.0 *** + + Noteworthy changes compared to the previous release, 6.20240107.0: + + Bug fixes: + - Minor fix of newline handling when reading user input lines. + + Miscellaneous: + - Bump major version to 7. Note: There is no breaking change in 7.x + compared to 6.x; the major version bump is done due to a change in + the versioning scheme used by Limine (move to proper Semantic + Versioning). + - Fix versions of external dependencies in "bootstrap" script. + +2024-01-07 Mintsuki + + *** Release 6.20240107.0 *** + + Noteworthy changes compared to the previous release, 6.20231227.0: + + New features: + - Limine boot protocol: Add support for compressed internal modules. + + Miscellaneous: + - Improve error reporting from host "limine" executable. + - Track "master" branch of stb_image upstream. + +2023-12-27 Mintsuki + + *** Release 6.20231227.0 *** + + Noteworthy changes compared to the previous release, 6.20231226.0: + + Bug fixes: + - Actually regenerate build system with GNU Autoconf 2.72 instead of + GNU Autoconf 2.71. + +2023-12-26 Mintsuki + + *** Release 6.20231226.0 *** + + Noteworthy changes compared to the previous release, 6.20231216.0: + + New features: + - Ignore trailing whitespace in Limine config file. + + Miscellaneous: + - Regenerate build system with GNU Autoconf 2.72. + - Minor build system adjustments. + +2023-12-16 Mintsuki + + *** Release 6.20231216.0 *** + + Noteworthy changes compared to the previous release, 6.20231210.0: + + New features: + - Added a `--print-datadir` switch to the `limine` program in order + to print the directory where Limine files are stored when installed + on a host distro. + + Miscellaneous: + - General improvements to the `--help` texts for the `limine` program. + +2023-12-10 Mintsuki + + *** Release 6.20231210.0 *** + + This is the first release in the 6.x series. + + Breaking changes compared to the 5.x series: + + - Support for the unmaintained ext2/3/4 filesystems dropped in compliance + with the Limine bootloader design philosophy. + - Chainloading protocol split into 2 protocols: EFI chainloading and BIOS + chainloading. + +2023-12-07 Mintsuki + + *** Release 5.20231207.1 *** + + Noteworthy changes compared to the previous release, 5.20231207.0: + + Bug fixes: + - multiboot2/elf: Fix issue where ELF vaddrs were taken into account + instead of paddrs when loading executables. + - Fix implementation of a function that detects whether a given memory + range is physical RAM. + +2023-12-07 Mintsuki + + *** Release 5.20231207.0 *** + + Noteworthy changes compared to the previous release, 5.20231124.0: + + New features: + - multiboot2 protocol: Add support for relocatable header tag. + - multiboot2 protocol: Add support for console flags header tag. + - Limine protocol: Add support for EFI memory map feature. + + Miscellaneous: + - Improve error reporting from host "limine" executable. + - Do not ignore errors from mtools when building limine-uefi-cd.bin. + +2023-11-24 Mintsuki + + *** Release 5.20231124.0 *** + + Compared to previous release, 5.20231121.0: + + New features: + - Add support for rebooting to UEFI firmware setup. + + Bug fixes: + - Fix SMP (multiprocessor) initialisation regression on aarch64. + + Miscellaneous: + - Move to new, handwritten ChangeLog from autogenerated one. diff --git a/limine/FAQ.md b/limine/FAQ.md new file mode 100644 index 0000000..1516078 --- /dev/null +++ b/limine/FAQ.md @@ -0,0 +1,35 @@ +# Frequently Asked Questions + +### Why not support filesystem X or feature Y? (eg: LUKS, LVM) + +The idea with Limine is to remove the responsibility of parsing filesystems and +formats, aside from the bare minimum necessities (eg: FAT*, ISO9660), from the +bootloader itself. It is a needless duplication of efforts to have bootloaders +support all possible filesystems and formats, and it leads to massive, bloated +bootloaders as a result (eg: GRUB2). + +What is needed is to simply make sure the bootloader is capable of reading its +own files, configuration, and be able to load kernel/module files from disk. +The kernel should be responsible for parsing everything else as it sees fit. + +### What about LUKS? What about security? Encrypt the kernel! + +Simply put, this is unnecessary. Putting the kernel/modules in a readable FAT32 +partition and letting Limine know about their BLAKE2B checksums in the config +file provides as much security as encrypting the kernel does. + +### What if a malicious actor modifies the config file? + +While this is a pointless effort on legacy x86 BIOS, it is a reasonable +expectation to secure the boot sequence on UEFI systems with Secure Boot. +Limine provides a way to modify its own EFI executable to bake in the BLAKE2B +checksum of the config file itself. The EFI executable can then get signed with +a key added to the firmware's keychain. This prevents modifications to the +config file (and in turn the checksums contained there) from going unnoticed. + +### I do not want to have a separate FAT boot partition! What can I do? + +It is `$year_following_2012` now and most PCs are equipped with UEFI and simply +won't boot without a FAT EFI system partition anyways. +It is not unreasonable to share the EFI system partition with the OS's /boot +and store kernels, initramfses, and any other files needed for boot there. diff --git a/limine/GNUmakefile.in b/limine/GNUmakefile.in new file mode 100644 index 0000000..065d0ef --- /dev/null +++ b/limine/GNUmakefile.in @@ -0,0 +1,401 @@ +.SUFFIXES: + +override SOURCE_DATE_EPOCH := @SOURCE_DATE_EPOCH@ +export SOURCE_DATE_EPOCH + +override SOURCE_DATE_EPOCH_TOUCH := @SOURCE_DATE_EPOCH_TOUCH@ + +override PACKAGE_TARNAME := @PACKAGE_TARNAME@ +override PACKAGE_VERSION := @PACKAGE_VERSION@ +override DIST_OUTPUT := $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) + +override prefix := @prefix@ +override exec_prefix := @exec_prefix@ + +override bindir := @bindir@ +override datarootdir := @datarootdir@ +override mandir := @mandir@ +override docdir := @docdir@ + +override BUILDDIR := @BUILDDIR@ +override BINDIR := $(BUILDDIR)/bin + +override SRCDIR := @SRCDIR@ + +override SPACE := $(subst ,, ) +override COMMA := , + +override MKESCAPE = $(subst $(SPACE),\ ,$(1)) +override SHESCAPE = $(subst ','\'',$(1)) +override NASMESCAPE = $(subst ','"'$(COMMA) \"'\"$(COMMA) '"',$(1)) + +override BUILD_BIOS := @BUILD_BIOS@ +override BUILD_UEFI_X86_64 := @BUILD_UEFI_X86_64@ +override BUILD_UEFI_IA32 := @BUILD_UEFI_IA32@ +override BUILD_UEFI_AARCH64 := @BUILD_UEFI_AARCH64@ +override BUILD_UEFI_RISCV64 := @BUILD_UEFI_RISCV64@ +override BUILD_UEFI_LOONGARCH64 := @BUILD_UEFI_LOONGARCH64@ +override BUILD_UEFI_CD := @BUILD_UEFI_CD@ +override BUILD_BIOS_PXE := @BUILD_BIOS_PXE@ +override BUILD_BIOS_CD := @BUILD_BIOS_CD@ + +INSTALL := @INSTALL@ +INSTALL_PROGRAM := @INSTALL_PROGRAM@ +INSTALL_DATA := @INSTALL_DATA@ +STRIP := @STRIP@ + +MKDIR_P := @MKDIR_P@ +export MKDIR_P +GREP := @GREP@ +export GREP +SED := @SED@ +export SED +AWK := @AWK@ +export AWK + +CC := @CC@ + +CPPFLAGS := @CPPFLAGS@ +CFLAGS := @CFLAGS@ +LDFLAGS := @LDFLAGS@ +LIBS := @LIBS@ + +CC_FOR_TARGET := @CC_FOR_TARGET@ +export CC_FOR_TARGET +LD_FOR_TARGET := @LD_FOR_TARGET@ +export LD_FOR_TARGET +OBJCOPY_FOR_TARGET := @OBJCOPY_FOR_TARGET@ +export OBJCOPY_FOR_TARGET +OBJDUMP_FOR_TARGET := @OBJDUMP_FOR_TARGET@ +export OBJDUMP_FOR_TARGET +READELF_FOR_TARGET := @READELF_FOR_TARGET@ +export READELF_FOR_TARGET + +override WERROR_FLAG := @WERROR_FLAG@ +export WERROR_FLAG + +CFLAGS_FOR_TARGET := @CFLAGS_FOR_TARGET@ +export CFLAGS_FOR_TARGET +CPPFLAGS_FOR_TARGET := @CPPFLAGS_FOR_TARGET@ +export CPPFLAGS_FOR_TARGET +LDFLAGS_FOR_TARGET := @LDFLAGS_FOR_TARGET@ +export LDFLAGS_FOR_TARGET +NASMFLAGS_FOR_TARGET := @NASMFLAGS_FOR_TARGET@ +export NASMFLAGS_FOR_TARGET + +override STAGE1_FILES := $(shell find '$(call SHESCAPE,$(SRCDIR))/stage1' -type f -name '*.asm' | LC_ALL=C sort) + +.PHONY: all +all: $(call MKESCAPE,$(BINDIR))/Makefile + $(MAKE) all1 + +.PHONY: all1 +all1: $(BUILD_UEFI_X86_64) $(BUILD_UEFI_IA32) $(BUILD_UEFI_AARCH64) $(BUILD_UEFI_RISCV64) $(BUILD_UEFI_LOONGARCH64) $(BUILD_BIOS) + $(MAKE) '$(call SHESCAPE,$(BINDIR))/limine' + $(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' + +$(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h: $(call MKESCAPE,$(BINDIR))/limine-bios-hdd.bin + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cd '$(call SHESCAPE,$(BINDIR))' && '$(call SHESCAPE,$(SRCDIR))/host/hgen.sh' >limine-bios-hdd.h + +override LIMINE_NO_BIOS := +ifneq ($(BUILD_BIOS),limine-bios) +override LIMINE_NO_BIOS := -DLIMINE_NO_BIOS +endif + +$(call MKESCAPE,$(BINDIR))/limine: $(call MKESCAPE,$(BINDIR))/Makefile $(call MKESCAPE,$(SRCDIR))/host/limine.c $(if $(filter $(BUILD_BIOS),limine-bios),$(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h) + $(SED) 's/%VERSION%/@PACKAGE_VERSION@/g;s/%COPYRIGHT%/@LIMINE_COPYRIGHT@/g' <'$(call SHESCAPE,$(SRCDIR))/host/limine.c' >'$(call SHESCAPE,$(BINDIR))/limine.c' + $(MAKE) -C '$(call SHESCAPE,$(BINDIR))' limine \ + CC="$(CC)" \ + CFLAGS="$(CFLAGS) -Wall -Wextra $(WERROR_FLAG)" \ + CPPFLAGS='$(CPPFLAGS) $(LIMINE_NO_BIOS) -DLIMINE_DATADIR=\"$(call SHESCAPE,$(datarootdir))/limine\"' \ + LDFLAGS="$(LDFLAGS)" \ + LIBS="$(LIBS)" + +$(call MKESCAPE,$(BINDIR))/Makefile: $(call MKESCAPE,$(SRCDIR))/host/host.mk $(call MKESCAPE,$(SRCDIR))/host/.gitignore + mkdir -p '$(call SHESCAPE,$(BINDIR))' + cp '$(call SHESCAPE,$(SRCDIR))/host/host.mk' '$(call SHESCAPE,$(BINDIR))/Makefile' + cp '$(call SHESCAPE,$(SRCDIR))/host/.gitignore' '$(call SHESCAPE,$(BINDIR))/' + +.PHONY: limine +limine: + $(MAKE) '$(call SHESCAPE,$(BINDIR))/limine' + +.PHONY: clean +clean: limine-bios-clean limine-uefi-ia32-clean limine-uefi-x86-64-clean limine-uefi-aarch64-clean limine-uefi-riscv64-clean limine-uefi-loongarch64-clean + rm -rf '$(call SHESCAPE,$(BINDIR))' '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp' + +.PHONY: install +install: all + $(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(docdir))' + $(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/COPYING' '$(call SHESCAPE,$(DESTDIR)$(docdir))/' + $(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(docdir))/LICENSES' + $(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt' '$(call SHESCAPE,$(DESTDIR)$(docdir))/LICENSES/' + $(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/3RDPARTY.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/' + $(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/CONFIG.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/' + $(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/FAQ.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/' + $(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/USAGE.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/' + $(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1' + $(INSTALL_DATA) '$(call SHESCAPE,$(BUILDDIR))/man/man1/limine.1' '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/' + $(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(datarootdir))' + $(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine' +ifeq ($(BUILD_BIOS),limine-bios) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios.sys' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifneq ($(BUILD_BIOS_CD),no) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios-cd.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifneq ($(BUILD_UEFI_CD),no) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifneq ($(BUILD_BIOS_PXE),no) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios-pxe.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifeq ($(BUILD_UEFI_AARCH64),limine-uefi-aarch64) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifeq ($(BUILD_UEFI_RISCV64),limine-uefi-riscv64) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifeq ($(BUILD_UEFI_LOONGARCH64),limine-uefi-loongarch64) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTLOONGARCH64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifeq ($(BUILD_UEFI_X86_64),limine-uefi-x86-64) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif +ifeq ($(BUILD_UEFI_IA32),limine-uefi-ia32) + $(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTIA32.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/' +endif + $(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(bindir))' + $(INSTALL_PROGRAM) '$(call SHESCAPE,$(BINDIR))/limine' '$(call SHESCAPE,$(DESTDIR)$(bindir))/' + +.PHONY: install-strip +install-strip: install + $(STRIP) '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine' + +.PHONY: uninstall +uninstall: + rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/COPYING' + rm -rf '$(call SHESCAPE,$(DESTDIR)$(docdir))/LICENSES' + rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/3RDPARTY.md' + rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/CONFIG.md' + rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/FAQ.md' + rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/USAGE.md' + rm -f '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/limine.1' + rm -f '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine' + rm -rf '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine' + +$(call MKESCAPE,$(BUILDDIR))/stage1.stamp: $(STAGE1_FILES) $(call MKESCAPE,$(BUILDDIR))/decompressor-build/decompressor.bin $(call MKESCAPE,$(BUILDDIR))/common-bios/stage2.bin.gz + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cd '$(call SHESCAPE,$(SRCDIR))/stage1/hdd' && nasm bootsect.asm -Wall -w-unknown-warning -w-reloc $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-hdd.bin' +ifneq ($(BUILD_BIOS_CD),no) + cd '$(call SHESCAPE,$(SRCDIR))/stage1/cd' && nasm bootsect.asm -Wall -w-unknown-warning -w-reloc $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-cd.bin' +endif +ifneq ($(BUILD_BIOS_PXE),no) + cd '$(call SHESCAPE,$(SRCDIR))/stage1/pxe' && nasm bootsect.asm -Wall -w-unknown-warning -w-reloc $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-pxe.bin' +endif + cp '$(call SHESCAPE,$(BUILDDIR))/common-bios/limine-bios.sys' '$(call SHESCAPE,$(BINDIR))/' + touch '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp' + +.PHONY: limine-bios +limine-bios: common-bios decompressor + $(MAKE) '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp' + +$(call MKESCAPE,$(BINDIR))/limine-uefi-cd.bin: $(if $(BUILD_UEFI_IA32),$(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI) $(if $(BUILD_UEFI_X86_64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI) $(if $(BUILD_UEFI_AARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI) $(if $(BUILD_UEFI_RISCV64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI) $(if $(BUILD_UEFI_LOONGARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI) +ifneq ($(BUILD_UEFI_CD),no) + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + rm -f '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' + dd if=/dev/zero of='$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' bs=512 count=5760 2>/dev/null + mformat -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' -f 2880 -N 12345678 :: + LIMINE_UEFI_CD_TMP="$$(mktemp -d)"; \ + mkdir -p "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT; \ + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \ + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \ + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \ + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \ + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \ + find "$$LIMINE_UEFI_CD_TMP" -exec touch -t $(SOURCE_DATE_EPOCH_TOUCH) '{}' + && \ + mcopy -D o -s -m -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' "$$LIMINE_UEFI_CD_TMP"/EFI :: && \ + rm -rf "$$LIMINE_UEFI_CD_TMP" +endif + +.PHONY: limine-uefi-cd +limine-uefi-cd: + $(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' + +$(call MKESCAPE,$(BINDIR))/BOOTX64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' '$(call SHESCAPE,$(BINDIR))/' + +.PHONY: limine-uefi-x86-64 +limine-uefi-x86-64: + $(MAKE) common-uefi-x86-64 + $(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI' + +$(call MKESCAPE,$(BINDIR))/BOOTIA32.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' '$(call SHESCAPE,$(BINDIR))/' + +.PHONY: limine-uefi-ia32 +limine-uefi-ia32: + $(MAKE) common-uefi-ia32 + $(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTIA32.EFI' + +$(call MKESCAPE,$(BINDIR))/BOOTAA64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' '$(call SHESCAPE,$(BINDIR))/' + +.PHONY: limine-uefi-aarch64 +limine-uefi-aarch64: + $(MAKE) common-uefi-aarch64 + $(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI' + +$(call MKESCAPE,$(BINDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' '$(call SHESCAPE,$(BINDIR))/' + +.PHONY: limine-uefi-riscv64 +limine-uefi-riscv64: + $(MAKE) common-uefi-riscv64 + $(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI' + +$(call MKESCAPE,$(BINDIR))/BOOTLOONGARCH64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI + $(MKDIR_P) '$(call SHESCAPE,$(BINDIR))' + cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI' '$(call SHESCAPE,$(BINDIR))/' + +.PHONY: limine-uefi-loongarch64 +limine-uefi-loongarch64: + $(MAKE) common-uefi-loongarch64 + $(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTLOONGARCH64.EFI' + +.PHONY: limine-bios-clean +limine-bios-clean: common-bios-clean decompressor-clean + +.PHONY: limine-uefi-x86-64-clean +limine-uefi-x86-64-clean: common-uefi-x86-64-clean + +.PHONY: limine-uefi-ia32-clean +limine-uefi-ia32-clean: common-uefi-ia32-clean + +.PHONY: limine-uefi-aarch64-clean +limine-uefi-aarch64-clean: common-uefi-aarch64-clean + +.PHONY: limine-uefi-riscv64-clean +limine-uefi-riscv64-clean: common-uefi-riscv64-clean + +.PHONY: limine-uefi-loongarch64-clean +limine-uefi-loongarch64-clean: common-uefi-loongarch64-clean + +.PHONY: dist +dist: + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" + cp -r '$(call SHESCAPE,$(SRCDIR))'/.git '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)"/ + cd '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" && git checkout . + cd '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" && ./bootstrap + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/flanterm/.git" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/flanterm/.gitignore" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/freestnd-c-hdrs/.git" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/limine-protocol/.git" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/picoefi/.git" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/picoefi/.gitignore" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/cc-runtime" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/libfdt/.git" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/tinf" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/common/lib/stb_image.h.nopatch" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/.git" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/.gitignore" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/.forgejo" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/README.md" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/autom4te.cache" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/test" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/test.mk" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/logo.png" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/screenshot.png" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/bochsrc" + echo "$(PACKAGE_VERSION)" > '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/version" + cd '$(call SHESCAPE,$(BUILDDIR))' && tar -cf "$(DIST_OUTPUT).tar" "$(DIST_OUTPUT)" + cd '$(call SHESCAPE,$(BUILDDIR))' && gzip < "$(DIST_OUTPUT).tar" > "$(DIST_OUTPUT).tar.gz" + cd '$(call SHESCAPE,$(BUILDDIR))' && ( bzip2 < "$(DIST_OUTPUT).tar" > "$(DIST_OUTPUT).tar.bz2" || rm -f "$(DIST_OUTPUT).tar.bz2" ) + cd '$(call SHESCAPE,$(BUILDDIR))' && ( xz < "$(DIST_OUTPUT).tar" > "$(DIST_OUTPUT).tar.xz" || rm -f "$(DIST_OUTPUT).tar.xz" ) + cd '$(call SHESCAPE,$(BUILDDIR))' && rm "$(DIST_OUTPUT).tar" + rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" + +.PHONY: distclean +distclean: clean + rm -rf edk2-ovmf config.log config.status GNUmakefile config.h man/man1/limine.1 + +.PHONY: maintainer-clean +maintainer-clean: distclean + cd '$(call SHESCAPE,$(SRCDIR))' && rm -rf flanterm common/lib/stb_image.h.nopatch common/lib/stb_image.h decompressor/tinf tinf libfdt freestnd-c-hdrs cc-runtime common/cc-runtime.s2.c decompressor/cc-runtime.c limine-protocol picoefi configure timestamps build-aux *'~' autom4te.cache aclocal.m4 *.tar* + +.PHONY: common-uefi-x86-64 +common-uefi-x86-64: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \ + TARGET=uefi-x86-64 \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64' + +.PHONY: common-uefi-x86-64-clean +common-uefi-x86-64-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64' + +.PHONY: common-uefi-aarch64 +common-uefi-aarch64: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \ + TARGET=uefi-aarch64 \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64' + +.PHONY: common-uefi-aarch64-clean +common-uefi-aarch64-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64' + +.PHONY: common-uefi-riscv64 +common-uefi-riscv64: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \ + TARGET=uefi-riscv64 \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64' + +.PHONY: common-uefi-riscv64-clean +common-uefi-riscv64-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64' + +.PHONY: common-uefi-loongarch64 +common-uefi-loongarch64: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \ + TARGET=uefi-loongarch64 \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64' + +.PHONY: common-uefi-loongarch64-clean +common-uefi-loongarch64-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64' + +.PHONY: common-uefi-ia32 +common-uefi-ia32: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \ + TARGET=uefi-ia32 \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32' + +.PHONY: common-uefi-ia32-clean +common-uefi-ia32-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32' + +.PHONY: common-bios +common-bios: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \ + TARGET=bios \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-bios' + +.PHONY: common-bios-clean +common-bios-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-bios' + +.PHONY: decompressor +decompressor: + $(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/decompressor' -f decompressor.mk \ + BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/decompressor-build' + +.PHONY: decompressor-clean +decompressor-clean: + rm -rf '$(call SHESCAPE,$(BUILDDIR))/decompressor-build' + +-include test.mk diff --git a/limine/INSTALL.md b/limine/INSTALL.md new file mode 100644 index 0000000..764b0a6 --- /dev/null +++ b/limine/INSTALL.md @@ -0,0 +1,50 @@ +# Build and Install Instructions + +> **NOTE:** This document is about building and installing Limine. +> For information about deployment for usage, see [USAGE.md](USAGE.md). + +## Prerequisites + +In order to build Limine, the following programs have to be installed: +common UNIX tools (also known as `coreutils`), +`GNU make`, `grep`, `sed`, `find`, `awk`, `gzip`, `nasm`, `mtools` +(optional, necessary to build `limine-uefi-cd.bin`). +Furthermore, `gcc` or `llvm/clang` must also be installed, alongside +the respective binutils. + +## Configure + +If using a release tarball (recommended, see +https://codeberg.org/Limine/Limine/releases), run `./configure` directly. + +If checking out from the repository, run `./bootstrap` first in order to +download the necessary [dependencies](3RDPARTY.md) and generate the configure +script (`GNU autoconf` required). + +`./configure` takes arguments and environment variables; for more information +on these, run `./configure --help`. + +> **NOTE:** `./configure` by default does not build any Limine port. Make sure +> to read the output of `./configure --help` and enable any or all ports! + +Limine supports both in-tree and out-of-tree builds. Simply run the `configure` +script from the directory you wish to execute the build in. The following +`make` commands are supposed to be run inside the build directory. + +## Building + +To build Limine, run: +```bash +make # (or gmake where applicable) +``` + +## Installing + +This step will install Limine files to `share`, `include`, and `bin` +directories in the specified prefix (default is `/usr/local`, see +`./configure --help`. + +To install Limine, run: +```bash +make install # (or gmake where applicable) +``` diff --git a/limine/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt b/limine/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt new file mode 100644 index 0000000..ff0021a --- /dev/null +++ b/limine/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt @@ -0,0 +1,7 @@ + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice and this list of conditions, without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. diff --git a/limine/README.md b/limine/README.md new file mode 100644 index 0000000..5eb644b --- /dev/null +++ b/limine/README.md @@ -0,0 +1,102 @@ +# Limine [![Matrix Server](https://img.shields.io/matrix/limine:matrix.org?color=000000&label=Matrix&logo=matrix)](https://matrix.to/#/#limine:matrix.org) [![Fluxer](https://img.shields.io/badge/Fluxer-Chat-4f3cc9?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij4KICA8cGF0aCBkPSJNMyA3LjVjMi41LTMgNS0zIDcuNSAwczUgMyA3LjUgMCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiLz4KICA8cGF0aCBkPSJNMyAxNi41YzIuNS0zIDUtMyA3LjUgMHM1IDMgNy41IDAiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBmaWxsPSJub25lIi8+Cjwvc3ZnPgo=&style=flat)](https://fluxer.gg/ZRviNMvT) + +

+ Limine's logo +

+ +### What is Limine? + +Limine (pronounced as demonstrated [here](https://www.merriam-webster.com/dictionary/in%20limine)) +is a modern, advanced, portable, multiprotocol bootloader and boot manager, also used +as the reference implementation for the [Limine boot protocol](https://codeberg.org/Limine/limine-protocol/src/branch/trunk/PROTOCOL.md). + +### Community, Support, and Donations + +#### Donate +If you want to support the work I ([@mintsuki](https://codeberg.org/Mintsuki)) do on Limine, feel free to donate to me on Liberapay: +

Donate using Liberapay

+ +Donations welcome, but absolutely not mandatory! + +#### Community +We have a Matrix room at [`#limine:matrix.org`](https://matrix.to/#/#limine:matrix.org) +and a [Fluxer community](https://fluxer.gg/ZRviNMvT) if you need support, info, or you just want to hang out with us. + +### Limine's boot menu + +![Reference screenshot](screenshot.png?raw=true "Reference screenshot") + +[Photo by Pixabay](https://www.pexels.com/photo/brown-leafed-tree-on-open-field-under-white-clouds-and-blue-sky-35857/) + +### Supported architectures +* IA-32 (32-bit x86) +* x86-64 +* aarch64 (arm64) +* riscv64 +* loongarch64 (experimental support!) + +### Supported boot protocols +* Linux +* [Limine](https://codeberg.org/Limine/limine-protocol/src/branch/trunk/PROTOCOL.md) +* Multiboot 1 +* Multiboot 2 +* Chainloading + +### Supported partitioning schemes +* MBR +* GPT +* Unpartitioned media + +### Supported filesystems +* FAT12/16/32 +* ISO9660 (CDs/DVDs) + +If your filesystem isn't listed here, please read [the FAQ](FAQ.md) first, especially before +opening issues or pull requests related to this. + +### Minimum system requirements +For 32-bit x86 systems, support is only ensured starting with those with +Pentium Pro (i686) class CPUs. + +All x86-64, aarch64, riscv64 and loongarch64 (UEFI) systems are supported. + +## Packaging status + +All Limine releases since 7.x use [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for their naming. + +[![Packaging status](https://repology.org/badge/vertical-allrepos/limine.svg?columns=3)](https://repology.org/project/limine/versions) + +## Binary releases + +For convenience, for point releases, binaries are distributed. These binaries +are shipped in the `-binary` branches and tags of this repository +(see [branches](https://codeberg.org/Limine/Limine/branches) and +[tags](https://codeberg.org/Limine/Limine/tags)). + +For example, to clone the latest binary release of the `10.x` branch, one can do: +```bash +git clone https://codeberg.org/Limine/Limine.git --branch=v10.x-binary --depth=1 +``` +or, to clone a specific binary point release (for example `10.8.5`): +```bash +git clone https://codeberg.org/Limine/Limine.git --branch=v10.8.5-binary --depth=1 +``` + +In order to rebuild host utilities like `limine`, simply run `make` in the binary +release directory. + +Host utility binaries are provided for Windows. + +## Build and Install Instructions + +*The following steps are not necessary if cloning a binary release.* + +See [INSTALL.md](INSTALL.md). + +## Usage + +See [USAGE.md](USAGE.md). + +## 3rd Party Software Acknowledgments + +See [3RDPARTY.md](3RDPARTY.md). diff --git a/limine/USAGE.md b/limine/USAGE.md new file mode 100644 index 0000000..f791775 --- /dev/null +++ b/limine/USAGE.md @@ -0,0 +1,107 @@ +# Usage + +> **NOTE:** The Limine files referred to here are those contained inside +> ${PREFIX}/share/, installed there as a product of the steps described in +> [INSTALL.md](INSTALL.md). + +## UEFI +The `BOOT*.EFI` files are valid EFI applications that can be simply copied to +the `/EFI/BOOT` directory of a FAT formatted EFI system partition. These files +can be installed there and coexist with a BIOS installation of Limine +(see below) so that the disk will be bootable on both BIOS and UEFI systems. + +A valid config file should also be provided as described in +[CONFIG.md](CONFIG.md). + +## Secure Boot +Limine can be booted with secure boot if the executable is signed and the key +used to sign it is added to the firmware's keychain. This should be done in +combination with enrolling the BLAKE2B hash of the Limine config file into the +Limine EFI executable image itself for verification purposes. +For more information see the `limine enroll-config` program and +[the FAQ](FAQ.md). + +## BIOS/MBR +In order to install Limine on a MBR device (which can just be a raw image +file), run `limine bios-install` as such: + +```bash +limine bios-install +``` + +The boot device must contain the `limine-bios.sys` and `limine.conf` files in +either a `boot/limine`, `boot`, `limine`, or root directory of one of the +partitions, formatted with a supported file system. See [CONFIG.md](CONFIG.md). + +## BIOS/GPT +If using a GPT formatted device, create a partition on the GPT device (usually +of "BIOS boot" type) of at least 32KiB in size, and pass the 1-based number +of the partition to `limine bios-install` as a second argument; such as: + +```bash +limine bios-install <1-based stage 2 partition number> +``` + +The boot device must contain the `limine-bios.sys` and `limine.conf` files in +either a `boot/limine`, `boot`, `limine`, or root directory of one of the +partitions, formatted with a supported file system. See [CONFIG.md](CONFIG.md). + +## BIOS/UEFI hybrid ISO creation +In order to create a hybrid ISO with Limine, place the +`limine-uefi-cd.bin`, `limine-bios-cd.bin`, `limine-bios.sys`, and +`limine.conf` files into a directory which will serve as the root of the +created ISO. +(`limine-bios.sys` and `limine.conf` must either be in the root, `limine`, +`boot`, or `boot/limine` directory; `limine-uefi-cd.bin` and +`limine-bios-cd.bin` can reside anywhere). + +After that, create a `/EFI/BOOT` directory and copy the +relevant Limine EFI executables over (such as `BOOTX64.EFI`). + +Place any other file you want to be on the final ISO in said directory, then +run: +``` +xorriso -as mkisofs -R -r -J -b \ + -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \ + -apm-block-size 2048 --efi-boot \ + -efi-boot-part --efi-boot-image --protective-msdos-label \ + -o image.iso +``` + +*Note: `xorriso` is required.* + +And do not forget to also run `limine bios-install` on the generated image: +``` +limine bios-install image.iso +``` + +`` is the relative path of +`limine-bios-cd.bin` inside the root directory. +For example, if it was copied in `/boot/limine-bios-cd.bin`, +it would be `boot/limine-bios-cd.bin`. + +`` is the relative path of +`limine-uefi-cd.bin` inside the root directory. +For example, if it was copied in +`/boot/limine-uefi-cd.bin`, it would be +`boot/limine-uefi-cd.bin`. + +## BIOS/PXE boot +The `limine-bios-pxe.bin` binary is a valid PXE boot image. +In order to boot Limine from PXE it is necessary to setup a DHCP server with +support for PXE booting. This can either be accomplished using a single DHCP +server or your existing DHCP server and a proxy DHCP server such as dnsmasq. + +`limine.conf` and `limine-bios.sys` are expected to be on the server used for +boot. + +## UEFI/PXE boot +The `BOOT*.EFI` files are compatible with UEFI PXE. +The steps needed to boot Limine are the same as with BIOS PXE, +except that the `limine-bios.sys` file is not needed on the server. + +## Configuration +The `limine.conf` file contains Limine's configuration. + +More info on the format of `limine.conf` can be found in +[`CONFIG.md`](CONFIG.md). diff --git a/limine/bochsrc b/limine/bochsrc new file mode 100644 index 0000000..8d7e277 --- /dev/null +++ b/limine/bochsrc @@ -0,0 +1,7 @@ +cpu: count=2, reset_on_triple_fault=0 +memory: guest=1024, host=1024 +ata0-master: type=disk, path="test.hdd", mode=flat +boot: c +clock: sync=realtime, rtc_sync=1, time0=utc +port_e9_hack: enabled=1 +magic_break: enabled=1 diff --git a/limine/bootstrap b/limine/bootstrap new file mode 100755 index 0000000..c1a9679 --- /dev/null +++ b/limine/bootstrap @@ -0,0 +1,170 @@ +#! /bin/sh + +set -ex + +srcdir="$(dirname "$0")" +test -z "$srcdir" && srcdir=. + +: "${AUTORECONF:=autoreconf}" +: "${AUTOMAKE:=automake}" + +cd "$srcdir" + +AUXFILES="config.guess config.sub install-sh" + +clone_repo_commit() { + if test -d "$2/.git"; then + git -C "$2" reset --hard + git -C "$2" clean -fd + if ! git -C "$2" -c advice.detachedHead=false checkout $3; then + rm -rf "$2" + fi + else + if test -d "$2"; then + set +x + echo "error: '$2' is not a Git repository" 1>&2 + exit 1 + fi + fi + if ! test -d "$2"; then + git clone $1 "$2" + if ! git -C "$2" -c advice.detachedHead=false 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" 1>&2 + 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" 1>&2 + 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" 1>&2 + echo "incorrect hash:" 1>&2 + $SHA256_COMMAND "$2" 1>&2 + rm -f "$2" + exit 1 + fi + fi +} + +if ! test -f version; then + clone_repo_commit \ + https://codeberg.org/OSDev/freestnd-c-hdrs-0bsd.git \ + freestnd-c-hdrs \ + 097259a899d30f0a4b7a694de2de5fdda942e923 + + clone_repo_commit \ + https://codeberg.org/OSDev/cc-runtime.git \ + cc-runtime \ + dae79833b57a01b9fd3e359ee31def69f5ae899b + cp cc-runtime/src/cc-runtime.c common/cc-runtime.s2.c + cp cc-runtime/src/cc-runtime.c decompressor/cc-runtime.c + + clone_repo_commit \ + https://codeberg.org/Limine/limine-protocol.git \ + limine-protocol \ + e42d010a761e4e9ac6bad1b578110ba72c61e1d9 + + clone_repo_commit \ + https://codeberg.org/PicoEFI/PicoEFI.git \ + picoefi \ + 8b79fdaa72ee548a8ea24e3dc4d87bf281312865 + + clone_repo_commit \ + https://github.com/jibsen/tinf.git \ + tinf \ + 57ffa1f1d5e3dde19011b2127bd26d01689b694b + mkdir -p decompressor/tinf + cp tinf/src/tinf.h tinf/src/tinflate.c tinf/src/tinfgzip.c decompressor/tinf/ + patch -p0 < decompressor/tinf.patch + rm -f decompressor/tinf/*.orig + + clone_repo_commit \ + https://codeberg.org/Mintsuki/Flanterm.git \ + flanterm \ + 5d93c648bee81ce7d19f7a6671ec2bc3aaa834f2 + + download_by_hash \ + https://github.com/nothings/stb/raw/5c205738c191bcb0abc65c4febfa9bd25ff35234/stb_image.h \ + common/lib/stb_image.h.nopatch \ + 594c2fe35d49488b4382dbfaec8f98366defca819d916ac95becf3e75f4200b3 + cp common/lib/stb_image.h.nopatch common/lib/stb_image.h + patch -p0 < common/stb_image.patch + rm -f common/lib/stb_image.h.orig + + clone_repo_commit \ + https://codeberg.org/OSDev/libfdt.git \ + libfdt \ + 7bf94e6347129d17eca263112296ad170dec28a9 +fi + +# Create timestamps file +if test -d .git && git log -1 >/dev/null 2>&1; then + cat >timestamps <timestamps </dev/null 2>&1; then + set +x + echo "error: Broken autoreconf detected, but missing or broken automake." 1>&2 + echo " Please make sure automake is installed and working." 1>&2 + exit 1 + fi + AUTOMAKE_LIBDIR="$($AUTOMAKE --print-libdir)" + if test -z "$AUTOMAKE_LIBDIR"; then + # Assume `true` was passed as $AUTOMAKE + continue + fi + mkdir -p build-aux + cp -v "$AUTOMAKE_LIBDIR/$auxfile" build-aux/ + chmod +x build-aux/$auxfile + fi +done + +set +x + +printf "\nSource tree bootstrapped successfully!\n" diff --git a/limine/common/common.mk b/limine/common/common.mk new file mode 100644 index 0000000..0e2dc5b --- /dev/null +++ b/limine/common/common.mk @@ -0,0 +1,675 @@ +.SUFFIXES: + +override SRCDIR := $(shell pwd -P) + +override SPACE := $(subst ,, ) + +override MKESCAPE = $(subst $(SPACE),\ ,$(1)) +override SHESCAPE = $(subst ','\'',$(1)) +override OBJESCAPE = $(subst .a ,.a' ',$(subst .o ,.o' ',$(call SHESCAPE,$(1)))) + +override CC_FOR_TARGET_IS_CLANG := $(shell ! $(CC_FOR_TARGET) --version 2>/dev/null | $(GREP) -q '^Target: '; echo $$?) + +COM_OUTPUT := false +E9_OUTPUT := false + +override S2CFLAGS := -Os + +override BASE_CFLAGS := $(CFLAGS_FOR_TARGET) + +override CFLAGS_FOR_TARGET += \ + -g \ + -Wall \ + -Wextra \ + -Wshadow \ + -Wvla \ + $(WERROR_FLAG) \ + -std=gnu11 \ + -nostdinc \ + -ffreestanding \ + -ffunction-sections \ + -fdata-sections \ + -fno-stack-protector \ + -fno-stack-check \ + -fno-omit-frame-pointer \ + -fno-strict-aliasing \ + -fno-lto + +override CPPFLAGS_FOR_TARGET := \ + -I . \ + -I libc-compat \ + -I ../limine-protocol/include \ + -I ../flanterm/src \ + -I ../libfdt/src \ + -I '$(call SHESCAPE,$(BUILDDIR))/..' \ + -isystem ../freestnd-c-hdrs/include \ + $(CPPFLAGS_FOR_TARGET) \ + -DCOM_OUTPUT=$(COM_OUTPUT) \ + -DE9_OUTPUT=$(E9_OUTPUT) \ + -DFLANTERM_IN_FLANTERM \ + -MMD \ + -MP + +$(call MKESCAPE,$(BUILDDIR))/libfdt/src/fdt_overlay.o: override CFLAGS_FOR_TARGET += \ + -Wno-unused-parameter + +$(call MKESCAPE,$(BUILDDIR))/flanterm/src/flanterm_backends/fb.o: override CPPFLAGS_FOR_TARGET += \ + -DFLANTERM_FB_DISABLE_BUMP_ALLOC + +override NASMFLAGS_FOR_TARGET += \ + -g \ + -Wall \ + -w-unknown-warning \ + -w-reloc \ + $(WERROR_FLAG) + +override NASMFLAGS_FOR_TARGET := \ + $(patsubst -g,-g -F dwarf,$(NASMFLAGS_FOR_TARGET)) + +ifeq ($(TARGET),bios) + ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target i686-unknown-none-elf + endif + override CFLAGS_FOR_TARGET += \ + -fno-PIC \ + -m32 \ + -march=i686 \ + -mabi=sysv \ + -mno-80387 \ + -mno-mmx + override CPPFLAGS_FOR_TARGET := \ + $(CPPFLAGS_FOR_TARGET) \ + -DBIOS + override NASMFLAGS_FOR_TARGET := \ + -f elf32 \ + $(NASMFLAGS_FOR_TARGET) \ + -DIA32_TARGET \ + -DBIOS +endif + +ifeq ($(TARGET),uefi-x86-64) + ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target x86_64-unknown-none-elf + endif + override CFLAGS_FOR_TARGET += \ + -fPIE \ + -fshort-wchar \ + -m64 \ + -march=x86-64 \ + -mabi=sysv \ + -mno-80387 \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 \ + -mno-red-zone + override CPPFLAGS_FOR_TARGET := \ + -I ../picoefi/inc \ + $(CPPFLAGS_FOR_TARGET) \ + -DUEFI + override NASMFLAGS_FOR_TARGET := \ + -f elf64 \ + $(NASMFLAGS_FOR_TARGET) \ + -DX86_64_TARGET \ + -DUEFI +endif + +ifeq ($(TARGET),uefi-ia32) + ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target i686-unknown-none-elf + endif + override CFLAGS_FOR_TARGET += \ + -fPIE \ + -fshort-wchar \ + -m32 \ + -march=i686 \ + -mabi=sysv \ + -mno-80387 \ + -mno-mmx + override CPPFLAGS_FOR_TARGET := \ + -I ../picoefi/inc \ + $(CPPFLAGS_FOR_TARGET) \ + -DUEFI + override NASMFLAGS_FOR_TARGET := \ + -f elf32 \ + $(NASMFLAGS_FOR_TARGET) \ + -DIA32_TARGET \ + -DUEFI +endif + +ifeq ($(TARGET),uefi-aarch64) + ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target aarch64-unknown-none-elf + endif + override CFLAGS_FOR_TARGET += \ + -fPIE \ + -fshort-wchar \ + -mcpu=generic \ + -march=armv8-a+nofp+nosimd \ + -mgeneral-regs-only + override CPPFLAGS_FOR_TARGET := \ + -I ../picoefi/inc \ + $(CPPFLAGS_FOR_TARGET) \ + -DUEFI +endif + +ifeq ($(TARGET),uefi-riscv64) + ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target riscv64-unknown-none-elf + endif + override CFLAGS_FOR_TARGET += \ + -fPIE \ + -fshort-wchar \ + -march=rv64imac_zicsr_zifencei \ + -mabi=lp64 \ + -mno-relax + override CPPFLAGS_FOR_TARGET := \ + -I ../picoefi/inc \ + $(CPPFLAGS_FOR_TARGET) \ + -DUEFI +endif + +ifeq ($(TARGET),uefi-loongarch64) + ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target loongarch64-unknown-none-elf + endif + override CFLAGS_FOR_TARGET += \ + -fPIE \ + -fshort-wchar \ + -march=loongarch64 \ + -mabi=lp64s \ + -mfpu=none \ + -msimd=none + override CPPFLAGS_FOR_TARGET := \ + -I ../picoefi/inc \ + $(CPPFLAGS_FOR_TARGET) \ + -DUEFI +endif + +override LDFLAGS_FOR_TARGET += \ + -nostdlib \ + -z max-page-size=0x1000 \ + --gc-sections + +ifeq ($(TARGET),bios) + override LDFLAGS_FOR_TARGET += \ + -m elf_i386 \ + -static \ + --build-id=sha1 +endif + +ifeq ($(TARGET),uefi-x86-64) + override LDFLAGS_FOR_TARGET += \ + -m elf_x86_64 \ + -pie \ + -z text +endif + +ifeq ($(TARGET),uefi-ia32) + override LDFLAGS_FOR_TARGET += \ + -m elf_i386 \ + -pie \ + -z text +endif + +ifeq ($(TARGET),uefi-aarch64) + override LDFLAGS_FOR_TARGET += \ + -m aarch64elf \ + -pie \ + -z text +endif + +ifeq ($(TARGET),uefi-riscv64) + override LDFLAGS_FOR_TARGET += \ + -m elf64lriscv \ + --no-relax \ + -pie \ + -z text +endif + +ifeq ($(TARGET),uefi-loongarch64) + override LDFLAGS_FOR_TARGET += \ + -m elf64loongarch \ + -pie \ + -z text +endif + +ifeq ($(TARGET),bios) + override C_FILES := $(shell cd .. && find common flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort) + override S_FILES := $(shell cd .. && find common -type f -name '*.S' | LC_ALL=C sort) + + override ASMX86_FILES := $(shell cd .. && find common -type f -name '*.asm_x86' | LC_ALL=C sort) + override ASM32_FILES := $(shell cd .. && find common -type f -name '*.asm_ia32' | LC_ALL=C sort) + override ASMB_FILES := $(shell cd .. && find common -type f -name '*.asm_bios_ia32' | LC_ALL=C sort) + + override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASMB_FILES:.asm_bios_ia32=.o) $(ASMX86_FILES:.asm_x86=.o)) + override OBJ_S2 := $(filter %.s2.o,$(OBJ)) +endif +ifeq ($(TARGET),uefi-x86-64) + override C_FILES := $(shell cd .. && find common picoefi/x86_64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort) + override S_FILES := $(shell cd .. && find common picoefi/x86_64 -type f -name '*.S' | LC_ALL=C sort) + + override ASMX86_FILES := $(shell cd .. && find common -type f -name '*.asm_x86' | LC_ALL=C sort) + override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_x86_64' | LC_ALL=C sort) + override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_x86_64' | LC_ALL=C sort) + + override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_x86_64=.o) $(ASM64U_FILES:.asm_uefi_x86_64=.o) $(ASMX86_FILES:.asm_x86=.o)) +endif +ifeq ($(TARGET),uefi-ia32) + override C_FILES := $(shell cd .. && find common picoefi/ia32 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort) + override S_FILES := $(shell cd .. && find common picoefi/ia32 -type f -name '*.S' | LC_ALL=C sort) + + override ASMX86_FILES := $(shell cd .. && find common -type f -name '*.asm_x86' | LC_ALL=C sort) + override ASM32_FILES := $(shell cd .. && find common -type f -name '*.asm_ia32' | LC_ALL=C sort) + override ASM32U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_ia32' | LC_ALL=C sort) + + override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASM32U_FILES:.asm_uefi_ia32=.o) $(ASMX86_FILES:.asm_x86=.o)) +endif +ifeq ($(TARGET),uefi-aarch64) + override C_FILES := $(shell cd .. && find common picoefi/aarch64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort) + override S_FILES := $(shell cd .. && find common picoefi/aarch64 -type f -name '*.S' | LC_ALL=C sort) + + override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_aarch64' | LC_ALL=C sort) + override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_aarch64' | LC_ALL=C sort) + + override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_aarch64=.o) $(ASM64U_FILES:.asm_uefi_aarch64=.o)) +endif +ifeq ($(TARGET),uefi-riscv64) + override C_FILES := $(shell cd .. && find common picoefi/riscv64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort) + override S_FILES := $(shell cd .. && find common picoefi/riscv64 -type f -name '*.S' | LC_ALL=C sort) + + override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_riscv64' | LC_ALL=C sort) + override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_riscv64' | LC_ALL=C sort) + + override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_riscv64=.o) $(ASM64U_FILES:.asm_uefi_riscv64=.o)) +endif +ifeq ($(TARGET),uefi-loongarch64) + override C_FILES := $(shell cd .. && find common picoefi/loongarch64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort) + override S_FILES := $(shell cd .. && find common picoefi/loongarch64 -type f -name '*.S' | LC_ALL=C sort) + + override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_loongarch64' | LC_ALL=C sort) + override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_loongarch64' | LC_ALL=C sort) + + override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_loongarch64=.o) $(ASM64U_FILES:.asm_uefi_loongarch64=.o)) +endif + +override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d) $(C_FILES:.S=.d)) + +.PHONY: all + +ifeq ($(TARGET),bios) +all: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys $(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz +endif +ifeq ($(TARGET),uefi-x86-64) +all: $(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI +endif +ifeq ($(TARGET),uefi-ia32) +all: $(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI +endif +ifeq ($(TARGET),uefi-aarch64) +all: $(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI +endif +ifeq ($(TARGET),uefi-riscv64) +all: $(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI +endif +ifeq ($(TARGET),uefi-loongarch64) +all: $(call MKESCAPE,$(BUILDDIR))/BOOTLOONGARCH64.EFI +endif + +ifeq ($(TARGET),bios) + +$(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz: $(call MKESCAPE,$(BUILDDIR))/stage2.bin + gzip -n -9 < '$(call SHESCAPE,$<)' > '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/stage2.bin: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys + dd if='$(call SHESCAPE,$<)' bs=$$(( 0x$$("$(READELF_FOR_TARGET)" -S '$(call SHESCAPE,$(BUILDDIR))/limine.elf' | $(GREP) '\.text\.stage3' | $(SED) 's/^.*] //' | $(AWK) '{print $$3}' | $(SED) 's/^0*//') - 0xf000 )) count=1 of='$(call SHESCAPE,$@)' 2>/dev/null + +$(call MKESCAPE,$(BUILDDIR))/stage2.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' stage2 32 '\.text\.stage2' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/stage2.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/stage2.map.S' '$(call SHESCAPE,$(BUILDDIR))/stage2.map.d' + +$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nos3map.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 32 '\.text' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d' + +$(call MKESCAPE,$(BUILDDIR))/limine-bios.sys: $(call MKESCAPE,$(BUILDDIR))/limine_stage2only.elf $(call MKESCAPE,$(BUILDDIR))/limine.elf + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$(BUILDDIR))/limine.elf' '$(call SHESCAPE,$@)' + chmod -x '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker_stage2only.ld: linker_bios.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_STAGE2ONLY linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_stage2only.elf: $(OBJ_S2) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld' + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -T'$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld' -o '$(call SHESCAPE,$@)' || \ + ( echo "This error may mean that stage 2 was trying to use stage 3 symbols before loading stage 3" && \ + false ) + +$(call MKESCAPE,$(BUILDDIR))/linker_nos2map.ld: linker_bios.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP -DLINKER_NOS2MAP linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' + +$(call MKESCAPE,$(BUILDDIR))/empty: + touch '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/empty' + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -T'$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' -o '$(call SHESCAPE,$@)' + $(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin' + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + $(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \ + $(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o + $(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin' + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + $(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \ + $(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' -T'$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_bios.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_nos3map.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/stage2.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/empty' + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' -o '$(call SHESCAPE,$@)' + $(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin' + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + $(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \ + $(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o + $(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin' + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + $(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \ + $(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_bios.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/stage2.map.o $(call MKESCAPE,$(BUILDDIR))/full.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/empty' + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)' + $(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin' + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + $(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \ + $(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o + $(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin' + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + $(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \ + $(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)' + +endif + +ifeq ($(TARGET),uefi-x86-64) + +$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d' + +$(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)' + chmod -x '$(call SHESCAPE,$@)' + dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null + +$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_x86_64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_x86_64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_x86_64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_x86_64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +endif + +ifeq ($(TARGET),uefi-aarch64) + +$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d' + +$(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)' + chmod -x '$(call SHESCAPE,$@)' + dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null + +$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_aarch64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_aarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_aarch64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_aarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-riscv64) + +$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d' + +$(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)' + chmod -x '$(call SHESCAPE,$@)' + dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null + +$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_riscv64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_riscv64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-loongarch64) + +$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d' + +$(call MKESCAPE,$(BUILDDIR))/BOOTLOONGARCH64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)' + chmod -x '$(call SHESCAPE,$@)' + dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null + +$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_loongarch64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_loongarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_loongarch64.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_loongarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-ia32) + +$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf + cd '$(call SHESCAPE,$(BUILDDIR))' && \ + '$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 32 '\.text' + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)' + rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d' + +$(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)' + chmod -x '$(call SHESCAPE,$@)' + dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null + +$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_ia32.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_ia32.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_ia32.ld.in + $(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))' + $(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_ia32.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + +$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o + $(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld' + $(LD_FOR_TARGET) \ + -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \ + $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)' + +endif + +-include $(HEADER_DEPS) + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.c + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.S + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' + +ifeq ($(TARGET),bios) +$(call MKESCAPE,$(BUILDDIR))/%.s2.o: ../%.s2.c + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(S2CFLAGS) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),bios) +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_ia32 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_bios_ia32 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-x86-64) +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86_64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_x86_64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-aarch64) +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_aarch64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_aarch64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-riscv64) +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_riscv64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_riscv64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-loongarch64) +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_loongarch64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_loongarch64 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' +endif + +ifeq ($(TARGET),uefi-ia32) +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_ia32 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_ia32 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86 + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' +endif diff --git a/limine/common/crypt/blake2b.c b/limine/common/crypt/blake2b.c new file mode 100644 index 0000000..b62183f --- /dev/null +++ b/limine/common/crypt/blake2b.c @@ -0,0 +1,220 @@ +// This blake2b implementation comes from the GNU coreutils project. +// https://github.com/coreutils/coreutils/blob/master/src/blake2/blake2b-ref.c + +#include +#include +#include +#include +#include + +#define BLAKE2B_BLOCK_BYTES 128 +#define BLAKE2B_KEY_BYTES 64 +#define BLAKE2B_SALT_BYTES 16 +#define BLAKE2B_PERSONAL_BYTES 16 + +static const uint64_t blake2b_iv[8] = { + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, +}; + +static const uint8_t blake2b_sigma[12][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, +}; + +struct blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCK_BYTES]; + size_t buf_len; + uint8_t last_node; +}; + +struct blake2b_param { + uint8_t digest_length; + uint8_t key_length; + uint8_t fan_out; + uint8_t depth; + uint32_t leaf_length; + uint32_t node_offset; + uint32_t xof_length; + uint8_t node_depth; + uint8_t inner_length; + uint8_t reserved[14]; + uint8_t salt[BLAKE2B_SALT_BYTES]; + uint8_t personal[BLAKE2B_PERSONAL_BYTES]; +} __attribute__((packed)); + +// Safe unaligned load/store helpers for architectures with strict alignment (ARM64, RISC-V) +static inline uint64_t load64_le(const void *src) { + uint64_t val; + memcpy(&val, src, sizeof(val)); + return val; +} + +static inline void store64_le(void *dst, uint64_t val) { + memcpy(dst, &val, sizeof(val)); +} + +static void blake2b_increment_counter(struct blake2b_state *state, uint64_t inc) { + state->t[0] += inc; + state->t[1] += state->t[0] < inc; +} + +static inline uint64_t rotr64(uint64_t w, unsigned c) { + return (w >> c) | (w << (64 - c)); +} + +#define G(r, i, a, b, c, d) do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while (0) + +#define ROUND(r) do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while (0) + +static void blake2b_compress(struct blake2b_state *state, const uint8_t block[static BLAKE2B_BLOCK_BYTES]) { + uint64_t m[16]; + uint64_t v[16]; + + for (int i = 0; i < 16; i++) { + m[i] = load64_le(block + i * sizeof(m[i])); + } + + for (int i = 0; i < 8; i++) { + v[i] = state->h[i]; + } + + v[8] = blake2b_iv[0]; + v[9] = blake2b_iv[1]; + v[10] = blake2b_iv[2]; + v[11] = blake2b_iv[3]; + v[12] = blake2b_iv[4] ^ state->t[0]; + v[13] = blake2b_iv[5] ^ state->t[1]; + v[14] = blake2b_iv[6] ^ state->f[0]; + v[15] = blake2b_iv[7] ^ state->f[1]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + ROUND(10); + ROUND(11); + + for (int i = 0; i < 8; i++) { + state->h[i] = state->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +static void blake2b_init(struct blake2b_state *state) { + struct blake2b_param param = {0}; + + param.digest_length = BLAKE2B_OUT_BYTES; + param.fan_out = 1; + param.depth = 1; + + memset(state, 0, sizeof(struct blake2b_state)); + + for (int i = 0; i < 8; i++) { + state->h[i] = blake2b_iv[i]; + } + + for (int i = 0; i < 8; i++) { + state->h[i] ^= load64_le((uint8_t *)¶m + sizeof(state->h[i]) * i); + } +} + +static void blake2b_update(struct blake2b_state *state, const void *in, size_t in_len) { + if (in_len == 0) { + return; + } + + size_t left = state->buf_len; + size_t fill = BLAKE2B_BLOCK_BYTES - left; + + if (in_len > fill) { + state->buf_len = 0; + + memcpy(state->buf + left, in, fill); + blake2b_increment_counter(state, BLAKE2B_BLOCK_BYTES); + blake2b_compress(state, state->buf); + + in += fill; + in_len -= fill; + + while (in_len > BLAKE2B_BLOCK_BYTES) { + blake2b_increment_counter(state, BLAKE2B_BLOCK_BYTES); + blake2b_compress(state, in); + + in += BLAKE2B_BLOCK_BYTES; + in_len -= BLAKE2B_BLOCK_BYTES; + } + } + + memcpy(state->buf + state->buf_len, in, in_len); + state->buf_len += in_len; +} + +static void blake2b_final(struct blake2b_state *state, void *out) { + uint8_t buffer[BLAKE2B_OUT_BYTES] = {0}; + + blake2b_increment_counter(state, state->buf_len); + state->f[0] = (uint64_t)-1; + memset(state->buf + state->buf_len, 0, BLAKE2B_BLOCK_BYTES - state->buf_len); + blake2b_compress(state, state->buf); + + for (int i = 0; i < 8; i++) { + store64_le(buffer + sizeof(state->h[i]) * i, state->h[i]); + } + + memcpy(out, buffer, BLAKE2B_OUT_BYTES); + memset(buffer, 0, sizeof(buffer)); +} + +void blake2b(void *out, const void *in, size_t in_len) { + struct blake2b_state state = {0}; + + blake2b_init(&state); + blake2b_update(&state, in, in_len); + blake2b_final(&state, out); +} diff --git a/limine/common/crypt/blake2b.h b/limine/common/crypt/blake2b.h new file mode 100644 index 0000000..313a7c6 --- /dev/null +++ b/limine/common/crypt/blake2b.h @@ -0,0 +1,10 @@ +#ifndef CRYPT__BLAKE2B_H__ +#define CRYPT__BLAKE2B_H__ + +#include + +#define BLAKE2B_OUT_BYTES 64 + +void blake2b(void *out, const void *in, size_t in_len); + +#endif diff --git a/limine/common/drivers/disk.h b/limine/common/drivers/disk.h new file mode 100644 index 0000000..bb3e83e --- /dev/null +++ b/limine/common/drivers/disk.h @@ -0,0 +1,26 @@ +#ifndef DRIVERS__DISK_H__ +#define DRIVERS__DISK_H__ + +#include +#include +#include +#include + +#if defined (UEFI) + +#include + +struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle); + +#endif + +enum { + DISK_SUCCESS, + DISK_NO_MEDIA, + DISK_FAILURE +}; + +void disk_create_index(void); +int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count); + +#endif diff --git a/limine/common/drivers/disk.s2.c b/limine/common/drivers/disk.s2.c new file mode 100644 index 0000000..eba8e24 --- /dev/null +++ b/limine/common/drivers/disk.s2.c @@ -0,0 +1,846 @@ +#include +#include +#include +#include +#include +#if defined (BIOS) +# include +#elif defined (UEFI) +# include +# include +#endif +#include +#include +#include +#include +#include +#include + +#define DEFAULT_FASTEST_XFER_SIZE 64 +#define MAX_FASTEST_XFER_SIZE 512 + +#if defined (BIOS) + +struct dpte { + uint16_t io_port; + uint16_t control_port; + uint8_t head_reg_upper; + uint8_t bios_vendor_specific; + uint8_t irq_info; + uint8_t block_count_multiple; + uint8_t dma_info; + uint8_t pio_info; + uint16_t flags; + uint16_t reserved; + uint8_t revision; + uint8_t checksum; +} __attribute__((packed)); + +struct bios_drive_params { + uint16_t buf_size; + uint16_t info_flags; + uint32_t cyl; + uint32_t heads; + uint32_t sects; + uint64_t lba_count; + uint16_t bytes_per_sect; + uint16_t dpte_off; + uint16_t dpte_seg; +} __attribute__((packed)); + +struct dap { + uint16_t size; + uint16_t count; + uint16_t offset; + uint16_t segment; + uint64_t lba; +}; + +#define XFER_BUF_SIZE (xfer_sizes[SIZEOF_ARRAY(xfer_sizes) - 1] * 512) +static const size_t xfer_sizes[] = { 1, 2, 4, 8, 16, 24, 32, 48, 64 }; +static uint8_t *xfer_buf = NULL; + +static size_t fastest_xfer_size(struct volume *volume) { + struct dap dap = {0}; + + if (xfer_buf == NULL) + xfer_buf = conv_mem_alloc(XFER_BUF_SIZE); + + size_t fastest_size = 1; + uint64_t last_speed = (uint64_t)-1; + + for (size_t i = 0; i < SIZEOF_ARRAY(xfer_sizes); i++) { + if (xfer_sizes[i] * volume->sector_size > XFER_BUF_SIZE) { + break; + } + + dap.size = 16; + dap.count = xfer_sizes[i]; + dap.segment = rm_seg(xfer_buf); + dap.offset = rm_off(xfer_buf); + dap.lba = 0; + + uint64_t start_timestamp = rdtsc(); + for (size_t j = 0; j < XFER_BUF_SIZE / 512; j += xfer_sizes[i]) { + struct rm_regs r = {0}; + r.eax = 0x4200; + r.edx = volume->drive; + r.esi = (uint32_t)rm_off(&dap); + r.ds = rm_seg(&dap); + rm_int(0x13, &r, &r); + if (r.eflags & EFLAGS_CF) { + int ah = (r.eax >> 8) & 0xff; + print("Disk error %x. Drive %x", ah, volume->drive); + return 8; + } + dap.lba += xfer_sizes[i]; + } + uint64_t end_timestamp = rdtsc(); + + uint64_t speed = end_timestamp - start_timestamp; + + if (speed < last_speed) { + last_speed = speed; + fastest_size = xfer_sizes[i]; + } + } + + return fastest_size; +} + +int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) { + struct dap dap = {0}; + + if (count * volume->sector_size > XFER_BUF_SIZE) + panic(false, "XFER"); + + if (xfer_buf == NULL) + xfer_buf = conv_mem_alloc(XFER_BUF_SIZE); + + dap.size = 16; + dap.count = count; + dap.segment = rm_seg(xfer_buf); + dap.offset = rm_off(xfer_buf); + dap.lba = block; + + struct rm_regs r = {0}; + r.eax = 0x4200; + r.edx = volume->drive; + r.esi = (uint32_t)rm_off(&dap); + r.ds = rm_seg(&dap); + + rm_int(0x13, &r, &r); + + if (r.eflags & EFLAGS_CF) { + return DISK_FAILURE; + } + + if (buf != NULL) + memcpy(buf, xfer_buf, count * volume->sector_size); + + return DISK_SUCCESS; +} + +static bool detect_sector_size(struct volume *volume) { + struct dap dap = {0}; + + if (xfer_buf == NULL) + xfer_buf = conv_mem_alloc(XFER_BUF_SIZE); + + dap.size = 16; + dap.count = 1; + dap.segment = rm_seg(xfer_buf); + dap.offset = rm_off(xfer_buf); + dap.lba = 0; + + struct rm_regs r = {0}; + r.eax = 0x4200; + r.edx = volume->drive; + r.esi = (uint32_t)rm_off(&dap); + r.ds = rm_seg(&dap); + + struct rm_regs r_copy = r; + struct dap dap_copy = dap; + + memset(xfer_buf, 0, XFER_BUF_SIZE); + + rm_int(0x13, &r, &r); + + if (r.eflags & EFLAGS_CF) { + return false; + } + + size_t sector_size_a = 0; + for (long i = XFER_BUF_SIZE - 1; i >= 0; i--) { + if (xfer_buf[i] != 0) { + sector_size_a = i + 1; + break; + } + } + + r = r_copy; + dap = dap_copy; + + memset(xfer_buf, 0xff, XFER_BUF_SIZE); + + rm_int(0x13, &r, &r); + + if (r.eflags & EFLAGS_CF) { + return false; + } + + size_t sector_size_b = 0; + for (long i = XFER_BUF_SIZE - 1; i >= 0; i--) { + if (xfer_buf[i] != 0xff) { + sector_size_b = i + 1; + break; + } + } + + volume->sector_size = sector_size_a > sector_size_b ? sector_size_a : sector_size_b; + + if (volume->sector_size == 0) { + return false; + } + + return true; +} + +void disk_create_index(void) { + // Disk count (only non-removable) at 0040:0075 + uint8_t bda_disk_count = mminb(rm_desegment(0x0040, 0x0075)); + + int optical_indices = 1, hdd_indices = 1, consumed_bda_disks = 0; + + for (uint8_t drive = 0x80; drive < 0xf0; drive++) { + struct rm_regs r = {0}; + struct bios_drive_params drive_params; + + r.eax = 0x4800; + r.edx = drive; + r.ds = rm_seg(&drive_params); + r.esi = rm_off(&drive_params); + + drive_params.buf_size = sizeof(struct bios_drive_params); + + rm_int(0x13, &r, &r); + + if (r.eflags & EFLAGS_CF) { + continue; + } + + bool is_removable = drive_params.info_flags & (1 << 2); + + struct dpte *dpte = NULL; + if (drive_params.buf_size >= 0x1e + && (drive_params.dpte_seg != 0x0000 || drive_params.dpte_off != 0x0000) + && (drive_params.dpte_seg != 0xffff || drive_params.dpte_off != 0xffff)) { + dpte = (void *)rm_desegment(drive_params.dpte_seg, drive_params.dpte_off); + if ((dpte->control_port & 0xff00) != 0xa000) { + // Check for removable (5) or ATAPI (6) + is_removable = is_removable || ((dpte->flags & (1 << 5)) || (dpte->flags & (1 << 6))); + } + } + + struct volume *block = ext_mem_alloc(sizeof(struct volume)); + + block->drive = drive; + block->partition = 0; + block->first_sect = 0; + block->max_partition = -1; + + if (!detect_sector_size(block)) { + pmm_free(block, sizeof(struct volume)); + continue; + } + + // Normalize sect_count to 512-byte sectors for consistency with partitions + // Preserve (uint64_t)-1 sentinel value (means "unknown size") + if (drive_params.lba_count == (uint64_t)-1 || drive_params.lba_count == 0) { + block->sect_count = (uint64_t)-1; + } else { + block->sect_count = drive_params.lba_count * (block->sector_size / 512); + } + + // Detect optical drives via DPTE ATAPI bit (bit 6) or sector size heuristic + bool is_atapi = (dpte != NULL && (dpte->flags & (1 << 6))); + block->is_optical = is_atapi || (block->sector_size == 2048 && is_removable); + + if (!is_removable && !block->is_optical) { + if (consumed_bda_disks == bda_disk_count) { + pmm_free(block, sizeof(struct volume)); + continue; + } + consumed_bda_disks++; + } + + if (block->is_optical) { + block->index = optical_indices++; + } else { + block->index = hdd_indices++; + } + + block->fastest_xfer_size = fastest_xfer_size(block); + + if (gpt_get_guid(&block->guid, block)) { + block->guid_valid = true; + } + + volume_index = pmm_realloc( + volume_index, + volume_index_i * sizeof(void *), + (volume_index_i + 1) * sizeof(void *) + ); + volume_index[volume_index_i++] = block; + + for (int part = 0; ; part++) { + struct volume *p = ext_mem_alloc(sizeof(struct volume)); + int ret = part_get(p, block, part); + + if (ret == END_OF_TABLE || ret == INVALID_TABLE) { + pmm_free(p, sizeof(struct volume)); + break; + } + if (ret == NO_PARTITION) { + pmm_free(p, sizeof(struct volume)); + continue; + } + + volume_index = pmm_realloc( + volume_index, + volume_index_i * sizeof(void *), + (volume_index_i + 1) * sizeof(void *) + ); + volume_index[volume_index_i++] = p; + + block->max_partition++; + } + } +} + +#endif + +#if defined (UEFI) + +int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) { + EFI_STATUS status; + + status = volume->block_io->ReadBlocks(volume->block_io, + volume->block_io->Media->MediaId, + block, count * volume->sector_size, buf); + + switch (status) { + case EFI_SUCCESS: return DISK_SUCCESS; + case EFI_NO_MEDIA: return DISK_NO_MEDIA; + default: return DISK_FAILURE; + } +} + +static struct volume *pxe_from_efi_handle(EFI_HANDLE efi_handle) { + static struct volume *vol = NULL; + + // There's only one PXE volume + if (vol) { + return vol; + } + + EFI_STATUS status; + + EFI_GUID pxe_base_code_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID; + EFI_PXE_BASE_CODE *pxe_base_code = NULL; + + status = gBS->HandleProtocol(efi_handle, &pxe_base_code_guid, (void **)&pxe_base_code); + if (status) { + return NULL; + } + + if (!pxe_base_code->Mode->DhcpDiscoverValid) { + print("PXE somehow didn't use DHCP?\n"); + return NULL; + } + + if (pxe_base_code->Mode->UsingIpv6) { + print("Sorry, unsupported: PXE IPv6\n"); + return NULL; + } + + vol = pxe_bind_volume(efi_handle, pxe_base_code); + return vol; +} + +#define UNIQUE_SECTOR_POOL_SIZE 65536 +static uint8_t *unique_sector_pool; +static bool unique_sectors_calculated = false; + +static void find_unique_sectors(void); + +static struct volume *volume_by_unique_sector(void *b2b) { + for (size_t i = 0; i < volume_index_i; i++) { + if (volume_index[i]->unique_sector_valid == false) { + continue; + } + + if (memcmp(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES) == 0) { + return volume_index[i]; + } + } + + return NULL; +} + +// Search for matching hash including invalidated volumes (for collision detection) +static struct volume *volume_by_sector_hash(void *b2b) { + for (size_t i = 0; i < volume_index_i; i++) { + if (volume_index[i]->unique_sector_valid == false + && memcmp(volume_index[i]->unique_sector_b2b, (uint8_t[BLAKE2B_OUT_BYTES]){0}, BLAKE2B_OUT_BYTES) == 0) { + // Hash was never set, skip + continue; + } + + if (memcmp(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES) == 0) { + return volume_index[i]; + } + } + + return NULL; +} + +static bool is_efi_handle_to_skip(EFI_HANDLE efi_handle) { + EFI_STATUS status; + + EFI_GUID dp_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; + EFI_DEVICE_PATH_PROTOCOL *dp = NULL; + + EFI_GUID guids_to_skip[] = { + // skip 7CCE9C94-983F-4D0A-8143-B6C05545B223 since it is apparently used by exposed + // ROM devices that we do not want to touch + // (see https://github.com/limine-bootloader/limine/issues/521#issuecomment-3160168795) + {0x7CCE9C94, 0x983F, 0x4D0A, {0x81, 0x43, 0xB6, 0xC0, 0x55, 0x45, 0xB2, 0x23}}, + }; + + status = gBS->HandleProtocol(efi_handle, &dp_guid, (void **)&dp); + if (status) { + return false; + } + + for (;;) { + if (dp->Type == END_DEVICE_PATH_TYPE && dp->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) { + break; + } + + uint16_t len = *(uint16_t *)dp->Length; + + // Validate minimum device path node size before accessing type-specific data + if (len < sizeof(EFI_DEVICE_PATH_PROTOCOL)) { + break; // Malformed device path node + } + + if (dp->Type == HARDWARE_DEVICE_PATH && dp->SubType == HW_VENDOR_DP) { + // Vendor device path must be large enough to contain a GUID + if (len >= sizeof(EFI_DEVICE_PATH_PROTOCOL) + sizeof(EFI_GUID)) { + EFI_GUID *vendor_guid = (void *)dp + sizeof(EFI_DEVICE_PATH_PROTOCOL); + + for (size_t i = 0; i < SIZEOF_ARRAY(guids_to_skip); i++) { + if (memcmp(vendor_guid, &guids_to_skip[i], sizeof(EFI_GUID)) == 0) { + return true; + } + } + } + } + dp = (void *)dp + len; + } + + return false; +} + +static bool is_efi_handle_optical(EFI_HANDLE efi_handle) { + EFI_STATUS status; + + EFI_GUID dp_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; + EFI_DEVICE_PATH_PROTOCOL *dp = NULL; + + status = gBS->HandleProtocol(efi_handle, &dp_guid, (void **)&dp); + if (status) { + return false; + } + + for (;;) { + if (dp->Type == END_DEVICE_PATH_TYPE && dp->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) { + break; + } + + if (dp->Type == MEDIA_DEVICE_PATH && dp->SubType == MEDIA_CDROM_DP) { + return true; + } + + uint16_t len = *(uint16_t *)dp->Length; + if (len < sizeof(EFI_DEVICE_PATH_PROTOCOL)) { + break; // Malformed device path node + } + dp = (void *)dp + len; + } + + return false; +} + +static EFI_DEVICE_PATH_PROTOCOL *get_device_path(EFI_HANDLE efi_handle) { + EFI_STATUS status; + EFI_GUID dp_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; + EFI_DEVICE_PATH_PROTOCOL *dp = NULL; + + status = gBS->HandleProtocol(efi_handle, &dp_guid, (void **)&dp); + if (status) { + return NULL; + } + return dp; +} + +// Compare device paths up to (but not including) partition nodes +static bool device_paths_match_disk(EFI_DEVICE_PATH_PROTOCOL *dp1, + EFI_DEVICE_PATH_PROTOCOL *dp2) { + if (dp1 == NULL || dp2 == NULL) { + return false; + } + + while (!IsDevicePathEnd(dp1) && !IsDevicePathEnd(dp2)) { + // Stop at partition nodes + if (dp1->Type == MEDIA_DEVICE_PATH && + (dp1->SubType == MEDIA_HARDDRIVE_DP || dp1->SubType == MEDIA_CDROM_DP)) { + break; + } + if (dp2->Type == MEDIA_DEVICE_PATH && + (dp2->SubType == MEDIA_HARDDRIVE_DP || dp2->SubType == MEDIA_CDROM_DP)) { + break; + } + + uint16_t len1 = DevicePathNodeLength(dp1); + uint16_t len2 = DevicePathNodeLength(dp2); + + if (len1 != len2) { + return false; + } + + if (len1 < sizeof(EFI_DEVICE_PATH_PROTOCOL)) { + return false; + } + + if (memcmp(dp1, dp2, len1) != 0) { + return false; + } + + dp1 = (void *)dp1 + len1; + dp2 = (void *)dp2 + len2; + } + + return true; +} + +static struct volume *volume_by_device_path(EFI_HANDLE query_handle) { + EFI_DEVICE_PATH_PROTOCOL *query_dp = get_device_path(query_handle); + if (query_dp == NULL) { + return NULL; + } + + for (size_t i = 0; i < volume_index_i; i++) { + EFI_DEVICE_PATH_PROTOCOL *vol_dp = get_device_path(volume_index[i]->efi_handle); + if (vol_dp == NULL) { + continue; + } + + if (device_paths_match_disk(query_dp, vol_dp)) { + // Convert first_sect from 512-byte sectors to device LBAs + int sector_size = volume_index[i]->sector_size; + if ((volume_index[i]->first_sect * 512) % sector_size) { + continue; // Misaligned, skip this volume + } + uint64_t first_sect_lba = (volume_index[i]->first_sect * 512) / sector_size; + + EFI_DEVICE_PATH_PROTOCOL *qp = query_dp; + while (!IsDevicePathEnd(qp)) { + if (qp->Type == MEDIA_DEVICE_PATH && qp->SubType == MEDIA_HARDDRIVE_DP) { + uint16_t len = DevicePathNodeLength(qp); + // UEFI spec size is 42 bytes, but sizeof() may be larger due to padding + if (len < 42) { + break; + } + HARDDRIVE_DEVICE_PATH *query_hd = (HARDDRIVE_DEVICE_PATH *)qp; + if (first_sect_lba == query_hd->PartitionStart) { + return volume_index[i]; + } + break; + } + if (qp->Type == MEDIA_DEVICE_PATH && qp->SubType == MEDIA_CDROM_DP) { + uint16_t len = DevicePathNodeLength(qp); + if (len < sizeof(CDROM_DEVICE_PATH)) { + break; + } + CDROM_DEVICE_PATH *query_cd = (CDROM_DEVICE_PATH *)qp; + if (first_sect_lba == query_cd->PartitionStart) { + return volume_index[i]; + } + break; + } + uint16_t len = DevicePathNodeLength(qp); + if (len < sizeof(EFI_DEVICE_PATH_PROTOCOL)) { + break; + } + qp = (void *)qp + len; + } + + if (IsDevicePathEnd(qp) && volume_index[i]->partition == 0) { + return volume_index[i]; + } + } + } + + return NULL; +} + +struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle) { + EFI_STATUS status; + + EFI_GUID block_io_guid = BLOCK_IO_PROTOCOL; + EFI_BLOCK_IO *block_io = NULL; + + if (is_efi_handle_to_skip(efi_handle)) { + return NULL; + } + + status = gBS->HandleProtocol(efi_handle, &block_io_guid, (void **)&block_io); + if (status) { + return pxe_from_efi_handle(efi_handle); + } + + // Try device path matching first (primary method) + struct volume *ret = volume_by_device_path(efi_handle); + if (ret != NULL) { + return ret; + } + + // Fallback to unique sector matching + uint64_t bdev_size = ((uint64_t)block_io->Media->LastBlock + 1) * (uint64_t)block_io->Media->BlockSize; + if (bdev_size >= UNIQUE_SECTOR_POOL_SIZE) { + status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, + 0, + UNIQUE_SECTOR_POOL_SIZE, + unique_sector_pool); + if (status == 0) { + find_unique_sectors(); + + uint8_t b2b[BLAKE2B_OUT_BYTES]; + blake2b(b2b, unique_sector_pool, UNIQUE_SECTOR_POOL_SIZE); + + ret = volume_by_unique_sector(b2b); + if (ret != NULL) { + // Verify size, block size, and partition status match + if (block_io->Media->BlockSize == (uint32_t)ret->sector_size + && bdev_size == ret->sect_count * 512 + && block_io->Media->LogicalPartition == (ret->partition != 0)) { + return ret; + } + } + } + } + + return NULL; +} + +static void find_unique_sectors(void) { + if (unique_sectors_calculated) { + return; + } + unique_sectors_calculated = true; + + EFI_STATUS status; + + for (size_t i = 0; i < volume_index_i; i++) { + if ((volume_index[i]->first_sect * 512) % volume_index[i]->sector_size) { + continue; + } + + size_t first_sect = (volume_index[i]->first_sect * 512) / volume_index[i]->sector_size; + + // sect_count is always in 512-byte sectors + if (volume_index[i]->sect_count * 512 < UNIQUE_SECTOR_POOL_SIZE) { + continue; + } + + status = volume_index[i]->block_io->ReadBlocks( + volume_index[i]->block_io, + volume_index[i]->block_io->Media->MediaId, + first_sect, + UNIQUE_SECTOR_POOL_SIZE, + unique_sector_pool); + if (status != 0) { + continue; + } + + uint8_t b2b[BLAKE2B_OUT_BYTES]; + blake2b(b2b, unique_sector_pool, UNIQUE_SECTOR_POOL_SIZE); + + // Check for collision BEFORE storing hash (so we don't find ourselves) + // This searches all volumes including previously invalidated ones + struct volume *collision = volume_by_sector_hash(b2b); + + // Always store the hash so future volumes can detect collisions + memcpy(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES); + + if (collision == NULL) { + volume_index[i]->unique_sector_valid = true; + continue; + } + + // Collision found - invalidate both volumes + collision->unique_sector_valid = false; + volume_index[i]->unique_sector_valid = false; + } +} + +static void find_part_handles(EFI_HANDLE *handles, size_t handle_count) { + for (size_t i = 0; i < handle_count; i++) { + struct volume *vol = disk_volume_from_efi_handle(handles[i]); + if (vol == NULL) { + continue; + } + vol->efi_part_handle = handles[i]; + } +} + +void disk_create_index(void) { + EFI_STATUS status; + + unique_sector_pool = ext_mem_alloc(UNIQUE_SECTOR_POOL_SIZE); + + EFI_HANDLE tmp_handles[1]; + + EFI_GUID block_io_guid = BLOCK_IO_PROTOCOL; + EFI_HANDLE *handles = tmp_handles; + UINTN handles_size = sizeof(tmp_handles); + + status = gBS->LocateHandle(ByProtocol, &block_io_guid, NULL, &handles_size, handles); + + // we only care about the first handle, so ignore if we get EFI_BUFFER_TOO_SMALL + if (status != EFI_BUFFER_TOO_SMALL && status != EFI_SUCCESS) { + EFI_GUID pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID; + status = gBS->LocateHandle(ByProtocol, &pxe_guid, NULL, &handles_size, handles); + // likewise, all that matters is that the protocol is present + if (status == EFI_BUFFER_TOO_SMALL || status == EFI_SUCCESS) { + return; + } + + goto fail; + } + + handles = ext_mem_alloc(handles_size); + + status = gBS->LocateHandle(ByProtocol, &block_io_guid, NULL, &handles_size, handles); + + if (status != EFI_SUCCESS) { +fail: + panic(false, "LocateHandle for BLOCK_IO_PROTOCOL failed. Machine not supported by Limine UEFI."); + } + + int optical_indices = 1, hdd_indices = 1; + + size_t handle_count = handles_size / sizeof(EFI_HANDLE); + + for (size_t i = 0; i < handle_count; i++) { + EFI_BLOCK_IO *drive = NULL; + + if (is_efi_handle_to_skip(handles[i])) { + continue; + } + + status = gBS->HandleProtocol(handles[i], &block_io_guid, (void **)&drive); + + if (status != 0 || drive == NULL || drive->Media->LastBlock == 0) + continue; + + if (drive->Media->LogicalPartition) + continue; + + // Read test to ensure device is responsive (skipping this causes hangs on some systems) + status = drive->ReadBlocks(drive, drive->Media->MediaId, 0, 4096, unique_sector_pool); + if (status) { + continue; + } + + if (drive->Media->BlockSize == 0) { + continue; + } + if (drive->Media->LastBlock == UINT64_MAX) { + continue; + } + + struct volume *block = ext_mem_alloc(sizeof(struct volume)); + + bool is_optical = is_efi_handle_optical(handles[i]) || + (drive->Media->ReadOnly && drive->Media->BlockSize == 2048); + + if (is_optical) { + block->index = optical_indices++; + block->is_optical = true; + } else { + block->index = hdd_indices++; + } + + block->efi_handle = handles[i]; + block->block_io = drive; + block->partition = 0; + block->sector_size = drive->Media->BlockSize; + block->first_sect = 0; + // Normalize sect_count to 512-byte sectors for consistency with partitions + block->sect_count = (drive->Media->LastBlock + 1) * (drive->Media->BlockSize / 512); + block->max_partition = -1; + + if (drive->Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION3) { + block->fastest_xfer_size = drive->Media->OptimalTransferLengthGranularity; + } + + if (block->fastest_xfer_size == 0) { + block->fastest_xfer_size = DEFAULT_FASTEST_XFER_SIZE; + } else if (block->fastest_xfer_size >= MAX_FASTEST_XFER_SIZE) { + block->fastest_xfer_size = MAX_FASTEST_XFER_SIZE; + } + + if (gpt_get_guid(&block->guid, block)) { + block->guid_valid = true; + } + + volume_index = pmm_realloc( + volume_index, + volume_index_i * sizeof(void *), + (volume_index_i + 1) * sizeof(void *) + ); + volume_index[volume_index_i++] = block; + + for (int part = 0; ; part++) { + struct volume _p = {0}; + + int ret = part_get(&_p, block, part); + + if (ret == END_OF_TABLE || ret == INVALID_TABLE) + break; + if (ret == NO_PARTITION) + continue; + + struct volume *p = ext_mem_alloc(sizeof(struct volume)); + memcpy(p, &_p, sizeof(struct volume)); + + volume_index = pmm_realloc( + volume_index, + volume_index_i * sizeof(void *), + (volume_index_i + 1) * sizeof(void *) + ); + volume_index[volume_index_i++] = p; + + block->max_partition++; + } + } + + find_part_handles(handles, handle_count); + + pmm_free(handles, handles_size); +} + +#endif diff --git a/limine/common/drivers/edid.c b/limine/common/drivers/edid.c new file mode 100644 index 0000000..d14dd05 --- /dev/null +++ b/limine/common/drivers/edid.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (BIOS) + +#include + +struct edid_info_struct *get_edid_info(void) { + static struct edid_info_struct *buf = NULL; + + if (buf == NULL) + buf = conv_mem_alloc(sizeof(struct edid_info_struct)); + + struct rm_regs r = {0}; + + r.eax = 0x4f15; + r.ebx = 0x0001; + r.edi = (uint32_t)rm_off(buf); + r.ds = (uint32_t)rm_seg(buf); + r.es = r.ds; + rm_int(0x10, &r, &r); + + if ((r.eax & 0x00ff) != 0x4f) + goto fail; + if ((r.eax & 0xff00) != 0) + goto fail; + + for (size_t i = 0; i < sizeof(struct edid_info_struct); i++) + if (((uint8_t *)buf)[i] != 0) + goto success; + +fail: + printv("edid: Could not fetch EDID data.\n"); + return NULL; + +success: + printv("edid: Success.\n"); + return buf; +} + +#endif + +#if defined (UEFI) + +#include + +struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle) { + struct edid_info_struct *buf = ext_mem_alloc(sizeof(struct edid_info_struct)); + + EFI_STATUS status; + + EFI_EDID_ACTIVE_PROTOCOL *edid = NULL; + EFI_GUID edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID; + + status = gBS->HandleProtocol(gop_handle, &edid_guid, (void **)&edid); + + if (status) + goto fail; + + if (edid->SizeOfEdid < sizeof(struct edid_info_struct)) + goto fail; + + memcpy(buf, edid->Edid, sizeof(struct edid_info_struct)); + + for (size_t i = 0; i < sizeof(struct edid_info_struct); i++) + if (((uint8_t *)buf)[i] != 0) + goto success; + +fail: + pmm_free(buf, sizeof(struct edid_info_struct)); + printv("edid: Could not fetch EDID data.\n"); + return NULL; + +success: + printv("edid: Success.\n"); + return buf; +} + +#endif diff --git a/limine/common/drivers/edid.h b/limine/common/drivers/edid.h new file mode 100644 index 0000000..81599ea --- /dev/null +++ b/limine/common/drivers/edid.h @@ -0,0 +1,43 @@ +#ifndef DRIVERS__EDID_H__ +#define DRIVERS__EDID_H__ + +#include + +struct edid_info_struct { + uint8_t padding[8]; + uint16_t manufacturer_id_be; + uint16_t edid_id_code; + uint32_t serial_num; + uint8_t man_week; + uint8_t man_year; + uint8_t edid_version; + uint8_t edid_revision; + uint8_t video_input_type; + uint8_t max_hor_size; + uint8_t max_ver_size; + uint8_t gamma_factor; + uint8_t dpms_flags; + uint8_t chroma_info[10]; + uint8_t est_timings1; + uint8_t est_timings2; + uint8_t man_res_timing; + uint16_t std_timing_id[8]; + uint8_t det_timing_desc1[18]; + uint8_t det_timing_desc2[18]; + uint8_t det_timing_desc3[18]; + uint8_t det_timing_desc4[18]; + uint8_t unused; + uint8_t checksum; +} __attribute__((packed)); + +#if defined (UEFI) +#include + +struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle); +#endif + +#if defined (BIOS) +struct edid_info_struct *get_edid_info(void); +#endif + +#endif diff --git a/limine/common/drivers/gop.c b/limine/common/drivers/gop.c new file mode 100644 index 0000000..59fa374 --- /dev/null +++ b/limine/common/drivers/gop.c @@ -0,0 +1,380 @@ +#if defined (UEFI) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint16_t linear_masks_to_bpp(uint32_t red_mask, uint32_t green_mask, + uint32_t blue_mask, uint32_t alpha_mask) { + uint32_t compound_mask = red_mask | green_mask | blue_mask | alpha_mask; + uint16_t ret = 32; + while ((compound_mask & (1 << 31)) == 0) { + ret--; + compound_mask <<= 1; + } + return ret; +} + +static void linear_mask_to_mask_shift( + uint8_t *mask, uint8_t *shift, uint32_t linear_mask) { + *shift = 0; + *mask = 0; + if (linear_mask == 0) { + return; + } + while ((linear_mask & 1) == 0) { + (*shift)++; + linear_mask >>= 1; + } + while ((linear_mask & 1) == 1) { + (*mask)++; + linear_mask >>= 1; + } +} + +static bool validate_pitch(struct fb_info *ret, size_t mode) { + uint64_t bytes_per_pixel = ret->framebuffer_bpp / 8; + if (bytes_per_pixel == 0 + || ret->framebuffer_pitch % bytes_per_pixel != 0 + || ret->framebuffer_pitch < ret->framebuffer_width * bytes_per_pixel) { + printv("gop: Mode %u has invalid pitch %u (width=%u, bpp=%u), skipping.\n", + (uint32_t)mode, (uint32_t)ret->framebuffer_pitch, + (uint32_t)ret->framebuffer_width, (uint32_t)ret->framebuffer_bpp); + return false; + } + return true; +} + +// Most of this code taken from https://wiki.osdev.org/GOP + +static bool mode_to_fb_info(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, size_t mode) { + EFI_STATUS status; + + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info; + UINTN mode_info_size; + + status = gop->QueryMode(gop, mode, &mode_info_size, &mode_info); + + if (status) { + return false; + } + + switch (mode_info->PixelFormat) { + case PixelBlueGreenRedReserved8BitPerColor: + ret->framebuffer_bpp = 32; + ret->red_mask_size = 8; + ret->red_mask_shift = 16; + ret->green_mask_size = 8; + ret->green_mask_shift = 8; + ret->blue_mask_size = 8; + ret->blue_mask_shift = 0; + break; + case PixelRedGreenBlueReserved8BitPerColor: + ret->framebuffer_bpp = 32; + ret->red_mask_size = 8; + ret->red_mask_shift = 0; + ret->green_mask_size = 8; + ret->green_mask_shift = 8; + ret->blue_mask_size = 8; + ret->blue_mask_shift = 16; + break; + case PixelBitMask: + if ((mode_info->PixelInformation.RedMask + | mode_info->PixelInformation.GreenMask + | mode_info->PixelInformation.BlueMask + | mode_info->PixelInformation.ReservedMask) == 0) { + return false; + } + ret->framebuffer_bpp = linear_masks_to_bpp( + mode_info->PixelInformation.RedMask, + mode_info->PixelInformation.GreenMask, + mode_info->PixelInformation.BlueMask, + mode_info->PixelInformation.ReservedMask); + linear_mask_to_mask_shift(&ret->red_mask_size, + &ret->red_mask_shift, + mode_info->PixelInformation.RedMask); + linear_mask_to_mask_shift(&ret->green_mask_size, + &ret->green_mask_shift, + mode_info->PixelInformation.GreenMask); + linear_mask_to_mask_shift(&ret->blue_mask_size, + &ret->blue_mask_shift, + mode_info->PixelInformation.BlueMask); + break; + default: + return false; + } + + ret->memory_model = 0x06; + ret->framebuffer_pitch = mode_info->PixelsPerScanLine * (ret->framebuffer_bpp / 8); + ret->framebuffer_width = mode_info->HorizontalResolution; + ret->framebuffer_height = mode_info->VerticalResolution; + + if (!validate_pitch(ret, mode)) { + return false; + } + + return true; +} + +bool gop_force_16 = false; + +static bool try_mode(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, + size_t mode, uint64_t width, uint64_t height, int bpp, + struct fb_info *fbs, size_t fbs_count, + bool *setmode_called) { + EFI_STATUS status; + + if (!mode_to_fb_info(ret, gop, mode)) { + return false; + } + + if (width != 0 && height != 0 && bpp != 0) { + if (ret->framebuffer_width != width + || ret->framebuffer_height != height + || ret->framebuffer_bpp != bpp) { + return false; + } + } + + if (gop_force_16) { + if (ret->framebuffer_width >= 65536 + || ret->framebuffer_height >= 65536 + || ret->framebuffer_pitch >= 65536) { + return false; + } + } + + for (size_t i = 0; i < fbs_count; i++) { + if (gop->Mode->FrameBufferBase == fbs[i].framebuffer_addr) { + return false; + } + } + + printv("gop: Found matching mode %X, attempting to set...\n", (uint64_t)mode); + + if (mode == gop->Mode->Mode && *setmode_called) { + printv("gop: Mode was already set, perfect!\n"); + } else { + status = gop->SetMode(gop, mode); + + if (status) { + printv("gop: Failed to set video mode %X, moving on...\n", (uint64_t)mode); + return false; + } + + *setmode_called = true; + } + + // Recalculate pitch from gop->Mode->Info, as some firmware (e.g. Apple + // Macs) report incorrect PixelsPerScanLine via QueryMode. + ret->framebuffer_pitch = gop->Mode->Info->PixelsPerScanLine * (ret->framebuffer_bpp / 8); + + if (!validate_pitch(ret, mode)) { + return false; + } + + ret->framebuffer_addr = gop->Mode->FrameBufferBase; + + fb_clear(ret); + + return true; +} + +static struct fb_info *get_mode_list(size_t *count, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop) { + UINTN modes_count = gop->Mode->MaxMode; + + struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info)); + + size_t actual_count = 0; + for (size_t i = 0; i < modes_count; i++) { + if (mode_to_fb_info(&ret[actual_count], gop, i)) { + actual_count++; + } + } + + struct fb_info *tmp = ext_mem_alloc(actual_count * sizeof(struct fb_info)); + memcpy(tmp, ret, actual_count * sizeof(struct fb_info)); + + pmm_free(ret, modes_count * sizeof(struct fb_info)); + ret = tmp; + + *count = actual_count; + return ret; +} + +#define MAX_PRESET_MODES 128 +no_unwind static int preset_modes[MAX_PRESET_MODES]; +no_unwind static bool setmode_called[MAX_PRESET_MODES]; +no_unwind static bool preset_modes_initialised = false; + +void init_gop(struct fb_info **ret, size_t *_fbs_count, + uint64_t target_width, uint64_t target_height, uint16_t target_bpp) { + if (preset_modes_initialised == false) { + for (size_t i = 0; i < MAX_PRESET_MODES; i++) { + preset_modes[i] = -1; + setmode_called[i] = false; + } + preset_modes_initialised = true; + } + + EFI_STATUS status; + + EFI_HANDLE tmp_handles[1]; + + EFI_HANDLE *handles = tmp_handles; + UINTN handles_size = sizeof(EFI_HANDLE); + EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + + status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles); + + if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) { + *_fbs_count = 0; + return; + } + + handles = ext_mem_alloc(handles_size); + + status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles); + if (status != EFI_SUCCESS) { + pmm_free(handles, handles_size); + *_fbs_count = 0; + return; + } + + size_t handles_count = handles_size / sizeof(EFI_HANDLE); + + *ret = ext_mem_alloc(handles_count * sizeof(struct fb_info)); + + const struct resolution fallback_resolutions[] = { + { 0, 0, 0 }, // Overridden by EDID + { 0, 0, 0 }, // Overridden by preset + { 1024, 768, 32 }, + { 800, 600, 32 }, + { 640, 480, 32 }, + { 1024, 768, 24 }, + { 800, 600, 24 }, + { 640, 480, 24 }, + { 1024, 768, 16 }, + { 800, 600, 16 }, + { 640, 480, 16 } + }; + + size_t fbs_count = 0; + for (size_t i = 0; i < handles_count && i < MAX_PRESET_MODES; i++) { + struct fb_info *fb = &(*ret)[fbs_count]; + + uint64_t _target_width = target_width; + uint64_t _target_height = target_height; + uint64_t _target_bpp = target_bpp; + + EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; + + status = gBS->HandleProtocol(handles[i], &gop_guid, (void **)&gop); + if (status != EFI_SUCCESS) { + continue; + } + + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info; + UINTN mode_info_size; + + status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode, + &mode_info_size, &mode_info); + + if (status == EFI_NOT_STARTED) { + status = gop->SetMode(gop, 0); + if (status) { + continue; + } + setmode_called[i] = true; + status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode, + &mode_info_size, &mode_info); + } + + if (status) { + continue; + } + + if (preset_modes[i] == -1) { + preset_modes[i] = gop->Mode->Mode; + } + + fb->edid = get_edid_info(handles[i]); + + UINTN modes_count = gop->Mode->MaxMode; + + size_t current_fallback = 0; + + if (!_target_width || !_target_height || !_target_bpp) { + goto fallback; + } else { + printv("gop: Requested resolution of %ux%ux%u\n", + _target_width, _target_height, _target_bpp); + } + +retry: + for (size_t j = 0; j < modes_count; j++) { + if (try_mode(fb, gop, j, _target_width, _target_height, _target_bpp, *ret, fbs_count, &setmode_called[i])) { + goto success; + } + } + +fallback: + if (current_fallback == 0) { + current_fallback++; + + if (fb->edid != NULL) { + uint64_t edid_width = (uint64_t)fb->edid->det_timing_desc1[2]; + edid_width += ((uint64_t)fb->edid->det_timing_desc1[4] & 0xf0) << 4; + uint64_t edid_height = (uint64_t)fb->edid->det_timing_desc1[5]; + edid_height += ((uint64_t)fb->edid->det_timing_desc1[7] & 0xf0) << 4; + if (edid_width >= mode_info->HorizontalResolution + && edid_height >= mode_info->VerticalResolution) { + _target_width = edid_width; + _target_height = edid_height; + _target_bpp = 32; + goto retry; + } + } + } + + if (current_fallback == 1) { + current_fallback++; + + if (try_mode(fb, gop, preset_modes[i], 0, 0, 0, *ret, fbs_count, &setmode_called[i])) { + goto success; + } + } + + if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) { + _target_width = fallback_resolutions[current_fallback].width; + _target_height = fallback_resolutions[current_fallback].height; + _target_bpp = fallback_resolutions[current_fallback].bpp; + + current_fallback++; + goto retry; + } + + continue; + +success:; + size_t mode_count; + fb->mode_list = get_mode_list(&mode_count, gop); + fb->mode_count = mode_count; + + fbs_count++; + } + + pmm_free(handles, handles_size); + + gop_force_16 = false; + + *_fbs_count = fbs_count; +} + +#endif diff --git a/limine/common/drivers/gop.h b/limine/common/drivers/gop.h new file mode 100644 index 0000000..24a463a --- /dev/null +++ b/limine/common/drivers/gop.h @@ -0,0 +1,18 @@ +#ifndef DRIVERS__GOP_H__ +#define DRIVERS__GOP_H__ + +#if defined (UEFI) + +#include +#include +#include +#include + +void init_gop(struct fb_info **ret, size_t *_fbs_count, + uint64_t target_width, uint64_t target_height, uint16_t target_bpp); + +extern bool gop_force_16; + +#endif + +#endif diff --git a/limine/common/drivers/serial.c b/limine/common/drivers/serial.c new file mode 100644 index 0000000..eae75d9 --- /dev/null +++ b/limine/common/drivers/serial.c @@ -0,0 +1,50 @@ +#if defined (BIOS) + +#include +#include +#include +#include +#include + +static bool serial_initialised = false; +uint32_t serial_baudrate; + +static void serial_initialise(void) { + if (serial_initialised || !serial) { + return; + } + + // Init com1 + outb(0x3f8 + 3, 0x00); + outb(0x3f8 + 1, 0x00); + outb(0x3f8 + 3, 0x80); + + uint16_t divisor = (uint16_t)(115200 / serial_baudrate); + outb(0x3f8 + 0, divisor & 0xff); + outb(0x3f8 + 1, (divisor >> 8) & 0xff); + + outb(0x3f8 + 1, 0x00); + outb(0x3f8 + 3, 0x03); + outb(0x3f8 + 2, 0xc7); + outb(0x3f8 + 4, 0x0b); + + serial_initialised = true; +} + +void serial_out(uint8_t b) { + serial_initialise(); + + while ((inb(0x3f8 + 5) & 0x20) == 0); + outb(0x3f8, b); +} + +int serial_in(void) { + serial_initialise(); + + if ((inb(0x3f8 + 5) & 0x01) == 0) { + return -1; + } + return inb(0x3f8); +} + +#endif diff --git a/limine/common/drivers/serial.h b/limine/common/drivers/serial.h new file mode 100644 index 0000000..139e4c2 --- /dev/null +++ b/limine/common/drivers/serial.h @@ -0,0 +1,15 @@ +#ifndef DRIVERS__SERIAL_H__ +#define DRIVERS__SERIAL_H__ + +#if defined (BIOS) + +#include + +extern uint32_t serial_baudrate; + +void serial_out(uint8_t b); +int serial_in(void); + +#endif + +#endif diff --git a/limine/common/drivers/vbe.c b/limine/common/drivers/vbe.c new file mode 100644 index 0000000..735d16b --- /dev/null +++ b/limine/common/drivers/vbe.c @@ -0,0 +1,364 @@ +#if defined (BIOS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct vbe_info_struct { + char signature[4]; + uint8_t version_min; + uint8_t version_maj; + uint16_t oem_off; + uint16_t oem_seg; + uint32_t capabilities; + uint16_t vid_modes_off; + uint16_t vid_modes_seg; + uint16_t vid_mem_blocks; + uint16_t software_rev; + uint16_t vendor_off; + uint16_t vendor_seg; + uint16_t prod_name_off; + uint16_t prod_name_seg; + uint16_t prod_rev_off; + uint16_t prod_rev_seg; + uint8_t reserved[222]; + uint8_t oem_data[256]; +} __attribute__((packed)); + +struct vbe_mode_info_struct { + uint16_t mode_attributes; + uint8_t wina_attributes; + uint8_t winb_attributes; + uint16_t win_granularity; + uint16_t win_size; + uint16_t wina_segment; + uint16_t winb_segment; + uint32_t win_farptr; + uint16_t bytes_per_scanline; + + uint16_t res_x; + uint16_t res_y; + uint8_t charsize_x; + uint8_t charsize_y; + uint8_t plane_count; + uint8_t bpp; + uint8_t bank_count; + uint8_t memory_model; + uint8_t bank_size; + uint8_t image_count; + uint8_t reserved0; + + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; + uint8_t rsvd_mask_size; + uint8_t rsvd_mask_shift; + uint8_t direct_color_info; + + uint32_t framebuffer_addr; + uint8_t reserved1[6]; + + uint16_t lin_bytes_per_scanline; + uint8_t banked_image_count; + uint8_t lin_image_count; + uint8_t lin_red_mask_size; + uint8_t lin_red_mask_shift; + uint8_t lin_green_mask_size; + uint8_t lin_green_mask_shift; + uint8_t lin_blue_mask_size; + uint8_t lin_blue_mask_shift; + uint8_t lin_rsvd_mask_size; + uint8_t lin_rsvd_mask_shift; + uint32_t max_pixel_clock; + + uint8_t reserved2[190]; +} __attribute__((packed)); + +static bool get_vbe_info(struct vbe_info_struct *buf) { + struct rm_regs r = {0}; + + r.eax = 0x4f00; + r.edi = (uint32_t)buf; + rm_int(0x10, &r, &r); + + if ((r.eax & 0xff00) >> 8 != 0 + || (r.eax & 0x00ff) != 0x4f) { + return false; + } + + return true; +} + +static bool get_vbe_mode_info(struct vbe_mode_info_struct *buf, + uint16_t mode) { + struct rm_regs r = {0}; + + r.eax = 0x4f01; + r.ecx = (uint32_t)mode; + r.edi = (uint32_t)buf; + rm_int(0x10, &r, &r); + + if ((r.eax & 0xff00) >> 8 != 0 + || (r.eax & 0x00ff) != 0x4f) { + return false; + } + + return true; +} + +static bool set_vbe_mode(uint16_t mode) { + struct rm_regs r = {0}; + + r.eax = 0x4f02; + r.ebx = (uint32_t)mode | (1 << 14); + rm_int(0x10, &r, &r); + + if ((r.eax & 0xff00) >> 8 != 0 + || (r.eax & 0x00ff) != 0x4f) { + return false; + } + + return true; +} + +// Maximum number of video modes to enumerate to prevent infinite loops +// from corrupted VBE mode lists without a proper 0xffff terminator +#define VBE_MAX_MODES 512 + +struct fb_info *vbe_get_mode_list(size_t *count) { + struct vbe_info_struct vbe_info; + if (!get_vbe_info(&vbe_info)) { + return NULL; + } + + uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg, + vbe_info.vid_modes_off); + + size_t modes_count = 0; + for (size_t i = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) { + struct vbe_mode_info_struct vbe_mode_info; + if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) { + continue; + } + + // We only support RGB for now + if (vbe_mode_info.memory_model != 0x06) + continue; + // We only support linear modes + if (!(vbe_mode_info.mode_attributes & (1 << 7))) + continue; + + uint16_t pitch = (vbe_info.version_maj < 3) + ? vbe_mode_info.bytes_per_scanline + : vbe_mode_info.lin_bytes_per_scanline; + uint16_t bytes_per_pixel = vbe_mode_info.bpp / 8; + if (bytes_per_pixel == 0 + || pitch % bytes_per_pixel != 0 + || pitch < (uint32_t)vbe_mode_info.res_x * bytes_per_pixel) + continue; + + modes_count++; + } + + struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info)); + + for (size_t i = 0, j = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) { + struct vbe_mode_info_struct vbe_mode_info; + if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) { + continue; + } + + // We only support RGB for now + if (vbe_mode_info.memory_model != 0x06) + continue; + // We only support linear modes + if (!(vbe_mode_info.mode_attributes & (1 << 7))) + continue; + + uint16_t pitch = (vbe_info.version_maj < 3) + ? vbe_mode_info.bytes_per_scanline + : vbe_mode_info.lin_bytes_per_scanline; + uint16_t bytes_per_pixel = vbe_mode_info.bpp / 8; + if (bytes_per_pixel == 0 + || pitch % bytes_per_pixel != 0 + || pitch < (uint32_t)vbe_mode_info.res_x * bytes_per_pixel) + continue; + + ret[j].memory_model = vbe_mode_info.memory_model; + + ret[j].framebuffer_width = vbe_mode_info.res_x; + ret[j].framebuffer_height = vbe_mode_info.res_y; + ret[j].framebuffer_bpp = vbe_mode_info.bpp; + + if (vbe_info.version_maj < 3) { + ret[j].framebuffer_pitch = vbe_mode_info.bytes_per_scanline; + ret[j].red_mask_size = vbe_mode_info.red_mask_size; + ret[j].red_mask_shift = vbe_mode_info.red_mask_shift; + ret[j].green_mask_size = vbe_mode_info.green_mask_size; + ret[j].green_mask_shift = vbe_mode_info.green_mask_shift; + ret[j].blue_mask_size = vbe_mode_info.blue_mask_size; + ret[j].blue_mask_shift = vbe_mode_info.blue_mask_shift; + } else { + ret[j].framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline; + ret[j].red_mask_size = vbe_mode_info.lin_red_mask_size; + ret[j].red_mask_shift = vbe_mode_info.lin_red_mask_shift; + ret[j].green_mask_size = vbe_mode_info.lin_green_mask_size; + ret[j].green_mask_shift = vbe_mode_info.lin_green_mask_shift; + ret[j].blue_mask_size = vbe_mode_info.lin_blue_mask_size; + ret[j].blue_mask_shift = vbe_mode_info.lin_blue_mask_shift; + } + + j++; + } + + *count = modes_count; + + return ret; +} + +bool init_vbe(struct fb_info *ret, + uint16_t target_width, uint16_t target_height, uint16_t target_bpp) { + printv("vbe: Initialising...\n"); + + size_t current_fallback = 0; + + struct vbe_info_struct vbe_info; + if (!get_vbe_info(&vbe_info)) { + return false; + } + + printv("vbe: Version: %u.%u\n", vbe_info.version_maj, vbe_info.version_min); + printv("vbe: OEM: %s\n", (char *)rm_desegment(vbe_info.oem_seg, vbe_info.oem_off)); + printv("vbe: Graphics vendor: %s\n", (char *)rm_desegment(vbe_info.vendor_seg, vbe_info.vendor_off)); + printv("vbe: Product name: %s\n", (char *)rm_desegment(vbe_info.prod_name_seg, vbe_info.prod_name_off)); + printv("vbe: Product revision: %s\n", (char *)rm_desegment(vbe_info.prod_rev_seg, vbe_info.prod_rev_off)); + + uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg, + vbe_info.vid_modes_off); + + struct resolution fallback_resolutions[] = { + { 1024, 768, 32 }, + { 800, 600, 32 }, + { 640, 480, 32 }, + { 1024, 768, 24 }, + { 800, 600, 24 }, + { 640, 480, 24 }, + { 1024, 768, 16 }, + { 800, 600, 16 }, + { 640, 480, 16 } + }; + + if (!target_width || !target_height || !target_bpp) { + struct edid_info_struct *edid_info = get_edid_info(); + if (edid_info != NULL) { + int edid_width = (int)edid_info->det_timing_desc1[2]; + edid_width += ((int)edid_info->det_timing_desc1[4] & 0xf0) << 4; + int edid_height = (int)edid_info->det_timing_desc1[5]; + edid_height += ((int)edid_info->det_timing_desc1[7] & 0xf0) << 4; + if (edid_width && edid_height) { + target_width = edid_width; + target_height = edid_height; + target_bpp = 32; + printv("vbe: EDID detected screen resolution of %ux%u\n", + target_width, target_height); + goto retry; + } + } + goto fallback; + } else { + printv("vbe: Requested resolution of %ux%ux%u\n", + target_width, target_height, target_bpp); + } + +retry: + for (size_t i = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) { + struct vbe_mode_info_struct vbe_mode_info; + if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) { + continue; + } + if (vbe_mode_info.res_x == target_width + && vbe_mode_info.res_y == target_height + && vbe_mode_info.bpp == target_bpp) { + // We only support RGB for now + if (vbe_mode_info.memory_model != 0x06) + continue; + // We only support linear modes + if (!(vbe_mode_info.mode_attributes & (1 << 7))) + continue; + printv("vbe: Found matching mode %x, attempting to set...\n", vid_modes[i]); + if (vid_modes[i] == current_video_mode) { + printv("vbe: Mode was already set, perfect!\n"); + } else if (!set_vbe_mode(vid_modes[i])) { + current_video_mode = -1; + printv("vbe: Failed to set video mode %x, moving on...\n", vid_modes[i]); + continue; + } + current_video_mode = vid_modes[i]; + + printv("vbe: Framebuffer address: %x\n", vbe_mode_info.framebuffer_addr); + ret->memory_model = vbe_mode_info.memory_model; + ret->framebuffer_addr = vbe_mode_info.framebuffer_addr; + ret->framebuffer_width = vbe_mode_info.res_x; + ret->framebuffer_height = vbe_mode_info.res_y; + ret->framebuffer_bpp = vbe_mode_info.bpp; + if (vbe_info.version_maj < 3) { + ret->framebuffer_pitch = vbe_mode_info.bytes_per_scanline; + ret->red_mask_size = vbe_mode_info.red_mask_size; + ret->red_mask_shift = vbe_mode_info.red_mask_shift; + ret->green_mask_size = vbe_mode_info.green_mask_size; + ret->green_mask_shift = vbe_mode_info.green_mask_shift; + ret->blue_mask_size = vbe_mode_info.blue_mask_size; + ret->blue_mask_shift = vbe_mode_info.blue_mask_shift; + } else { + ret->framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline; + ret->red_mask_size = vbe_mode_info.lin_red_mask_size; + ret->red_mask_shift = vbe_mode_info.lin_red_mask_shift; + ret->green_mask_size = vbe_mode_info.lin_green_mask_size; + ret->green_mask_shift = vbe_mode_info.lin_green_mask_shift; + ret->blue_mask_size = vbe_mode_info.lin_blue_mask_size; + ret->blue_mask_shift = vbe_mode_info.lin_blue_mask_shift; + } + + uint16_t bytes_per_pixel = ret->framebuffer_bpp / 8; + if (bytes_per_pixel == 0 + || ret->framebuffer_pitch % bytes_per_pixel != 0 + || ret->framebuffer_pitch < (uint32_t)ret->framebuffer_width * bytes_per_pixel) { + printv("vbe: Mode %x has invalid pitch %u (width=%u, bpp=%u), skipping.\n", + vid_modes[i], (uint32_t)ret->framebuffer_pitch, + (uint32_t)ret->framebuffer_width, (uint32_t)ret->framebuffer_bpp); + continue; + } + + fb_clear(ret); + + return true; + } + } + +fallback: + if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) { + target_width = fallback_resolutions[current_fallback].width; + target_height = fallback_resolutions[current_fallback].height; + target_bpp = fallback_resolutions[current_fallback].bpp; + current_fallback++; + goto retry; + } + + return false; +} + +#endif diff --git a/limine/common/drivers/vbe.h b/limine/common/drivers/vbe.h new file mode 100644 index 0000000..f5da1f8 --- /dev/null +++ b/limine/common/drivers/vbe.h @@ -0,0 +1,14 @@ +#ifndef DRIVERS__VBE_H__ +#define DRIVERS__VBE_H__ + +#include +#include +#include +#include + +bool init_vbe(struct fb_info *ret, + uint16_t target_width, uint16_t target_height, uint16_t target_bpp); + +struct fb_info *vbe_get_mode_list(size_t *count); + +#endif diff --git a/limine/common/drivers/vga_textmode.c b/limine/common/drivers/vga_textmode.c new file mode 100644 index 0000000..fefb982 --- /dev/null +++ b/limine/common/drivers/vga_textmode.c @@ -0,0 +1,369 @@ +#if defined (BIOS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1) + +static void draw_cursor(struct textmode_context *ctx) { + uint8_t pal = ctx->back_buffer[ctx->cursor_offset + 1]; + ctx->video_mem[ctx->cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4); +} + +static void text_save_state(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->saved_state_text_palette = ctx->text_palette; + ctx->saved_state_cursor_offset = ctx->cursor_offset; +} + +static void text_restore_state(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = ctx->saved_state_text_palette; + ctx->cursor_offset = ctx->saved_state_cursor_offset; +} + +static void text_swap_palette(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette << 4) | (ctx->text_palette >> 4); +} + +static void text_scroll(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + + // move the text up by one row + for (size_t i = _ctx->scroll_top_margin * VD_COLS; + i < (_ctx->scroll_bottom_margin - 1) * VD_COLS; i++) { + ctx->back_buffer[i] = ctx->back_buffer[i + VD_COLS]; + } + // clear the last line of the screen + for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS; + i < _ctx->scroll_bottom_margin * VD_COLS; i += 2) { + ctx->back_buffer[i] = ' '; + ctx->back_buffer[i + 1] = ctx->text_palette; + } +} + +static void text_revscroll(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + + // move the text up by one row + for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) { + ctx->back_buffer[i + VD_COLS] = ctx->back_buffer[i]; + if (i == _ctx->scroll_top_margin * VD_COLS) { + break; + } + } + // clear the first line of the screen + for (size_t i = _ctx->scroll_top_margin * VD_COLS; + i < (_ctx->scroll_top_margin + 1) * VD_COLS; i += 2) { + ctx->back_buffer[i] = ' '; + ctx->back_buffer[i + 1] = ctx->text_palette; + } +} + +static void text_clear(struct flanterm_context *_ctx, bool move) { + struct textmode_context *ctx = (void *)_ctx; + + for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) { + ctx->back_buffer[i] = ' '; + ctx->back_buffer[i + 1] = ctx->text_palette; + } + if (move) { + ctx->cursor_offset = 0; + } +} + +static void text_full_refresh(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + + for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) { + ctx->video_mem[i] = ctx->front_buffer[i]; + ctx->back_buffer[i] = ctx->front_buffer[i]; + } + + if (_ctx->cursor_enabled) { + draw_cursor(ctx); + ctx->old_cursor_offset = ctx->cursor_offset; + } +} + +static void text_double_buffer_flush(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + + if (_ctx->cursor_enabled) { + draw_cursor(ctx); + } + + if (ctx->cursor_offset != ctx->old_cursor_offset || _ctx->cursor_enabled == false) { + ctx->video_mem[ctx->old_cursor_offset + 1] = ctx->back_buffer[ctx->old_cursor_offset + 1]; + } + + for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) { + if (ctx->back_buffer[i] == ctx->front_buffer[i]) { + continue; + } + + ctx->front_buffer[i] = ctx->back_buffer[i]; + + if (_ctx->cursor_enabled && i == ctx->cursor_offset + 1) { + continue; + } + + ctx->video_mem[i] = ctx->back_buffer[i]; + } + + if (_ctx->cursor_enabled) { + ctx->old_cursor_offset = ctx->cursor_offset; + } +} + +static void text_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) { + struct textmode_context *ctx = (void *)_ctx; + + *x = (ctx->cursor_offset % VD_COLS) / 2; + *y = ctx->cursor_offset / VD_COLS; +} + +static void text_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) { + struct textmode_context *ctx = (void *)_ctx; + + if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS + || new_x >= VD_COLS / 2 || new_y >= VD_ROWS) { + return; + } + + ctx->back_buffer[new_y * VD_COLS + new_x * 2] = ctx->back_buffer[old_y * VD_COLS + old_x * 2]; + ctx->back_buffer[new_y * VD_COLS + new_x * 2 + 1] = ctx->back_buffer[old_y * VD_COLS + old_x * 2 + 1]; +} + +static void text_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) { + struct textmode_context *ctx = (void *)_ctx; + + if (x >= VD_COLS / 2) { + if ((int)x < 0) { + x = 0; + } else { + x = VD_COLS / 2 - 1; + } + } + if (y >= VD_ROWS) { + if ((int)y < 0) { + y = 0; + } else { + y = VD_ROWS - 1; + } + } + ctx->cursor_offset = y * VD_COLS + x * 2; +} + +static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + +static void text_set_text_fg(struct flanterm_context *_ctx, size_t fg) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0xf0) | ansi_colours[fg]; +} + +static void text_set_text_bg(struct flanterm_context *_ctx, size_t bg) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0x0f) | (ansi_colours[bg] << 4); +} + +static void text_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3)); +} + +static void text_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4); +} + +static void text_set_text_fg_rgb(struct flanterm_context *ctx, uint32_t n) { + (void)ctx; + (void)n; +} + +static void text_set_text_bg_rgb(struct flanterm_context *ctx, uint32_t n) { + (void)ctx; + (void)n; +} + +static void text_set_text_fg_default(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0xf0) | 7; +} + +static void text_set_text_bg_default(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette &= 0x0f; +} + +static void text_set_text_fg_default_bright(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0xf0) | (7 | (1 << 3)); +} + +static void text_set_text_bg_default_bright(struct flanterm_context *_ctx) { + struct textmode_context *ctx = (void *)_ctx; + ctx->text_palette = (ctx->text_palette & 0x0f) | ((1 << 3) << 4); +} + +static void text_putchar(struct flanterm_context *_ctx, uint8_t c) { + struct textmode_context *ctx = (void *)_ctx; + + ctx->back_buffer[ctx->cursor_offset] = c; + ctx->back_buffer[ctx->cursor_offset + 1] = ctx->text_palette; + if (ctx->cursor_offset / VD_COLS == _ctx->scroll_bottom_margin - 1 + && ctx->cursor_offset % VD_COLS == VD_COLS - 2) { + if (_ctx->scroll_enabled) { + text_scroll(_ctx); + ctx->cursor_offset -= ctx->cursor_offset % VD_COLS; + } + } else if (ctx->cursor_offset >= (VIDEO_BOTTOM - 1)) { + ctx->cursor_offset -= ctx->cursor_offset % VD_COLS; + } else { + ctx->cursor_offset += 2; + } +} + +static void text_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) { + struct textmode_context *ctx = (void *)_ctx; + + if (ctx->back_buffer != NULL) { + _free(ctx->back_buffer, VD_ROWS * VD_COLS); + ctx->back_buffer = NULL; + } + + if (ctx->front_buffer != NULL) { + _free(ctx->front_buffer, VD_ROWS * VD_COLS); + ctx->front_buffer = NULL; + } + + pmm_free(ctx, sizeof(struct textmode_context)); +} + +void vga_textmode_init(bool managed) { + term_notready(); + + if (quiet) { + return; + } + + if (current_video_mode != 0x3) { + struct rm_regs r = {0}; + r.eax = 0x0003; + rm_int(0x10, &r, &r); + + current_video_mode = 0x3; + } + + terms = ext_mem_alloc(sizeof(void *)); + terms_i = 1; + + terms[0] = ext_mem_alloc(sizeof(struct textmode_context)); + + struct flanterm_context *term = terms[0]; + struct textmode_context *ctx = (void *)term; + + if (ctx->back_buffer == NULL) { + ctx->back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS); + } else { + memset(ctx->back_buffer, 0, VD_ROWS * VD_COLS); + } + if (ctx->front_buffer == NULL) { + ctx->front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS); + } else { + memset(ctx->front_buffer, 0, VD_ROWS * VD_COLS); + } + + ctx->cursor_offset = 0; + ctx->text_palette = 0x07; + + ctx->video_mem = (volatile uint8_t *)0xb8000; + + text_clear(term, false); + + // VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor + + if (!managed) { + term->cursor_enabled = false; + + outb(0x3d4, 0x0a); + outb(0x3d5, (inb(0x3d5) & 0xc0) | 14); + outb(0x3d4, 0x0b); + outb(0x3d5, (inb(0x3d5) & 0xe0) | 15); + outb(0x3d4, 0x0f); + outb(0x3d5, 0); + outb(0x3d4, 0x0e); + outb(0x3d5, 0); + + struct rm_regs r = {0}; + r.eax = 0x0200; + rm_int(0x10, &r, &r); + } else { + outb(0x3d4, 0x0a); + outb(0x3d5, 0x20); + } + + text_double_buffer_flush(term); + + if (managed && serial) { + term->cols = 80; + term->rows = 24; + } else { + term->cols = 80; + term->rows = 25; + } + + term->raw_putchar = text_putchar; + term->clear = text_clear; + term->set_cursor_pos = text_set_cursor_pos; + term->get_cursor_pos = text_get_cursor_pos; + term->set_text_fg = text_set_text_fg; + term->set_text_bg = text_set_text_bg; + term->set_text_fg_bright = text_set_text_fg_bright; + term->set_text_bg_bright = text_set_text_bg_bright; + term->set_text_fg_rgb = text_set_text_fg_rgb; + term->set_text_bg_rgb = text_set_text_bg_rgb; + term->set_text_fg_default = text_set_text_fg_default; + term->set_text_bg_default = text_set_text_bg_default; + term->set_text_fg_default_bright = text_set_text_fg_default_bright; + term->set_text_bg_default_bright = text_set_text_bg_default_bright; + term->move_character = text_move_character; + term->scroll = text_scroll; + term->revscroll = text_revscroll; + term->swap_palette = text_swap_palette; + term->save_state = text_save_state; + term->restore_state = text_restore_state; + term->double_buffer_flush = text_double_buffer_flush; + term->full_refresh = text_full_refresh; + term->deinit = text_deinit; + + flanterm_context_reinit(term); + + if (!managed) { + term->cursor_enabled = false; + } + + term->full_refresh(term); + + if (!managed) { + term->deinit(term, pmm_free_size_t); + pmm_free(terms, sizeof(void *)); + terms_i = 0; + terms = NULL; + term_backend = _NOT_READY; + } else { + term_backend = TEXTMODE; + } +} + +#endif diff --git a/limine/common/drivers/vga_textmode.h b/limine/common/drivers/vga_textmode.h new file mode 100644 index 0000000..c7bca53 --- /dev/null +++ b/limine/common/drivers/vga_textmode.h @@ -0,0 +1,35 @@ +#ifndef DRIVERS__VGA_TEXTMODE_H__ +#define DRIVERS__VGA_TEXTMODE_H__ + +#if defined (BIOS) + +#include +#include +#include +#include + +#define VD_COLS (80 * 2) +#define VD_ROWS 25 + +struct textmode_context { + struct flanterm_context term; + + volatile uint8_t *video_mem; + + uint8_t *back_buffer; + uint8_t *front_buffer; + + size_t cursor_offset; + size_t old_cursor_offset; + bool cursor_status; + uint8_t text_palette; + + uint8_t saved_state_text_palette; + size_t saved_state_cursor_offset; +}; + +void vga_textmode_init(bool managed); + +#endif + +#endif diff --git a/limine/common/efi_thunk.asm_uefi_aarch64 b/limine/common/efi_thunk.asm_uefi_aarch64 new file mode 100644 index 0000000..a524cb0 --- /dev/null +++ b/limine/common/efi_thunk.asm_uefi_aarch64 @@ -0,0 +1,12 @@ +.section .text + +.global efi_main +.extern uefi_entry + +efi_main: + mov x30, xzr + mov x29, xzr + + b uefi_entry + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/efi_thunk.asm_uefi_ia32 b/limine/common/efi_thunk.asm_uefi_ia32 new file mode 100644 index 0000000..3bcaee5 --- /dev/null +++ b/limine/common/efi_thunk.asm_uefi_ia32 @@ -0,0 +1,10 @@ +section .text + +global efi_main +extern uefi_entry +efi_main: + xor eax, eax + mov [esp], eax + jmp uefi_entry + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/efi_thunk.asm_uefi_loongarch64 b/limine/common/efi_thunk.asm_uefi_loongarch64 new file mode 100644 index 0000000..3184c67 --- /dev/null +++ b/limine/common/efi_thunk.asm_uefi_loongarch64 @@ -0,0 +1,10 @@ +.section .text + +.global efi_main +.extern uefi_entry +efi_main: + move $fp, $r0 + move $ra, $r0 + b uefi_entry + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/efi_thunk.asm_uefi_riscv64 b/limine/common/efi_thunk.asm_uefi_riscv64 new file mode 100644 index 0000000..966faee --- /dev/null +++ b/limine/common/efi_thunk.asm_uefi_riscv64 @@ -0,0 +1,11 @@ +.section .text + +.global efi_main +.extern uefi_entry +efi_main: +.option norelax + mv fp, zero + mv ra, zero + j uefi_entry + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/efi_thunk.asm_uefi_x86_64 b/limine/common/efi_thunk.asm_uefi_x86_64 new file mode 100644 index 0000000..dad1647 --- /dev/null +++ b/limine/common/efi_thunk.asm_uefi_x86_64 @@ -0,0 +1,10 @@ +section .text + +global efi_main +extern uefi_entry +efi_main: + xor eax, eax + mov [rsp], rax + jmp uefi_entry + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/entry.s2.c b/limine/common/entry.s2.c new file mode 100644 index 0000000..261a40b --- /dev/null +++ b/limine/common/entry.s2.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct volume *boot_volume; + +#if defined (BIOS) + +bool stage3_loaded = false; +static bool stage3_found = false; + +extern symbol stage3_addr; +extern symbol limine_bios_sys_size; +extern symbol build_id_s2; +extern symbol build_id_s3; + +static bool stage3_init(struct volume *part) { + struct file_handle *stage3; + + bool old_cif = case_insensitive_fopen; + case_insensitive_fopen = true; + if (true + && (stage3 = fopen(part, "/boot/limine/limine-bios.sys")) == NULL + && (stage3 = fopen(part, "/boot/limine-bios.sys")) == NULL + && (stage3 = fopen(part, "/limine/limine-bios.sys")) == NULL + && (stage3 = fopen(part, "/limine-bios.sys")) == NULL + ) { + case_insensitive_fopen = old_cif; + return false; + } + case_insensitive_fopen = old_cif; + + stage3_found = true; + + if (stage3->size != (size_t)limine_bios_sys_size) { + print("limine-bios.sys size incorrect.\n"); + return false; + } + + fread(stage3, stage3_addr, + (uintptr_t)stage3_addr - 0xf000, + stage3->size - ((uintptr_t)stage3_addr - 0xf000)); + + fclose(stage3); + + if (memcmp(build_id_s2 + 16, build_id_s3 + 16, 20) != 0) { + print("limine-bios.sys build ID mismatch.\n"); + return false; + } + + stage3_loaded = true; + + return true; +} + +enum { + BOOTED_FROM_HDD = 0, + BOOTED_FROM_PXE = 1, + BOOTED_FROM_CD = 2 +}; + +noreturn void entry(uint8_t boot_drive, int boot_from) { + // XXX DO NOT MOVE A20 ENABLE CALL + if (!a20_enable()) { + panic(false, "Could not enable A20 line"); + } + + calibrate_tsc(); + uint64_t usec_at_entry = rdtsc_usec(); + + init_e820(); + init_memmap(); + + init_idt(); + + disk_create_index(); + + if (boot_from == BOOTED_FROM_HDD || boot_from == BOOTED_FROM_CD) { + boot_volume = volume_get_by_bios_drive(boot_drive); + } else if (boot_from == BOOTED_FROM_PXE) { + pxe_init(); + boot_volume = pxe_bind_volume(); + } + + if (boot_volume == NULL) { + panic(false, "Could not determine boot drive"); + } + + volume_iterate_parts(boot_volume, + if (stage3_init(_PART)) { + break; + } + ); + + if (!stage3_found) { + print("\n" + "!! Stage 3 file not found!\n" + "!! Have you copied limine-bios.sys to the root, /boot, /limine, or /boot/limine\n" + "!! directories of one of the partitions on the boot device?\n\n"); + } + + if (!stage3_loaded) { + panic(false, "Failed to load stage 3."); + } + + term_fallback(); + + usec_at_bootloader_entry = usec_at_entry; + stage3_common(); +} + +#endif diff --git a/limine/common/entry.s3.c b/limine/common/entry.s3.c new file mode 100644 index 0000000..520232f --- /dev/null +++ b/limine/common/entry.s3.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void stage3_common(void); + +#if defined (UEFI) +extern symbol __slide, __image_base, __image_end; +extern symbol _start; + +noreturn void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { + gST = SystemTable; + gBS = SystemTable->BootServices; + gRT = SystemTable->RuntimeServices; + efi_image_handle = ImageHandle; + + calibrate_tsc(); + usec_at_bootloader_entry = rdtsc_usec(); + + EFI_STATUS status; + + const char *deferred_error = NULL; + +#if defined (__x86_64__) + if ((uintptr_t)__slide >= 0x100000000) { + size_t image_size = ALIGN_UP((uintptr_t)__image_end - (uintptr_t)__image_base, 4096); + size_t image_size_pages = ALIGN_UP((size_t)image_size, 4096) / 4096; + size_t new_base; + for (new_base = 0x1000; new_base + (size_t)image_size < 0x100000000; new_base += 0x1000) { + EFI_PHYSICAL_ADDRESS _new_base = (EFI_PHYSICAL_ADDRESS)new_base; + status = gBS->AllocatePages(AllocateAddress, EfiLoaderCode, image_size_pages, &_new_base); + if (status == 0) { + goto new_base_gotten; + } + } + deferred_error = "Limine does not support being loaded above 4GiB and no alternative loading spot found"; + goto defer_error; +new_base_gotten: + memcpy((void *)new_base, __slide, (size_t)image_size); + __attribute__((ms_abi)) + void (*new_entry_point)(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable); + new_entry_point = (void *)(new_base + ((uintptr_t)_start - (uintptr_t)__slide)); + new_entry_point(ImageHandle, SystemTable); + __builtin_unreachable(); + } + +defer_error: +#endif + + gST->ConOut->EnableCursor(gST->ConOut, false); + + init_memmap(); + + term_fallback(); + + status = gBS->SetWatchdogTimer(0, 0x10000, 0, NULL); + if (status) { + print("WARNING: Failed to disable watchdog timer!\n"); + } + + if (deferred_error != NULL) { + panic(false, "%s", deferred_error); + } + +#if defined (__x86_64__) || defined (__i386__) + init_gdt(); +#endif + + disk_create_index(); + + boot_volume = NULL; + + EFI_HANDLE current_handle = ImageHandle; + for (size_t j = 0; j < 25; j++) { + if (current_handle == NULL) { +could_not_match: + print("WARNING: Could not meaningfully match the boot device handle with a volume.\n"); + print(" Using the first volume containing a Limine configuration!\n"); + print("\n"); + print("THIS IS A BUG! Please report this issue upstream.\n"); + print("Press any key to continue...\n"); + for (;;) { + int ret = pit_sleep_and_quit_on_keypress(65535); + if (ret != 0) { + break; + } + } + + for (size_t i = 0; i < volume_index_i; i++) { + struct file_handle *f; + + bool old_cif = case_insensitive_fopen; + case_insensitive_fopen = true; + if ( + false +#if defined (UEFI) + || (f = fopen(volume_index[i], "/EFI/limine/limine.conf")) != NULL + || (f = fopen(volume_index[i], "/EFI/BOOT/limine.conf")) != NULL +#endif + || (f = fopen(volume_index[i], "/boot/limine/limine.conf")) != NULL + || (f = fopen(volume_index[i], "/boot/limine.conf")) != NULL + || (f = fopen(volume_index[i], "/limine/limine.conf")) != NULL + || (f = fopen(volume_index[i], "/limine.conf")) != NULL + ) { + goto opened; + } + + case_insensitive_fopen = old_cif; + continue; + +opened: + case_insensitive_fopen = old_cif; + + fclose(f); + + if (volume_index[i]->backing_dev != NULL) { + boot_volume = volume_index[i]->backing_dev; + } else { + boot_volume = volume_index[i]; + } + + break; + } + + if (boot_volume != NULL) { + stage3_common(); + } + + panic(false, "No volume contained a Limine configuration file"); + } + + EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; + EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL; + + status = gBS->HandleProtocol(current_handle, &loaded_img_prot_guid, + (void **)&loaded_image); + + if (status) { + goto could_not_match; + } + + boot_volume = disk_volume_from_efi_handle(loaded_image->DeviceHandle); + + if (boot_volume != NULL) { + stage3_common(); + } + + current_handle = loaded_image->ParentHandle; + } + + goto could_not_match; +} +#endif + +noreturn void stage3_common(void) { +#if defined (__x86_64__) || defined (__i386__) + init_flush_irqs(); + init_io_apics(); +#endif + +#if defined (__riscv) +#if defined (UEFI) + RISCV_EFI_BOOT_PROTOCOL *rv_proto = get_riscv_boot_protocol(); + if (rv_proto == NULL || rv_proto->GetBootHartId(rv_proto, &bsp_hartid) != EFI_SUCCESS) { + panic(false, "failed to get BSP's hartid"); + } +#else +#error riscv: only UEFI is supported +#endif +#endif + + term_notready(); + +#if defined (UEFI) + init_bli(); +#endif + + menu(true); +} diff --git a/limine/common/entry_asm.s2.asm_bios_ia32 b/limine/common/entry_asm.s2.asm_bios_ia32 new file mode 100644 index 0000000..565e635 --- /dev/null +++ b/limine/common/entry_asm.s2.asm_bios_ia32 @@ -0,0 +1,31 @@ +extern bss_begin +extern bss_end +extern entry +extern gdt + +section .entry progbits alloc exec nowrite align=16 + +global _start +_start: + cld + + ; Zero out .bss + xor al, al + mov edi, bss_begin + mov ecx, bss_end + sub ecx, bss_begin + rep stosb + + lgdt [gdt] + jmp 0x18:.reload_cs + .reload_cs: + mov eax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + jmp entry + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/fs/fat32.h b/limine/common/fs/fat32.h new file mode 100644 index 0000000..0b6b89e --- /dev/null +++ b/limine/common/fs/fat32.h @@ -0,0 +1,11 @@ +#ifndef FS__FAT32_H__ +#define FS__FAT32_H__ + +#include +#include + +char *fat32_get_label(struct volume *part); + +struct file_handle *fat32_open(struct volume *part, const char *path); + +#endif diff --git a/limine/common/fs/fat32.s2.c b/limine/common/fs/fat32.s2.c new file mode 100644 index 0000000..cfac741 --- /dev/null +++ b/limine/common/fs/fat32.s2.c @@ -0,0 +1,733 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define FAT32_LFN_MAX_ENTRIES 20 +#define FAT32_LFN_MAX_FILENAME_LENGTH (FAT32_LFN_MAX_ENTRIES * 13 + 1) + +#define FAT32_ATTRIBUTE_SUBDIRECTORY 0x10 +#define FAT32_LFN_ATTRIBUTE 0x0F +#define FAT32_ATTRIBUTE_VOLLABEL 0x08 + +struct fat32_context { + struct volume *part; + int type; + char *label; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t number_of_fats; + uint32_t hidden_sectors; + uint32_t sectors_per_fat; + uint32_t fat_start_lba; + uint32_t data_start_lba; + uint32_t root_directory_cluster; + uint16_t root_entries; + uint32_t root_start; + uint32_t root_size; +}; + +struct fat32_file_handle { + struct fat32_context context; + uint32_t first_cluster; + uint32_t size_bytes; + + uint32_t *cluster_chain; + size_t chain_len; +}; + +struct fat32_bpb { + union { + struct { + uint8_t jump[3]; + char oem[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t fats_count; + uint16_t root_entries_count; + uint16_t sectors_count_16; + uint8_t media_descriptor_type; + uint16_t sectors_per_fat_16; + uint16_t sectors_per_track; + uint16_t heads_count; + uint32_t hidden_sectors_count; + uint32_t sectors_count_32; + uint32_t sectors_per_fat_32; + uint16_t flags; + uint16_t fat_version_number; + uint32_t root_directory_cluster; + uint16_t fs_info_sector; + uint16_t backup_boot_sector; + uint8_t reserved[12]; + uint8_t drive_number; + uint8_t nt_flags; + uint8_t signature; + uint32_t volume_serial_number; + char label[11]; + char system_identifier[8]; + } __attribute__((packed)); + uint8_t padding[512]; + }; +} __attribute__((packed)); + +struct fat32_directory_entry { + char file_name_and_ext[8 + 3]; + uint8_t attribute; + uint8_t file_data_1[8]; + uint16_t cluster_num_high; + uint8_t file_data_2[4]; + uint16_t cluster_num_low; + uint32_t file_size_bytes; +} __attribute__((packed)); + +struct fat32_lfn_entry { + uint8_t sequence_number; + char name1[10]; + uint8_t attribute; + uint8_t type; + uint8_t dos_checksum; + char name2[12]; + uint16_t first_cluster; + char name3[4]; +} __attribute__((packed)); + +static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name); + +static int fat32_init_context(struct fat32_context* context, struct volume *part) { + context->part = part; + + struct fat32_bpb bpb; + if (!volume_read(context->part, &bpb, 0, sizeof(struct fat32_bpb))) { + return 1; + } + + // Sanity check of bpb + + // Checks for FAT12/16 + if (strncmp((((void *)&bpb) + 0x36), "FAT", 3) == 0) { + goto signature_valid; + } + + // Checks for FAT32 + if (strncmp((((void *)&bpb) + 0x52), "FAT", 3) == 0) { + goto signature_valid; + } + + // Checks for FAT32 (with 64-bit sector count) + if (strncmp((((void *)&bpb) + 0x03), "FAT32", 5) == 0) { + goto signature_valid; + } + + return 1; + +signature_valid:; + + const uint8_t sector_per_cluster_valid_values[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + for (size_t i = 0; i < SIZEOF_ARRAY(sector_per_cluster_valid_values); i++) { + if (bpb.sectors_per_cluster == sector_per_cluster_valid_values[i]) { + goto sector_per_cluster_valid; + } + } + + return 1; + +sector_per_cluster_valid:; + + const uint16_t bytes_per_sector_valid_values[] = { 512, 1024, 2048, 4096 }; + for (size_t i = 0; i < SIZEOF_ARRAY(bytes_per_sector_valid_values); i++) { + if (bpb.bytes_per_sector == bytes_per_sector_valid_values[i]) { + goto bytes_per_sector_valid; + } + } + + return 1; + +bytes_per_sector_valid:; + + // Validate fats_count (typically 1 or 2, but allow up to 4) + if (bpb.fats_count == 0 || bpb.fats_count > 4) { + return 1; + } + + // The boot sector itself occupies at least sector 0 + if (bpb.reserved_sectors == 0) { + return 1; + } + + // The following mess to identify the FAT type is from the FAT spec + // at paragraph 3.5 + size_t root_dir_sects = ((bpb.root_entries_count * 32) + (bpb.bytes_per_sector - 1)) / bpb.bytes_per_sector; + + // Calculate total sectors and metadata sectors separately to check for underflow + uint64_t total_sects = bpb.sectors_count_16 ? bpb.sectors_count_16 : bpb.sectors_count_32; + uint64_t sectors_per_fat = bpb.sectors_per_fat_16 ? bpb.sectors_per_fat_16 : bpb.sectors_per_fat_32; + uint64_t metadata_sects = (uint64_t)bpb.reserved_sectors + ((uint64_t)bpb.fats_count * sectors_per_fat) + root_dir_sects; + + // Check for underflow before subtraction + if (metadata_sects >= total_sects) { + return 1; // Invalid filesystem: metadata exceeds total size + } + + size_t data_sects = total_sects - metadata_sects; + size_t clusters_count = data_sects / bpb.sectors_per_cluster; + + if (clusters_count < 4085) { + context->type = 12; + } else if (clusters_count < 65525) { + context->type = 16; + } else { + context->type = 32; + } + + context->bytes_per_sector = bpb.bytes_per_sector; + context->sectors_per_cluster = bpb.sectors_per_cluster; + context->reserved_sectors = bpb.reserved_sectors; + context->number_of_fats = bpb.fats_count; + context->hidden_sectors = bpb.hidden_sectors_count; + context->sectors_per_fat = context->type == 32 ? bpb.sectors_per_fat_32 : bpb.sectors_per_fat_16; + if (context->sectors_per_fat == 0) { + return 1; + } + context->root_directory_cluster = bpb.root_directory_cluster; + context->fat_start_lba = bpb.reserved_sectors; + context->root_entries = bpb.root_entries_count; + + // FAT12/16 require a non-zero root directory entry count + if (context->type != 32 && context->root_entries == 0) { + return 1; + } + + // Calculate root_start with overflow check + uint64_t root_start_64 = (uint64_t)context->reserved_sectors + (uint64_t)context->number_of_fats * context->sectors_per_fat; + if (root_start_64 > UINT32_MAX) { + return 1; // Overflow in root_start calculation + } + context->root_start = (uint32_t)root_start_64; + context->root_size = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), context->bytes_per_sector); + switch (context->type) { + case 12: + case 16: + // Check for overflow in data_start_lba calculation + if (__builtin_add_overflow(context->root_start, context->root_size, &context->data_start_lba)) { + return 1; + } + break; + case 32: + context->data_start_lba = context->root_start; + break; + default: + __builtin_unreachable(); + } + + // get the volume label + struct fat32_directory_entry _current_directory; + struct fat32_directory_entry *current_directory; + + switch (context->type) { + case 12: + case 16: + current_directory = NULL; + break; + case 32: + _current_directory.cluster_num_low = context->root_directory_cluster & 0xFFFF; + _current_directory.cluster_num_high = context->root_directory_cluster >> 16; + current_directory = &_current_directory; + break; + default: + __builtin_unreachable(); + } + + char *vol_label; + if (fat32_open_in(context, current_directory, (struct fat32_directory_entry *)&vol_label, NULL) == 0) { + context->label = vol_label; + } else { + context->label = NULL; + } + + return 0; +} + +static int read_cluster_from_map(struct fat32_context *context, uint32_t cluster, uint32_t *out) { + uint64_t fat_base = (uint64_t)context->fat_start_lba * context->bytes_per_sector; + uint64_t fat_size = (uint64_t)context->sectors_per_fat * context->bytes_per_sector; + + switch (context->type) { + case 12: { + *out = 0; + uint16_t tmp = 0; + uint64_t offset = (uint64_t)cluster + (uint64_t)(cluster / 2); + + // Ensure 2-byte reads won't exceed FAT table bounds + if (offset + sizeof(uint16_t) > fat_size) { + return -1; + } + + if (!volume_read(context->part, &tmp, fat_base + offset, sizeof(uint16_t))) { + return -1; + } + if (cluster % 2 == 0) { + *out = tmp & 0xfff; + } else { + *out = tmp >> 4; + } + break; + } + case 16: { + *out = 0; + uint64_t offset = (uint64_t)cluster * sizeof(uint16_t); + if (offset + sizeof(uint16_t) > fat_size) { + return -1; + } + if (!volume_read(context->part, out, fat_base + offset, sizeof(uint16_t))) { + return -1; + } + break; + } + case 32: { + uint64_t offset = (uint64_t)cluster * sizeof(uint32_t); + if (offset + sizeof(uint32_t) > fat_size) { + return -1; + } + if (!volume_read(context->part, out, fat_base + offset, sizeof(uint32_t))) { + return -1; + } + *out &= 0x0fffffff; + break; + } + default: + __builtin_unreachable(); + } + + return 0; +} + +// Maximum cluster chain length to prevent memory exhaustion (64MB of cluster chain data) +#define FAT32_MAX_CHAIN_LENGTH (64 * 1024 * 1024 / sizeof(uint32_t)) + +static uint32_t *cache_cluster_chain(struct fat32_context *context, + uint32_t initial_cluster, + size_t *_chain_length) { + uint32_t cluster_limit = (context->type == 12 ? 0xfef : 0) + | (context->type == 16 ? 0xffef : 0) + | (context->type == 32 ? 0xfffffef : 0); + if (initial_cluster < 0x2 || initial_cluster > cluster_limit) + return NULL; + + // Limit chain length to prevent memory exhaustion from malicious filesystems + size_t max_clusters = cluster_limit - 1; + if (max_clusters > FAT32_MAX_CHAIN_LENGTH) { + max_clusters = FAT32_MAX_CHAIN_LENGTH; + } + + uint32_t cluster = initial_cluster; + size_t chain_length; + for (chain_length = 1; chain_length <= max_clusters; chain_length++) { + if (read_cluster_from_map(context, cluster, &cluster) != 0) { + return NULL; + } + if (cluster < 0x2 || cluster > cluster_limit) + break; + } + + if (chain_length > max_clusters) { + // Circular or corrupted cluster chain detected + return NULL; + } + + size_t alloc_size; + if (__builtin_mul_overflow(chain_length, sizeof(uint32_t), &alloc_size)) { + return NULL; + } + uint32_t *cluster_chain = ext_mem_alloc(alloc_size); + cluster = initial_cluster; + for (size_t i = 0; i < chain_length; i++) { + cluster_chain[i] = cluster; + if (read_cluster_from_map(context, cluster, &cluster) != 0) { + pmm_free(cluster_chain, alloc_size); + return NULL; + } + } + *_chain_length = chain_length; + return cluster_chain; +} + +static bool read_cluster_chain(struct fat32_context *context, + uint32_t *cluster_chain, + size_t chain_len, + void *buf, uint64_t loc, uint64_t count) { + uint64_t block_size = (uint64_t)context->sectors_per_cluster * (uint64_t)context->bytes_per_sector; + for (uint64_t progress = 0; progress < count;) { + uint64_t block = (loc + progress) / block_size; + + // Bounds check: ensure block index is within cluster chain + if (block >= chain_len) { + return false; + } + + // Validate cluster number before arithmetic to prevent underflow + uint32_t cluster = cluster_chain[block]; + if (cluster < 2) { + return false; + } + + uint64_t chunk = count - progress; + uint64_t offset = (loc + progress) % block_size; + if (chunk > block_size - offset) + chunk = block_size - offset; + + uint64_t base = ((uint64_t)context->data_start_lba + (uint64_t)(cluster - 2) * context->sectors_per_cluster) * context->bytes_per_sector; + if (!volume_read(context->part, buf + progress, base + offset, chunk)) { + return false; + } + + progress += chunk; + } + + return true; +} + +// Copy ucs-2 characters to char*, with bounds checking +static void fat32_lfncpy(char* destination, size_t dest_size, size_t dest_offset, + const void* source, unsigned int size) { + for (unsigned int i = 0; i < size; i++) { + if (dest_offset + i >= dest_size) { + return; // Prevent buffer overflow + } + // ignore high bytes + *(((uint8_t*) destination) + dest_offset + i) = *(((uint8_t*) source) + (i * 2)); + } +} + +static bool fat32_filename_to_8_3(char *dest, const char *src) { + int i = 0, j = 0; + bool ext = false; + + for (size_t k = 0; k < 8+3; k++) + dest[k] = ' '; + + while (src[i]) { + if (src[i] == '.') { + if (ext) { + // This is a double extension here, just give up. + return false; + } + ext = true; + j = 8; + i++; + continue; + } + if (j >= 8+3 || (j >= 8 && !ext)) { + // Filename too long, give up. + return false; + } + dest[j++] = toupper(src[i++]); + } + + return true; +} + +static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name) { + size_t block_size = context->sectors_per_cluster * context->bytes_per_sector; + char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0}; + + size_t dir_chain_len; + struct fat32_directory_entry *directory_entries; + + if (directory != NULL) { + uint32_t current_cluster_number = directory->cluster_num_low; + if (context->type == 32) + current_cluster_number |= (uint32_t)directory->cluster_num_high << 16; + + uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len); + + if (directory_cluster_chain == NULL) + return -1; + + // Check for integer overflow in allocation size + size_t alloc_size; + if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) { + // Limit directory size to 256MB to prevent memory exhaustion + pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t)); + return -1; + } + + directory_entries = ext_mem_alloc(alloc_size); + + if (!read_cluster_chain(context, directory_cluster_chain, dir_chain_len, directory_entries, 0, alloc_size)) { + pmm_free(directory_entries, alloc_size); + pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t)); + return -1; + } + + pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t)); + } else { + dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size); + + // Check for overflow + size_t alloc_size; + if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) { + return -1; + } + + directory_entries = ext_mem_alloc(alloc_size); + + if (!volume_read(context->part, directory_entries, (uint64_t)context->root_start * context->bytes_per_sector, context->root_entries * sizeof(struct fat32_directory_entry))) { + pmm_free(directory_entries, alloc_size); + return -1; + } + } + + int ret; + + for (size_t i = 0; i < (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry); i++) { + if (directory_entries[i].file_name_and_ext[0] == 0x00) { + // no more entries here + break; + } + + if (name == NULL) { + if (directory_entries[i].attribute != FAT32_ATTRIBUTE_VOLLABEL) { + continue; + } + char *r = ext_mem_alloc(12); + memcpy(r, directory_entries[i].file_name_and_ext, 11); + // remove trailing spaces + for (int j = 10; j >= 0; j--) { + if (r[j] == ' ') { + r[j] = 0; + continue; + } + break; + } + *((char **)file) = r; + ret = 0; + goto out; + } + + if (directory_entries[i].attribute == FAT32_LFN_ATTRIBUTE) { + struct fat32_lfn_entry* lfn = (struct fat32_lfn_entry*) &directory_entries[i]; + + if (lfn->sequence_number & 0b01000000) { + // this lfn is the first entry in the table, clear the lfn buffer + memset(current_lfn, ' ', sizeof(current_lfn)); + } + + const unsigned int seq_num = lfn->sequence_number & 0b00011111; + if (seq_num == 0) { + continue; // Invalid sequence number, skip + } + const unsigned int lfn_index = (seq_num - 1U) * 13U; + if (lfn_index >= FAT32_LFN_MAX_ENTRIES * 13) { + continue; + } + + fat32_lfncpy(current_lfn, sizeof(current_lfn), lfn_index + 0, lfn->name1, 5); + fat32_lfncpy(current_lfn, sizeof(current_lfn), lfn_index + 5, lfn->name2, 6); + fat32_lfncpy(current_lfn, sizeof(current_lfn), lfn_index + 11, lfn->name3, 2); + + if (lfn_index != 0) + continue; + + // remove trailing spaces + for (int j = SIZEOF_ARRAY(current_lfn) - 2; j >= -1; j--) { + if (j == -1 || current_lfn[j] != ' ') { + current_lfn[j + 1] = 0; + break; + } + } + + int (*strcmpfn)(const char *, const char *) = case_insensitive_fopen ? strcasecmp : strcmp; + + if (strcmpfn(current_lfn, name) == 0) { + // Ensure i+1 is within bounds before accessing + if (i + 1 >= (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry)) { + ret = -1; + goto out; + } + // Validate that the next entry is a valid SFN entry (not LFN, deleted, or end-of-dir) + struct fat32_directory_entry *sfn_entry = &directory_entries[i+1]; + if (sfn_entry->file_name_and_ext[0] == 0x00 || + (uint8_t)sfn_entry->file_name_and_ext[0] == 0xE5 || + sfn_entry->attribute == FAT32_LFN_ATTRIBUTE) { + // Corrupted LFN sequence - expected SFN entry not found + ret = -1; + goto out; + } + *file = *sfn_entry; + ret = 0; + goto out; + } + } + + if (directory_entries[i].attribute & (1 << 3)) { + // It is a volume label, skip + continue; + } + // SFN + char fn[8+3]; + if (!fat32_filename_to_8_3(fn, name)) { + continue; + } + if (!strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) { + *file = directory_entries[i]; + ret = 0; + goto out; + } + } + + // file not found + ret = -1; + +out: + pmm_free(directory_entries, dir_chain_len * block_size); + return ret; +} + +char *fat32_get_label(struct volume *part) { + struct fat32_context context; + if (fat32_init_context(&context, part) != 0) { + return NULL; + } + + return context.label; +} + +static void fat32_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count); +static void fat32_close(struct file_handle *file); + +struct file_handle *fat32_open(struct volume *part, const char *path) { + struct fat32_context context; + int r = fat32_init_context(&context, part); + + if (r) { + return NULL; + } + + struct fat32_directory_entry _current_directory; + struct fat32_directory_entry *current_directory; + struct fat32_directory_entry current_file; + unsigned int current_index = 0; + char current_part[FAT32_LFN_MAX_FILENAME_LENGTH]; + + // skip trailing slashes + while (path[current_index] == '/') { + current_index++; + } + + // walk down the directory tree + switch (context.type) { + case 12: + case 16: + current_directory = NULL; + break; + case 32: + _current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF; + _current_directory.cluster_num_high = context.root_directory_cluster >> 16; + current_directory = &_current_directory; + break; + default: + __builtin_unreachable(); + } + + for (;;) { + bool expect_directory = false; + bool found_terminator = false; + + for (unsigned int i = 0; i < SIZEOF_ARRAY(current_part) - 1; i++) { + // Check for overflow before computing path index + unsigned int path_idx; + if (__builtin_add_overflow(i, current_index, &path_idx)) { + return NULL; // Path index would overflow + } + + if (path[path_idx] == 0) { + memcpy(current_part, path + current_index, i); + current_part[i] = 0; + expect_directory = false; + found_terminator = true; + break; + } + + if (path[path_idx] == '/') { + memcpy(current_part, path + current_index, i); + current_part[i] = 0; + // Check for overflow before updating current_index + unsigned int new_index; + if (__builtin_add_overflow(current_index, i + 1, &new_index)) { + return NULL; // current_index would overflow + } + current_index = new_index; + expect_directory = true; + found_terminator = true; + break; + } + } + + // If loop completed without finding terminator, path component is too long + if (!found_terminator) { + return NULL; + } + + if ((r = fat32_open_in(&context, current_directory, ¤t_file, current_part)) != 0) { + return NULL; + } + + if (expect_directory) { + if (!(current_file.attribute & FAT32_ATTRIBUTE_SUBDIRECTORY)) { + return NULL; + } + _current_directory = current_file; + current_directory = &_current_directory; + } else { + struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle)); + struct fat32_file_handle *ret = ext_mem_alloc(sizeof(struct fat32_file_handle)); + + ret->context = context; + ret->first_cluster = current_file.cluster_num_low; + if (context.type == 32) + ret->first_cluster |= (uint64_t)current_file.cluster_num_high << 16; + + ret->size_bytes = current_file.file_size_bytes; + // Initialize chain_len before calling cache_cluster_chain + // (cache_cluster_chain may return NULL without setting it for empty files) + ret->chain_len = 0; + ret->cluster_chain = cache_cluster_chain(&context, ret->first_cluster, &ret->chain_len); + + if (ret->cluster_chain == NULL && ret->size_bytes != 0) { + pmm_free(ret, sizeof(struct fat32_file_handle)); + pmm_free(handle, sizeof(struct file_handle)); + return NULL; + } + + handle->fd = (void *)ret; + handle->read = (void *)fat32_read; + handle->close = (void *)fat32_close; + handle->size = ret->size_bytes; + handle->vol = part; +#if defined (UEFI) + handle->efi_part_handle = part->efi_part_handle; +#endif + + return handle; + } + } +} + +static void fat32_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) { + struct fat32_file_handle *f = file->fd; + if (!read_cluster_chain(&f->context, f->cluster_chain, f->chain_len, buf, loc, count)) { + panic(false, "fat32: cluster chain read failed (corrupted filesystem?)"); + } +} + +static void fat32_close(struct file_handle *file) { + struct fat32_file_handle *f = file->fd; + pmm_free(f->cluster_chain, f->chain_len * sizeof(uint32_t)); + pmm_free(f, sizeof(struct fat32_file_handle)); +} diff --git a/limine/common/fs/file.h b/limine/common/fs/file.h new file mode 100644 index 0000000..86474bd --- /dev/null +++ b/limine/common/fs/file.h @@ -0,0 +1,45 @@ +#ifndef FS__FILE_H__ +#define FS__FILE_H__ + +#include +#include +#include +#include +#if defined (UEFI) +# include +#endif + +extern bool case_insensitive_fopen; + +bool fs_get_guid(struct guid *guid, struct volume *part); +char *fs_get_label(struct volume *part); + +struct file_handle { + bool is_memfile; + bool readall; + struct volume *vol; + char *path; + size_t path_len; + void *fd; + void (*read)(void *fd, void *buf, uint64_t loc, uint64_t count); + void (*close)(void *fd); + uint64_t size; +#if defined (UEFI) + EFI_HANDLE efi_part_handle; +#endif + bool pxe; + uint32_t pxe_ip; + uint16_t pxe_port; +}; + +struct file_handle *fopen(struct volume *part, const char *filename); +void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count); +void fclose(struct file_handle *fd); +void *freadall(struct file_handle *fd, uint32_t type); +void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs +#if defined (__i386__) + , void (*memcpy_to_64)(uint64_t dst, void *src, size_t count) +#endif +); + +#endif diff --git a/limine/common/fs/file.s2.c b/limine/common/fs/file.s2.c new file mode 100644 index 0000000..3bc064b --- /dev/null +++ b/limine/common/fs/file.s2.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *fs_get_label(struct volume *part) { + char *ret; + + if ((ret = fat32_get_label(part)) != NULL) { + return ret; + } + + return NULL; +} + +bool fs_get_guid(struct guid *guid, struct volume *part) { + (void)guid; (void)part; + + return false; +} + +bool case_insensitive_fopen = false; + +struct file_handle *fopen(struct volume *part, const char *filename) { + size_t filename_new_len = strlen(filename) + 2; + char *filename_new = ext_mem_alloc(filename_new_len); + + if (filename[0] != '/') { + filename_new[0] = '/'; + strcpy(&filename_new[1], filename); + } else { + strcpy(filename_new, filename); + } + + filename = filename_new; + + struct file_handle *ret; + + if (part->pxe) { + if ((ret = tftp_open(part, "", filename)) == NULL) { + goto err; + } + pmm_free(filename_new, filename_new_len); + return ret; + } + + if ((ret = iso9660_open(part, filename)) != NULL) { + goto success; + } + if ((ret = fat32_open(part, filename)) != NULL) { + goto success; + } + +err: + pmm_free(filename_new, filename_new_len); + return NULL; + +success: + ret->path = (char *)filename; + ret->path_len = filename_new_len; + + return ret; +} + +void fclose(struct file_handle *fd) { + if (fd->is_memfile) { + if (fd->readall == false) { + pmm_free(fd->fd, fd->size); + } + } else { + fd->close(fd); + } + pmm_free(fd->path, fd->path_len); + pmm_free(fd, sizeof(struct file_handle)); +} + +void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count) { + if (fd->is_memfile) { + if (loc >= fd->size || count > fd->size - loc) { + panic(false, "fread: attempted out of bounds read"); + } + memcpy(buf, fd->fd + loc, count); + } else { + fd->read(fd, buf, loc, count); + } +} + +void *freadall(struct file_handle *fd, uint32_t type) { + return freadall_mode(fd, type, false +#if defined (__i386__) + , NULL +#endif + ); +} + +void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs +#if defined (__i386__) + , void (*memcpy_to_64)(uint64_t dst, void *src, size_t count) +#endif +) { +#if defined (__i386__) + static uint64_t high_ret; + + if (memcpy_to_64 == NULL) { + allow_high_allocs = false; + } +#endif + + if (fd->is_memfile) { + if (fd->readall) { +#if defined (__i386__) + if (allow_high_allocs == true) { + high_ret = (uintptr_t)fd->fd; + return &high_ret; + } +#endif + return fd->fd; + } +#if defined (UEFI) && defined (__x86_64__) + if (!allow_high_allocs && (uintptr_t)fd->fd >= 0x100000000) { + void *newptr = ext_mem_alloc_type(fd->size, type); + memcpy(newptr, fd->fd, fd->size); + pmm_free(fd->fd, fd->size); + fd->fd = newptr; + } else { +#endif + memmap_alloc_range((uint64_t)(size_t)fd->fd, ALIGN_UP(fd->size, 4096), type, 0, true, false, false); +#if defined (UEFI) && defined (__x86_64__) + } +#endif + fd->readall = true; +#if defined (__i386__) + if (allow_high_allocs == true) { + high_ret = (uintptr_t)fd->fd; + return &high_ret; + } +#endif + return fd->fd; + } else { + void *ret = ext_mem_alloc_type_aligned_mode(fd->size, type, 4096, allow_high_allocs); +#if defined (__i386__) + if (allow_high_allocs == true) { + high_ret = *(uint64_t *)ret; + if (high_ret < 0x100000000) { + ret = (void *)(uintptr_t)high_ret; + goto low_ret; + } + void *pool = ext_mem_alloc(0x100000); + for (uint64_t i = 0; i < fd->size; i += 0x100000) { + size_t count; + if (fd->size - i < 0x100000) { + count = fd->size - i; + } else { + count = 0x100000; + } + fd->read(fd, pool, i, count); + memcpy_to_64(high_ret + i, pool, count); + } + pmm_free(pool, 0x100000); + fd->close(fd); + return &high_ret; + } +low_ret: +#endif + fd->read(fd, ret, 0, fd->size); + fd->close(fd); + fd->fd = ret; + fd->readall = true; + fd->is_memfile = true; +#if defined (__i386__) + if (allow_high_allocs == true) { + return &high_ret; + } +#endif + return ret; + } +} diff --git a/limine/common/fs/iso9660.h b/limine/common/fs/iso9660.h new file mode 100644 index 0000000..6ab6d26 --- /dev/null +++ b/limine/common/fs/iso9660.h @@ -0,0 +1,10 @@ +#ifndef FS__ISO9660_H__ +#define FS__ISO9660_H__ + +#include +#include +#include + +struct file_handle *iso9660_open(struct volume *vol, const char *path); + +#endif diff --git a/limine/common/fs/iso9660.s2.c b/limine/common/fs/iso9660.s2.c new file mode 100644 index 0000000..a953c27 --- /dev/null +++ b/limine/common/fs/iso9660.s2.c @@ -0,0 +1,544 @@ +#include +#include +#include +#include +#include +#include + +#define ISO9660_SECTOR_SIZE (2 << 10) + +struct iso9660_context { + struct volume *vol; + void *root; + uint32_t root_size; +}; + +struct iso9660_extent { + uint32_t LBA; + uint32_t size; +}; + +struct iso9660_file_handle { + struct iso9660_context *context; + uint64_t total_size; + uint32_t extent_count; + struct iso9660_extent *extents; +}; + +#define ISO9660_FLAG_MULTI_EXTENT 0x80 + +#define ISO9660_FIRST_VOLUME_DESCRIPTOR 0x10 +#define ISO9660_VOLUME_DESCRIPTOR_SIZE ISO9660_SECTOR_SIZE +#define ROCK_RIDGE_MAX_FILENAME 255 +#define ISO9660_MAX_EXTENT_COUNT 65536 + +// --- Both endian structures --- +struct BE16_t { uint16_t little, big; } __attribute__((packed)); +struct BE32_t { uint32_t little, big; } __attribute__((packed)); + +// --- Directory entries --- +struct iso9660_directory_entry { + uint8_t length; + uint8_t extended_attribute_length; + struct BE32_t extent; + struct BE32_t extent_size; + uint8_t datetime[7]; + uint8_t flags; + uint8_t interleaved_unit_size; + uint8_t interleaved_gap_size; + struct BE16_t volume_seq; + uint8_t filename_size; + char name[]; +} __attribute__((packed)); + +// --- Volume descriptors --- +// VDT = Volume Descriptor Type +enum { + ISO9660_VDT_BOOT_RECORD, + ISO9660_VDT_PRIMARY, + ISO9660_VDT_SUPPLEMENTARY, + ISO9660_VDT_PARTITION_DESCRIPTOR, + ISO9660_VDT_TERMINATOR = 255 +}; + +struct iso9660_volume_descriptor { + uint8_t type; + char identifier[5]; + uint8_t version; +} __attribute__((packed)); + +struct iso9660_primary_volume { + struct iso9660_volume_descriptor volume_descriptor; + + union { + struct { + uint8_t unused0[1]; + char system_identifier[32]; + char volume_identifier[32]; + uint8_t unused1[8]; + struct BE32_t space_size; + uint8_t unused2[32]; + struct BE16_t set_size; + struct BE16_t volume_seq; + struct BE16_t LBA_size; + struct BE32_t path_table_size; + + uint32_t LBA_path_table_little; + uint32_t LBA_optional_path_table_little; + uint32_t LBA_path_table_big; + uint32_t LBA_optional_path_table_big; + + struct iso9660_directory_entry root; + } __attribute__((packed)); + + uint8_t padding[2041]; + }; +} __attribute__((packed)); + + +// --- Implementation --- +struct iso9660_contexts_node { + struct iso9660_context context; + struct iso9660_contexts_node *next; +}; + +static struct iso9660_contexts_node *contexts = NULL; + +// Maximum number of volume descriptors to scan before giving up +#define ISO9660_MAX_VOLUME_DESCRIPTORS 256 + +// Maximum directory size to prevent memory exhaustion (64MB) +#define ISO9660_MAX_DIR_SIZE (64 * 1024 * 1024) + +static void iso9660_find_PVD(struct iso9660_primary_volume *desc, struct volume *vol) { + uint32_t lba = ISO9660_FIRST_VOLUME_DESCRIPTOR; + uint32_t max_lba = ISO9660_FIRST_VOLUME_DESCRIPTOR + ISO9660_MAX_VOLUME_DESCRIPTORS; + + while (lba < max_lba) { + uint64_t offset = (uint64_t)lba * ISO9660_SECTOR_SIZE; + if (!volume_read(vol, desc, offset, sizeof(struct iso9660_primary_volume))) { + panic(false, "ISO9660: failed to read volume descriptor"); + } + + switch (desc->volume_descriptor.type) { + case ISO9660_VDT_PRIMARY: + return; + case ISO9660_VDT_TERMINATOR: + panic(false, "ISO9660: no primary volume descriptor"); + break; + } + + ++lba; + } + + panic(false, "ISO9660: exceeded maximum volume descriptor search limit"); +} + +static void iso9660_cache_root(struct volume *vol, + void **root, + uint32_t *root_size) { + struct iso9660_primary_volume pv; + iso9660_find_PVD(&pv, vol); + + *root_size = pv.root.extent_size.little; + + // Validate root directory size to prevent memory exhaustion + if (*root_size == 0 || *root_size > ISO9660_MAX_DIR_SIZE) { + panic(false, "ISO9660: Invalid root directory size"); + } + + *root = ext_mem_alloc(*root_size); + uint64_t offset = (uint64_t)pv.root.extent.little * ISO9660_SECTOR_SIZE; + if (!volume_read(vol, *root, offset, *root_size)) { + panic(false, "ISO9660: failed to read root directory"); + } +} + +static struct iso9660_context *iso9660_get_context(struct volume *vol) { + struct iso9660_contexts_node *current = contexts; + while (current) { + if (current->context.vol == vol) + return ¤t->context; + current = current->next; + } + + // The context is not cached at this point + struct iso9660_contexts_node *node = ext_mem_alloc(sizeof(struct iso9660_contexts_node)); + node->context.vol = vol; + iso9660_cache_root(vol, &node->context.root, &node->context.root_size); + + node->next = contexts; + contexts = node; + return &node->context; +} + +static bool load_name(char *buf, size_t limit, struct iso9660_directory_entry *entry) { + unsigned char* sysarea = ((unsigned char*)entry) + sizeof(struct iso9660_directory_entry) + entry->filename_size; + + // Validate entry->length is large enough + if (entry->length < sizeof(struct iso9660_directory_entry) + entry->filename_size) { + goto use_iso_name; + } + + size_t sysarea_len = entry->length - sizeof(struct iso9660_directory_entry) - entry->filename_size; + if ((entry->filename_size & 0x1) == 0) { + if (sysarea_len == 0) { + goto use_iso_name; + } + sysarea++; + sysarea_len--; + } + + int rrnamelen = 0; + unsigned char *nm_entry = NULL; + while ((sysarea_len >= 4) && (sysarea[3] == 1)) { + // Validate entry length doesn't exceed remaining sysarea + if (sysarea[2] > sysarea_len) { + break; + } + if (sysarea[0] == 'N' && sysarea[1] == 'M') { + // Validate Rock Ridge NM entry length + // sysarea[2] is total entry length, must be >= 5 (header) and within sysarea bounds + if (sysarea[2] >= 5 && sysarea[2] <= sysarea_len) { + rrnamelen = sysarea[2] - 5; + nm_entry = sysarea; + } + break; + } + // Prevent infinite loop from zero-length entry + if (sysarea[2] == 0) { + break; + } + sysarea_len -= sysarea[2]; + sysarea += sysarea[2]; + } + + size_t name_len = 0; + if (rrnamelen > 0 && nm_entry != NULL) { + /* rock ridge naming scheme */ + name_len = rrnamelen; + if (name_len >= limit) { + panic(false, "iso9660: Filename size exceeded"); + } + memcpy(buf, nm_entry + 5, name_len); + buf[name_len] = 0; + return true; + } + +use_iso_name: + name_len = entry->filename_size; + if (name_len >= limit) { + panic(false, "iso9660: Filename size exceeded"); + } + // Validate that entry->length can actually hold the filename + // entry->length must be >= sizeof(struct) + filename_size for safe access + if (entry->length < sizeof(struct iso9660_directory_entry) + name_len) { + // Corrupted entry: claimed filename_size exceeds actual entry data + // Clamp name_len to what's actually available + if (entry->length <= sizeof(struct iso9660_directory_entry)) { + name_len = 0; + } else { + name_len = entry->length - sizeof(struct iso9660_directory_entry); + } + } + size_t j; + for (j = 0; j < name_len; j++) { + if (entry->name[j] == ';') + break; + if (entry->name[j] == '.' && j + 1 < name_len && entry->name[j+1] == ';') + break; + buf[j] = entry->name[j]; + } + buf[j] = 0; + return false; +} + +// Advance to the next directory entry in the buffer +// Returns NULL if no more entries or invalid entry +static struct iso9660_directory_entry *iso9660_next_entry(void *current, void *buffer_end) { + struct iso9660_directory_entry *entry = current; + + if (entry->length == 0) { + // Skip to next sector boundary + uintptr_t current_addr = (uintptr_t)current; + uintptr_t next_sector = ALIGN_UP(current_addr + 1, ISO9660_SECTOR_SIZE); + if (next_sector >= (uintptr_t)buffer_end) + return NULL; + entry = (struct iso9660_directory_entry *)next_sector; + if (entry->length == 0) + return NULL; + return entry; + } + + void *next = (uint8_t *)current + entry->length; + if (next >= buffer_end) + return NULL; + + entry = next; + + // Handle zero-length entries (padding at sector boundaries) + if (entry->length == 0) { + uintptr_t next_sector = ALIGN_UP((uintptr_t)next + 1, ISO9660_SECTOR_SIZE); + if (next_sector >= (uintptr_t)buffer_end) + return NULL; + entry = (struct iso9660_directory_entry *)next_sector; + if (entry->length == 0) + return NULL; + } + + // Validate minimum entry size + if (entry->length < sizeof(struct iso9660_directory_entry)) + return NULL; + + return entry; +} + +static struct iso9660_directory_entry *iso9660_find(void *buffer, uint32_t size, const char *filename) { + while (size) { + struct iso9660_directory_entry *entry = buffer; + + if (entry->length == 0) { + if (size <= ISO9660_SECTOR_SIZE) + return NULL; + size_t prev_size = size; + size = ALIGN_DOWN(size, ISO9660_SECTOR_SIZE); + // If size didn't change (was already aligned), force move to next sector + if (prev_size == size) { + if (size <= ISO9660_SECTOR_SIZE) + return NULL; + size -= ISO9660_SECTOR_SIZE; + buffer += ISO9660_SECTOR_SIZE; + } else { + buffer += prev_size - size; + } + continue; + } + + // Validate entry->length doesn't exceed remaining buffer + if (entry->length > size) { + return NULL; // Corrupted directory entry + } + + // Minimum valid directory entry size + if (entry->length < sizeof(struct iso9660_directory_entry)) { + return NULL; // Corrupted directory entry + } + + char entry_filename[256]; + bool rr = load_name(entry_filename, 256, entry); + + if (rr && !case_insensitive_fopen) { + if (strcmp(filename, entry_filename) == 0) { + return buffer; + } + } else { + if (strcasecmp(filename, entry_filename) == 0) { + return buffer; + } + } + + size -= entry->length; + buffer += entry->length; + } + + return NULL; +} + +static void iso9660_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count); +static void iso9660_close(struct file_handle *file); + +struct file_handle *iso9660_open(struct volume *vol, const char *path) { + char buf[6]; + const uint64_t signature = ISO9660_FIRST_VOLUME_DESCRIPTOR * ISO9660_SECTOR_SIZE + 1; + if (!volume_read(vol, buf, signature, 5)) { + return NULL; + } + buf[5] = '\0'; + if (strcmp(buf, "CD001") != 0) { + return NULL; + } + + struct iso9660_file_handle *ret = ext_mem_alloc(sizeof(struct iso9660_file_handle)); + + ret->context = iso9660_get_context(vol); + + while (*path == '/') + ++path; + + struct iso9660_directory_entry *current = ret->context->root; + uint32_t current_size = ret->context->root_size; + + bool first = true; + + uint32_t next_sector = 0; + uint32_t next_size = 0; + + char filename[ROCK_RIDGE_MAX_FILENAME]; + while (true) { + // Skip any consecutive slashes + while (*path == '/') { + path++; + } + + // Check if we've reached the end of the path (handles trailing slashes) + if (*path == '\0') { + // Use the current directory's extent info + // For root, this was set from ret->context->root + // For subdirs, it was set from the last matched entry + if (!first) { + pmm_free(current, current_size); + } + pmm_free(ret, sizeof(struct iso9660_file_handle)); + return NULL; + } + + char *aux = filename; + char *aux_end = filename + ROCK_RIDGE_MAX_FILENAME - 1; + while (!(*path == '/' || *path == '\0')) { + if (aux >= aux_end) { + panic(false, "iso9660: Path component exceeds maximum length"); + } + *aux++ = *path++; + } + *aux = '\0'; + + struct iso9660_directory_entry *entry = iso9660_find(current, current_size, filename); + if (!entry) { + if (!first) { + pmm_free(current, current_size); + } + pmm_free(ret, sizeof(struct iso9660_file_handle)); + return NULL; // Not found :( + } + + next_sector = entry->extent.little; + next_size = entry->extent_size.little; + + if (*path == '\0') { + // Found the file - collect all extents for multi-extent files + void *buffer_end = (uint8_t *)current + current_size; + + // First pass: count extents and calculate total size + uint32_t extent_count = 1; + uint64_t total_size = entry->extent_size.little; + struct iso9660_directory_entry *e = entry; + + while (e->flags & ISO9660_FLAG_MULTI_EXTENT) { + struct iso9660_directory_entry *next = iso9660_next_entry(e, buffer_end); + if (next == NULL) + break; + e = next; + extent_count++; + total_size += e->extent_size.little; + + // Sanity check to prevent runaway on corrupted directories + if (extent_count >= ISO9660_MAX_EXTENT_COUNT) { + break; + } + } + + // Allocate extent array + ret->extents = ext_mem_alloc(extent_count * sizeof(struct iso9660_extent)); + ret->extent_count = extent_count; + ret->total_size = total_size; + + // Second pass: populate extent array + e = entry; + for (uint32_t i = 0; i < extent_count; i++) { + ret->extents[i].LBA = e->extent.little; + ret->extents[i].size = e->extent_size.little; + if (i + 1 < extent_count) { + struct iso9660_directory_entry *next = iso9660_next_entry(e, buffer_end); + if (next == NULL) + break; + e = next; + } + } + + // Free the directory buffer if we allocated one + if (!first) { + pmm_free(current, current_size); + } + + goto setup_handle; + } + + path++; // Skip the '/' separator + + if (!first) { + pmm_free(current, current_size); + } + + // Validate directory size to prevent memory exhaustion + if (next_size == 0 || next_size > ISO9660_MAX_DIR_SIZE) { + pmm_free(ret, sizeof(struct iso9660_file_handle)); + return NULL; + } + + current_size = next_size; + current = ext_mem_alloc(current_size); + + first = false; + + uint64_t dir_offset = (uint64_t)next_sector * ISO9660_SECTOR_SIZE; + if (!volume_read(vol, current, dir_offset, current_size)) { + pmm_free(current, current_size); + pmm_free(ret, sizeof(struct iso9660_file_handle)); + return NULL; + } + } + +setup_handle:; + struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle)); + + handle->fd = ret; + handle->read = (void *)iso9660_read; + handle->close = (void *)iso9660_close; + handle->size = ret->total_size; + handle->vol = vol; +#if defined (UEFI) + handle->efi_part_handle = vol->efi_part_handle; +#endif + + return handle; +} + +static void iso9660_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) { + struct iso9660_file_handle *f = file->fd; + + // Find which extent 'loc' falls into and read across extents as needed + uint64_t extent_start = 0; + for (uint32_t i = 0; i < f->extent_count && count > 0; i++) { + uint64_t extent_size = f->extents[i].size; + uint64_t extent_end = extent_start + extent_size; + + if (loc < extent_end) { + // Read starts (or continues) in this extent + uint64_t offset_in_extent = (loc > extent_start) ? (loc - extent_start) : 0; + uint64_t bytes_available = extent_size - offset_in_extent; + uint64_t to_read = (count < bytes_available) ? count : bytes_available; + + uint64_t disk_offset = (uint64_t)f->extents[i].LBA * ISO9660_SECTOR_SIZE + offset_in_extent; + + if (!volume_read(f->context->vol, buf, disk_offset, to_read)) { + panic(false, "iso9660: failed to read file data"); + } + + buf = (uint8_t *)buf + to_read; + loc += to_read; + count -= to_read; + } + + extent_start = extent_end; + } + + if (count > 0) { + panic(false, "iso9660: read beyond end of file"); + } +} + +static void iso9660_close(struct file_handle *file) { + struct iso9660_file_handle *f = file->fd; + pmm_free(f->extents, f->extent_count * sizeof(struct iso9660_extent)); + pmm_free(f, sizeof(struct iso9660_file_handle)); +} diff --git a/limine/common/gensyms.sh b/limine/common/gensyms.sh new file mode 100755 index 0000000..e79bec7 --- /dev/null +++ b/limine/common/gensyms.sh @@ -0,0 +1,51 @@ +#! /bin/sh + +set -e + +LC_ALL=C +export LC_ALL + +TMP0="$(mktemp)" + +cat >"$TMP0" </dev/null +EOF + +chmod +x "$TMP0" + +"$TMP0" && set -o pipefail + +rm "$TMP0" + +TMP1="$(mktemp)" +TMP2="$(mktemp)" +TMP3="$(mktemp)" +TMP4="$(mktemp)" + +trap 'rm -f "$TMP1" "$TMP2" "$TMP3" "$TMP4"' EXIT + +"$OBJDUMP_FOR_TARGET" -t "$1" | ( "$SED" '/[[:<:]]d[[:>:]]/d' 2>/dev/null || "$SED" '/\bd\b/d' ) | sort > "$TMP1" +"$GREP" "F $4" < "$TMP1" | cut -d' ' -f1 > "$TMP2" +"$GREP" "F $4" < "$TMP1" | "$AWK" 'NF{ print $NF }' > "$TMP3" + +echo ".section .$2_map" > "$TMP4" +echo ".globl $2_map" >> "$TMP4" +echo "$2_map:" >> "$TMP4" + +if [ "$3" = "32" ]; then + paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.long 0x/g;s/$/"/g;s/#/\ +.asciz "/g' >> "$TMP4" + echo ".long 0xffffffff" >> "$TMP4" +elif [ "$3" = "64" ]; then + paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.quad 0x/g;s/$/"/g;s/#/\ +.asciz "/g' >> "$TMP4" + echo ".quad 0xffffffffffffffff" >> "$TMP4" +fi + +echo '.section .note.GNU-stack,"",%progbits' >> "$TMP4" + +mv "$TMP4" "$2.map.S" diff --git a/limine/common/lib/acpi.c b/limine/common/lib/acpi.c new file mode 100644 index 0000000..32e9c65 --- /dev/null +++ b/limine/common/lib/acpi.c @@ -0,0 +1,475 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Following function based on https://github.com/managarm/lai/blob/master/helpers/pc-bios.c's function lai_bios_calc_checksum() +uint8_t acpi_checksum(void *ptr, size_t size) { + uint8_t sum = 0, *_ptr = ptr; + for (size_t i = 0; i < size; i++) + sum += _ptr[i]; + return sum; +} + +#if defined (BIOS) + +void *acpi_get_rsdp(void) { + size_t ebda = EBDA; + + for (size_t i = ebda; i < 0x100000; i += 16) { + if (i == ebda + 1024) { + // We probed the 1st KiB of the EBDA as per spec, move onto 0xe0000 + i = 0xe0000; + } + if (!memcmp((char *)i, "RSD PTR ", 8) + && !acpi_checksum((void *)i, 20)) { + printv("acpi: Found RSDP at %p\n", i); + return (void *)i; + } + } + + return NULL; +} + +/// Returns the RSDP v1 pointer if available or else NULL. +void *acpi_get_rsdp_v1(void) { + // In BIOS according to the ACPI spec (see ACPI 6.2 section + // 5.2.5.1 'Finding the RSDP on IA-PC Systems') it either contains + // the RSDP or the XSDP and it cannot contain both. So, we directly + // use acpi_get_rsdp function to find the RSDP and if it has the correct + // revision, return it. + struct rsdp *rsdp = acpi_get_rsdp(); + + if (rsdp != NULL && rsdp->rev < 2) + return rsdp; + + return NULL; +} + +void acpi_get_smbios(void **smbios32, void **smbios64) { + *smbios32 = NULL; + *smbios64 = NULL; + + for (size_t i = 0xf0000; i < 0x100000; i += 16) { + struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)i; + + if (!memcmp(ptr->anchor_str, "_SM_", 4) && + !acpi_checksum((void *)ptr, ptr->length)) { + printv("acpi: Found SMBIOS 32-bit entry point at %p\n", i); + *smbios32 = (void *)ptr; + break; + } + } + + for (size_t i = 0xf0000; i < 0x100000; i += 16) { + struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)i; + + if (!memcmp(ptr->anchor_str, "_SM3_", 5) && + !acpi_checksum((void *)ptr, ptr->length)) { + printv("acpi: Found SMBIOS 64-bit entry point at %p\n", i); + *smbios64 = (void *)ptr; + break; + } + } +} + +#endif + +#if defined (UEFI) + +#include + +void *acpi_get_rsdp(void) { + EFI_GUID acpi_2_guid = ACPI_20_TABLE_GUID; + EFI_GUID acpi_1_guid = ACPI_TABLE_GUID; + + void *rsdp = NULL; + + for (size_t i = 0; i < gST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i]; + + bool is_xsdp = memcmp(&cur_table->VendorGuid, &acpi_2_guid, sizeof(EFI_GUID)) == 0; + bool is_rsdp = memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) == 0; + + if (!is_xsdp && !is_rsdp) + continue; + + if ((is_xsdp && acpi_checksum(cur_table->VendorTable, sizeof(struct rsdp)) != 0) || // XSDP is 36 bytes wide + (is_rsdp && acpi_checksum(cur_table->VendorTable, 20) != 0)) // RSDP is 20 bytes wide + continue; + + printv("acpi: Found %s at %p\n", is_xsdp ? "XSDP" : "RSDP", cur_table->VendorTable); + + // We want to return the XSDP if it exists rather then returning + // the RSDP. We need to add a check for that since the table entries + // are not in the same order for all EFI systems since it might be the + // case where the RSDP occurs before the XSDP. + if (is_xsdp) { + rsdp = (void *)cur_table->VendorTable; + break; // Found it!. + } else { + // Found the RSDP but we continue to loop since we might + // find the XSDP. + rsdp = (void *)cur_table->VendorTable; + } + } + + return rsdp; +} + +/// Returns the RSDP v1 pointer if available or else NULL. +void *acpi_get_rsdp_v1(void) { + // To maintain GRUB compatibility we will need to probe for the RSDP + // again since UEFI can contain both XSDP and RSDP (see ACPI 6.2 section + // 5.2.5.2 'Finding the RSDP on UEFI Enabled Systems') and in the acpi_get_rsdp + // function we look for the RSDP with the latest revision. + EFI_GUID acpi_1_guid = ACPI_TABLE_GUID; + + for (size_t i = 0; i < gST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i]; + + if (memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) != 0) + continue; + + if (acpi_checksum(cur_table->VendorTable, 20) != 0) + continue; + + return (void *)cur_table->VendorTable; + } + + return NULL; +} + +void acpi_get_smbios(void **smbios32, void **smbios64) { + *smbios32 = NULL; + *smbios64 = NULL; + + for (size_t i = 0; i < gST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i]; + EFI_GUID smbios_guid = SMBIOS_TABLE_GUID; + + if (memcmp(&cur_table->VendorGuid, &smbios_guid, sizeof(EFI_GUID)) != 0) + continue; + + struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)cur_table->VendorTable; + + if (acpi_checksum((void *)ptr, ptr->length) != 0) + continue; + + printv("acpi: Found SMBIOS 32-bit entry point at %p\n", ptr); + + *smbios32 = (void *)ptr; + + break; + } + + for (size_t i = 0; i < gST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i]; + EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID; + + if (memcmp(&cur_table->VendorGuid, &smbios3_guid, sizeof(EFI_GUID)) != 0) + continue; + + struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)cur_table->VendorTable; + + if (acpi_checksum((void *)ptr, ptr->length) != 0) + continue; + + printv("acpi: Found SMBIOS 64-bit entry point at %p\n", ptr); + + *smbios64 = (void *)ptr; + + break; + } +} + +#endif + +/// Returns the RSDP v2 pointer if available or else NULL. +void *acpi_get_rsdp_v2(void) { + // Since the acpi_get_rsdp function already looks for the XSDP we can + // just check if it has the correct revision and return the pointer :^) + struct rsdp *rsdp = acpi_get_rsdp(); + + if (rsdp != NULL && rsdp->rev >= 2) + return rsdp; + + return NULL; +} + +void *acpi_get_table(const char *signature, int index) { + int cnt = 0; + + struct rsdp *rsdp = acpi_get_rsdp(); + if (rsdp == NULL) + return NULL; + + bool use_xsdt = false; + if (rsdp->rev >= 2 && rsdp->xsdt_addr + && (sizeof(uintptr_t) >= 8 || rsdp->xsdt_addr <= UINT32_MAX)) + use_xsdt = true; + + struct rsdt *rsdt; + if (use_xsdt) + rsdt = (struct rsdt *)(uintptr_t)rsdp->xsdt_addr; + else + rsdt = (struct rsdt *)(uintptr_t)rsdp->rsdt_addr; + + if (rsdt == NULL) { + return NULL; + } + + // Validate RSDT/XSDT header length + if (rsdt->header.length < sizeof(struct sdt)) { + printv("acpi: Invalid %s header length\n", use_xsdt ? "XSDT" : "RSDT"); + return NULL; + } + + size_t entry_size = use_xsdt ? 8 : 4; + size_t entry_count = (rsdt->header.length - sizeof(struct sdt)) / entry_size; + + for (size_t i = 0; i < entry_count; i++) { + struct sdt *ptr; + if (use_xsdt) + ptr = (struct sdt *)(uintptr_t)((uint64_t *)rsdt->ptrs_start)[i]; + else + ptr = (struct sdt *)(uintptr_t)((uint32_t *)rsdt->ptrs_start)[i]; + + if (ptr == NULL) { + continue; + } + + if (!memcmp(ptr->signature, signature, 4) + && !acpi_checksum(ptr, ptr->length) + && cnt++ == index) { + printv("acpi: Found \"%s\" at %p\n", signature, ptr); + return ptr; + } + } + + printv("acpi: \"%s\" not found\n", signature); + return NULL; +} + +static bool acpi_padding_is_safe(uint64_t base, uint64_t length) { + if (length == 0) { + return true; + } + + uint64_t top = base + length; + + for (size_t i = 0; i < memmap_entries; i++) { + uint64_t entry_base = memmap[i].base; + uint64_t entry_top = entry_base + memmap[i].length; + + if (entry_base >= top || entry_top <= base) { + continue; + } + + if (memmap[i].type != MEMMAP_USABLE && memmap[i].type != MEMMAP_RESERVED) { + return false; + } + } + + return true; +} + +static void map_single_table(uint64_t addr, uint32_t len) { +#if defined (__i386__) + if (addr >= 0x100000000) { + print("acpi: warning: Cannot get length of ACPI table above 4GiB\n"); + return; + } +#endif + + uint32_t length = len != (uint32_t)-1 ? len : *(uint32_t *)(uintptr_t)(addr + 4); + + uint64_t aligned_base = ALIGN_DOWN(addr, 4096); + uint64_t aligned_top = ALIGN_UP(addr + length, 4096); + + if (!acpi_padding_is_safe(aligned_base, addr - aligned_base)) { + aligned_base = addr; + } + if (!acpi_padding_is_safe(addr + length, aligned_top - (addr + length))) { + aligned_top = addr + length; + } + + uint64_t memmap_type = pmm_check_type(addr); + + if (memmap_type != MEMMAP_ACPI_RECLAIMABLE && memmap_type != MEMMAP_ACPI_NVS) { + memmap_alloc_range(aligned_base, aligned_top - aligned_base, MEMMAP_RESERVED_MAPPED, 0, true, false, true); + } +} + + +void acpi_map_tables(void) { + struct rsdp *rsdp = acpi_get_rsdp(); + if (rsdp == NULL) + return; + + uint64_t rsdp_length; + if (rsdp->rev < 2) { + rsdp_length = 20; + } else { + rsdp_length = rsdp->length; + } + + map_single_table((uintptr_t)rsdp, rsdp_length); + + if (!(rsdp->rev >= 2 && rsdp->xsdt_addr)) { + goto no_xsdt; + } + + struct rsdt *xsdt = (void *)(uintptr_t)rsdp->xsdt_addr; + if (xsdt->header.length < sizeof(struct sdt)) { + goto no_xsdt; + } + size_t xsdt_entry_count = (xsdt->header.length - sizeof(struct sdt)) / 8; + + map_single_table((uintptr_t)xsdt, (uint32_t)-1); + + for (size_t i = 0; i < xsdt_entry_count; i++) { + uint64_t entry = ((uint64_t *)xsdt->ptrs_start)[i]; + if (entry == 0) + continue; + struct sdt *sdt = (void *)(uintptr_t)entry; + + map_single_table((uintptr_t)sdt, (uint32_t)-1); + } + +no_xsdt:; + if (rsdp->rsdt_addr == 0) { + goto no_rsdt; + } + + struct rsdt *rsdt = (void *)(uintptr_t)rsdp->rsdt_addr; + if (rsdt->header.length < sizeof(struct sdt)) { + goto no_rsdt; + } + size_t rsdt_entry_count = (rsdt->header.length - sizeof(struct sdt)) / 4; + + map_single_table((uintptr_t)rsdt, (uint32_t)-1); + + for (size_t i = 0; i < rsdt_entry_count; i++) { + uint32_t entry = ((uint32_t *)rsdt->ptrs_start)[i]; + if (entry == 0) + continue; + struct sdt *sdt = (void *)(uintptr_t)entry; + + map_single_table((uintptr_t)sdt, (uint32_t)-1); + } + +no_rsdt:; + uint8_t *fadt = acpi_get_table("FACP", 0); + if (fadt == NULL) { + return; + } + uint32_t fadt_length; + memcpy(&fadt_length, fadt + 4, sizeof(fadt_length)); + + // Read the single fields from the FADT without defining a struct for the whole table + if (fadt_length >= 132 + 8) { + uint64_t x_facs; + memcpy(&x_facs, fadt + 132, sizeof(x_facs)); + if (x_facs != 0) { + map_single_table(x_facs, (uint32_t)-1); + } + } + if (fadt_length >= 140 + 8) { + uint64_t x_dsdt; + memcpy(&x_dsdt, fadt + 140, sizeof(x_dsdt)); + if (x_dsdt != 0) { + map_single_table(x_dsdt, (uint32_t)-1); + } + } + if (fadt_length >= 36 + 4) { + uint32_t facs; + memcpy(&facs, fadt + 36, sizeof(facs)); + if (facs != 0) { + map_single_table(facs, (uint32_t)-1); + } + } + if (fadt_length >= 40 + 4) { + uint32_t dsdt; + memcpy(&dsdt, fadt + 40, sizeof(dsdt)); + if (dsdt != 0) { + map_single_table(dsdt, (uint32_t)-1); + } + } +} + +void smbios_map_tables(void) { + void *smbios32_ptr = NULL, *smbios64_ptr = NULL; + acpi_get_smbios(&smbios32_ptr, &smbios64_ptr); + + if (smbios32_ptr != NULL) { + struct smbios_entry_point_32 *smbios32 = smbios32_ptr; + map_single_table((uintptr_t)smbios32, smbios32->length); + if (smbios32->table_address != 0) { + map_single_table(smbios32->table_address, smbios32->table_length); + } + } + + if (smbios64_ptr != NULL) { + struct smbios_entry_point_64 *smbios64 = smbios64_ptr; + map_single_table((uintptr_t)smbios64, smbios64->length); + if (smbios64->table_address != 0) { + map_single_table(smbios64->table_address, smbios64->table_maximum_size); + } + } +} + +#if defined (UEFI) +void efi_map_runtime_entries(void) { + size_t entry_count = efi_mmap_size / efi_desc_size; + + for (size_t i = 0; i < entry_count; i++) { + EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size; + + if (entry->Type != EfiRuntimeServicesCode + && entry->Type != EfiRuntimeServicesData) { + continue; + } + + uint64_t base = entry->PhysicalStart; + uint64_t length; + if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &length)) { + continue; + } + + memmap_alloc_range(base, length, MEMMAP_RESERVED_MAPPED, 0, true, false, true); + } + + // Explicitly map the EFI system table and the data it references. + // The UEFI spec does not guarantee these reside in EfiRuntimeServicesData, + // so we map them separately to ensure they are always accessible via HHDM. + map_single_table((uintptr_t)gST, sizeof(*gST)); + + if (gST->RuntimeServices != NULL) { + map_single_table((uintptr_t)gST->RuntimeServices, + sizeof(*gST->RuntimeServices)); + } + + if (gST->ConfigurationTable != NULL && gST->NumberOfTableEntries > 0) { + uint64_t ct_size; + if (!__builtin_mul_overflow(gST->NumberOfTableEntries, + (uint64_t)sizeof(EFI_CONFIGURATION_TABLE), &ct_size) + && ct_size <= UINT32_MAX) { + map_single_table((uintptr_t)gST->ConfigurationTable, (uint32_t)ct_size); + } + } + + if (gST->FirmwareVendor != NULL) { + size_t len = 0; + while (gST->FirmwareVendor[len] != 0) { + len++; + } + map_single_table((uintptr_t)gST->FirmwareVendor, + (len + 1) * sizeof(*gST->FirmwareVendor)); + } +} +#endif diff --git a/limine/common/lib/acpi.h b/limine/common/lib/acpi.h new file mode 100644 index 0000000..89dd158 --- /dev/null +++ b/limine/common/lib/acpi.h @@ -0,0 +1,205 @@ +#ifndef LIB__ACPI_H__ +#define LIB__ACPI_H__ + +#include +#include +#include + +#define EBDA (ebda_get()) + +#if defined (BIOS) +static inline uintptr_t ebda_get(void) { + uintptr_t ebda = (uintptr_t)mminw(0x40e) << 4; + + // Sanity checks + if (ebda < 0x80000 || ebda >= 0xa0000) { + ebda = 0x80000; + } + + return ebda; +} +#endif + +struct sdt { + char signature[4]; + uint32_t length; + uint8_t rev; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + uint32_t oem_rev; + char creator_id[4]; + uint32_t creator_rev; +} __attribute__((packed)); + +struct rsdp { + char signature[8]; + uint8_t checksum; + char oem_id[6]; + uint8_t rev; + uint32_t rsdt_addr; + // Revision 2 only after this comment + uint32_t length; + uint64_t xsdt_addr; + uint8_t ext_checksum; + uint8_t reserved[3]; +} __attribute__((packed)); + +struct rsdt { + struct sdt header; + char ptrs_start[]; +} __attribute__((packed)); + +struct smbios_entry_point_32 { + char anchor_str[4]; + /// This value summed with all the values of the table. + uint8_t checksum; + /// Length of the entry point table. + uint8_t length; + /// Major version of SMBIOS. + uint8_t major_version; + /// Minor version of SMBIOS. + uint8_t minor_version; + /// Size of the largest SMBIOS structure, in bytes, and encompasses the + /// structure’s formatted area and text strings + uint16_t max_structure_size; + uint8_t entry_point_revision; + char formatted_area[5]; + + char intermediate_anchor_str[5]; + /// Checksum for values from intermediate anchor str to the + /// end of table. + uint8_t intermediate_checksum; + /// Total length of SMBIOS Structure Table, pointed to by the structure + /// table address, in bytes. + uint16_t table_length; + /// 32-bit physical starting address of the read-only SMBIOS Structure + /// Table. + uint32_t table_address; + /// Total number of structures present in the SMBIOS Structure Table. + uint16_t number_of_structures; + /// Indicates compliance with a revision of this specification. + uint8_t bcd_revision; +} __attribute__((packed)); + +struct smbios_entry_point_64 { + char anchor_str[5]; + /// This value summed with all the values of the table. + uint8_t checksum; + /// Length of the entry point table. + uint8_t length; + /// Major version of SMBIOS. + uint8_t major_version; + /// Minor version of SMBIOS. + uint8_t minor_version; + uint8_t docrev; + uint8_t entry_point_revision; + uint8_t reserved; + /// Size of the SMBIOS Structure Table, in bytes. + uint32_t table_maximum_size; + /// 64-bit physical starting address of the read-only SMBIOS Structure + /// Table. + uint64_t table_address; +} __attribute__((packed)); + +struct madt { + struct sdt header; + uint32_t local_controller_addr; + uint32_t flags; + char madt_entries_begin[]; +} __attribute__((packed)); + +struct madt_header { + uint8_t type; + uint8_t length; +} __attribute__((packed)); + +struct madt_lapic { + struct madt_header header; + uint8_t acpi_processor_uid; + uint8_t lapic_id; + uint32_t flags; +} __attribute__((packed)); + +struct madt_x2apic { + struct madt_header header; + uint8_t reserved[2]; + uint32_t x2apic_id; + uint32_t flags; + uint32_t acpi_processor_uid; +} __attribute__((packed)); + +struct madt_io_apic { + uint8_t type; + uint8_t length; + uint8_t apic_id; + uint8_t reserved; + uint32_t address; + uint32_t gsib; +} __attribute__((packed)); + +struct madt_lapic_nmi { + struct madt_header header; // type=4, length=6 + uint8_t acpi_processor_uid; // 0xff = all processors + uint16_t flags; // MPS INTI flags + uint8_t lint; // 0 or 1 +} __attribute__((packed)); + +struct madt_x2apic_nmi { + struct madt_header header; // type=0x0a, length=12 + uint16_t flags; // MPS INTI flags + uint32_t acpi_processor_uid; // 0xffffffff = all processors + uint8_t lint; // 0 or 1 + uint8_t reserved[3]; +} __attribute__((packed)); + +struct madt_gicc { + struct madt_header header; + uint8_t reserved1[2]; + uint32_t iface_no; + uint32_t acpi_uid; + uint32_t flags; + uint32_t parking_ver; + uint32_t perf_gsiv; + uint64_t parking_addr; + uint64_t gicc_base_addr; + uint64_t gicv_base_addr; + uint64_t gich_base_addr; + uint32_t vgic_maint_gsiv; + uint64_t gicr_base_addr; + uint64_t mpidr; + uint8_t power_eff_class; + uint8_t reserved2; + uint16_t spe_overflow_gsiv; +} __attribute__((packed)); + +// Reference: https://github.com/riscv-non-isa/riscv-acpi/issues/15 +struct madt_riscv_intc { + struct madt_header header; + uint8_t version; + uint8_t reserved; + uint32_t flags; + uint64_t hartid; + uint32_t acpi_processor_uid; +} __attribute__((packed)); + +#define MADT_RISCV_INTC_ENABLED ((uint32_t)1 << 0) +#define MADT_RISCV_INTC_ONLINE_CAPABLE ((uint32_t)1 << 1) + +uint8_t acpi_checksum(void *ptr, size_t size); +void *acpi_get_rsdp(void); + +void *acpi_get_rsdp_v1(void); +void *acpi_get_rsdp_v2(void); + +void *acpi_get_table(const char *signature, int index); +void acpi_get_smbios(void **smbios32, void **smbios64); + +void acpi_map_tables(void); +void smbios_map_tables(void); + +#if defined (UEFI) +void efi_map_runtime_entries(void); +#endif + +#endif diff --git a/limine/common/lib/bli.c b/limine/common/lib/bli.c new file mode 100644 index 0000000..4aee0f2 --- /dev/null +++ b/limine/common/lib/bli.c @@ -0,0 +1,90 @@ +#if defined (UEFI) + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIMINE_BRAND L"Limine " LIMINE_VERSION + +static EFI_GUID bli_vendor_guid = { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } }; + +// The buffer must be at least 21 bytes long +void uint64_to_decwstr(uint64_t value, wchar_t *buf) { + wchar_t tmp[21]; + size_t i = 0; + + if (buf == NULL) { + return; + } + + if (value == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + // Convert digits in reverse order + while (value > 0) { + tmp[i++] = '0' + (value % 10); + value /= 10; + } + + // Reverse the string into the buffer + for (size_t j = 0; j < i; j++) { + buf[j] = tmp[i - j - 1]; + } + buf[i] = '\0'; +} + +void bli_set_loader_time(wchar_t *variable, uint64_t time) { + if (time == 0) + return; + + wchar_t time_wstr[21]; + uint64_to_decwstr(time, time_wstr); + + size_t len = 0; + while (time_wstr[len] != L'\0') len++; + + gRT->SetVariable(variable, + &bli_vendor_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + (len + 1) * sizeof(wchar_t), + time_wstr); +} + +void init_bli(void) { + bli_set_loader_time(L"LoaderTimeInitUSec", usec_at_bootloader_entry); + + gRT->SetVariable(L"LoaderInfo", + &bli_vendor_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(LIMINE_BRAND), + LIMINE_BRAND); + + char part_uuid_str[37]; + guid_to_string(&boot_volume->part_guid, part_uuid_str); + + // Convert part_uuid_str to a wide-char string + wchar_t part_uuid[37]; + for (size_t i = 0; i < 37; i++) { + part_uuid[i] = (wchar_t) part_uuid_str[i]; + } + + gRT->SetVariable(L"LoaderDevicePartUUID", + &bli_vendor_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(part_uuid), + part_uuid); +} + +void bli_on_boot(void) { + bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec()); +} + +#endif diff --git a/limine/common/lib/bli.h b/limine/common/lib/bli.h new file mode 100644 index 0000000..2e48041 --- /dev/null +++ b/limine/common/lib/bli.h @@ -0,0 +1,11 @@ +#ifndef LIB__BLI_H__ +#define LIB__BLI_H__ + +#if defined (UEFI) + +void init_bli(void); +void bli_on_boot(void); + +#endif + +#endif diff --git a/limine/common/lib/config.c b/limine/common/lib/config.c new file mode 100644 index 0000000..2f4c9fe --- /dev/null +++ b/limine/common/lib/config.c @@ -0,0 +1,756 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONFIG_B2SUM_SIGNATURE "++CONFIG_B2SUM_SIGNATURE++" +#define CONFIG_B2SUM_EMPTY "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + +const char *config_b2sum = CONFIG_B2SUM_SIGNATURE CONFIG_B2SUM_EMPTY; + +static bool config_get_entry_name(char *ret, size_t index, size_t limit); +static char *config_get_entry(size_t *size, size_t index); + +#define SEPARATOR '\n' + +bool config_ready = false; +no_unwind bool bad_config = false; + +static char *config_addr; + +#if defined (UEFI) + +#define EFI_APP_PATH_LEN 128 +static char efi_app_path[128] = {0}; + +static bool init_efi_app_path(size_t *len_out) { + EFI_STATUS status; + EFI_LOADED_IMAGE_PROTOCOL *loaded_image; + EFI_DEVICE_PATH_PROTOCOL *path; + CHAR16 *file_path, *p, *last_slash; + + EFI_GUID loaded_image_protocol_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; + + status = gBS->HandleProtocol(efi_image_handle, &loaded_image_protocol_guid, + (void **)&loaded_image); + if (status != 0) { + return false; + } + + path = loaded_image->FilePath; + + while (!(path->Type == END_DEVICE_PATH_TYPE && path->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) { + if (path->Type == MEDIA_DEVICE_PATH && path->SubType == MEDIA_FILEPATH_DP) { + goto found; + } + + uint16_t node_length = *((uint16_t *)&path->Length[0]); + if (node_length < 4) { + return false; + } + path = (void *)path + node_length; + } + + return false; + +found: + file_path = (CHAR16 *)((void *)path + 4); + + last_slash = NULL; + for (p = file_path; *p; p++) { + if (*p == L'\\') { + last_slash = p; + } + } + + if (last_slash) { + size_t len = (last_slash - file_path) + 1; + if (len >= EFI_APP_PATH_LEN) { + len = EFI_APP_PATH_LEN - 1; + } + + for (size_t i = 0; i < len; i++) { + efi_app_path[i] = (char)(file_path[i] & 0xff); + if (efi_app_path[i] == '\\') { + efi_app_path[i] = '/'; + } + } + efi_app_path[len] = 0; + if (len_out != NULL) { + *len_out = len; + } + } else { + efi_app_path[0] = '/'; + efi_app_path[1] = 0; + if (len_out != NULL) { + *len_out = 1; + } + } + + return true; +} +#endif + +int init_config_disk(struct volume *part) { +#if defined (UEFI) + bool use_default_efi_search_path = false; + + size_t len; + if (!init_efi_app_path(&len)) { + use_default_efi_search_path = true; + } else { + if (len + sizeof("limine.conf") >= EFI_APP_PATH_LEN) { + use_default_efi_search_path = true; + } else { + strcpy(efi_app_path + len, "limine.conf"); + } + } +#endif + + struct file_handle *f; + + bool old_cif = case_insensitive_fopen; + case_insensitive_fopen = true; + if ( + false +#if defined (UEFI) + || (f = fopen(part, use_default_efi_search_path ? "/EFI/BOOT/limine.conf" : efi_app_path)) != NULL +#endif + || (f = fopen(part, "/boot/limine/limine.conf")) != NULL + || (f = fopen(part, "/boot/limine.conf")) != NULL + || (f = fopen(part, "/limine/limine.conf")) != NULL + || (f = fopen(part, "/limine.conf")) != NULL + ) { + goto opened; + } + + case_insensitive_fopen = old_cif; + return -1; + +opened: + case_insensitive_fopen = old_cif; + + size_t config_size = f->size + 2; + config_addr = ext_mem_alloc(config_size); + + fread(f, config_addr, 0, f->size); + + fclose(f); + + return init_config(config_size); +} + +struct smbios_struct_header { + uint8_t type; + uint8_t length; + uint16_t handle; +} __attribute__((packed)); + +static size_t smbios_struct_size(struct smbios_struct_header *hdr, size_t remaining) { + // Validate minimum structure header size + if (remaining < sizeof(struct smbios_struct_header)) { + return 0; + } + if (hdr->length < sizeof(struct smbios_struct_header)) { + return 0; // Invalid structure + } + if (hdr->length > remaining) { + return 0; // Structure header claims more than remaining + } + + const char *string_data = (void *)((uintptr_t)hdr + hdr->length); + size_t string_area_max = remaining - hdr->length; + size_t i = 1; + for (; i < string_area_max && (string_data[i - 1] != '\0' || string_data[i] != '\0'); i++); + if (i >= string_area_max) { + return 0; // Unterminated string area + } + return hdr->length + i + 1; +} + +bool init_config_smbios(void) { + struct smbios_entry_point_32 *smbios_entry_32 = NULL; + struct smbios_entry_point_64 *smbios_entry_64 = NULL; + acpi_get_smbios((void **)&smbios_entry_32, (void **)&smbios_entry_64); + if (smbios_entry_32 == NULL && smbios_entry_64 == NULL) { + return false; + } + + struct smbios_struct_header *hdr = NULL; + size_t struct_count = 0; + size_t table_length = 0; + + if (smbios_entry_64) { + hdr = (void *)(uintptr_t) smbios_entry_64->table_address; + table_length = smbios_entry_64->table_maximum_size; + } else { + hdr = (void *)(uintptr_t) smbios_entry_32->table_address; + struct_count = smbios_entry_32->number_of_structures; + table_length = smbios_entry_32->table_length; + } + + if (hdr == NULL || table_length == 0) { + return false; + } + + size_t structure_bytes_processed = 0; + for (size_t struct_num = 0; hdr && (!struct_count || struct_num < struct_count); struct_num++) { + size_t remaining = table_length - structure_bytes_processed; + if (remaining < sizeof(struct smbios_struct_header)) { + return false; + } + + if (hdr->type == 127) + return false; + + size_t struct_size = smbios_struct_size(hdr, remaining); + if (struct_size == 0) { + return false; // Invalid structure + } + + if (hdr->type == 11 && hdr->length >= sizeof(struct smbios_struct_header)) { + const char *string_data = (void *)((uintptr_t) hdr + hdr->length); + size_t string_area_size = struct_size - hdr->length; + + size_t prefix_len = sizeof("limine:config:") - 1; + if (string_area_size > prefix_len && !strncmp(string_data, "limine:config:", prefix_len)) { + size_t total_len = strnlen(string_data, string_area_size); + if (total_len <= prefix_len) + continue; + size_t config_size = total_len - prefix_len + 2; + config_addr = ext_mem_alloc(config_size); + memcpy(config_addr, &string_data[prefix_len], config_size - 1); + config_addr[config_size - 1] = '\0'; + return !init_config(config_size); + } + } + + structure_bytes_processed += struct_size; + if (structure_bytes_processed >= table_length) { + return false; + } + + hdr = (void *)((uintptr_t) hdr + struct_size); + } + + return false; +} + +#define NOT_CHILD (-1) +#define DIRECT_CHILD 0 +#define INDIRECT_CHILD 1 + +static int is_child(char *buf, size_t limit, + size_t current_depth, size_t index) { + if (!config_get_entry_name(buf, index, limit)) + return NOT_CHILD; + if (strlen(buf) < current_depth + 1) + return NOT_CHILD; + for (size_t j = 0; j < current_depth; j++) + if (buf[j] != '/') + return NOT_CHILD; + if (buf[current_depth] == '/') + return INDIRECT_CHILD; + return DIRECT_CHILD; +} + +static bool is_directory(char *buf, size_t limit, + size_t current_depth, size_t index) { + switch (is_child(buf, limit, current_depth + 1, index + 1)) { + default: + case NOT_CHILD: + return false; + case INDIRECT_CHILD: + bad_config = true; + panic(true, "config: Malformed config file. Parentless child."); + case DIRECT_CHILD: + return true; + } +} + +static struct menu_entry *create_menu_tree(struct menu_entry *parent, + size_t current_depth, size_t index) { + struct menu_entry *root = NULL, *prev = NULL; + + for (size_t i = index; ; i++) { + static char name[64]; + + switch (is_child(name, 64, current_depth, i)) { + case NOT_CHILD: + return root; + case INDIRECT_CHILD: + continue; + case DIRECT_CHILD: + break; + } + + struct menu_entry *entry = ext_mem_alloc(sizeof(struct menu_entry)); + + if (root == NULL) + root = entry; + + config_get_entry_name(name, i, 64); + + bool default_expanded = name[current_depth] == '+'; + + char *n = &name[current_depth + default_expanded]; + while (*n == ' ') { + n++; + } + + size_t n_len = strlen(n); + if (n_len >= sizeof(entry->name)) { + n_len = sizeof(entry->name) - 1; + } + memcpy(entry->name, n, n_len); + entry->name[n_len] = 0; + entry->parent = parent; + + size_t entry_size; + char *config_entry = config_get_entry(&entry_size, i); + entry->body = ext_mem_alloc(entry_size + 1); + memcpy(entry->body, config_entry, entry_size); + entry->body[entry_size] = 0; + + if (is_directory(name, 64, current_depth, i)) { + entry->sub = create_menu_tree(entry, current_depth + 1, i + 1); + entry->expanded = default_expanded; + } + + char *comment = config_get_value(entry->body, 0, "COMMENT"); + if (comment != NULL) { + entry->comment = strdup(comment); + } + + if (prev != NULL) + prev->next = entry; + prev = entry; + } +} + +struct menu_entry *menu_tree = NULL; + +struct macro { + char name[1024]; + char value[2048]; + struct macro *next; +}; + +static struct macro *macros = NULL; + +int init_config(size_t config_size) { + config_b2sum += sizeof(CONFIG_B2SUM_SIGNATURE) - 1; + + if (memcmp((void *)config_b2sum, CONFIG_B2SUM_EMPTY, 128) != 0) { + editor_enabled = false; + + uint8_t out_buf[BLAKE2B_OUT_BYTES]; + blake2b(out_buf, config_addr, config_size - 2); + uint8_t hash_buf[BLAKE2B_OUT_BYTES]; + + for (size_t i = 0; i < BLAKE2B_OUT_BYTES; i++) { + hash_buf[i] = digit_to_int(config_b2sum[i * 2]) << 4 | digit_to_int(config_b2sum[i * 2 + 1]); + } + + if (memcmp(hash_buf, out_buf, BLAKE2B_OUT_BYTES) != 0) { + panic(false, "!!! CHECKSUM MISMATCH FOR CONFIG FILE !!!"); + } + } + + // add trailing newline if not present + config_addr[config_size - 2] = '\n'; + + size_t config_alloc_size = config_size; + + // remove windows carriage returns and spaces at the start and end of lines, if any + for (size_t i = 0; i < config_size; i++) { + size_t skip = 0; + if (config_addr[i] == ' ' || config_addr[i] == '\t') { + while (i + skip < config_size && (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t')) { + skip++; + } + if (i + skip < config_size && config_addr[i + skip] == '\n') { + goto skip_loop; + } + skip = 0; + } + while (i + skip < config_size + && ((config_addr[i + skip] == '\r') + || ((!i || config_addr[i - 1] == '\n') && (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t'))) + ) { + skip++; + } +skip_loop: + if (skip) { + for (size_t j = i; j < config_size - skip; j++) + config_addr[j] = config_addr[j + skip]; + config_size -= skip; + } + } + + // Load macros + struct macro *arch_macro = ext_mem_alloc(sizeof(struct macro)); + strcpy(arch_macro->name, "ARCH"); +#if defined (__x86_64__) + strcpy(arch_macro->value, "x86-64"); +#elif defined (__i386__) + { + uint32_t eax, ebx, ecx, edx; + if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) { + strcpy(arch_macro->value, "ia-32"); + } else { + strcpy(arch_macro->value, "x86-64"); + } + } +#elif defined (__aarch64__) + strcpy(arch_macro->value, "aarch64"); +#elif defined (__riscv) + strcpy(arch_macro->value, "riscv64"); +#elif defined (__loongarch64) + strcpy(arch_macro->value, "loongarch64"); +#else +#error "Unspecified architecture" +#endif + arch_macro->next = macros; + macros = arch_macro; + + struct macro *fw_type_macro = ext_mem_alloc(sizeof(struct macro)); + strcpy(fw_type_macro->name, "FW_TYPE"); +#if defined (UEFI) + strcpy(fw_type_macro->value, "UEFI"); +#else + strcpy(fw_type_macro->value, "BIOS"); +#endif + fw_type_macro->next = macros; + macros = fw_type_macro; + + for (size_t i = 0; i < config_size;) { + if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0) + || (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) { + struct macro *macro = ext_mem_alloc(sizeof(struct macro)); + + i += i ? 3 : 2; + size_t j; + for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) { + if (j >= sizeof(macro->name) - 1) { + bad_config = true; + panic(true, "config: Macro name too long (max %U)", (uint64_t)(sizeof(macro->name) - 1)); + } + macro->name[j] = config_addr[i]; + } + + if (config_addr[i] == '\n' || config_addr[i] == 0 || config_addr[i+1] != '=') { + pmm_free(macro, sizeof(struct macro)); + continue; + } + i += 2; + + macro->name[j] = 0; + + for (j = 0; config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) { + if (j >= sizeof(macro->value) - 1) { + bad_config = true; + panic(true, "config: Macro value too long (max %U)", (uint64_t)(sizeof(macro->value) - 1)); + } + macro->value[j] = config_addr[i]; + } + macro->value[j] = 0; + + macro->next = macros; + macros = macro; + + continue; + } + + i++; + } + + // Expand macros + if (macros != NULL) { + // Check for overflow before multiplication + if (config_size > SIZE_MAX / 4) { + bad_config = true; + panic(true, "config: Config file too large for macro expansion"); + } + size_t new_config_size = config_size * 4; + char *new_config = ext_mem_alloc(new_config_size); + + size_t i, in; + for (i = 0, in = 0; i < config_size;) { + if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0) + || (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) { + size_t orig_i = i; + i += i ? 3 : 2; + while (i < config_size && config_addr[i] != '}') { + i++; + } + if (i >= config_size) { + bad_config = true; + panic(true, "config: Malformed macro usage"); + } + i++; // skip '}' + if (i >= config_size || config_addr[i++] != '=') { + i = orig_i; + goto next; + } + while (config_addr[i] != '\n' && config_addr[i] != 0) { + i++; + if (i >= config_size) { + bad_config = true; + panic(true, "config: Malformed macro usage"); + } + } + continue; + } + +next: + if (config_size - i >= 2 && memcmp(config_addr + i, "${", 2) == 0) { + char *macro_name = ext_mem_alloc(1024); + i += 2; + size_t j; + for (j = 0; j < 1023 && config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) { + macro_name[j] = config_addr[i]; + } + if (config_addr[i] != '}') { + bad_config = true; + panic(true, "config: Malformed macro usage"); + } + i++; + macro_name[j] = 0; + char *macro_value = ""; + struct macro *macro = macros; + for (;;) { + if (macro == NULL) { + break; + } + if (strcmp(macro->name, macro_name) == 0) { + macro_value = macro->value; + break; + } + macro = macro->next; + } + pmm_free(macro_name, 1024); + for (j = 0; macro_value[j] != 0; j++, in++) { + if (in >= new_config_size) { + goto overflow; + } + new_config[in] = macro_value[j]; + } + continue; + } + + if (in >= new_config_size) { +overflow: + bad_config = true; + panic(true, "config: Macro-induced buffer overflow"); + } + new_config[in++] = config_addr[i++]; + } + + pmm_free(config_addr, config_alloc_size); + + config_addr = new_config; + config_size = in; + + // Free macros + struct macro *macro = macros; + for (;;) { + if (macro == NULL) { + break; + } + struct macro *next = macro->next; + pmm_free(macro, sizeof(struct macro)); + macro = next; + } + macros = NULL; + } + + config_ready = true; + + menu_tree = create_menu_tree(NULL, 1, 0); + + size_t s; + char *c = config_get_entry(&s, 0); + if (c != NULL) { + while (*c != '/' && c > config_addr) { + c--; + } + if (*c == '/' && c > config_addr) { + c[-1] = 0; + } + } + + return 0; +} + +static bool config_get_entry_name(char *ret, size_t index, size_t limit) { + if (!config_ready) + return false; + + char *p = config_addr; + + for (size_t i = 0; i <= index; i++) { + while (*p != '/') { + if (!*p) + return false; + p++; + } + p++; + if ((p - 1) != config_addr && *(p - 2) != '\n') + i--; + } + + p--; + + size_t i; + for (i = 0; i < (limit - 1); i++) { + if (p[i] == SEPARATOR) + break; + ret[i] = p[i]; + } + + ret[i] = 0; + return true; +} + +static char *config_get_entry(size_t *size, size_t index) { + if (!config_ready) + return NULL; + + char *ret; + char *p = config_addr; + + for (size_t i = 0; i <= index; i++) { + while (*p != '/') { + if (!*p) + return NULL; + p++; + } + p++; + if ((p - 1) != config_addr && *(p - 2) != '\n') + i--; + } + + do { + p++; + } while (*p != '\n' && *p != '\0'); + + ret = p; + +cont: + while (*p != '/' && *p) + p++; + + if (*p && *(p - 1) != '\n') { + p++; + goto cont; + } + + *size = p - ret; + + return ret; +} + +static const char *lastkey; + +struct conf_tuple config_get_tuple(const char *config, size_t index, + const char *key1, const char *key2) { + // Static buffers for return values. + // Callers must copy the result if they need persistence across calls. + #define CONF_TUPLE_BUF_SIZE 4096 + static char value1_buf[CONF_TUPLE_BUF_SIZE]; + static char value2_buf[CONF_TUPLE_BUF_SIZE]; + + struct conf_tuple conf_tuple; + + char *tmp = config_get_value(config, index, key1); + if (tmp == NULL) { + return (struct conf_tuple){0}; + } + size_t len = strlen(tmp); + if (len >= CONF_TUPLE_BUF_SIZE) { + len = CONF_TUPLE_BUF_SIZE - 1; + } + memcpy(value1_buf, tmp, len); + value1_buf[len] = '\0'; + conf_tuple.value1 = value1_buf; + + const char *lk1 = lastkey; + + tmp = config_get_value(lk1, 0, key2); + if (tmp != NULL) { + len = strlen(tmp); + if (len >= CONF_TUPLE_BUF_SIZE) { + len = CONF_TUPLE_BUF_SIZE - 1; + } + memcpy(value2_buf, tmp, len); + value2_buf[len] = '\0'; + conf_tuple.value2 = value2_buf; + } else { + conf_tuple.value2 = NULL; + } + + const char *lk2 = lastkey; + + const char *next_value1 = config_get_value(config, index + 1, key1); + + const char *lk3 = lastkey; + + if (conf_tuple.value2 != NULL && next_value1 != NULL) { + if ((uintptr_t)lk2 > (uintptr_t)lk3) { + conf_tuple.value2 = NULL; + } + } + + return conf_tuple; +} + +char *config_get_value(const char *config, size_t index, const char *key) { + // Static buffer for return values. + // Callers must copy the result if they need persistence across calls. + #define CONFIG_VALUE_BUF_SIZE 4096 + static char buf[CONFIG_VALUE_BUF_SIZE]; + + if (!key || !config_ready) + return NULL; + + if (config == NULL) + config = config_addr; + + size_t key_len = strlen(key); + + for (size_t i = 0; config[i]; i++) { + if (!strncasecmp(&config[i], key, key_len) && config[i + key_len] == ':') { + if (i && config[i - 1] != SEPARATOR) + continue; + if (index--) + continue; + i += key_len + 1; + while (config[i] == ' ' || config[i] == '\t') { + i++; + } + size_t value_len; + for (value_len = 0; + config[i + value_len] != SEPARATOR && config[i + value_len]; + value_len++); + if (value_len >= CONFIG_VALUE_BUF_SIZE) { + value_len = CONFIG_VALUE_BUF_SIZE - 1; + } + memcpy(buf, config + i, value_len); + buf[value_len] = '\0'; + lastkey = config + i; + return buf; + } + } + + return NULL; +} diff --git a/limine/common/lib/config.h b/limine/common/lib/config.h new file mode 100644 index 0000000..00c8094 --- /dev/null +++ b/limine/common/lib/config.h @@ -0,0 +1,36 @@ +#ifndef LIB__CONFIG_H__ +#define LIB__CONFIG_H__ + +#include +#include +#include + +extern bool config_ready; +extern bool bad_config; + +struct menu_entry { + char name[64]; + char *comment; + struct menu_entry *parent; + struct menu_entry *sub; + bool expanded; + char *body; + struct menu_entry *next; +}; + +struct conf_tuple { + char *value1; + char *value2; +}; + +extern struct menu_entry *menu_tree; + +int init_config_disk(struct volume *part); +bool init_config_smbios(void); +int init_config(size_t config_size); + +char *config_get_value(const char *config, size_t index, const char *key); +struct conf_tuple config_get_tuple(const char *config, size_t index, + const char *key1, const char *key2); + +#endif diff --git a/limine/common/lib/elf.c b/limine/common/lib/elf.c new file mode 100644 index 0000000..ee686bd --- /dev/null +++ b/limine/common/lib/elf.c @@ -0,0 +1,1209 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 + +#define PT_LOAD 0x00000001 +#define PT_DYNAMIC 0x00000002 +#define PT_INTERP 0x00000003 +#define PT_PHDR 0x00000006 + +#define DT_NULL 0x00000000 +#define DT_NEEDED 0x00000001 +#define DT_RELA 0x00000007 +#define DT_RELASZ 0x00000008 +#define DT_RELAENT 0x00000009 +#define DT_RELR 0x00000024 +#define DT_RELRSZ 0x00000023 +#define DT_RELRENT 0x00000025 +#define DT_SYMTAB 0x00000006 +#define DT_SYMENT 0x0000000b +#define DT_STRTAB 0x00000005 +#define DT_PLTREL 0x00000014 +#define DT_PLTRELSZ 0x00000002 +#define DT_JMPREL 0x00000017 +#define DT_FLAGS_1 0x6ffffffb + +#define DF_1_PIE 0x08000000 + +#define ABI_SYSV 0x00 +#define ARCH_X86_64 0x3e +#define ARCH_X86_32 0x03 +#define ARCH_AARCH64 0xb7 +#define ARCH_RISCV 0xf3 +#define ARCH_LOONGARCH 0x102 +#define BITS_LE 0x01 +#define ELFCLASS32 0x01 +#define ELFCLASS64 0x02 +#define SHT_RELA 0x00000004 +#define SHN_UNDEF 0x00000000 +#define STB_WEAK 0x00000002 +#define R_X86_64_NONE 0x00000000 +#define R_AARCH64_NONE 0x00000000 +#define R_RISCV_NONE 0x00000000 +#define R_LARCH_NONE 0x00000000 +#define R_X86_64_RELATIVE 0x00000008 +#define R_AARCH64_RELATIVE 0x00000403 +#define R_RISCV_RELATIVE 0x00000003 +#define R_LARCH_RELATIVE 0x00000003 +#define R_X86_64_GLOB_DAT 0x00000006 +#define R_AARCH64_GLOB_DAT 0x00000401 +#define R_X86_64_JUMP_SLOT 0x00000007 +#define R_AARCH64_JUMP_SLOT 0x00000402 +#define R_RISCV_JUMP_SLOT 0x00000005 +#define R_LARCH_JUMP_SLOT 0x00000005 +#define R_X86_64_64 0x00000001 +#define R_RISCV_64 0x00000002 +#define R_LARCH_64 0x00000002 +#define R_AARCH64_ABS64 0x00000101 + +#define R_INTERNAL_RELR 0xfffffff0 + +/* Indices into identification array */ +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 + +struct elf32_hdr { + uint8_t ident[16]; + uint16_t type; + uint16_t machine; + uint32_t version; + uint32_t entry; + uint32_t phoff; + uint32_t shoff; + uint32_t flags; + uint16_t hdr_size; + uint16_t phdr_size; + uint16_t ph_num; + uint16_t shdr_size; + uint16_t sh_num; + uint16_t shstrndx; +}; + +struct elf64_phdr { + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +}; + +struct elf32_phdr { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +}; + +struct elf64_rela { + uint64_t r_addr; + uint32_t r_info; + uint32_t r_symbol; + uint64_t r_addend; +}; + +struct elf32_dyn { + uint32_t d_tag; + uint32_t d_un; +}; + +struct elf64_dyn { + uint64_t d_tag; + uint64_t d_un; +}; + +static bool elf32_validate(struct elf32_hdr *hdr) { + if (strncmp((char *)hdr->ident, "\177ELF", 4)) { + panic(true, "elf: Not a valid ELF file."); + } + + if (hdr->ident[EI_DATA] != BITS_LE) { + panic(true, "elf: Not a Little-endian ELF file."); + } + + if (hdr->machine != ARCH_X86_32) { + panic(true, "elf: Not an IA-32 ELF file."); + } + + return true; +} + +static bool elf64_validate(struct elf64_hdr *hdr) { + if (strncmp((char *)hdr->ident, "\177ELF", 4)) { + panic(true, "elf: Not a valid ELF file."); + } + + if (hdr->ident[EI_DATA] != BITS_LE) { + panic(true, "elf: Not a Little-endian ELF file."); + } + +#if defined (__x86_64__) || defined (__i386__) + if (hdr->machine != ARCH_X86_64) { + panic(true, "elf: Not an x86-64 ELF file."); + } +#elif defined (__aarch64__) + if (hdr->machine != ARCH_AARCH64) { + panic(true, "elf: Not an aarch64 ELF file."); + } +#elif defined (__riscv) + if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) { + panic(true, "elf: Not a riscv64 ELF file."); + } +#elif defined (__loongarch64) + if (hdr->machine != ARCH_LOONGARCH && hdr->ident[EI_CLASS] == ELFCLASS64) { + panic(true, "elf: Not a loongarch64 ELF file."); + } +#else +#error Unknown architecture +#endif + + return true; +} + +int elf_bits(uint8_t *elf) { + struct elf64_hdr *hdr = (void *)elf; + + if (strncmp((char *)hdr->ident, "\177ELF", 4)) { + return -1; + } + + switch (hdr->machine) { + case ARCH_X86_64: + case ARCH_AARCH64: + return 64; + case ARCH_RISCV: + case ARCH_LOONGARCH: + if (hdr->ident[EI_CLASS] == ELFCLASS64) return 64; + if (hdr->ident[EI_CLASS] == ELFCLASS32) return 32; + return -1; + case ARCH_X86_32: + return 32; + default: + return -1; + } +} + +struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf) { + struct elf_section_hdr_info info = {0}; + + struct elf64_hdr *hdr = (void *)elf; + + elf64_validate(hdr); + + info.num = hdr->sh_num; + info.section_entry_size = hdr->shdr_size; + info.str_section_idx = hdr->shstrndx; + info.section_offset = hdr->shoff; + + return info; +} + +struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf) { + struct elf_section_hdr_info info = {0}; + + struct elf32_hdr *hdr = (void *)elf; + + elf32_validate(hdr); + + info.num = hdr->sh_num; + info.section_entry_size = hdr->shdr_size; + info.str_section_idx = hdr->shstrndx; + info.section_offset = hdr->shoff; + + return info; +} + +static bool elf64_is_relocatable(uint8_t *elf, struct elf64_hdr *hdr) { + if (hdr->phdr_size < sizeof(struct elf64_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)"); + } + + if (hdr->type != ET_DYN) { + return false; + } + + // Find PT_DYNAMIC segment + for (size_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_DYNAMIC) { + continue; + } + + if (phdr->p_filesz == 0) { + panic(true, "elf: ELF file type is ET_DYN, but PT_DYNAMIC segment has 0 size"); + } + + return true; + } + + panic(true, "elf: ELF file type is ET_DYN, but PT_DYNAMIC segment missing"); +} + +static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) { + if (hdr->phdr_size < sizeof(struct elf64_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)"); + } + + uint64_t symtab_offset = 0; + uint64_t symtab_ent = 0; + uint64_t symtab_size = 0; // Size of symbol table (if known) + uint64_t strtab_offset = 0; + uint64_t strtab_size = 0; // Size of string table (if known) + + uint64_t dt_pltrel = 0; + uint64_t dt_pltrelsz = 0; + uint64_t dt_jmprel = 0; + + uint64_t relr_offset = 0; + uint64_t relr_size = 0; + uint64_t relr_ent = 0; + + uint64_t rela_offset = 0; + uint64_t rela_size = 0; + uint64_t rela_ent = 0; + + // Find DYN segment + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_DYNAMIC) + continue; + + for (uint16_t j = 0; j < phdr->p_filesz / sizeof(struct elf64_dyn); j++) { + struct elf64_dyn *dyn = (void *)elf + (phdr->p_offset + j * sizeof(struct elf64_dyn)); + + switch (dyn->d_tag) { + case DT_RELA: + rela_offset = dyn->d_un; + break; + case DT_RELAENT: + rela_ent = dyn->d_un; + break; + case DT_RELASZ: + rela_size = dyn->d_un; + break; + case DT_RELR: + relr_offset = dyn->d_un; + break; + case DT_RELRENT: + relr_ent = dyn->d_un; + break; + case DT_RELRSZ: + relr_size = dyn->d_un; + break; + case DT_SYMTAB: + symtab_offset = dyn->d_un; + break; + case DT_STRTAB: + strtab_offset = dyn->d_un; + break; + case DT_SYMENT: + symtab_ent = dyn->d_un; + if (symtab_ent < sizeof(struct elf64_sym)) { + panic(true, "elf: symtab_ent < sizeof(struct elf64_sym)"); + } + break; + case DT_PLTREL: + dt_pltrel = dyn->d_un; + break; + case DT_PLTRELSZ: + dt_pltrelsz = dyn->d_un; + break; + case DT_JMPREL: + dt_jmprel = dyn->d_un; + break; + case DT_NEEDED: + panic(true, "elf: ELF file attempts to load a dynamically linked library"); + case DT_NULL: + goto end_of_pt_segment; + } + } + + break; + } +end_of_pt_segment: + + if (rela_offset != 0) { + bool rela_translated = false; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (_phdr->p_vaddr <= rela_offset && _phdr->p_vaddr + _phdr->p_filesz > rela_offset) { + rela_offset -= _phdr->p_vaddr; + rela_offset += _phdr->p_offset; + rela_translated = true; + break; + } + } + if (!rela_translated) { + panic(true, "elf: RELA vaddr not in any PT_LOAD segment"); + } + } + + if (relr_offset != 0) { + bool relr_translated = false; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (_phdr->p_vaddr <= relr_offset && _phdr->p_vaddr + _phdr->p_filesz > relr_offset) { + relr_offset -= _phdr->p_vaddr; + relr_offset += _phdr->p_offset; + relr_translated = true; + break; + } + } + if (!relr_translated) { + panic(true, "elf: RELR vaddr not in any PT_LOAD segment"); + } + } + + if (symtab_offset != 0) { + bool symtab_translated = false; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (_phdr->p_vaddr <= symtab_offset && _phdr->p_vaddr + _phdr->p_filesz > symtab_offset) { + // Calculate size as remaining bytes in segment from symtab start + symtab_size = _phdr->p_filesz - (symtab_offset - _phdr->p_vaddr); + symtab_offset -= _phdr->p_vaddr; + symtab_offset += _phdr->p_offset; + symtab_translated = true; + break; + } + } + if (!symtab_translated) { + panic(true, "elf: SYMTAB vaddr not in any PT_LOAD segment"); + } + } + + if (strtab_offset != 0) { + bool strtab_translated = false; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (_phdr->p_vaddr <= strtab_offset && _phdr->p_vaddr + _phdr->p_filesz > strtab_offset) { + // Calculate size as remaining bytes in segment from strtab start + strtab_size = _phdr->p_filesz - (strtab_offset - _phdr->p_vaddr); + strtab_offset -= _phdr->p_vaddr; + strtab_offset += _phdr->p_offset; + strtab_translated = true; + break; + } + } + if (!strtab_translated) { + panic(true, "elf: STRTAB vaddr not in any PT_LOAD segment"); + } + } + + if (dt_jmprel != 0) { + bool jmprel_translated = false; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (_phdr->p_vaddr <= dt_jmprel && _phdr->p_vaddr + _phdr->p_filesz > dt_jmprel) { + dt_jmprel -= _phdr->p_vaddr; + dt_jmprel += _phdr->p_offset; + jmprel_translated = true; + break; + } + } + if (!jmprel_translated) { + panic(true, "elf: JMPREL vaddr not in any PT_LOAD segment"); + } + } + + size_t relocs_i = 0; + if (relr_size != 0) { + if (relr_ent != 8) { + panic(true, "elf: relr_ent != 8"); + } + for (size_t i = 0; i < relr_size / relr_ent; i++) { + uint64_t entry = *((uint64_t *)(elf + relr_offset + i * relr_ent)); + + if ((entry & 1) == 0) { + relocs_i++; + } else { + relocs_i += __builtin_popcountll(entry) - 1; + } + } + } + size_t relr_count = relocs_i; + if (rela_size != 0) { + if (rela_ent < sizeof(struct elf64_rela)) { + panic(true, "elf: rela_ent < sizeof(struct elf64_rela)"); + } + relocs_i += rela_size / rela_ent; + } + if (dt_pltrelsz != 0) { + if (dt_pltrel != DT_RELA) { + panic(true, "elf: dt_pltrel != DT_RELA"); + } + if (rela_ent == 0) { + panic(true, "elf: dt_pltrelsz != 0 but rela_ent == 0"); + } + relocs_i += dt_pltrelsz / rela_ent; + } + struct elf64_rela **relocs = ext_mem_alloc(relocs_i * sizeof(struct elf64_rela *)); + + if (relr_size != 0) { + size_t relr_i; + for (relr_i = 0; relr_i < relr_count; relr_i++) { + relocs[relr_i] = ext_mem_alloc(sizeof(struct elf64_rela)); + relocs[relr_i]->r_info = R_INTERNAL_RELR; + } + + // This logic is partially lifted from https://maskray.me/blog/2021-10-31-relative-relocations-and-relr + uint64_t where = 0; + relr_i = 0; + for (size_t i = 0; i < relr_size / relr_ent; i++) { + uint64_t entry = *((uint64_t *)(elf + relr_offset + i * relr_ent)); + + if ((entry & 1) == 0) { + where = entry; + relocs[relr_i++]->r_addr = where; + where += 8; + } else { + for (size_t j = 0; (entry >>= 1) != 0; j++) { + if ((entry & 1) != 0) { + relocs[relr_i++]->r_addr = where + j * 8; + } + } + where += 63 * 8; + } + } + } + + if (rela_size != 0) { + for (uint64_t i = relr_count, offset = 0; offset < rela_size; offset += rela_ent) { + relocs[i++] = (void *)elf + (rela_offset + offset); + } + } + + if (dt_pltrelsz != 0) { + for (uint64_t i = relr_count + rela_size / rela_ent, offset = 0; offset < dt_pltrelsz; offset += rela_ent) { + relocs[i++] = (void *)elf + (dt_jmprel + offset); + } + } + + for (size_t i = 0; i < relocs_i; i++) { + struct elf64_rela *relocation = relocs[i]; + + // Relocation is before buffer + if (relocation->r_addr < vaddr) + continue; + + // Relocation is after buffer + if (size < 8 || relocation->r_addr > vaddr + size - 8) + continue; + + // It's inside it, calculate where it is + uint64_t *ptr = (uint64_t *)((buffer - vaddr) + relocation->r_addr); + + switch (relocation->r_info) { +#if defined (__x86_64__) || defined (__i386__) + case R_X86_64_NONE: +#elif defined (__aarch64__) + case R_AARCH64_NONE: +#elif defined (__riscv) + case R_RISCV_NONE: +#elif defined (__loongarch64) + case R_LARCH_NONE: +#endif + { + break; + } +#if defined (__x86_64__) || defined (__i386__) + case R_X86_64_RELATIVE: +#elif defined (__aarch64__) + case R_AARCH64_RELATIVE: +#elif defined (__riscv) + case R_RISCV_RELATIVE: +#elif defined (__loongarch64) + case R_LARCH_RELATIVE: +#endif + { + *ptr = slide + relocation->r_addend; + break; + } + case R_INTERNAL_RELR: + { + *ptr += slide; + break; + } +#if defined (__x86_64__) || defined (__i386__) + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: +#elif defined (__aarch64__) + case R_AARCH64_GLOB_DAT: + case R_AARCH64_JUMP_SLOT: +#elif defined (__riscv) + case R_RISCV_JUMP_SLOT: +#elif defined (__loongarch64) + case R_LARCH_JUMP_SLOT: +#endif + { + if (symtab_offset == 0 || symtab_ent == 0) { + panic(true, "elf: Relocation requires symbol table but none present"); + } + if (symtab_size == 0) { + panic(true, "elf: Symtab vaddr translation failed"); + } + // Validate symbol index is within bounds + uint64_t sym_offset = symtab_ent * (uint64_t)relocation->r_symbol; + if (sym_offset + sizeof(struct elf64_sym) > symtab_size) { + panic(true, "elf: Symbol index %u out of bounds", relocation->r_symbol); + } + struct elf64_sym *s = (void *)elf + symtab_offset + sym_offset; + if (s->st_shndx == SHN_UNDEF) { + if ((s->st_info >> 4) == STB_WEAK) { + *ptr = 0; + break; + } + if (strtab_size == 0) { + panic(true, "elf: Strtab vaddr translation failed"); + } + // Validate string table access + if (s->st_name >= strtab_size) { + panic(true, "elf: Symbol name offset out of bounds"); + } + panic(true, "elf: Unresolved symbol \"%s\"", elf + strtab_offset + s->st_name); + } + *ptr = slide + s->st_value +#if defined (__aarch64__) + + relocation->r_addend +#endif + ; + break; + } +#if defined (__x86_64__) || defined (__i386__) + case R_X86_64_64: +#elif defined (__aarch64__) + case R_AARCH64_ABS64: +#elif defined (__riscv) + case R_RISCV_64: +#elif defined (__loongarch64) + case R_LARCH_64: +#endif + { + if (symtab_offset == 0 || symtab_ent == 0) { + panic(true, "elf: Relocation requires symbol table but none present"); + } + if (symtab_size == 0) { + panic(true, "elf: Symtab vaddr translation failed"); + } + // Validate symbol index is within bounds + uint64_t sym_offset = symtab_ent * (uint64_t)relocation->r_symbol; + if (sym_offset + sizeof(struct elf64_sym) > symtab_size) { + panic(true, "elf: Symbol index %u out of bounds", relocation->r_symbol); + } + struct elf64_sym *s = (void *)elf + symtab_offset + sym_offset; + if (s->st_shndx == SHN_UNDEF) { + if ((s->st_info >> 4) == STB_WEAK) { + *ptr = 0; + break; + } + if (strtab_size == 0) { + panic(true, "elf: Strtab vaddr translation failed"); + } + // Validate string table access + if (s->st_name >= strtab_size) { + panic(true, "elf: Symbol name offset out of bounds"); + } + panic(true, "elf: Unresolved symbol \"%s\"", elf + strtab_offset + s->st_name); + } + *ptr = slide + s->st_value + relocation->r_addend; + break; + } + default: { + panic(true, "elf: Unknown relocation type: %x", relocation->r_info); + } + } + } + + for (size_t i = 0; i < relr_count; i++) { + pmm_free(relocs[i], sizeof(struct elf64_rela)); + } + pmm_free(relocs, relocs_i * sizeof(struct elf64_rela *)); + + return true; +} + +bool elf64_load_section(uint8_t *elf, size_t file_size, void *buffer, const char *name, size_t limit, uint64_t slide) { + struct elf64_hdr *hdr = (void *)elf; + + elf64_validate(hdr); + + if (hdr->sh_num == 0) { + return false; + } + + if (hdr->shdr_size < sizeof(struct elf64_shdr)) { + panic(true, "elf: shdr_size < sizeof(struct elf64_shdr)"); + } + + if (hdr->shstrndx >= hdr->sh_num) { + return false; + } + + // Validate section header table is within file bounds + uint64_t shdr_table_end = (uint64_t)hdr->shoff + (uint64_t)hdr->sh_num * hdr->shdr_size; + if (shdr_table_end > file_size) { + return false; + } + + struct elf64_shdr *shstrtab = (void *)elf + (hdr->shoff + hdr->shstrndx * hdr->shdr_size); + + // Validate shstrtab offset and size are within file bounds + if (shstrtab->sh_offset >= file_size || shstrtab->sh_size > file_size - shstrtab->sh_offset) { + return false; + } + + char *names = (void *)elf + shstrtab->sh_offset; + + for (uint16_t i = 0; i < hdr->sh_num; i++) { + struct elf64_shdr *section = (void *)elf + (hdr->shoff + i * hdr->shdr_size); + + // Validate sh_name is within the string table + if (section->sh_name >= shstrtab->sh_size) { + continue; + } + + // Ensure the string is NUL-terminated within the string table + if (!memchr(&names[section->sh_name], '\0', shstrtab->sh_size - section->sh_name)) { + continue; + } + + if (strcmp(&names[section->sh_name], name) == 0) { + // Validate section data is within file bounds + if (section->sh_offset >= file_size || section->sh_size > file_size - section->sh_offset) { + return false; + } + + if (limit == 0) { + *(void **)buffer = ext_mem_alloc(section->sh_size); + buffer = *(void **)buffer; + limit = section->sh_size; + } + if (section->sh_size > limit) { + return false; + } + memcpy(buffer, elf + section->sh_offset, section->sh_size); + return elf64_apply_relocations(elf, hdr, buffer, section->sh_addr, section->sh_size, slide); + } + } + + return false; +} + +static uint64_t elf64_max_align(uint8_t *elf) { + uint64_t ret = 0; + + struct elf64_hdr *hdr = (void *)elf; + + if (hdr->phdr_size < sizeof(struct elf64_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)"); + } + + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) { + continue; + } + + if (phdr->p_align > 1 && (phdr->p_align & (phdr->p_align - 1)) != 0) { + panic(true, "elf: p_align is not a power of 2"); + } + + if (phdr->p_align > ret) { + ret = phdr->p_align; + } + } + + if (ret == 0) { + panic(true, "elf: Executable has no loadable segments"); + } + + return ret; +} + +static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct mem_range **_ranges, uint64_t *_ranges_count) { + struct elf64_hdr *hdr = (void *)elf; + + uint64_t ranges_count = 0; + + if (hdr->phdr_size < sizeof(struct elf64_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)"); + } + + bool is_reloc = elf64_is_relocatable(elf, hdr); + + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) { + continue; + } + + if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) { + if (!is_reloc) { + continue; + } + } + + ranges_count++; + } + + if (ranges_count == 0) { + panic(true, "elf: No higher half PHDRs exist"); + } + + struct mem_range *ranges = ext_mem_alloc(ranges_count * sizeof(struct mem_range)); + + size_t r = 0; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) { + continue; + } + + if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) { + if (!is_reloc) { + continue; + } + } + + uint64_t load_addr = phdr->p_vaddr + slide; + uint64_t this_top = load_addr + phdr->p_memsz; + + uint64_t align = phdr->p_align <= 1 ? 1 : phdr->p_align; + ranges[r].base = load_addr & ~(align - 1); + ranges[r].length = ALIGN_UP(this_top - ranges[r].base, align); + + if (phdr->p_flags & ELF_PF_X) { + ranges[r].permissions |= MEM_RANGE_X; + } + + if (phdr->p_flags & ELF_PF_W) { + ranges[r].permissions |= MEM_RANGE_W; + } + + if (phdr->p_flags & ELF_PF_R) { + ranges[r].permissions |= MEM_RANGE_R; + } + + r++; + } + + *_ranges_count = ranges_count; + *_ranges = ranges; +} + +bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) { + struct elf64_hdr *hdr = (void *)elf; + + elf64_validate(hdr); + + if (hdr->type != ET_EXEC && hdr->type != ET_DYN) { + panic(true, "elf: ELF file not of type ET_EXEC nor ET_DYN"); + } + + if (hdr->phdr_size < sizeof(struct elf64_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)"); + } + + // Validate program header offset and size don't overflow + uint64_t phdr_table_end; + if (__builtin_mul_overflow((uint64_t)hdr->ph_num, (uint64_t)hdr->phdr_size, &phdr_table_end) || + __builtin_add_overflow(phdr_table_end, hdr->phoff, &phdr_table_end)) { + panic(true, "elf: Program header table size overflow"); + } + + if (phdr_table_end > file_size) { + panic(true, "elf: Program header table extends beyond file bounds"); + } + + if (is_reloc) { + *is_reloc = false; + } + if (elf64_is_relocatable(elf, hdr)) { + if (is_reloc) { + *is_reloc = true; + } + } + + uint64_t slide = 0; + size_t try_count = 0; + size_t max_simulated_tries = 0x10000; + + uint64_t entry = hdr->entry; + + uint64_t max_align = elf64_max_align(elf); + + uint64_t image_size = 0; + + bool lower_to_higher = false; + + uint64_t min_vaddr = (uint64_t)-1; + uint64_t max_vaddr = 0; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) { + continue; + } + + if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) { + if (!*is_reloc) { + panic(true, "elf: Lower half PHDRs are not allowed"); + } + lower_to_higher = true; + } else { + if (lower_to_higher) { + panic(true, "elf: Mix of lower and higher half PHDRs in relocatable kernel"); + } + } + + uint64_t phdr_end; + if (__builtin_add_overflow(phdr->p_vaddr, phdr->p_memsz, &phdr_end)) { + panic(true, "elf: p_vaddr + p_memsz overflow in PHDR %u", i); + } + + // check for overlapping phdrs + for (uint16_t j = 0; j < hdr->ph_num; j++) { + struct elf64_phdr *phdr_in = (void *)elf + (hdr->phoff + j * hdr->phdr_size); + + if (phdr_in->p_type != PT_LOAD || phdr_in->p_memsz == 0) { + continue; + } + + if (phdr_in->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) { + if (!*is_reloc) { + continue; + } + } + + if (phdr_in == phdr) { + continue; + } + + uint64_t phdr_in_end; + if (__builtin_add_overflow(phdr_in->p_vaddr, phdr_in->p_memsz, &phdr_in_end)) { + panic(true, "elf: p_vaddr + p_memsz overflow in PHDR %u", j); + } + + if ((phdr_in->p_vaddr >= phdr->p_vaddr + && phdr_in->p_vaddr < phdr_end) + || + (phdr_in_end > phdr->p_vaddr + && phdr_in_end <= phdr_end)) { + panic(true, "elf: Attempted to load ELF file with overlapping PHDRs (%u and %u overlap)", i, j); + } + + if (ranges != NULL) { + uint64_t page_rounded_base = ALIGN_DOWN(phdr->p_vaddr, 4096); + uint64_t page_rounded_top = ALIGN_UP(phdr_end, 4096); + uint64_t page_rounded_base_in = ALIGN_DOWN(phdr_in->p_vaddr, 4096); + uint64_t page_rounded_top_in = ALIGN_UP(phdr_in_end, 4096); + + if ((page_rounded_base >= page_rounded_base_in + && page_rounded_base < page_rounded_top_in) + || + (page_rounded_top > page_rounded_base_in + && page_rounded_top <= page_rounded_top_in)) { + if ((phdr->p_flags & 0b111) != (phdr_in->p_flags & 0b111)) { + panic(true, "elf: Attempted to load ELF file with PHDRs with different permissions sharing the same memory page."); + } + } + } + } + + if (phdr->p_vaddr < min_vaddr) { + min_vaddr = phdr->p_vaddr; + } + + if (phdr_end > max_vaddr) { + max_vaddr = phdr_end; + } + } + + if (min_vaddr == (uint64_t)-1) { + panic(true, "elf: No usable PHDRs exist"); + } + + if (lower_to_higher) { + slide = FIXED_HIGHER_HALF_OFFSET_64 - min_vaddr; + } + + image_size = max_vaddr - min_vaddr; + + *physical_base = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, max_align); + *virtual_base = min_vaddr; + + if (_image_size) { + *_image_size = image_size; + } + +again: + if (*is_reloc && kaslr) { + slide = (rand32() & ~(max_align - 1)) + (lower_to_higher ? FIXED_HIGHER_HALF_OFFSET_64 - min_vaddr : 0); + + if (*virtual_base + slide + image_size < 0xffffffff80000000 /* this comparison relies on overflow */) { + if (++try_count == max_simulated_tries) { + panic(true, "elf: Image wants to load too high"); + } + goto again; + } + } + + uint64_t bss_size = 0; + + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) { + continue; + } + + // Sanity checks + if (phdr->p_filesz > phdr->p_memsz) { + panic(true, "elf: p_filesz > p_memsz"); + } + + // Validate p_offset + p_filesz doesn't overflow or exceed file size + uint64_t offset_end; + if (__builtin_add_overflow(phdr->p_offset, phdr->p_filesz, &offset_end)) { + panic(true, "elf: p_offset + p_filesz overflow"); + } + if (offset_end > file_size) { + panic(true, "elf: p_offset + p_filesz exceeds file size"); + } + + uint64_t load_addr = *physical_base + (phdr->p_vaddr - *virtual_base); + +#if defined (__aarch64__) + uint64_t this_top = load_addr + phdr->p_memsz; + + uint64_t mem_base, mem_size; + + uint64_t align = phdr->p_align <= 1 ? 1 : phdr->p_align; + mem_base = load_addr & ~(align - 1); + mem_size = this_top - mem_base; +#endif + + memcpy((void *)(uintptr_t)load_addr, elf + (phdr->p_offset), phdr->p_filesz); + + if (phdr->p_vaddr + phdr->p_memsz == max_vaddr) { + bss_size = phdr->p_memsz - phdr->p_filesz; + } + + if (!elf64_apply_relocations(elf, hdr, (void *)(uintptr_t)load_addr, phdr->p_vaddr, phdr->p_memsz, slide)) { + panic(true, "elf: Failed to apply relocations"); + } + +#if defined (__aarch64__) + clean_dcache_poc(mem_base, mem_base + mem_size); + inval_icache_pou(mem_base, mem_base + mem_size); +#endif + } + + if (_image_size_before_bss != NULL) { + *_image_size_before_bss = image_size - bss_size; + } + + *virtual_base += slide; + *entry_point = entry + slide; + if (_slide) { + *_slide = slide; + } + + if (ranges_count != NULL && ranges != NULL) { + elf64_get_ranges(elf, slide, ranges, ranges_count); + } + + return true; +} + +bool elf32_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point, + struct elsewhere_range **ranges) { + struct elf32_hdr *hdr = (void *)elf; + + elf32_validate(hdr); + + if (hdr->type != ET_EXEC && hdr->type != ET_DYN) { + panic(true, "elf: ELF file not of type ET_EXEC nor ET_DYN"); + } + + *entry_point = hdr->entry; + bool entry_adjusted = false; + + if (hdr->phdr_size < sizeof(struct elf32_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf32_phdr)"); + } + + uint64_t phdr_table_size32 = (uint64_t)hdr->ph_num * hdr->phdr_size; + if (hdr->phoff > file_size || phdr_table_size32 > file_size - hdr->phoff) { + panic(true, "elf: Program header table extends beyond file bounds"); + } + + size_t image_size = 0; + uint64_t min_paddr = (uint64_t)-1; + uint64_t max_paddr = 0; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf32_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) + continue; + + if (phdr->p_paddr < min_paddr) { + min_paddr = phdr->p_paddr; + } + + uint64_t top = (uint64_t)phdr->p_paddr + phdr->p_memsz; + if (top > max_paddr) { + max_paddr = top; + } + } + image_size = max_paddr - min_paddr; + + void *elsewhere = ext_mem_alloc(image_size); + + *ranges = ext_mem_alloc(sizeof(struct elsewhere_range)); + + (*ranges)->elsewhere = (uintptr_t)elsewhere; + (*ranges)->target = min_paddr; + (*ranges)->length = image_size; + + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf32_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) + continue; + + // Sanity checks + if (phdr->p_filesz > phdr->p_memsz) { + panic(true, "elf: p_filesz > p_memsz"); + } + if ((uint64_t)phdr->p_offset + phdr->p_filesz > file_size) { + panic(true, "elf: p_offset + p_filesz exceeds file size"); + } + + memcpy(elsewhere + (phdr->p_paddr - min_paddr), elf + phdr->p_offset, phdr->p_filesz); + + if (!entry_adjusted + && *entry_point >= phdr->p_vaddr + && *entry_point < (phdr->p_vaddr + phdr->p_memsz)) { + *entry_point -= phdr->p_vaddr; + *entry_point += phdr->p_paddr; + entry_adjusted = true; + } + } + + return true; +} + +bool elf64_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point, + struct elsewhere_range **ranges) { + struct elf64_hdr *hdr = (void *)elf; + + elf64_validate(hdr); + + if (hdr->type != ET_EXEC && hdr->type != ET_DYN) { + panic(true, "elf: ELF file not of type ET_EXEC nor ET_DYN"); + } + + *entry_point = hdr->entry; + bool entry_adjusted = false; + + if (hdr->phdr_size < sizeof(struct elf64_phdr)) { + panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)"); + } + + uint64_t phdr_table_size64 = (uint64_t)hdr->ph_num * hdr->phdr_size; + if (hdr->phoff > file_size || phdr_table_size64 > file_size - hdr->phoff) { + panic(true, "elf: Program header table extends beyond file bounds"); + } + + size_t image_size = 0; + uint64_t min_paddr = (uint64_t)-1; + uint64_t max_paddr = 0; + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) + continue; + + if (phdr->p_paddr < min_paddr) { + min_paddr = phdr->p_paddr; + } + + uint64_t top; + if (__builtin_add_overflow(phdr->p_paddr, phdr->p_memsz, &top)) { + panic(true, "elf: p_paddr + p_memsz overflow"); + } + if (top > max_paddr) { + max_paddr = top; + } + } + image_size = max_paddr - min_paddr; + + void *elsewhere = ext_mem_alloc(image_size); + + *ranges = ext_mem_alloc(sizeof(struct elsewhere_range)); + + (*ranges)->elsewhere = (uintptr_t)elsewhere; + (*ranges)->target = min_paddr; + (*ranges)->length = image_size; + + for (uint16_t i = 0; i < hdr->ph_num; i++) { + struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size); + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) + continue; + + // Sanity checks + if (phdr->p_filesz > phdr->p_memsz) { + panic(true, "elf: p_filesz > p_memsz"); + } + uint64_t offset_end; + if (__builtin_add_overflow(phdr->p_offset, phdr->p_filesz, &offset_end) + || offset_end > file_size) { + panic(true, "elf: p_offset + p_filesz exceeds file size"); + } + + memcpy(elsewhere + (phdr->p_paddr - min_paddr), elf + phdr->p_offset, phdr->p_filesz); + + if (!entry_adjusted + && *entry_point >= phdr->p_vaddr + && *entry_point < (phdr->p_vaddr + phdr->p_memsz)) { + *entry_point -= phdr->p_vaddr; + *entry_point += phdr->p_paddr; + entry_adjusted = true; + } + } + + return true; +} diff --git a/limine/common/lib/elf.h b/limine/common/lib/elf.h new file mode 100644 index 0000000..e8d5b28 --- /dev/null +++ b/limine/common/lib/elf.h @@ -0,0 +1,87 @@ +#ifndef LIB__ELF_H__ +#define LIB__ELF_H__ + +#include +#include +#include +#include + +#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000) + +#define ELF_PF_X 1 +#define ELF_PF_W 2 +#define ELF_PF_R 4 + +struct elf_section_hdr_info { + uint32_t section_entry_size; + uint32_t str_section_idx; + uint32_t num; + uint64_t section_offset; +}; + +int elf_bits(uint8_t *elf); + +struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf); +struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf); + +bool elf64_load_section(uint8_t *elf, size_t file_size, void *buffer, const char *name, size_t limit, uint64_t slide); +bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc); + +bool elf32_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point, + struct elsewhere_range **ranges); +bool elf64_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point, + struct elsewhere_range **ranges); + +struct elf64_hdr { + uint8_t ident[16]; + uint16_t type; + uint16_t machine; + uint32_t version; + uint64_t entry; + uint64_t phoff; + uint64_t shoff; + uint32_t flags; + uint16_t hdr_size; + uint16_t phdr_size; + uint16_t ph_num; + uint16_t shdr_size; + uint16_t sh_num; + uint16_t shstrndx; +}; + +struct elf64_shdr { + uint32_t sh_name; + uint32_t sh_type; + uint64_t sh_flags; + uint64_t sh_addr; + uint64_t sh_offset; + uint64_t sh_size; + uint32_t sh_link; + uint32_t sh_info; + uint64_t sh_addralign; + uint64_t sh_entsize; +}; + +struct elf32_shdr { + uint32_t sh_name; + uint32_t sh_type; + uint32_t sh_flags; + uint32_t sh_addr; + uint32_t sh_offset; + uint32_t sh_size; + uint32_t sh_link; + uint32_t sh_info; + uint32_t sh_addralign; + uint32_t sh_entsize; +}; + +struct elf64_sym { + uint32_t st_name; + uint8_t st_info; + uint8_t st_other; + uint16_t st_shndx; + uint64_t st_value; + uint64_t st_size; +}; + +#endif diff --git a/limine/common/lib/elsewhere.c b/limine/common/lib/elsewhere.c new file mode 100644 index 0000000..67d63f8 --- /dev/null +++ b/limine/common/lib/elsewhere.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include + +static bool elsewhere_overlap_check(uint64_t base1, uint64_t top1, + uint64_t base2, uint64_t top2) { + return (base1 < top2 && base2 < top1); +} + +bool elsewhere_append( + bool flexible_target, + struct elsewhere_range *ranges, uint64_t *ranges_count, + void *elsewhere, uint64_t *target, size_t t_length) { + // original target of -1 means "allocate after top of all ranges" + // flexible target is ignored + flexible_target = true; + if (*target == (uint64_t)-1) { + uint64_t top = 0; + + for (size_t i = 0; i < *ranges_count; i++) { + uint64_t r_top = ranges[i].target + ranges[i].length; + + if (top < r_top) { + top = r_top; + } + } + + *target = ALIGN_UP(top, 4096); + } + + uint64_t max_retries = 0x10000; + +retry: + if (max_retries-- == 0) { + return false; + } + + for (size_t i = 0; i < *ranges_count; i++) { + uint64_t t_top = *target + t_length; + + // Ensure allocation stays within 32-bit address space. + if (t_top > 0x100000000) { + return false; + } + + // Does it overlap with other elsewhere ranges targets? + { + uint64_t base = ranges[i].target; + uint64_t length = ranges[i].length; + uint64_t top = base + length; + + if (elsewhere_overlap_check(base, top, *target, t_top)) { + if (!flexible_target) { + return false; + } + *target = ALIGN_UP(top, 4096); + goto retry; + } + } + + // Does it overlap with other elsewhere ranges sources? + { + uint64_t base = ranges[i].elsewhere; + uint64_t length = ranges[i].length; + uint64_t top = base + length; + + if (elsewhere_overlap_check(base, top, *target, t_top)) { + if (!flexible_target) { + return false; + } + *target += 0x1000; + goto retry; + } + } + + // Make sure it is memory that actually exists. + if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE, + MEMMAP_USABLE, false, true, false)) { + if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE, + MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, false)) { + if (!flexible_target) { + return false; + } + *target += 0x1000; + goto retry; + } + } + } + + // Add the elsewhere range + ranges[*ranges_count].elsewhere = (uintptr_t)elsewhere; + ranges[*ranges_count].target = *target; + ranges[*ranges_count].length = t_length; + *ranges_count += 1; + + return true; +} diff --git a/limine/common/lib/elsewhere.h b/limine/common/lib/elsewhere.h new file mode 100644 index 0000000..99e1432 --- /dev/null +++ b/limine/common/lib/elsewhere.h @@ -0,0 +1,19 @@ +#ifndef LIB__ELSEWHERE_H__ +#define LIB__ELSEWHERE_H__ + +#include +#include +#include + +struct elsewhere_range { + uint64_t elsewhere; + uint64_t target; + uint64_t length; +}; + +bool elsewhere_append( + bool flexible_target, + struct elsewhere_range *ranges, uint64_t *ranges_count, + void *elsewhere, uint64_t *target, size_t t_length); + +#endif diff --git a/limine/common/lib/fb.c b/limine/common/lib/fb.c new file mode 100644 index 0000000..05096b9 --- /dev/null +++ b/limine/common/lib/fb.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include + +struct fb_info *fb_fbs; +size_t fb_fbs_count = 0; + +void fb_init(struct fb_info **ret, size_t *_fbs_count, + uint64_t target_width, uint64_t target_height, uint16_t target_bpp) { +#if defined (BIOS) + *ret = ext_mem_alloc(sizeof(struct fb_info)); + if (init_vbe(*ret, target_width, target_height, target_bpp)) { + *_fbs_count = 1; + + (*ret)->edid = get_edid_info(); + size_t mode_count; + (*ret)->mode_list = vbe_get_mode_list(&mode_count); + (*ret)->mode_count = mode_count; + } else { + *_fbs_count = 0; + pmm_free(*ret, sizeof(struct fb_info)); + } +#elif defined (UEFI) + init_gop(ret, _fbs_count, target_width, target_height, target_bpp); +#endif + + fb_fbs = *ret; + fb_fbs_count = *_fbs_count; +} + +void fb_clear(struct fb_info *fb) { + for (size_t y = 0; y < fb->framebuffer_height; y++) { + switch (fb->framebuffer_bpp) { + case 32: { + uint32_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr; + size_t row = (y * fb->framebuffer_pitch) / 4; + for (size_t x = 0; x < fb->framebuffer_width; x++) { + fbp[row + x] = 0; + } + break; + } + case 16: { + uint16_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr; + size_t row = (y * fb->framebuffer_pitch) / 2; + for (size_t x = 0; x < fb->framebuffer_width; x++) { + fbp[row + x] = 0; + } + break; + } + default: { + uint8_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr; + size_t row = y * fb->framebuffer_pitch; + size_t row_bytes = fb->framebuffer_width * (fb->framebuffer_bpp / 8); + for (size_t x = 0; x < row_bytes; x++) { + fbp[row + x] = 0; + } + break; + } + } + } +} diff --git a/limine/common/lib/fb.h b/limine/common/lib/fb.h new file mode 100644 index 0000000..fea4e28 --- /dev/null +++ b/limine/common/lib/fb.h @@ -0,0 +1,43 @@ +#ifndef LIB__FB_H__ +#define LIB__FB_H__ + +#include +#include +#include + +struct resolution { + uint64_t width; + uint64_t height; + uint16_t bpp; +}; + +struct fb_info { + uint64_t framebuffer_pitch; + uint64_t framebuffer_width; + uint64_t framebuffer_height; + uint16_t framebuffer_bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; + + uint64_t framebuffer_addr; + + struct edid_info_struct *edid; + + uint64_t mode_count; + struct fb_info *mode_list; +}; + +extern struct fb_info *fb_fbs; +extern size_t fb_fbs_count; + +void fb_init(struct fb_info **ret, size_t *_fbs_count, + uint64_t target_width, uint64_t target_height, uint16_t target_bpp); + +void fb_clear(struct fb_info *fb); + +#endif diff --git a/limine/common/lib/fdt.c b/limine/common/lib/fdt.c new file mode 100644 index 0000000..9e3e115 --- /dev/null +++ b/limine/common/lib/fdt.c @@ -0,0 +1,44 @@ +#if !defined(__i386__) && !defined(__x86_64__) + +#include +#include +#include + +static int fdt_get_or_add_chosen_node(void *fdt) { + int offset = fdt_subnode_offset(fdt, 0, "chosen"); + + if (offset == -FDT_ERR_NOTFOUND) { + offset = fdt_add_subnode(fdt, 0, "chosen"); + } + + return offset; +} + +int fdt_set_chosen_string(void *fdt, const char *name, const char *value) { + int chosen_offset = fdt_get_or_add_chosen_node(fdt); + if (chosen_offset < 0) { + return chosen_offset; + } + + return fdt_setprop_string(fdt, chosen_offset, name, value); +} + +int fdt_set_chosen_uint64(void *fdt, const char *name, uint64_t value) { + int chosen_offset = fdt_get_or_add_chosen_node(fdt); + if (chosen_offset < 0) { + return chosen_offset; + } + + return fdt_setprop_u64(fdt, chosen_offset, name, value); +} + +int fdt_set_chosen_uint32(void *fdt, const char *name, uint32_t value) { + int chosen_offset = fdt_get_or_add_chosen_node(fdt); + if (chosen_offset < 0) { + return chosen_offset; + } + + return fdt_setprop_u32(fdt, chosen_offset, name, value); +} + +#endif diff --git a/limine/common/lib/fdt.h b/limine/common/lib/fdt.h new file mode 100644 index 0000000..ba7a9a3 --- /dev/null +++ b/limine/common/lib/fdt.h @@ -0,0 +1,15 @@ +#ifndef LIB__FDT_H__ +#define LIB__FDT_H__ + +#if !defined(__x86_64__) && !defined(__i386__) + +#include +#include + +int fdt_set_chosen_string(void *fdt, const char *name, const char *value); +int fdt_set_chosen_uint64(void *fdt, const char *name, uint64_t value); +int fdt_set_chosen_uint32(void *fdt, const char *name, uint32_t value); + +#endif + +#endif // LIB__FDT_H__ diff --git a/limine/common/lib/getchar.c b/limine/common/lib/getchar.c new file mode 100644 index 0000000..b6c375b --- /dev/null +++ b/limine/common/lib/getchar.c @@ -0,0 +1,365 @@ +#include +#include +#include +#include +#include +#include +#include +#if defined (BIOS) +# include +#elif defined (UEFI) +# include +#endif +#include +#include + +int getchar(void) { + for (;;) { + int ret = pit_sleep_and_quit_on_keypress(65535); + if (ret != 0) { + return ret; + } + } +} + +int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) { + switch (scancode) { +#if defined (BIOS) + case 0x44: + return GETCHAR_F10; + case 0x4b: + return GETCHAR_CURSOR_LEFT; + case 0x4d: + return GETCHAR_CURSOR_RIGHT; + case 0x48: + return GETCHAR_CURSOR_UP; + case 0x50: + return GETCHAR_CURSOR_DOWN; + case 0x53: + return GETCHAR_DELETE; + case 0x4f: + return GETCHAR_END; + case 0x47: + return GETCHAR_HOME; + case 0x49: + return GETCHAR_PGUP; + case 0x51: + return GETCHAR_PGDOWN; + case 0x01: + return GETCHAR_ESCAPE; +#elif defined (UEFI) + case SCAN_F10: + return GETCHAR_F10; + case SCAN_LEFT: + return GETCHAR_CURSOR_LEFT; + case SCAN_RIGHT: + return GETCHAR_CURSOR_RIGHT; + case SCAN_UP: + return GETCHAR_CURSOR_UP; + case SCAN_DOWN: + return GETCHAR_CURSOR_DOWN; + case SCAN_DELETE: + return GETCHAR_DELETE; + case SCAN_END: + return GETCHAR_END; + case SCAN_HOME: + return GETCHAR_HOME; + case SCAN_PAGE_UP: + return GETCHAR_PGUP; + case SCAN_PAGE_DOWN: + return GETCHAR_PGDOWN; + case SCAN_ESC: + return GETCHAR_ESCAPE; +#endif + } + switch (ascii) { + case '\n': + case '\r': + return '\n'; + case '\b': + return '\b'; + case '\t': + return '\t'; + } + + if (shift_state & (GETCHAR_LCTRL | GETCHAR_RCTRL)) { + switch (ascii) { + case 'a': return GETCHAR_HOME; + case 'e': return GETCHAR_END; + case 'p': return GETCHAR_CURSOR_UP; + case 'n': return GETCHAR_CURSOR_DOWN; + case 'b': return GETCHAR_CURSOR_LEFT; + case 'f': return GETCHAR_CURSOR_RIGHT; + default: break; + } + } + + // Guard against non-printable values + if (ascii < 0x20 || ascii > 0x7e) { + return -1; + } + return ascii; +} + +#if defined (BIOS) +int _pit_sleep_and_quit_on_keypress(uint32_t ticks); + +static int input_sequence(void) { + int val = 0; + + for (;;) { + int ret = -1; + size_t retries = 0; + + while (ret == -1 && retries < 1000000) { + ret = serial_in(); + retries++; + } + if (ret == -1) { + return 0; + } + + switch (ret) { + case 'A': + return GETCHAR_CURSOR_UP; + case 'B': + return GETCHAR_CURSOR_DOWN; + case 'C': + return GETCHAR_CURSOR_RIGHT; + case 'D': + return GETCHAR_CURSOR_LEFT; + case 'F': + return GETCHAR_END; + case 'H': + return GETCHAR_HOME; + } + + if (ret > '9' || ret < '0') { + break; + } + + val *= 10; + val += ret - '0'; + } + + switch (val) { + case 3: + return GETCHAR_DELETE; + case 5: + return GETCHAR_PGUP; + case 6: + return GETCHAR_PGDOWN; + case 21: + return GETCHAR_F10; + } + + return 0; +} + +int pit_sleep_and_quit_on_keypress(int seconds) { + if (!serial) { + return _pit_sleep_and_quit_on_keypress(seconds * 18); + } + + for (int i = 0; i < seconds * 18; i++) { + int ret = _pit_sleep_and_quit_on_keypress(1); + + if (ret != 0) { + return ret; + } + + ret = serial_in(); + + if (ret != -1) { +again: + switch (ret) { + case '\r': + return '\n'; + case 0x1b: + stall(10); + ret = serial_in(); + if (ret == -1) { + return GETCHAR_ESCAPE; + } + if (ret == '[') { + return input_sequence(); + } + goto again; + case 0x7f: + return '\b'; + } + + return ret; + } + } + + return 0; +} +#endif + +#if defined (UEFI) +static int input_sequence(bool ext, + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto, + EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto) { + EFI_STATUS status; + EFI_KEY_DATA kd; + + int val = 0; + + for (;;) { + if (ext == false) { + status = sproto->ReadKeyStroke(sproto, &kd.Key); + } else { + status = exproto->ReadKeyStrokeEx(exproto, &kd); + } + + if (status != EFI_SUCCESS) { + return 0; + } + + switch (kd.Key.UnicodeChar) { + case 'A': + return GETCHAR_CURSOR_UP; + case 'B': + return GETCHAR_CURSOR_DOWN; + case 'C': + return GETCHAR_CURSOR_RIGHT; + case 'D': + return GETCHAR_CURSOR_LEFT; + case 'F': + return GETCHAR_END; + case 'H': + return GETCHAR_HOME; + } + + if (kd.Key.UnicodeChar > '9' || kd.Key.UnicodeChar < '0') { + break; + } + + val *= 10; + val += kd.Key.UnicodeChar - '0'; + } + + switch (val) { + case 3: + return GETCHAR_DELETE; + case 5: + return GETCHAR_PGUP; + case 6: + return GETCHAR_PGDOWN; + case 21: + return GETCHAR_F10; + } + + return 0; +} + +int pit_sleep_and_quit_on_keypress(int seconds) { + EFI_KEY_DATA kd; + + UINTN which; + + EFI_EVENT events[2]; + + EFI_GUID exproto_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + EFI_GUID sproto_guid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto = NULL; + EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto = NULL; + + bool use_sproto = false; + + if (gBS->HandleProtocol(gST->ConsoleInHandle, &exproto_guid, (void **)&exproto) != EFI_SUCCESS) { + if (gBS->HandleProtocol(gST->ConsoleInHandle, &sproto_guid, (void **)&sproto) != EFI_SUCCESS) { + if (gST->ConIn != NULL) { + sproto = gST->ConIn; + } else { + panic(false, "Your input device doesn't have an input protocol!"); + } + } + + events[0] = sproto->WaitForKey; + + use_sproto = true; + } else { + events[0] = exproto->WaitForKeyEx; + } + + gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]); + + gBS->SetTimer(events[1], TimerRelative, (uint64_t)10000000 * seconds); + +again: + memset(&kd, 0, sizeof(EFI_KEY_DATA)); + + gBS->WaitForEvent(2, events, &which); + + if (which == 1) { + gBS->CloseEvent(events[1]); + return 0; + } + + EFI_STATUS status; + if (use_sproto) { + status = sproto->ReadKeyStroke(sproto, &kd.Key); + } else { + status = exproto->ReadKeyStrokeEx(exproto, &kd); + } + + if (status != EFI_SUCCESS) { + goto again; + } + + if ((kd.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) { + kd.KeyState.KeyShiftState = 0; + } + + if (serial == true && kd.Key.ScanCode == 0x08) { + gBS->CloseEvent(events[1]); + return '\b'; + } + + if (kd.Key.ScanCode == SCAN_ESC) { + gBS->CloseEvent(events[1]); + + gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]); + + gBS->SetTimer(events[1], TimerRelative, 100000); + + gBS->WaitForEvent(2, events, &which); + + if (which == 1) { + gBS->CloseEvent(events[1]); + return GETCHAR_ESCAPE; + } + + if (use_sproto) { + status = sproto->ReadKeyStroke(sproto, &kd.Key); + } else { + status = exproto->ReadKeyStrokeEx(exproto, &kd); + } + + gBS->CloseEvent(events[1]); + gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]); + gBS->SetTimer(events[1], TimerRelative, (uint64_t)10000000 * seconds); + + if (status != EFI_SUCCESS) { + goto again; + } + + if (kd.Key.UnicodeChar == '[') { + gBS->CloseEvent(events[1]); + return input_sequence(!use_sproto, exproto, sproto); + } + } + + int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar, + kd.KeyState.KeyShiftState); + + if (ret == -1) { + goto again; + } + + gBS->CloseEvent(events[1]); + return ret; +} +#endif diff --git a/limine/common/lib/getchar.h b/limine/common/lib/getchar.h new file mode 100644 index 0000000..4bbb46d --- /dev/null +++ b/limine/common/lib/getchar.h @@ -0,0 +1,28 @@ +#ifndef LIB__GETCHAR_H__ +#define LIB__GETCHAR_H__ + +#include + +#define GETCHAR_CURSOR_LEFT (-10) +#define GETCHAR_CURSOR_RIGHT (-11) +#define GETCHAR_CURSOR_UP (-12) +#define GETCHAR_CURSOR_DOWN (-13) +#define GETCHAR_DELETE (-14) +#define GETCHAR_END (-15) +#define GETCHAR_HOME (-16) +#define GETCHAR_PGUP (-17) +#define GETCHAR_PGDOWN (-18) +#define GETCHAR_F10 (-19) +#define GETCHAR_ESCAPE (-20) + +#if defined (BIOS) +# define GETCHAR_RCTRL 0x4 +# define GETCHAR_LCTRL GETCHAR_RCTRL +#elif defined (UEFI) +# define GETCHAR_RCTRL EFI_RIGHT_CONTROL_PRESSED +# define GETCHAR_LCTRL EFI_LEFT_CONTROL_PRESSED +#endif + +int getchar(void); + +#endif diff --git a/limine/common/lib/gterm.c b/limine/common/lib/gterm.c new file mode 100644 index 0000000..d44b64a --- /dev/null +++ b/limine/common/lib/gterm.c @@ -0,0 +1,907 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Builtin font originally taken from: +// https://github.com/viler-int10h/vga-text-mode-fonts/raw/master/FONTS/PC-OTHER/TOSH-SAT.F16 +static const uint8_t builtin_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81, 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00, + 0x00, 0x3c, 0x7e, 0xff, 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x1e, 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12, 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x38, 0x2c, 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e, 0xfe, 0x7e, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda, 0x6c, 0x06, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x0c, 0x38, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x3c, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, + 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xd8, 0xc0, 0xc0, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78, 0xc0, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00, + 0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static struct image *background; + +static size_t margin = 64; +static size_t margin_gradient = 4; + +static uint32_t default_bg, default_fg; +static uint32_t default_bg_bright, default_fg_bright; + +static size_t bg_canvas_size; +static uint32_t *bg_canvas; + +#define A(rgb) (uint8_t)(rgb >> 24) +#define R(rgb) (uint8_t)(rgb >> 16) +#define G(rgb) (uint8_t)(rgb >> 8) +#define B(rgb) (uint8_t)(rgb) +#define ARGB(a, r, g, b) (((a) << 24) | (((r) & 0xFF) << 16) | (((g) & 0xFF) << 8) | ((b) & 0xFF)) + +static inline uint32_t colour_blend(uint32_t fg, uint32_t bg) { + unsigned alpha = 255 - A(fg); + unsigned inv_alpha = A(fg) + 1; + + uint8_t r = (uint8_t)((alpha * R(fg) + inv_alpha * R(bg)) / 256); + uint8_t g = (uint8_t)((alpha * G(fg) + inv_alpha * G(bg)) / 256); + uint8_t b = (uint8_t)((alpha * B(fg) + inv_alpha * B(bg)) / 256); + + return ARGB(0, r, g, b); +} + +static uint32_t blend_gradient_from_box(struct fb_info *fb, size_t x, size_t y, uint32_t bg_px, uint32_t hex) { + size_t distance, x_distance, y_distance; + size_t gradient_stop_x = fb->framebuffer_width - margin; + size_t gradient_stop_y = fb->framebuffer_height - margin; + + if (x < margin) + x_distance = margin - x; + else + x_distance = x - gradient_stop_x; + + if (y < margin) + y_distance = margin - y; + else + y_distance = y - gradient_stop_y; + + if (x >= margin && x < gradient_stop_x) { + distance = y_distance; + } else if (y >= margin && y < gradient_stop_y) { + distance = x_distance; + } else { + distance = sqrt((uint64_t)x_distance * (uint64_t)x_distance + + (uint64_t)y_distance * (uint64_t)y_distance); + } + + if (distance > margin_gradient) + return bg_px; + + uint8_t gradient_step = (0xff - A(hex)) / margin_gradient; + uint8_t new_alpha = A(hex) + gradient_step * distance; + + return colour_blend((hex & 0xffffff) | (new_alpha << 24), bg_px); +} + +typedef size_t fixedp6; // the last 6 bits are the fixed point part +static size_t fixedp6_to_int(fixedp6 value) { return value / 64; } +static fixedp6 int_to_fixedp6(size_t value) { return value * 64; } + +// Draw rect at coordinates, copying from the image to the fb and canvas, applying fn on every pixel +__attribute__((always_inline)) static inline void genloop(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend, uint32_t (*blend)(struct fb_info *fb, size_t x, size_t y, uint32_t orig)) { + uint8_t *img = background->img; + const size_t img_width = background->img_width, img_height = background->img_height, img_pitch = background->pitch, colsize = background->bpp / 8; + + if (xstart > xend) { + size_t tmp = xstart; + xstart = xend; + xend = tmp; + } + if (ystart > yend) { + size_t tmp = ystart; + ystart = yend; + yend = tmp; + } + + switch (background->type) { + case IMAGE_TILED: + for (size_t y = ystart; y < yend; y++) { + size_t image_y = y % img_height, image_x = xstart % img_width; + const size_t off = img_pitch * image_y; + size_t canvas_off = fb->framebuffer_width * y; + for (size_t x = xstart; x < xend; x++) { + uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off); + uint32_t i = blend(fb, x, y, img_pixel); + bg_canvas[canvas_off + x] = i; + if (++image_x == img_width) image_x = 0; // image_x = x % img_width, but modulo is too expensive + } + } + break; + + case IMAGE_CENTERED: + for (size_t y = ystart; y < yend; y++) { + int64_t image_y = (int64_t)y - background->y_displacement; + size_t canvas_off = fb->framebuffer_width * y; + if (image_y < 0 || (uint64_t)image_y >= background->y_size) { /* external part */ + for (size_t x = xstart; x < xend; x++) { + uint32_t i = blend(fb, x, y, background->back_colour); + bg_canvas[canvas_off + x] = i; + } + } + else { /* internal part */ + const size_t off = img_pitch * (size_t)image_y; + for (size_t x = xstart; x < xend; x++) { + uint32_t pixel; + int64_t image_x = (int64_t)x - background->x_displacement; + if (image_x < 0 || (uint64_t)image_x >= background->x_size) { + pixel = background->back_colour; + } else { + pixel = *(uint32_t*)(img + (size_t)image_x * colsize + off); + } + uint32_t i = blend(fb, x, y, pixel); + bg_canvas[canvas_off + x] = i; + } + } + } + break; + // For every pixel, ratio = img_width / gterm_width, img_x = x * ratio, x = (xstart + i) + // hence x = xstart * ratio + i * ratio + // so you can set x = xstart * ratio, and increment by ratio at each iteration + case IMAGE_STRETCHED: + for (size_t y = ystart; y < yend; y++) { + size_t img_y = (y * img_height) / fb->framebuffer_height; // calculate Y with full precision + size_t off = img_pitch * img_y; + size_t canvas_off = fb->framebuffer_width * y; + + size_t ratio = int_to_fixedp6(img_width) / fb->framebuffer_width; + fixedp6 img_x = ratio * xstart; + for (size_t x = xstart; x < xend; x++) { + uint32_t img_pixel = *(uint32_t*)(img + fixedp6_to_int(img_x) * colsize + off); + uint32_t i = blend(fb, x, y, img_pixel); + bg_canvas[canvas_off + x] = i; + img_x += ratio; + } + } + break; + } +} + +static uint32_t blend_external(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { (void)fb; (void)x; (void)y; return orig; } +static uint32_t blend_internal(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { (void)fb; (void)x; (void)y; return colour_blend(default_bg, orig); } +static uint32_t blend_margin(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { return blend_gradient_from_box(fb, x, y, orig, default_bg); } + +static void loop_external(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_external); } +static void loop_margin(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_margin); } +static void loop_internal(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_internal); } + +static void generate_canvas(struct fb_info *fb) { + if (background) { + // Free previous canvas if it exists + if (bg_canvas != NULL) { + pmm_free(bg_canvas, bg_canvas_size); + } + bg_canvas_size = fb->framebuffer_width * fb->framebuffer_height * sizeof(uint32_t); + bg_canvas = ext_mem_alloc(bg_canvas_size); + + // Clamp margin to half the framebuffer dimensions to prevent underflow + size_t max_margin = fb->framebuffer_width / 2; + if (fb->framebuffer_height / 2 < max_margin) { + max_margin = fb->framebuffer_height / 2; + } + size_t effective_margin = margin > max_margin ? max_margin : margin; + size_t effective_margin_gradient = margin_gradient > effective_margin ? effective_margin : margin_gradient; + + int64_t margin_no_gradient = (int64_t)effective_margin - effective_margin_gradient; + + if (margin_no_gradient < 0) { + margin_no_gradient = 0; + } + + size_t scan_stop_x = fb->framebuffer_width - margin_no_gradient; + size_t scan_stop_y = fb->framebuffer_height - margin_no_gradient; + + loop_external(fb, 0, fb->framebuffer_width, 0, margin_no_gradient); + loop_external(fb, 0, fb->framebuffer_width, scan_stop_y, fb->framebuffer_height); + loop_external(fb, 0, margin_no_gradient, margin_no_gradient, scan_stop_y); + loop_external(fb, scan_stop_x, fb->framebuffer_width, margin_no_gradient, scan_stop_y); + + size_t gradient_stop_x = fb->framebuffer_width - effective_margin; + size_t gradient_stop_y = fb->framebuffer_height - effective_margin; + + if (effective_margin_gradient) { + loop_margin(fb, margin_no_gradient, scan_stop_x, margin_no_gradient, effective_margin); + loop_margin(fb, margin_no_gradient, scan_stop_x, gradient_stop_y, scan_stop_y); + loop_margin(fb, margin_no_gradient, effective_margin, effective_margin, gradient_stop_y); + loop_margin(fb, gradient_stop_x, scan_stop_x, effective_margin, gradient_stop_y); + } + + loop_internal(fb, effective_margin, gradient_stop_x, effective_margin, gradient_stop_y); + } else { + bg_canvas = NULL; + } +} + +#if defined (__riscv) +__attribute__((target("arch=+zicbom"))) +static void riscv_flush_callback(volatile void *base, size_t length) { + const size_t cbom_block_size = 0x40; + uintptr_t start = ALIGN_DOWN((uintptr_t)base, cbom_block_size); + uintptr_t end = ALIGN_UP((uintptr_t)(base + length), cbom_block_size); + for (uintptr_t ptr = start; ptr < end; ptr += cbom_block_size) { + asm volatile("cbo.flush (%0)" :: "r"(ptr) : "memory"); + } +} +static void riscv_flush_callback_nozicbom(volatile void *base, size_t length) { + (void)base; + (void)length; + + // Without Zicbom, there is no portable instruction to flush dirty cache lines. + // Read through a dedicated eviction buffer to create cache pressure and displace + // dirty framebuffer lines. 128 KB covers typical RISC-V L1 D-caches (32-64 KB). + static volatile uint8_t *eviction_buf = NULL; + #define EVICTION_BUF_SIZE (128 * 1024) + if (eviction_buf == NULL) { + eviction_buf = ext_mem_alloc(EVICTION_BUF_SIZE); + } + + volatile uint64_t *p = (volatile uint64_t *)eviction_buf; + for (size_t i = 0; i < EVICTION_BUF_SIZE / sizeof(uint64_t); i += (64 / sizeof(uint64_t))) { + (void)p[i]; + } + asm volatile ("fence rw, rw" ::: "memory"); +} +#elif defined (__aarch64__) +static void aarch64_flush_callback(volatile void *base, size_t length) { + clean_dcache_poc((uintptr_t)base, (uintptr_t)base + length); +} +#endif + +bool gterm_init(struct fb_info **_fbs, size_t *_fbs_count, + char *config, size_t width, size_t height) { + static struct fb_info *fbs; + static size_t fbs_count; + + static bool prev_valid = false; + static char *prev_config; + static size_t prev_width, prev_height; + + if (prev_valid && config == prev_config && width == prev_width && height == prev_height) { + *_fbs = fbs; + *_fbs_count = fbs_count; + reset_term(); + return true; + } + + prev_valid = false; + + if (quiet) { + term_notready(); + return false; + } + +#if defined (UEFI) + if (serial || COM_OUTPUT) { + term_fallback(); + return true; + } +#endif + + term_notready(); + + // We force bpp to 32 + fb_init(&fbs, &fbs_count, width, height, 32); + + if (_fbs != NULL) { + *_fbs = fbs; + } + if (_fbs_count != NULL) { + *_fbs_count = fbs_count; + } + + if (fbs_count == 0) { + return false; + } + + // default scheme + margin = 64; + margin_gradient = 4; + + int fb_rotation = FLANTERM_FB_ROTATE_0; + char *rotation_str = config_get_value(config, 0, "INTERFACE_ROTATION"); + if (rotation_str != NULL) { + int rotation_val = strtoui(rotation_str, NULL, 10); + switch (rotation_val) { + case 90: fb_rotation = FLANTERM_FB_ROTATE_90; break; + case 180: fb_rotation = FLANTERM_FB_ROTATE_180; break; + case 270: fb_rotation = FLANTERM_FB_ROTATE_270; break; + } + } + + uint32_t ansi_colours[8]; + + ansi_colours[0] = 0x00000000; // black + ansi_colours[1] = 0x00aa0000; // red + ansi_colours[2] = 0x0000aa00; // green + ansi_colours[3] = 0x00aa5500; // brown + ansi_colours[4] = 0x000000aa; // blue + ansi_colours[5] = 0x00aa00aa; // magenta + ansi_colours[6] = 0x0000aaaa; // cyan + ansi_colours[7] = 0x00aaaaaa; // grey + + char *colours = config_get_value(config, 0, "TERM_PALETTE"); + if (colours != NULL) { + const char *first = colours; + size_t i; + for (i = 0; i < 8; i++) { + const char *last; + uint32_t col = strtoui(first, &last, 16); + if (first == last) + break; + ansi_colours[i] = col & 0xffffff; + if (*last == 0) + break; + first = last + 1; + } + } + + uint32_t ansi_bright_colours[8]; + + ansi_bright_colours[0] = 0x00555555; // black + ansi_bright_colours[1] = 0x00ff5555; // red + ansi_bright_colours[2] = 0x0055ff55; // green + ansi_bright_colours[3] = 0x00ffff55; // brown + ansi_bright_colours[4] = 0x005555ff; // blue + ansi_bright_colours[5] = 0x00ff55ff; // magenta + ansi_bright_colours[6] = 0x0055ffff; // cyan + ansi_bright_colours[7] = 0x00ffffff; // grey + + char *bright_colours = config_get_value(config, 0, "TERM_PALETTE_BRIGHT"); + if (bright_colours != NULL) { + const char *first = bright_colours; + size_t i; + for (i = 0; i < 8; i++) { + const char *last; + uint32_t col = strtoui(first, &last, 16); + if (first == last) + break; + ansi_bright_colours[i] = col & 0xffffff; + if (*last == 0) + break; + first = last + 1; + } + } + + default_bg = 0x00000000; // background (black) + default_fg = 0x00aaaaaa; // foreground (grey) + + default_bg_bright = 0x00555555; // background (black) + default_fg_bright = 0x00ffffff; // foreground (grey) + + char *theme_background = config_get_value(config, 0, "TERM_BACKGROUND"); + if (theme_background != NULL) { + default_bg = strtoui(theme_background, NULL, 16); + } + + char *theme_foreground = config_get_value(config, 0, "TERM_FOREGROUND"); + if (theme_foreground != NULL) { + default_fg = strtoui(theme_foreground, NULL, 16) & 0xffffff; + } + + char *theme_background_bright = config_get_value(config, 0, "TERM_BACKGROUND_BRIGHT"); + if (theme_background_bright != NULL) { + default_bg_bright = strtoui(theme_background_bright, NULL, 16); + } + + char *theme_foreground_bright = config_get_value(config, 0, "TERM_FOREGROUND_BRIGHT"); + if (theme_foreground_bright != NULL) { + default_fg_bright = strtoui(theme_foreground_bright, NULL, 16); + } + + size_t wallpaper_count = 0; + while (config_get_value(config, wallpaper_count, "WALLPAPER") != NULL) + wallpaper_count++; + + background = NULL; + if (wallpaper_count > 0) { + char *background_path = config_get_value(config, rand32() % wallpaper_count, "WALLPAPER"); + if (background_path != NULL) { + struct file_handle *bg_file; + if ((bg_file = uri_open(background_path)) != NULL) { + background = image_open(bg_file); + fclose(bg_file); + } + } + } + + if (background == NULL) { + margin = 0; + margin_gradient = 0; + } else { + if (theme_background == NULL) { + default_bg = 0x80000000; + } + } + + char *theme_margin = config_get_value(config, 0, "TERM_MARGIN"); + if (theme_margin != NULL) { + margin = strtoui(theme_margin, NULL, 10); + } + + char *theme_margin_gradient = config_get_value(config, 0, "TERM_MARGIN_GRADIENT"); + if (theme_margin_gradient != NULL) { + margin_gradient = strtoui(theme_margin_gradient, NULL, 10); + } + + if (margin_gradient > margin) { + margin_gradient = margin; + } + + size_t font_width = 8; + size_t font_height = 16; + size_t font_size = (font_width * font_height * FLANTERM_FB_FONT_GLYPHS) / 8; + +#define FONT_MAX 16384 + uint8_t *font = ext_mem_alloc(FONT_MAX); + + memcpy(font, builtin_font, 4096); + + size_t tmp_font_width, tmp_font_height; + + char *menu_font_size = config_get_value(config, 0, "TERM_FONT_SIZE"); + if (menu_font_size != NULL) { + if (!parse_resolution(&tmp_font_width, &tmp_font_height, NULL, menu_font_size)) { + print("Could not parse TERM_FONT_SIZE. Using default font.\n"); + goto no_load_font; + } + + if (tmp_font_width != 8) { + print("Font width must be 8, got %u. Using default font.\n", tmp_font_width); + goto no_load_font; + } + + size_t tmp_font_size = (tmp_font_width * tmp_font_height * FLANTERM_FB_FONT_GLYPHS) / 8; + + if (tmp_font_size > FONT_MAX) { + print("Font would be too large (%U bytes, %u bytes allowed). Not loading.\n", (uint64_t)tmp_font_size, FONT_MAX); + goto no_load_font; + } + + font_size = tmp_font_size; + } + + char *menu_font = config_get_value(config, 0, "TERM_FONT"); + if (menu_font != NULL) { + struct file_handle *f; + if ((f = uri_open(menu_font)) == NULL) { + print("menu: Could not open font file.\n"); + } else { + if (font_size > f->size) { + print("Font size too large for provided font file. Not loading.\n"); + fclose(f); + goto no_load_font; + } + fread(f, font, 0, font_size); + if (menu_font_size != NULL) { + font_width = tmp_font_width; + font_height = tmp_font_height; + } + fclose(f); + } + } + +no_load_font:; + size_t font_spacing = 1; + char *font_spacing_str = config_get_value(config, 0, "TERM_FONT_SPACING"); + if (font_spacing_str != NULL) { + font_spacing = strtoui(font_spacing_str, NULL, 10); + } + + size_t font_scale_x = 1; + size_t font_scale_y = 1; + bool font_scale_is_default = true; + + char *menu_font_scale = config_get_value(config, 0, "TERM_FONT_SCALE"); + if (menu_font_scale != NULL) { + parse_resolution(&font_scale_x, &font_scale_y, NULL, menu_font_scale); + if (font_scale_x > 8 || font_scale_y > 8) { + font_scale_x = 1; + font_scale_y = 1; + } else { + font_scale_is_default = false; + } + } + + terms_i = 0; + terms = ext_mem_alloc(fbs_count * sizeof(void *)); + + for (size_t i = 0; i < fbs_count; i++) { + struct fb_info *fb = &fbs[i]; + + // Ensure that this framebuffer uses 32-bits per pixel. + if (fb->framebuffer_bpp != 32) { + continue; + } + + if (fb_rotation == FLANTERM_FB_ROTATE_90 || fb_rotation == FLANTERM_FB_ROTATE_270) { + uint64_t tmp = fb->framebuffer_width; + fb->framebuffer_width = fb->framebuffer_height; + fb->framebuffer_height = tmp; + } + + if (background != NULL) { + char *background_layout = config_get_value(config, 0, "WALLPAPER_STYLE"); + if (background_layout != NULL && strcmp(background_layout, "centered") == 0) { + char *background_colour = config_get_value(config, 0, "BACKDROP"); + if (background_colour == NULL) + background_colour = "0"; + uint32_t bg_col = strtoui(background_colour, NULL, 16); + image_make_centered(background, fb->framebuffer_width, fb->framebuffer_height, bg_col); + } else if (background_layout != NULL && strcmp(background_layout, "tiled") == 0) { + } else { + image_make_stretched(background, fb->framebuffer_width, fb->framebuffer_height); + } + } + + generate_canvas(fb); + + if (font_scale_is_default) { + font_scale_x = 1; + font_scale_y = 1; + if (fb->framebuffer_width >= (1920 + 1920 / 3) && fb->framebuffer_height >= (1080 + 1080 / 3)) { + font_scale_x = 2; + font_scale_y = 2; + } + if (fb->framebuffer_width >= (3840 + 3840 / 3) && fb->framebuffer_height >= (2160 + 2160 / 3)) { + font_scale_x = 4; + font_scale_y = 4; + } + } + + if (fb_rotation == FLANTERM_FB_ROTATE_90 || fb_rotation == FLANTERM_FB_ROTATE_270) { + uint64_t tmp = fb->framebuffer_width; + fb->framebuffer_width = fb->framebuffer_height; + fb->framebuffer_height = tmp; + } + + terms[terms_i] = flanterm_fb_init(ext_mem_alloc_size_t, + pmm_free_size_t, + (void *)(uintptr_t)fb->framebuffer_addr, + fb->framebuffer_width, fb->framebuffer_height, fb->framebuffer_pitch, + fb->red_mask_size, fb->red_mask_shift, + fb->green_mask_size, fb->green_mask_shift, + fb->blue_mask_size, fb->blue_mask_shift, + bg_canvas, + ansi_colours, ansi_bright_colours, + &default_bg, &default_fg, + &default_bg_bright, &default_fg_bright, + font, font_width, font_height, font_spacing, + font_scale_x, font_scale_y, + margin, fb_rotation); + + if (terms[terms_i] != NULL) { + terms_i++; + } + + if (bg_canvas != NULL) { + pmm_free(bg_canvas, bg_canvas_size); + bg_canvas = NULL; + } + } + + pmm_free(font, FONT_MAX); + + if (background != NULL) { + image_close(background); + background = NULL; + } + + if (terms_i == 0) { + pmm_free(terms, fbs_count * sizeof(void *)); + return false; + } + + for (size_t i = 0; i < terms_i; i++) { + struct flanterm_context *term = terms[i]; + + if (serial) { + term->cols = term->cols > 80 ? 80 : term->cols; + term->rows = term->rows > 24 ? 24 : term->rows; + } + } + + size_t min_cols = (size_t)-1; + size_t min_rows = (size_t)-1; + + for (size_t i = 0; i < terms_i; i++) { + struct flanterm_context *term = terms[i]; + + if (term->cols < min_cols) { + min_cols = term->cols; + } + + if (term->rows < min_rows) { + min_rows = term->rows; + } + } + + for (size_t i = 0; i < terms_i; i++) { + struct flanterm_context *term = terms[i]; + + term->cols = min_cols; + term->rows = min_rows; + + flanterm_context_reinit(term); +#if defined (__riscv) + if (riscv_check_isa_extension("zicbom", NULL, NULL)) { + flanterm_fb_set_flush_callback(term, riscv_flush_callback); + } else { + flanterm_fb_set_flush_callback(term, riscv_flush_callback_nozicbom); + } +#elif defined (__aarch64__) + flanterm_fb_set_flush_callback(term, aarch64_flush_callback); +#endif + } + + term_backend = GTERM; + + prev_config = config; + prev_height = height; + prev_width = width; + prev_valid = true; + + return true; +} diff --git a/limine/common/lib/gterm.h b/limine/common/lib/gterm.h new file mode 100644 index 0000000..ec579dc --- /dev/null +++ b/limine/common/lib/gterm.h @@ -0,0 +1,11 @@ +#ifndef LIB__GTERM_H__ +#define LIB__GTERM_H__ + +#include +#include +#include + +bool gterm_init(struct fb_info **ret, size_t *_fbs_count, + char *config, size_t width, size_t height); + +#endif diff --git a/limine/common/lib/guid.c b/limine/common/lib/guid.c new file mode 100644 index 0000000..06730b0 --- /dev/null +++ b/limine/common/lib/guid.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +bool is_valid_guid(const char *s) { + for (size_t i = 0; ; i++) { + switch (i) { + case 8: + case 13: + case 18: + case 23: + if (s[i] != '-') + return false; + break; + case 36: + return s[i] == 0; + default: + if (digit_to_int(s[i]) == -1) + return false; + break; + } + } +} + +static void guid_convert_le_cluster(uint8_t *dest, const char *s, int len) { + size_t p = 0; + for (int i = len - 1; i >= 0; i--) { + int val = digit_to_int(s[i]); + + i % 2 ? (dest[p] = val) : (dest[p++] |= val << 4); + } +} + +static void guid_convert_be_cluster(uint8_t *dest, const char *s, int len) { + size_t p = 0; + for (int i = 0; i < len; i++) { + int val = digit_to_int(s[i]); + + i % 2 ? (dest[p++] |= val) : (dest[p] = val << 4); + } +} + +bool string_to_guid_be(struct guid *guid, const char *s) { + if (!is_valid_guid(s)) + return false; + + guid_convert_be_cluster((uint8_t *)guid + 0, s + 0, 8); + guid_convert_be_cluster((uint8_t *)guid + 4, s + 9, 4); + guid_convert_be_cluster((uint8_t *)guid + 6, s + 14, 4); + guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4); + guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12); + + return true; +} + +bool string_to_guid_mixed(struct guid *guid, const char *s) { + if (!is_valid_guid(s)) + return false; + + guid_convert_le_cluster((uint8_t *)guid + 0, s + 0, 8); + guid_convert_le_cluster((uint8_t *)guid + 4, s + 9, 4); + guid_convert_le_cluster((uint8_t *)guid + 6, s + 14, 4); + guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4); + guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12); + + return true; +} + +static void uint_to_hex(uint32_t num, char *dest, int len) { + const char digits[] = "0123456789abcdef"; + for (int i = 0; i < len; i++) { + dest[i] = digits[(num >> ((len - 1 - i) * 4)) & 0xf]; + } +} + +void guid_to_string(const struct guid *guid, char *s) { + uint_to_hex(guid->a, s, 8); + s[8] = '-'; + uint_to_hex(guid->b, s + 9, 4); + s[13] = '-'; + uint_to_hex(guid->c, s + 14, 4); + s[18] = '-'; + uint_to_hex(guid->d[0], s + 19, 2); + uint_to_hex(guid->d[1], s + 21, 2); + s[23] = '-'; + uint_to_hex(guid->d[2], s + 24, 2); + uint_to_hex(guid->d[3], s + 26, 2); + uint_to_hex(guid->d[4], s + 28, 2); + uint_to_hex(guid->d[5], s + 30, 2); + uint_to_hex(guid->d[6], s + 32, 2); + uint_to_hex(guid->d[7], s + 34, 2); + s[36] = 0; +} diff --git a/limine/common/lib/guid.h b/limine/common/lib/guid.h new file mode 100644 index 0000000..82b41c0 --- /dev/null +++ b/limine/common/lib/guid.h @@ -0,0 +1,20 @@ +#ifndef LIB__GUID_H__ +#define LIB__GUID_H__ + +#include +#include + +struct guid { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[8]; +}; + +bool is_valid_guid(const char *s); +bool string_to_guid_be(struct guid *guid, const char *s); +bool string_to_guid_mixed(struct guid *guid, const char *s); +// Assumption: s must be big enough to fit 36 characters and a null byte +void guid_to_string(const struct guid *guid, char *s); + +#endif diff --git a/limine/common/lib/image.c b/limine/common/lib/image.c new file mode 100644 index 0000000..ab9c33a --- /dev/null +++ b/limine/common/lib/image.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include + +void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour) { + image->type = IMAGE_CENTERED; + + image->x_displacement = (int64_t)frame_x_size / 2 - (int64_t)image->x_size / 2; + image->y_displacement = (int64_t)frame_y_size / 2 - (int64_t)image->y_size / 2; + image->back_colour = back_colour; +} + + +void image_make_stretched(struct image *image, int new_x_size, int new_y_size) { + image->type = IMAGE_STRETCHED; + + image->x_size = new_x_size; + image->y_size = new_y_size; +} + +struct image *image_open(struct file_handle *file) { + struct image *image = ext_mem_alloc(sizeof(struct image)); + + image->type = IMAGE_TILED; + + void *src = ext_mem_alloc(file->size); + + fread(file, src, 0, file->size); + + int x, y, bpp; + + image->img = stbi_load_from_memory(src, file->size, &x, &y, &bpp, 4); + + pmm_free(src, file->size); + + if (image->img == NULL) { + pmm_free(image, sizeof(struct image)); + return NULL; + } + + // Convert ABGR to XRGB + uint32_t *pptr = (void *)image->img; + size_t pixel_count = (size_t)x * (size_t)y; + for (size_t i = 0; i < pixel_count; i++) { + pptr[i] = (pptr[i] & 0x0000ff00) | ((pptr[i] & 0x00ff0000) >> 16) | ((pptr[i] & 0x000000ff) << 16); + } + + image->x_size = x; + image->y_size = y; + image->pitch = x * 4; + image->bpp = 32; + image->img_width = x; + image->img_height = y; + + return image; +} + +void image_close(struct image *image) { + stbi_image_free(image->img); + pmm_free(image, sizeof(struct image)); +} diff --git a/limine/common/lib/image.h b/limine/common/lib/image.h new file mode 100644 index 0000000..e5cc296 --- /dev/null +++ b/limine/common/lib/image.h @@ -0,0 +1,32 @@ +#ifndef LIB__IMAGE_H__ +#define LIB__IMAGE_H__ + +#include +#include + +struct image { + size_t x_size; + size_t y_size; + int type; + uint8_t *img; + int bpp; + int pitch; + size_t img_width; // x_size = scaled size, img_width = bitmap size + size_t img_height; + int64_t x_displacement; + int64_t y_displacement; + uint32_t back_colour; +}; + +enum { + IMAGE_TILED, + IMAGE_CENTERED, + IMAGE_STRETCHED +}; + +void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour); +void image_make_stretched(struct image *image, int new_x_size, int new_y_size); +struct image *image_open(struct file_handle *file); +void image_close(struct image *image); + +#endif diff --git a/limine/common/lib/libc.c b/limine/common/lib/libc.c new file mode 100644 index 0000000..d0a3de2 --- /dev/null +++ b/limine/common/lib/libc.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Slightly adapted strtoul() implementation from FreeBSD. +// https://github.com/freebsd/freebsd-src/blob/de1aa3dab23c06fec962a14da3e7b4755c5880cf/lib/libc/stdlib/strtoul.c +unsigned long strtoul(const char *nptr, char **endptr, int base) { + const char *s; + unsigned long acc; + char c; + unsigned long cutoff; + int neg, any, cutlim; + + s = nptr; + do { + c = *s++; + } while (isspace((unsigned char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X') && + ((s[1] >= '0' && s[1] <= '9') || + (s[1] >= 'A' && s[1] <= 'F') || + (s[1] >= 'a' && s[1] <= 'f'))) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + acc = any = 0; + if (base < 2 || base > 36) + goto noconv; + + cutoff = ULONG_MAX / base; + cutlim = ULONG_MAX % base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + //errno = ERANGE; + } else if (!any) { +noconv: + ;//errno = EINVAL; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} + +size_t strnlen(const char *str, size_t maxlen) { + size_t len; + + for (len = 0; len < maxlen && str[len]; len++); + + return len; +} + +void *memchr(const void *ptr, int ch, size_t n) { + uint8_t *p = (uint8_t *)ptr; + + for (size_t i = 0; i < n; i++) { + if (p[i] == ch) { + return (void *)ptr + i; + } + } + + return NULL; +} + +char *strchr(const char *str, int ch) { + for (size_t i = 0; ; i++) { + if (str[i] == (char)ch) { + return (char *)str + i; + } + if (str[i] == '\0') { + return NULL; + } + } +} + +char *strrchr(const char *str, int ch) { + char *p = NULL; + + for (size_t i = 0; ; i++) { + if (str[i] == (char)ch) { + p = (char *)str + i; + } + if (str[i] == '\0') { + break; + } + } + + return p; +} + +char *strdup(const char *s) { + size_t len = strlen(s) + 1; + char *buf = ext_mem_alloc(len); + memcpy(buf, s, len); + return buf; +} diff --git a/limine/common/lib/libc.h b/limine/common/lib/libc.h new file mode 100644 index 0000000..803ffd1 --- /dev/null +++ b/limine/common/lib/libc.h @@ -0,0 +1,36 @@ +#ifndef LIB__LIBC_H__ +#define LIB__LIBC_H__ + +#include +#include + +bool isprint(int c); +bool isspace(int c); +bool isalpha(int c); +bool isdigit(int c); + +int toupper(int c); +int tolower(int c); + +int abs(int i); + +void *memset(void *, int, size_t); +void *memcpy(void *restrict, const void *restrict, size_t); +int memcmp(const void *, const void *, size_t); +void *memmove(void *, const void *, size_t); +void *memchr(const void *, int, size_t); + +char *strcpy(char *, const char *); +char *strncpy(char *, const char *, size_t); +char *strchr(const char *, int); +char *strrchr(const char *, int); +size_t strlen(const char *); +size_t strnlen(const char *, size_t); +int strcmp(const char *, const char *); +int strcasecmp(const char *, const char *); +int strncmp(const char *, const char *, size_t); +int strncasecmp(const char *, const char *, size_t); +int inet_pton(const char *src, void *dst); +char *strdup(const char *); + +#endif diff --git a/limine/common/lib/libc.s2.c b/limine/common/lib/libc.s2.c new file mode 100644 index 0000000..ec2ca8c --- /dev/null +++ b/limine/common/lib/libc.s2.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include + +bool isprint(int c) { + return c >= ' ' && c <= '~'; +} + +bool isspace(int c) { + return (c >= '\t' && c <= 0xD) || c == ' '; +} + +bool isalpha(int c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +bool isdigit(int c) { + return c >= '0' && c <= '9'; +} + +int toupper(int c) { + if (c >= 'a' && c <= 'z') { + return c - 0x20; + } + return c; +} + +int tolower(int c) { + if (c >= 'A' && c <= 'Z') { + return c + 0x20; + } + return c; +} + +int abs(int i) { + return i < 0 ? -i : i; +} + +char *strcpy(char *dest, const char *src) { + size_t i; + + for (i = 0; src[i]; i++) + dest[i] = src[i]; + + dest[i] = 0; + + return dest; +} + +char *strncpy(char *dest, const char *src, size_t n) { + size_t i; + + for (i = 0; i < n && src[i]; i++) + dest[i] = src[i]; + for ( ; i < n; i++) + dest[i] = 0; + + return dest; +} + +int strcmp(const char *s1, const char *s2) { + for (size_t i = 0; ; i++) { + unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; + if (c1 != c2) { + return c1 < c2 ? -1 : 1; + } + if (c1 == 0) { + return 0; + } + } +} + +int strcasecmp(const char *s1, const char *s2) { + for (size_t i = 0; ; i++) { + unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; + if (tolower(c1) != tolower(c2)) { + return c1 < c2 ? -1 : 1; + } + if (c1 == 0) { + return 0; + } + } +} + +int strncmp(const char *s1, const char *s2, size_t n) { + for (size_t i = 0; i < n; i++) { + unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; + if (c1 != c2) { + return c1 < c2 ? -1 : 1; + } + if (c1 == 0) { + return 0; + } + } + + return 0; +} + +int strncasecmp(const char *s1, const char *s2, size_t n) { + for (size_t i = 0; i < n; i++) { + unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; + if (tolower(c1) != tolower(c2)) { + return c1 < c2 ? -1 : 1; + } + if (c1 == 0) { + return 0; + } + } + + return 0; +} + +size_t strlen(const char *str) { + size_t len; + + for (len = 0; str[len]; len++); + + return len; +} + +int inet_pton(const char *src, void *dst) { + uint8_t array[4]; + const char *current = src; + + for (int i = 0; i < 4; i++) { + const char *newcur; + uint64_t value = strtoui(current, &newcur, 10); + if (current == newcur) + return -1; + current = newcur; + if (i < 3) { + // Expect '.' delimiter between octets + if (*current != '.') + return -1; + current++; + } else { + // After last octet, string must end + if (*current != 0) + return -1; + } + if (value > 255) + return -1; + array[i] = value; + } + memcpy(dst, array, 4); + return 0; +} diff --git a/limine/common/lib/macros.aarch64_asm.h b/limine/common/lib/macros.aarch64_asm.h new file mode 100644 index 0000000..d59ef06 --- /dev/null +++ b/limine/common/lib/macros.aarch64_asm.h @@ -0,0 +1,52 @@ +// Branch to \el1 if in EL1, or to \el2 if in EL2 +// Uses \reg, halts if not in EL1 or EL2 +.macro PICK_EL reg, el1, el2 + mrs \reg, currentel + and \reg, \reg, #0b1100 + + cmp \reg, #0b0100 // EL1? + b.eq \el1 + cmp \reg, #0b1000 // EL2? + b.eq \el2 + + // Halt otherwise + msr daifset, #0b1111 +99: + wfi + b 99b +.endm + + +// Zero out all general purpose registers apart from X0 +.macro ZERO_REGS_EXCEPT_X0 + mov x1, xzr + mov x2, xzr + mov x3, xzr + mov x4, xzr + mov x5, xzr + mov x6, xzr + mov x7, xzr + mov x8, xzr + mov x9, xzr + mov x10, xzr + mov x11, xzr + mov x12, xzr + mov x13, xzr + mov x14, xzr + mov x15, xzr + mov x16, xzr + mov x17, xzr + mov x18, xzr + mov x19, xzr + mov x20, xzr + mov x21, xzr + mov x22, xzr + mov x23, xzr + mov x24, xzr + mov x25, xzr + mov x26, xzr + mov x27, xzr + mov x28, xzr + mov x29, xzr + mov x30, xzr +.endm diff --git a/limine/common/lib/memory.s2.c b/limine/common/lib/memory.s2.c new file mode 100644 index 0000000..a5353b2 --- /dev/null +++ b/limine/common/lib/memory.s2.c @@ -0,0 +1,53 @@ +#include +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) { + uint8_t *restrict pdest = (uint8_t *restrict)dest; + const uint8_t *restrict psrc = (const uint8_t *restrict)src; + + for (size_t i = 0; i < n; i++) { + pdest[i] = psrc[i]; + } + + return dest; +} + +void *memset(void *s, int c, size_t n) { + uint8_t *p = (uint8_t *)s; + + for (size_t i = 0; i < n; i++) { + p[i] = (uint8_t)c; + } + + return s; +} + +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *pdest = (uint8_t *)dest; + const uint8_t *psrc = (const uint8_t *)src; + + if ((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; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t *p1 = (const uint8_t *)s1; + const uint8_t *p2 = (const uint8_t *)s2; + + for (size_t i = 0; i < n; i++) { + if (p1[i] != p2[i]) { + return p1[i] < p2[i] ? -1 : 1; + } + } + + return 0; +} diff --git a/limine/common/lib/misc.c b/limine/common/lib/misc.c new file mode 100644 index 0000000..bbe6239 --- /dev/null +++ b/limine/common/lib/misc.c @@ -0,0 +1,417 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (UEFI) +EFI_SYSTEM_TABLE *gST; +EFI_BOOT_SERVICES *gBS; +EFI_RUNTIME_SERVICES *gRT; +EFI_HANDLE efi_image_handle; +EFI_MEMORY_DESCRIPTOR *efi_mmap = NULL; +UINTN efi_mmap_size = 0, efi_desc_size = 0, efi_mmap_key = 0; +UINT32 efi_desc_ver = 0; +#endif + +bool editor_enabled = true; +bool help_hidden = false; + +uint64_t usec_at_bootloader_entry; + +#if defined (UEFI) +bool is_efi_serial_present(void) { + EFI_STATUS status; + EFI_SERIAL_IO_PROTOCOL *serial_io = NULL; + EFI_GUID serial_io_guid = EFI_SERIAL_IO_PROTOCOL_GUID; + + status = gBS->LocateProtocol(&serial_io_guid, NULL, (void **)&serial_io); + if (status) { + return false; + } + + if (serial_io == NULL) { + return false; + } + + UINT32 control; + status = serial_io->GetControl(serial_io, &control); + if (status) { + return false; + } + + return true; +} +#endif + +bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf) { + size_t res[3] = {0}; + + const char *first = buf; + for (size_t i = 0; i < 3; i++) { + const char *last; + size_t x = strtoui(first, &last, 10); + if (first == last) + break; + res[i] = x; + if (*last == 0) + break; + first = last + 1; + } + + if (res[0] == 0 || res[1] == 0) + return false; + + if (res[2] == 0) + res[2] = 32; + + *width = res[0], *height = res[1]; + if (bpp != NULL) + *bpp = res[2]; + + return true; +} + +// This integer sqrt implementation has been adapted from: +// https://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2 +uint64_t sqrt(uint64_t a_nInput) { + uint64_t op = a_nInput; + uint64_t res = 0; + uint64_t one = (uint64_t)1 << 62; + + // "one" starts at the highest power of four <= than the argument. + while (one > op) { + one >>= 2; + } + + while (one != 0) { + if (op >= res + one) { + op = op - (res + one); + res = res + 2 * one; + } + res >>= 1; + one >>= 2; + } + + return res; +} + +size_t get_trailing_zeros(uint64_t val) { + for (size_t i = 0; i < 64; i++) { + if ((val & 1) != 0) { + return i; + } + val >>= 1; + } + return 64; +} + +void *get_device_tree_blob(const char *config, size_t extra_size) { + int ret; + + size_t size = 0; + void *dtb = NULL; + + { + char *dtb_path = NULL; + bool soft_panic; + if (config != NULL) { + dtb_path = config_get_value(config, 0, "dtb_path"); + soft_panic = true; + } + if (dtb_path == NULL) { + dtb_path = config_get_value(NULL, 0, "global_dtb"); + soft_panic = false; + } + if (dtb_path != NULL) { + struct file_handle *dtb_file; + if ((dtb_file = uri_open(dtb_path)) == NULL) + panic(soft_panic, "dtb: Failed to open device tree blob with path `%#`. Is the path correct?", dtb_path); + + dtb = freadall(dtb_file, MEMMAP_BOOTLOADER_RECLAIMABLE); + size = dtb_file->size; + fclose(dtb_file); + printv("dtb: loaded dtb at %p from file `%#`\n", dtb, dtb_path); + } + } + +#if defined (UEFI) + if (!dtb) { + EFI_GUID dtb_guid = EFI_DTB_TABLE_GUID; + for (size_t i = 0; i < gST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i]; + if (memcmp(&cur_table->VendorGuid, &dtb_guid, sizeof(EFI_GUID))) + continue; + size = fdt_totalsize(cur_table->VendorTable); + dtb = ext_mem_alloc(size); + ret = fdt_open_into(cur_table->VendorTable, dtb, size); + if (ret < 0) { + panic(true, "dtb: failed to resize new DTB"); + } + printv("dtb: found dtb at %p via EFI\n", cur_table->VendorTable); + break; + } + } +#endif + + if (extra_size == 0) { + return dtb; + } + + if (dtb) { + printv("dtb: dtb has size %X\n", (uint64_t)size); + + void *new_tab = ext_mem_alloc(size + extra_size); + + ret = fdt_open_into(dtb, new_tab, size + extra_size); + if (ret < 0) { + panic(true, "dtb: failed to resize new DTB"); + } + + pmm_free(dtb, size); + return new_tab; + } + + dtb = ext_mem_alloc(extra_size); + + ret = fdt_create_empty_tree(dtb, extra_size); + if (ret < 0) { + panic(true, "dtb: failed to create a device tree blob: '%s'", fdt_strerror(ret)); + } + + ret = fdt_setprop_u32(dtb, 0, "#address-cells", 2); + if (ret < 0) { + panic(true, "dtb: failed to set #address-cells: '%s'", fdt_strerror(ret)); + } + + ret = fdt_setprop_u32(dtb, 0, "#size-cells", 1); + if (ret < 0) { + panic(true, "dtb: failed to set #size-cells: '%s'", fdt_strerror(ret)); + } + + return dtb; +} + +#if defined (UEFI) + +#if defined (__riscv) + +RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void) { + EFI_GUID boot_proto_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; + RISCV_EFI_BOOT_PROTOCOL *proto; + + // LocateProtocol() is available from EFI version 1.1 + if (gBS->Hdr.Revision >= ((1 << 16) | 10)) { + if (gBS->LocateProtocol(&boot_proto_guid, NULL, (void **)&proto) == EFI_SUCCESS) { + return proto; + } + } + + UINTN bufsz = 0; + if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, NULL) != EFI_BUFFER_TOO_SMALL) + return NULL; + + EFI_HANDLE *handles_buf = ext_mem_alloc(bufsz); + if (handles_buf == NULL) + return NULL; + + if (bufsz < sizeof(EFI_HANDLE)) + goto error; + + if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, handles_buf) != EFI_SUCCESS) + goto error; + + if (gBS->HandleProtocol(handles_buf[0], &boot_proto_guid, (void **)&proto) != EFI_SUCCESS) + goto error; + + pmm_free(handles_buf, bufsz); + return proto; + +error: + pmm_free(handles_buf, bufsz); + return NULL; +} + +#endif + +no_unwind bool efi_boot_services_exited = false; + +bool efi_exit_boot_services(void) { + EFI_STATUS status; + + EFI_MEMORY_DESCRIPTOR tmp_mmap[1]; + efi_mmap_size = sizeof(tmp_mmap); + + gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &efi_mmap_key, &efi_desc_size, &efi_desc_ver); + + efi_mmap_size += 4096; + + status = gBS->FreePool(efi_mmap); + if (status) { + goto fail; + } + + status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&efi_mmap); + if (status) { + goto fail; + } + + EFI_MEMORY_DESCRIPTOR *efi_copy; + status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size * 2, (void **)&efi_copy); + if (status) { + goto fail; + } + + bli_on_boot(); + + const size_t EFI_COPY_MAX_ENTRIES = (efi_mmap_size * 2) / efi_desc_size; + + size_t retries = 0; + +retry: + status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &efi_mmap_key, &efi_desc_size, &efi_desc_ver); + if (retries == 0 && status) { + goto fail; + } + + // Be gone, UEFI! + status = gBS->ExitBootServices(efi_image_handle, efi_mmap_key); + if (status) { + if (retries == 128) { + goto fail; + } + retries++; + goto retry; + } + +#if defined(__x86_64__) || defined(__i386__) + asm volatile ("cli" ::: "memory"); +#elif defined (__aarch64__) + asm volatile ("msr daifset, #15" ::: "memory"); +#elif defined (__riscv) + asm volatile ("csrci sstatus, 0x2" ::: "memory"); +#elif defined (__loongarch64) + asm volatile ("csrxchg $r0, %0, 0x0" :: "r" (0x4) : "memory"); +#else +#error Unknown architecture +#endif + + // Go through new EFI memmap and free up bootloader entries + size_t entry_count = efi_mmap_size / efi_desc_size; + + size_t efi_copy_i = 0; + + for (size_t i = 0; i < entry_count; i++) { + EFI_MEMORY_DESCRIPTOR *orig_entry = (void *)efi_mmap + i * efi_desc_size; + EFI_MEMORY_DESCRIPTOR *new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; + + if (orig_entry->NumberOfPages == 0) { + continue; + } + + memcpy(new_entry, orig_entry, efi_desc_size); + + uint64_t base = orig_entry->PhysicalStart; + uint64_t length = orig_entry->NumberOfPages * 4096; + uint64_t top = base + length; + + // Find for a match in the untouched memory map + for (size_t j = 0; j < untouched_memmap_entries; j++) { + if (untouched_memmap[j].type != MEMMAP_USABLE) + continue; + + if (top > untouched_memmap[j].base && top <= untouched_memmap[j].base + untouched_memmap[j].length) { + if (untouched_memmap[j].base < base) { + new_entry->NumberOfPages = (base - untouched_memmap[j].base) / 4096; + + efi_copy_i++; + if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { + panic(false, "efi: New memory map exhausted"); + } + new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; + memcpy(new_entry, orig_entry, efi_desc_size); + + new_entry->NumberOfPages -= (base - untouched_memmap[j].base) / 4096; + new_entry->PhysicalStart = base; + new_entry->VirtualStart = 0; + + length = new_entry->NumberOfPages * 4096; + top = base + length; + } + + if (untouched_memmap[j].base > base) { + new_entry->NumberOfPages = (untouched_memmap[j].base - base) / 4096; + + efi_copy_i++; + if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { + panic(false, "efi: New memory map exhausted"); + } + new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; + memcpy(new_entry, orig_entry, efi_desc_size); + + new_entry->NumberOfPages -= (untouched_memmap[j].base - base) / 4096; + new_entry->PhysicalStart = untouched_memmap[j].base; + new_entry->VirtualStart = 0; + + base = new_entry->PhysicalStart; + length = new_entry->NumberOfPages * 4096; + top = base + length; + } + + if (length < untouched_memmap[j].length) { + panic(false, "efi: Memory map corruption"); + } + + new_entry->Type = EfiConventionalMemory; + + if (length == untouched_memmap[j].length) { + // It's a perfect match! + break; + } + + new_entry->NumberOfPages = untouched_memmap[j].length / 4096; + + efi_copy_i++; + if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { + panic(false, "efi: New memory map exhausted"); + } + new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; + memcpy(new_entry, orig_entry, efi_desc_size); + + new_entry->NumberOfPages = (length - untouched_memmap[j].length) / 4096; + new_entry->PhysicalStart = base + untouched_memmap[j].length; + new_entry->VirtualStart = 0; + + break; + } + } + + efi_copy_i++; + if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { + panic(false, "efi: New memory map exhausted"); + } + } + + efi_mmap = efi_copy; + efi_mmap_size = efi_copy_i * efi_desc_size; + + efi_boot_services_exited = true; + + printv("efi: Exited boot services.\n"); + + return true; + +fail: + panic(false, "efi: Failed to exit boot services"); +} + +#endif diff --git a/limine/common/lib/misc.h b/limine/common/lib/misc.h new file mode 100644 index 0000000..9123bb0 --- /dev/null +++ b/limine/common/lib/misc.h @@ -0,0 +1,130 @@ +#ifndef LIB__MISC_H__ +#define LIB__MISC_H__ + +#include +#include +#include +#include +#include +#include +#include +#if defined (UEFI) +# include +# if defined (__riscv) +# include +# endif +#endif + +#if defined (UEFI) +extern EFI_SYSTEM_TABLE *gST; +extern EFI_BOOT_SERVICES *gBS; +extern EFI_RUNTIME_SERVICES *gRT; +extern EFI_HANDLE efi_image_handle; +extern EFI_MEMORY_DESCRIPTOR *efi_mmap; +extern UINTN efi_mmap_size, efi_desc_size, efi_mmap_key; +extern UINT32 efi_desc_ver; + +extern bool efi_boot_services_exited; +bool efi_exit_boot_services(void); + +bool is_efi_serial_present(void); +#endif + +void *get_device_tree_blob(const char *config, size_t extra_size); + +extern struct volume *boot_volume; + +#if defined (BIOS) +extern bool stage3_loaded; +#endif + +extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic; + +extern uint64_t usec_at_bootloader_entry; + +bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf); + +bool get_absolute_path(char *path_ptr, const char *path, const char *pwd, size_t size); + +uint64_t sqrt(uint64_t a_nInput); +size_t get_trailing_zeros(uint64_t val); + +int digit_to_int(char c); +uint8_t bcd_to_int(uint8_t val); +uint8_t int_to_bcd(uint8_t val); + +noreturn void panic(bool allow_menu, const char *fmt, ...); + +int pit_sleep_and_quit_on_keypress(int seconds); + +uint64_t strtoui(const char *s, const char **end, int base); + +#define MIN(a, b) ({ \ + __auto_type MIN_a = (a); \ + __auto_type MIN_b = (b); \ + MIN_a > MIN_b ? MIN_b : MIN_a; \ +}) + +#define MAX(a, b) ({ \ + __auto_type MAX_a = (a); \ + __auto_type MAX_b = (b); \ + MAX_a > MAX_b ? MAX_a : MAX_b; \ +}) + +#define DIV_ROUNDUP(a, b) ({ \ + __auto_type DIV_ROUNDUP_a = (a); \ + __auto_type DIV_ROUNDUP_b = (b); \ + (DIV_ROUNDUP_a + (DIV_ROUNDUP_b - 1)) / DIV_ROUNDUP_b; \ +}) + +#define ALIGN_UP(x, a) ({ \ + __auto_type ALIGN_UP_value = (x); \ + __auto_type ALIGN_UP_align = (a); \ + ALIGN_UP_value = DIV_ROUNDUP(ALIGN_UP_value, ALIGN_UP_align) * ALIGN_UP_align; \ + ALIGN_UP_value; \ +}) + +#define ALIGN_DOWN(x, a) ({ \ + __auto_type ALIGN_DOWN_value = (x); \ + __auto_type ALIGN_DOWN_align = (a); \ + ALIGN_DOWN_value = (ALIGN_DOWN_value / ALIGN_DOWN_align) * ALIGN_DOWN_align; \ + ALIGN_DOWN_value; \ +}) + +#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0])) + +typedef char symbol[]; + +noreturn void stage3_common(void); + +#if defined (__x86_64__) || defined (__i386__) +noreturn void common_spinup(void *fnptr, int args, ...); +#elif defined (__aarch64__) +noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr, + uint64_t mair, uint64_t tcr, uint64_t ttbr0, + uint64_t ttbr1, uint64_t target_x0); +#elif defined (__riscv) +noreturn void riscv_spinup(uint64_t entry, uint64_t sp, uint64_t satp, uint64_t direct_map_offset); +#if defined (UEFI) +RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void); +#endif +#elif defined (__loongarch64) +noreturn void loongarch_spinup(uint64_t entry, uint64_t sp, uint64_t pgdl, + uint64_t pgdh, uint64_t direct_map_offset); +#else +#error Unknown architecture +#endif + +#define no_unwind __attribute__((section(".no_unwind"))) + +#define MEM_RANGE_X 1 +#define MEM_RANGE_W 2 +#define MEM_RANGE_R 4 + +struct mem_range { + uint64_t base; + uint64_t length; + uint64_t permissions; +}; + +#endif diff --git a/limine/common/lib/misc.s2.c b/limine/common/lib/misc.s2.c new file mode 100644 index 0000000..78cdb88 --- /dev/null +++ b/limine/common/lib/misc.s2.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +bool verbose = false; +bool quiet = false; +bool serial = false; +bool hash_mismatch_panic = false; + +uint8_t bcd_to_int(uint8_t val) { + return (val & 0x0f) + ((val & 0xf0) >> 4) * 10; +} +uint8_t int_to_bcd(uint8_t val) { + return (val % 10) | (val / 10) << 4; +} + +int digit_to_int(char c) { + if (c >= 'a' && c <= 'f') { + return (c - 'a') + 10; + } + if (c >= 'A' && c <= 'F') { + return (c - 'A') + 10; + } + if (c >= '0' && c <= '9'){ + return c - '0'; + } + + return -1; +} + +uint64_t strtoui(const char *s, const char **end, int base) { + uint64_t n = 0; + for (size_t i = 0; ; i++) { + int d = digit_to_int(s[i]); + if (d == -1 || d >= base) { + if (end != NULL) + *end = &s[i]; + break; + } + uint64_t mul_result; + if (__builtin_mul_overflow(n, (uint64_t)base, &mul_result)) { + if (end != NULL) + *end = &s[i]; + return UINT64_MAX; + } + if (__builtin_add_overflow(mul_result, (uint64_t)d, &n)) { + if (end != NULL) + *end = &s[i]; + return UINT64_MAX; + } + } + return n; +} + +bool get_absolute_path(char *path_ptr, const char *path, const char *pwd, size_t size) { + char *orig_ptr = path_ptr; + char *end_ptr = path_ptr + size - 1; + + if (size == 0) return false; + + if (!*path) { + size_t pwd_len = strlen(pwd); + if (pwd_len >= size) return false; + memcpy(path_ptr, pwd, pwd_len + 1); + return true; + } + + if (*path != '/') { + size_t pwd_len = strlen(pwd); + if (pwd_len >= size) return false; + memcpy(path_ptr, pwd, pwd_len + 1); + path_ptr += pwd_len; + } else { + *path_ptr = '/'; + path_ptr++; + path++; + } + + goto first_run; + + for (;;) { + switch (*path) { + case '/': + path++; +first_run: + if (*path == '/') continue; + if ((!strncmp(path, ".\0", 2)) + || (!strncmp(path, "./\0", 3))) { + goto term; + } + if ((!strncmp(path, "..\0", 3)) + || (!strncmp(path, "../\0", 4))) { + while (*path_ptr != '/') path_ptr--; + if (path_ptr == orig_ptr) path_ptr++; + goto term; + } + if (!strncmp(path, "../", 3)) { + while (*path_ptr != '/') path_ptr--; + if (path_ptr == orig_ptr) path_ptr++; + path += 2; + *path_ptr = 0; + continue; + } + if (!strncmp(path, "./", 2)) { + path += 1; + continue; + } + if (((path_ptr - 1) != orig_ptr) && (*(path_ptr - 1) != '/')) { + if (path_ptr >= end_ptr) return false; + *path_ptr = '/'; + path_ptr++; + } + continue; + case '\0': +term: + if ((*(path_ptr - 1) == '/') && ((path_ptr - 1) != orig_ptr)) + path_ptr--; + *path_ptr = 0; + return true; + default: + if (path_ptr >= end_ptr) return false; + *path_ptr = *path; + path++; + path_ptr++; + continue; + } + } +} diff --git a/limine/common/lib/panic.s2.c b/limine/common/lib/panic.s2.c new file mode 100644 index 0000000..817b651 --- /dev/null +++ b/limine/common/lib/panic.s2.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#if defined (UEFI) +# include +#endif +#include +#include +#include +#include +#include +#include + +noreturn void panic(bool allow_menu, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + + quiet = false; + + if ( +#if defined (BIOS) + stage3_loaded == true && +#endif + term_backend == _NOT_READY) { + term_fallback(); + } + + if ( +#if defined (BIOS) + stage3_loaded == true && +#endif + term_backend != FALLBACK) { + print("\033[31mPANIC\033[37;1m\033[0m: "); + } else { + print("PANIC: "); + } + vprint(fmt, args); + + va_end(args); + + print("\n"); + print_stacktrace(NULL); + + if ( +#if defined (BIOS) + stage3_loaded == true && +#elif defined (UEFI) + efi_boot_services_exited == false && +#endif + allow_menu == true) { + print("Press a key to return to %s.", booting_from_editor ? "editor" : "menu"); + + getchar(); + + // This fixes a crash + term_notready(); + + menu(false); +/* + fb_clear(&fbinfo); + + // release all uefi memory and return to firmware + pmm_release_uefi_mem(); + gBS->Exit(efi_image_handle, EFI_ABORTED, 0, NULL); +*/ + } else { +#if defined (BIOS) + print("Press CTRL+ALT+DEL to reboot."); + rm_hcf(); +#elif defined (UEFI) + print("System halted."); + for (;;) { +#if defined (__x86_64__) || defined (__i386__) + asm ("hlt"); +#elif defined (__aarch64__) || defined (__riscv) + asm ("wfi"); +#elif defined (__loongarch64) + asm ("idle 0"); +#else +#error Unknown architecture +#endif + } +#endif + } +} diff --git a/limine/common/lib/part.h b/limine/common/lib/part.h new file mode 100644 index 0000000..f09a293 --- /dev/null +++ b/limine/common/lib/part.h @@ -0,0 +1,110 @@ +#ifndef LIB__PART_H__ +#define LIB__PART_H__ + +#include +#include +#include +#include +#if defined (UEFI) +# include +# include +#endif + +#define NO_PARTITION (-1) +#define INVALID_TABLE (-2) +#define END_OF_TABLE (-3) + +struct volume { +#if defined (UEFI) + EFI_HANDLE efi_handle; + + // Block storage + EFI_HANDLE efi_part_handle; + EFI_BLOCK_IO *block_io; + + // PXE + EFI_PXE_BASE_CODE_PROTOCOL *pxe_base_code; + + bool unique_sector_valid; + uint8_t unique_sector_b2b[BLAKE2B_OUT_BYTES]; +#elif defined (BIOS) + int drive; +#endif + + size_t fastest_xfer_size; + + int index; + + bool is_optical; + bool pxe; + + int partition; + int sector_size; + struct volume *backing_dev; + + int max_partition; + + int cache_status; + uint8_t *cache; + uint64_t cached_block; + + uint64_t first_sect; + uint64_t sect_count; + + bool guid_valid; + struct guid guid; + bool part_guid_valid; + struct guid part_guid; + bool fslabel_valid; + char *fslabel; +}; + +bool is_valid_mbr(struct volume *volume); + +extern struct volume **volume_index; +extern size_t volume_index_i; + +bool gpt_get_guid(struct guid *guid, struct volume *volume); +uint32_t mbr_get_id(struct volume *volume); + +int part_get(struct volume *part, struct volume *volume, int partition); + +struct volume *volume_get_by_guid(struct guid *guid); +struct volume *volume_get_by_fslabel(char *fslabel); +struct volume *volume_get_by_coord(bool optical, int drive, int partition); +#if defined (BIOS) +struct volume *volume_get_by_bios_drive(int drive); +#endif + +bool volume_read(struct volume *part, void *buffer, uint64_t loc, uint64_t count); + +#define volume_iterate_parts(_VOLUME_, _BODY_) do { \ + struct volume *_VOLUME = _VOLUME_; \ + if (_VOLUME->pxe) { \ + do { \ + struct volume *_PART = _VOLUME; \ + _BODY_ \ + } while (0); \ + } else { \ + while (_VOLUME->backing_dev != NULL) { \ + _VOLUME = _VOLUME->backing_dev; \ + } \ + \ + int _PART_CNT = -1; \ + for (size_t _PARTNO = 0; ; _PARTNO++) { \ + if (_PART_CNT > _VOLUME->max_partition) \ + break; \ + \ + struct volume *_PART = volume_get_by_coord(_VOLUME->is_optical, \ + _VOLUME->index, _PARTNO); \ + if (_PART == NULL) \ + continue; \ + \ + _PART_CNT++; \ + \ + _BODY_ \ + } \ + } \ +} while (0) + +#endif diff --git a/limine/common/lib/part.s2.c b/limine/common/lib/part.s2.c new file mode 100644 index 0000000..422ec89 --- /dev/null +++ b/limine/common/lib/part.s2.c @@ -0,0 +1,645 @@ +#include +#include +#include +#include +#if defined (BIOS) +# include +#endif +#include +#include +#include +#include +#include + +enum { + CACHE_NOT_READY = 0, + CACHE_READY +}; + +static bool cache_block(struct volume *volume, uint64_t block) { + if (volume->cache_status == CACHE_READY && block == volume->cached_block) + return true; + + volume->cache_status = CACHE_NOT_READY; + + if (volume->cache == NULL) + volume->cache = + ext_mem_alloc(volume->fastest_xfer_size * volume->sector_size); + + if (volume->first_sect % (volume->sector_size / 512)) { + return false; + } + + uint64_t first_sect = volume->first_sect / (volume->sector_size / 512); + + uint64_t xfer_size = volume->fastest_xfer_size; + + // Check for overflow in sector offset calculation + uint64_t block_offset; + if (__builtin_mul_overflow(block, volume->fastest_xfer_size, &block_offset)) { + return false; + } + uint64_t read_sector; + if (__builtin_add_overflow(first_sect, block_offset, &read_sector)) { + return false; + } + + // Clamp xfer_size to remaining sectors in volume + if (volume->sect_count != (uint64_t)-1) { + uint64_t volume_sectors = volume->sect_count / (volume->sector_size / 512); + uint64_t end_sector; + if (__builtin_add_overflow(first_sect, volume_sectors, &end_sector)) { + end_sector = UINT64_MAX; + } + if (read_sector >= end_sector) { + return false; + } + uint64_t remaining = end_sector - read_sector; + if (xfer_size > remaining) { + xfer_size = remaining; + } + } + + int ret = disk_read_sectors(volume, volume->cache, read_sector, xfer_size); + if (ret != DISK_SUCCESS) { + return false; + } + + volume->cache_status = CACHE_READY; + volume->cached_block = block; + + return true; +} + +bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t count) { + if (volume->pxe) { + panic(false, "Attempted volume_read() on pxe"); + } + + if (volume->sect_count != (uint64_t)-1) { + // sect_count is always in 512-byte sectors for both whole disks and partitions + uint64_t part_size; + if (__builtin_mul_overflow(volume->sect_count, (uint64_t)512, &part_size)) { + return false; + } + if (loc >= part_size || count > part_size - loc) { + return false; + } + } + + uint64_t block_size = volume->fastest_xfer_size * volume->sector_size; + + uint64_t progress = 0; + while (progress < count) { + uint64_t block = (loc + progress) / block_size; + + if (!cache_block(volume, block)) + return false; + + uint64_t chunk = count - progress; + uint64_t offset = (loc + progress) % block_size; + if (chunk > block_size - offset) + chunk = block_size - offset; + + memcpy(buffer + progress, &volume->cache[offset], chunk); + progress += chunk; + } + + return true; +} + +struct gpt_table_header { + // the head + char signature[8]; + uint32_t revision; + uint32_t header_size; + uint32_t crc32; + uint32_t _reserved0; + + // the partitioning info + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + + // the guid + struct guid disk_guid; + + // entries related + uint64_t partition_entry_lba; + uint32_t number_of_partition_entries; + uint32_t size_of_partition_entry; + uint32_t partition_entry_array_crc32; +} __attribute__((packed)); + +struct gpt_entry { + struct guid partition_type_guid; + + struct guid unique_partition_guid; + + uint64_t starting_lba; + uint64_t ending_lba; + + uint64_t attributes; + + uint16_t partition_name[36]; +} __attribute__((packed)); + +bool gpt_get_guid(struct guid *guid, struct volume *volume) { + struct gpt_table_header header = {0}; + + int lb_guesses[] = { + 512, + 4096 + }; + int lb_size = -1; + + for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) { + // read header, located after the first block + if (!volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header))) + continue; + + // check the header + // 'EFI PART' + if (strncmp(header.signature, "EFI PART", 8)) + continue; + + lb_size = lb_guesses[i]; + break; + } + + if (lb_size == -1) { + return false; + } + + if (header.revision != 0x00010000) + return false; + + *guid = header.disk_guid; + + return true; +} + +static int gpt_get_part(struct volume *ret, struct volume *volume, int partition) { + struct gpt_table_header header = {0}; + + int lb_guesses[] = { + 512, + 4096 + }; + int lb_size = -1; + + for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) { + // read header, located after the first block + if (!volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header))) + continue; + + // check the header + // 'EFI PART' + if (strncmp(header.signature, "EFI PART", 8)) + continue; + + lb_size = lb_guesses[i]; + break; + } + + if (lb_size == -1) { + return INVALID_TABLE; + } + + if (header.revision != 0x00010000) + return INVALID_TABLE; + + // parse the entries if reached here + if ((uint32_t)partition >= header.number_of_partition_entries) + return END_OF_TABLE; + + // Validate partition entry size (must be at least as large as our struct) + uint32_t entry_size = header.size_of_partition_entry; + if (entry_size < sizeof(struct gpt_entry)) { + return INVALID_TABLE; + } + + // Check for potential integer overflow in offset calculation + uint64_t entry_offset; + if (__builtin_mul_overflow((uint64_t)header.partition_entry_lba, (uint64_t)lb_size, &entry_offset)) { + return INVALID_TABLE; // Multiplication overflow + } + // Use actual entry size from header for offset calculation + uint64_t partition_offset = (uint64_t)partition * entry_size; + if (__builtin_add_overflow(entry_offset, partition_offset, &entry_offset)) { + return INVALID_TABLE; // Addition overflow would occur + } + + struct gpt_entry entry = {0}; + if (!volume_read(volume, &entry, entry_offset, sizeof(entry))) { + return END_OF_TABLE; + } + + struct guid empty_guid = {0}; + if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid))) + return NO_PARTITION; + + // Validate that ending_lba >= starting_lba to prevent underflow + if (entry.ending_lba < entry.starting_lba) { + return NO_PARTITION; // Invalid partition geometry + } + + // Calculate sector multiplier for lb_size conversion + uint64_t sect_multiplier = lb_size / 512; + + // Check for overflow in first_sect calculation + uint64_t first_sect_result; + if (__builtin_mul_overflow(entry.starting_lba, sect_multiplier, &first_sect_result)) { + return NO_PARTITION; // Overflow in first_sect + } + + // Check for overflow in sect_count calculation + // First compute partition size in logical blocks + // Check if +1 would overflow (ending_lba == UINT64_MAX) + uint64_t partition_size = entry.ending_lba - entry.starting_lba; + if (partition_size == UINT64_MAX) { + return NO_PARTITION; // Partition size +1 would overflow + } + uint64_t partition_blocks = partition_size + 1; + uint64_t sect_count_result; + if (__builtin_mul_overflow(partition_blocks, sect_multiplier, §_count_result)) { + return NO_PARTITION; // Overflow in sect_count + } + +#if defined (UEFI) + ret->efi_handle = volume->efi_handle; + ret->block_io = volume->block_io; +#elif defined (BIOS) + ret->drive = volume->drive; +#endif + ret->fastest_xfer_size = volume->fastest_xfer_size; + ret->index = volume->index; + ret->is_optical = volume->is_optical; + ret->partition = partition + 1; + ret->sector_size = volume->sector_size; + ret->first_sect = first_sect_result; + ret->sect_count = sect_count_result; + ret->backing_dev = volume; + + struct guid guid; + if (!fs_get_guid(&guid, ret)) { + ret->guid_valid = false; + } else { + ret->guid_valid = true; + ret->guid = guid; + } + + char *fslabel = fs_get_label(ret); + if (fslabel == NULL) { + ret->fslabel_valid = false; + } else { + ret->fslabel_valid = true; + ret->fslabel = fslabel; + } + + ret->part_guid_valid = true; + ret->part_guid = entry.unique_partition_guid; + + return 0; +} + +struct mbr_entry { + uint8_t status; + uint8_t chs_first_sect[3]; + uint8_t type; + uint8_t chs_last_sect[3]; + uint32_t first_sect; + uint32_t sect_count; +} __attribute__((packed)); + +bool is_valid_mbr(struct volume *volume) { + // Check if actually valid mbr + uint16_t hint = 0; + + if (!volume_read(volume, &hint, 446, sizeof(uint8_t))) + return false; + if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) + return false; + if (!volume_read(volume, &hint, 462, sizeof(uint8_t))) + return false; + if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) + return false; + if (!volume_read(volume, &hint, 478, sizeof(uint8_t))) + return false; + if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) + return false; + if (!volume_read(volume, &hint, 494, sizeof(uint8_t))) + return false; + if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) + return false; + + char hintc[64]; + if (!volume_read(volume, hintc, 3, 4)) + return false; + if (memcmp(hintc, "NTFS", 4) == 0) + return false; + if (!volume_read(volume, hintc, 54, 3)) + return false; + if (memcmp(hintc, "FAT", 3) == 0) + return false; + if (!volume_read(volume, hintc, 82, 3)) + return false; + if (memcmp(hintc, "FAT", 3) == 0) + return false; + if (!volume_read(volume, hintc, 3, 5)) + return false; + if (memcmp(hintc, "FAT32", 5) == 0) + return false; + if (!volume_read(volume, &hint, 1080, sizeof(uint16_t))) + return false; + if (hint == 0xef53) + return false; + + return true; +} + +uint32_t mbr_get_id(struct volume *volume) { + if (!is_valid_mbr(volume)) { + return 0; + } + + uint32_t ret; + if (!volume_read(volume, &ret, 0x1b8, sizeof(uint32_t))) { + return 0; + } + + return ret; +} + +// Maximum number of logical partitions to prevent infinite loops from circular EBR chains +#define MAX_LOGICAL_PARTITIONS 256 + +static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part, + int partition) { + struct mbr_entry entry; + + // Limit partition index to prevent excessive iteration + if (partition >= MAX_LOGICAL_PARTITIONS) { + return END_OF_TABLE; + } + + uint64_t ebr_sector = 0; + uint64_t prev_ebr_sector = 0; + + for (int i = 0; i < partition; i++) { + uint64_t entry_offset = ebr_sector * 512 + 0x1ce; + + if (!volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry))) { + return END_OF_TABLE; + } + + if (entry.type != 0x0f && entry.type != 0x05) { + return END_OF_TABLE; + } + + prev_ebr_sector = ebr_sector; + ebr_sector = entry.first_sect; + + // Detect circular chain: if new sector points to 0 or backwards, it's invalid + // (EBR sectors should always increase within the extended partition) + if (ebr_sector == 0 || (i > 0 && ebr_sector <= prev_ebr_sector)) { + return END_OF_TABLE; // Circular or corrupted EBR chain + } + + // Also check that ebr_sector is within the extended partition bounds + if (ebr_sector >= extended_part->sect_count) { + return END_OF_TABLE; // EBR points outside extended partition + } + } + + uint64_t entry_offset = ebr_sector * 512 + 0x1be; + + if (!volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry))) { + return END_OF_TABLE; + } + + if (entry.type == 0) + return NO_PARTITION; + + // Validate sect_count is non-zero + if (entry.sect_count == 0) { + return NO_PARTITION; + } + + // Check for overflow in first_sect calculation + uint64_t first_sect_64; + if (__builtin_add_overflow(extended_part->first_sect, ebr_sector, &first_sect_64)) { + return NO_PARTITION; // Addition overflow + } + if (__builtin_add_overflow(first_sect_64, (uint64_t)entry.first_sect, &first_sect_64)) { + return NO_PARTITION; // Addition overflow + } + uint64_t partition_end; + if (__builtin_add_overflow(first_sect_64, (uint64_t)entry.sect_count, &partition_end)) { + return NO_PARTITION; // Partition would overflow + } + +#if defined (UEFI) + ret->efi_handle = extended_part->efi_handle; + ret->block_io = extended_part->block_io; +#elif defined (BIOS) + ret->drive = extended_part->drive; +#endif + ret->fastest_xfer_size = extended_part->fastest_xfer_size; + ret->index = extended_part->index; + ret->is_optical = extended_part->is_optical; + ret->partition = partition + 4 + 1; + ret->sector_size = extended_part->sector_size; + ret->first_sect = first_sect_64; + ret->sect_count = entry.sect_count; + ret->backing_dev = extended_part->backing_dev; + + struct guid guid; + if (!fs_get_guid(&guid, ret)) { + ret->guid_valid = false; + } else { + ret->guid_valid = true; + ret->guid = guid; + } + + char *fslabel = fs_get_label(ret); + if (fslabel == NULL) { + ret->fslabel_valid = false; + } else { + ret->fslabel_valid = true; + ret->fslabel = fslabel; + } + + ret->part_guid_valid = false; + + return 0; +} + +static int mbr_get_part(struct volume *ret, struct volume *volume, int partition) { + if (!is_valid_mbr(volume)) { + return INVALID_TABLE; + } + + struct mbr_entry entry; + + if (partition > 3) { + for (int i = 0; i < 4; i++) { + uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * i; + + if (!volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry))) { + continue; + } + + if (entry.type != 0x0f && entry.type != 0x05) + continue; + + // Validate extended partition has non-zero size + if (entry.sect_count == 0) { + continue; + } + + struct volume extended_part = {0}; + +#if defined (UEFI) + extended_part.efi_handle = volume->efi_handle; + extended_part.block_io = volume->block_io; +#elif defined (BIOS) + extended_part.drive = volume->drive; +#endif + extended_part.fastest_xfer_size = volume->fastest_xfer_size; + extended_part.index = volume->index; + extended_part.is_optical = volume->is_optical; + extended_part.partition = i + 1; + extended_part.sector_size = volume->sector_size; + extended_part.first_sect = entry.first_sect; + extended_part.sect_count = entry.sect_count; + extended_part.backing_dev = volume; + + return mbr_get_logical_part(ret, &extended_part, partition - 4); + } + + return END_OF_TABLE; + } + + uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * partition; + + if (!volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry))) { + return END_OF_TABLE; + } + + if (entry.type == 0) + return NO_PARTITION; + + // Validate sect_count is non-zero + if (entry.sect_count == 0) { + return NO_PARTITION; + } + +#if defined (UEFI) + ret->efi_handle = volume->efi_handle; + ret->block_io = volume->block_io; +#elif defined (BIOS) + ret->drive = volume->drive; +#endif + ret->fastest_xfer_size = volume->fastest_xfer_size; + ret->index = volume->index; + ret->is_optical = volume->is_optical; + ret->partition = partition + 1; + ret->sector_size = volume->sector_size; + ret->first_sect = entry.first_sect; + ret->sect_count = entry.sect_count; + ret->backing_dev = volume; + + struct guid guid; + if (!fs_get_guid(&guid, ret)) { + ret->guid_valid = false; + } else { + ret->guid_valid = true; + ret->guid = guid; + } + + char *fslabel = fs_get_label(ret); + if (fslabel == NULL) { + ret->fslabel_valid = false; + } else { + ret->fslabel_valid = true; + ret->fslabel = fslabel; + } + + ret->part_guid_valid = false; + + return 0; +} + +int part_get(struct volume *part, struct volume *volume, int partition) { + int ret; + + // Validate partition index is non-negative + if (partition < 0) { + return NO_PARTITION; + } + + ret = gpt_get_part(part, volume, partition); + if (ret != INVALID_TABLE) + return ret; + + ret = mbr_get_part(part, volume, partition); + if (ret != INVALID_TABLE) + return ret; + + return INVALID_TABLE; +} + +struct volume **volume_index = NULL; +size_t volume_index_i = 0; + +struct volume *volume_get_by_guid(struct guid *guid) { + for (size_t i = 0; i < volume_index_i; i++) { + if (volume_index[i]->guid_valid + && memcmp(&volume_index[i]->guid, guid, 16) == 0) { + return volume_index[i]; + } + if (volume_index[i]->part_guid_valid + && memcmp(&volume_index[i]->part_guid, guid, 16) == 0) { + return volume_index[i]; + } + } + + return NULL; +} + +struct volume *volume_get_by_fslabel(char *fslabel) { + for (size_t i = 0; i < volume_index_i; i++) { + if (volume_index[i]->fslabel_valid + && strcmp(volume_index[i]->fslabel, fslabel) == 0) { + return volume_index[i]; + } + } + + return NULL; +} + +struct volume *volume_get_by_coord(bool optical, int drive, int partition) { + for (size_t i = 0; i < volume_index_i; i++) { + if (volume_index[i]->index == drive + && volume_index[i]->is_optical == optical + && volume_index[i]->partition == partition) { + return volume_index[i]; + } + } + + return NULL; +} + +#if defined (BIOS) +struct volume *volume_get_by_bios_drive(int drive) { + for (size_t i = 0; i < volume_index_i; i++) { + if (volume_index[i]->drive == drive) { + return volume_index[i]; + } + } + + return NULL; +} +#endif diff --git a/limine/common/lib/pe.c b/limine/common/lib/pe.c new file mode 100644 index 0000000..c5e52a6 --- /dev/null +++ b/limine/common/lib/pe.c @@ -0,0 +1,496 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000) + +#define IMAGE_DOS_SIGNATURE 0x5a4d + +typedef struct _IMAGE_DOS_HEADER { + uint16_t e_magic; + uint16_t e_cblp; + uint16_t e_cp; + uint16_t e_crlc; + uint16_t e_cparhdr; + uint16_t e_minalloc; + uint16_t e_maxalloc; + uint16_t e_ss; + uint16_t e_sp; + uint16_t e_csum; + uint16_t e_ip; + uint16_t e_cs; + uint16_t e_lfarlc; + uint16_t e_ovno; + uint16_t e_res[4]; + uint16_t e_oemid; + uint16_t e_oeminfo; + uint16_t e_res2[10]; + uint32_t e_lfanew; +} IMAGE_DOS_HEADER; + +#define IMAGE_FILE_MACHINE_I386 0x14c +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 +#define IMAGE_FILE_MACHINE_RISCV64 0x5064 +#define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264 + +#define IMAGE_FILE_RELOCS_STRIPPED 1 +#define IMAGE_FILE_EXECUTABLE_IMAGE 2 + +typedef struct { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +} IMAGE_FILE_HEADER; + +typedef struct { + uint32_t VirtualAddress; + uint32_t Size; +} IMAGE_DATA_DIRECTORY; + +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 +#define IMAGE_DIRECTORY_ENTRY_TLS 9 +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 +#define IMAGE_DIRECTORY_ENTRY_IAT 12 +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 + +typedef struct { + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER64; + +#define IMAGE_NT_SIGNATURE 0x4550 + +typedef struct { + uint32_t Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} IMAGE_NT_HEADERS64; + +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +typedef struct { + char Name[8]; + uint32_t VirtualSize; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +} IMAGE_SECTION_HEADER; + +typedef struct { + union { + uint32_t Characteristics; + uint32_t OriginalFirstThunk; + }; + uint32_t TimeDateStamp; + uint32_t ForwarderChain; + uint32_t Name; + uint32_t FirstThunk; +} IMAGE_IMPORT_DESCRIPTOR; + +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_DIR64 10 + +typedef struct { + uint32_t VirtualAddress; + uint32_t SizeOfBlock; +} IMAGE_BASE_RELOCATION_BLOCK; + +static void pe64_validate(uint8_t *image, size_t file_size) { + IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image; + + if (file_size < sizeof(IMAGE_DOS_HEADER)) { + panic(true, "pe: File too small for DOS header"); + } + + if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) { + panic(true, "pe: Not a valid PE file"); + } + + if (file_size < sizeof(IMAGE_NT_HEADERS64)) { + panic(true, "pe: File too small for NT headers"); + } + + if (dos_hdr->e_lfanew > file_size - sizeof(IMAGE_NT_HEADERS64)) { + panic(true, "pe: e_lfanew offset out of bounds"); + } + + IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew); + + if (nt_hdrs->Signature != IMAGE_NT_SIGNATURE) { + panic(true, "pe: Not a valid PE file"); + } + + if (nt_hdrs->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + panic(true, "pe: Not a valid PE32+ file"); + } + +#if defined(__x86_64__) || defined(__i386__) + if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) { + panic(true, "pe: Not an x86-64 PE file"); + } +#elif defined(__aarch64__) + if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_ARM64) { + panic(true, "pe: Not an ARM64 PE file"); + } +#elif defined (__riscv) && (__riscv_xlen == 64) + if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_RISCV64) { + panic(true, "pe: Not a RISC-V PE file"); + } +#elif defined (__loongarch__) && (__loongarch_grlen == 64) + if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_LOONGARCH64) { + panic(true, "pe: Not a loongarch64 PE file"); + } +#else +#error Unknown architecture +#endif +} + +int pe_bits(uint8_t *image, size_t image_size) { + if (image_size < sizeof(IMAGE_DOS_HEADER)) { + return -1; + } + + IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image; + + if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) { + return -1; + } + + if (image_size < sizeof(IMAGE_NT_HEADERS64)) { + return -1; + } + + if ((size_t)dos_hdr->e_lfanew > image_size - sizeof(IMAGE_NT_HEADERS64)) { + return -1; + } + + IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew); + + if (nt_hdrs->Signature != IMAGE_NT_SIGNATURE) { + return -1; + } + + switch (nt_hdrs->FileHeader.Machine) { + case IMAGE_FILE_MACHINE_I386: + return 32; + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_ARM64: + case IMAGE_FILE_MACHINE_RISCV64: + case IMAGE_FILE_MACHINE_LOONGARCH64: + return 64; + } + + return -1; +} + +bool pe64_load(uint8_t *image, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **_ranges, uint64_t *_ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *image_size_before_bss, bool *_is_reloc) { + pe64_validate(image, file_size); + + IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image; + IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew); + + // Validate SizeOfOptionalHeader doesn't cause sections pointer to go out of bounds + size_t sections_offset = dos_hdr->e_lfanew + sizeof(uint32_t) + sizeof(IMAGE_FILE_HEADER) + nt_hdrs->FileHeader.SizeOfOptionalHeader; + size_t sections_end = sections_offset + (size_t)nt_hdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (sections_end > file_size) { + panic(true, "pe: Section headers extend beyond file bounds"); + } + + IMAGE_SECTION_HEADER *sections = (IMAGE_SECTION_HEADER *)((uintptr_t)&nt_hdrs->OptionalHeader + nt_hdrs->FileHeader.SizeOfOptionalHeader); + + bool is_reloc = true; + + if (nt_hdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) { + is_reloc = false; + } + + if (_is_reloc) { + *_is_reloc = is_reloc; + } + + uint64_t image_base = nt_hdrs->OptionalHeader.ImageBase; + uint64_t image_size = nt_hdrs->OptionalHeader.SizeOfImage; + uint64_t alignment = nt_hdrs->OptionalHeader.SectionAlignment; + + if (alignment > 1 && (alignment & (alignment - 1)) != 0) { + panic(true, "pe: SectionAlignment is not a power of 2"); + } + + bool lower_to_higher = false; + + if (image_base < FIXED_HIGHER_HALF_OFFSET_64) { + if (!is_reloc) { + panic(true, "pe: Lower half images are not allowed"); + } + + lower_to_higher = true; + } + + uint64_t slide = 0; + size_t try_count = 0; + size_t max_simulated_tries = 0x10000; + + if (lower_to_higher) { + slide = FIXED_HIGHER_HALF_OFFSET_64 - image_base; + } + + *physical_base = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, alignment); + *virtual_base = image_base; + + // Validate SizeOfHeaders doesn't exceed file size or image size + if (nt_hdrs->OptionalHeader.SizeOfHeaders > file_size + || nt_hdrs->OptionalHeader.SizeOfHeaders > image_size) { + panic(true, "pe: SizeOfHeaders exceeds file or image size"); + } + + memcpy((void *)(uintptr_t)*physical_base, image, nt_hdrs->OptionalHeader.SizeOfHeaders); + + if (_image_size) { + *_image_size = image_size; + } + + if (is_reloc && kaslr) { +again: + slide = (rand32() & ~(alignment - 1)) + (lower_to_higher ? FIXED_HIGHER_HALF_OFFSET_64 - image_base : 0); + + if (*virtual_base + slide + image_size < 0xffffffff80000000 /* this comparison relies on overflow */) { + if (++try_count == max_simulated_tries) { + panic(true, "pe: Image wants to load too high"); + } + goto again; + } + } + + for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) { + IMAGE_SECTION_HEADER *section = §ions[i]; + + uintptr_t section_base = *physical_base + section->VirtualAddress; + uint32_t section_raw_size = section->VirtualSize < section->SizeOfRawData ? section->VirtualSize : section->SizeOfRawData; + + // Validate section doesn't write past the image buffer + if ((uint64_t)section->VirtualAddress + section_raw_size > image_size) { + panic(true, "pe: Section %U exceeds image bounds", (uint64_t)i); + } + + // Validate section data doesn't exceed file bounds + if ((uint64_t)section->PointerToRawData + section_raw_size > file_size) { + panic(true, "pe: Section %U data extends beyond file bounds", (uint64_t)i); + } + + memcpy((void *)section_base, image + section->PointerToRawData, section_raw_size); + } + + if (nt_hdrs->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_BASERELOC + 1) { + panic(true, "pe: NumberOfRvaAndSizes too small for import/reloc directories"); + } + + IMAGE_DATA_DIRECTORY *import_dir = &nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + IMAGE_DATA_DIRECTORY *reloc_dir = &nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + + if (import_dir->Size != 0) { + if (import_dir->VirtualAddress >= image_size || + sizeof(IMAGE_IMPORT_DESCRIPTOR) > image_size - import_dir->VirtualAddress) { + panic(true, "pe: Import directory VirtualAddress out of bounds"); + } + IMAGE_IMPORT_DESCRIPTOR *import_desc = (IMAGE_IMPORT_DESCRIPTOR *)((uintptr_t)*physical_base + import_dir->VirtualAddress); + + if (import_desc->Name != 0) { + panic(true, "pe: Kernel must not have any imports"); + } + } + + if (reloc_dir->VirtualAddress != 0) { + if (reloc_dir->VirtualAddress >= image_size || + reloc_dir->Size > image_size - reloc_dir->VirtualAddress) { + panic(true, "pe: Relocation directory VirtualAddress out of bounds"); + } + size_t reloc_block_offset = 0; + + while (reloc_dir->Size - reloc_block_offset >= sizeof(IMAGE_BASE_RELOCATION_BLOCK)) { + IMAGE_BASE_RELOCATION_BLOCK *block = (IMAGE_BASE_RELOCATION_BLOCK *)((uintptr_t)*physical_base + reloc_dir->VirtualAddress + reloc_block_offset); + + // Validate SizeOfBlock to prevent infinite loop (if 0) and underflow (if too small) + if (block->SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION_BLOCK)) { + panic(true, "pe: Invalid relocation block size"); + } + + if (block->SizeOfBlock > reloc_dir->Size - reloc_block_offset) { + panic(true, "pe: Relocation block size exceeds directory"); + } + + if (block->VirtualAddress >= image_size) { + panic(true, "pe: Relocation block VirtualAddress out of bounds"); + } + + uintptr_t block_base = *physical_base + block->VirtualAddress; + size_t entries = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION_BLOCK)) / sizeof(uint16_t); + uint16_t *relocs = (uint16_t *)(block + 1); + + for (size_t i = 0; i < entries; i++) { + uint16_t type = relocs[i] >> 12; + uint16_t offset = relocs[i] & 0xfff; + + if (type == IMAGE_REL_BASED_ABSOLUTE) { + continue; + } + + size_t write_size; + switch (type) { + case IMAGE_REL_BASED_HIGHLOW: + if (lower_to_higher) { + panic(true, "pe: 32-bit relocations are incompatible with higher-half loading"); + } + write_size = 4; + break; + case IMAGE_REL_BASED_DIR64: write_size = 8; break; + default: + panic(true, "pe: Unsupported relocation type %u", type); + __builtin_unreachable(); + } + + if ((uint64_t)block->VirtualAddress + offset + write_size > image_size) { + panic(true, "pe: Relocation offset out of bounds"); + } + + switch (type) { + case IMAGE_REL_BASED_HIGHLOW: + *(uint32_t *)(block_base + offset) += slide; + break; + case IMAGE_REL_BASED_DIR64: + *(uint64_t *)(block_base + offset) += slide; + break; + } + } + + reloc_block_offset += block->SizeOfBlock; + } + } + + if (image_size_before_bss) { + *image_size_before_bss = image_size; + } + + *virtual_base += slide; + *entry_point = *virtual_base + nt_hdrs->OptionalHeader.AddressOfEntryPoint; + + if (_slide) { + *_slide = slide; + } + + if (_ranges && _ranges_count) { + size_t range_count = 0; + + bool headers_within_section = false; + + for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) { + IMAGE_SECTION_HEADER *section = §ions[i]; + + if (section->VirtualAddress == 0) { + headers_within_section = true; + } + + range_count++; + } + + if (!headers_within_section) { + range_count++; + } + + struct mem_range *ranges = ext_mem_alloc(range_count * sizeof(struct mem_range)); + + *_ranges = ranges; + *_ranges_count = range_count; + + size_t range_index = 0; + + if (!headers_within_section) { + struct mem_range *range = &ranges[range_index++]; + range->base = *virtual_base; + range->length = ALIGN_UP(nt_hdrs->OptionalHeader.SizeOfHeaders, 0x1000); + range->permissions = MEM_RANGE_R; + } + + for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) { + IMAGE_SECTION_HEADER *section = §ions[i]; + + uintptr_t misalign = section->VirtualAddress % alignment; + + struct mem_range *range = &ranges[range_index++]; + range->base = *virtual_base + ALIGN_DOWN(section->VirtualAddress, alignment); + range->length = ALIGN_UP(section->VirtualSize + misalign, alignment); + + if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) { + range->permissions |= MEM_RANGE_X; + } + + if (section->Characteristics & IMAGE_SCN_MEM_WRITE) { + range->permissions |= MEM_RANGE_W; + } + + if (section->Characteristics & IMAGE_SCN_MEM_READ) { + range->permissions |= MEM_RANGE_R; + } + } + } + + return true; +} diff --git a/limine/common/lib/pe.h b/limine/common/lib/pe.h new file mode 100644 index 0000000..7923916 --- /dev/null +++ b/limine/common/lib/pe.h @@ -0,0 +1,12 @@ +#ifndef LIB__PE_H__ +#define LIB__PE_H__ + +#include +#include +#include + +int pe_bits(uint8_t *image, size_t image_size); + +bool pe64_load(uint8_t *image, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc); + +#endif diff --git a/limine/common/lib/print.h b/limine/common/lib/print.h new file mode 100644 index 0000000..a5e9a99 --- /dev/null +++ b/limine/common/lib/print.h @@ -0,0 +1,16 @@ +#ifndef LIB__PRINT_H__ +#define LIB__PRINT_H__ + +#include +#include + +extern bool verbose; + +void print(const char *fmt, ...); +void vprint(const char *fmt, va_list args); + +#define printv(FMT, ...) do { \ + if (verbose) print(FMT, ##__VA_ARGS__); \ +} while (0) + +#endif diff --git a/limine/common/lib/print.s2.c b/limine/common/lib/print.s2.c new file mode 100644 index 0000000..3e34e0e --- /dev/null +++ b/limine/common/lib/print.s2.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#if defined (BIOS) +#include +#endif +#include +#include + +#if defined (BIOS) +static void s2_print(const char *s, size_t len) { + for (size_t i = 0; i < len; i++) { + struct rm_regs r = {0}; + r.eax = 0x0e00 | s[i]; + rm_int(0x10, &r, &r); + } +} +#endif + +static const char *base_digits = "0123456789abcdef"; + +#define PRINT_BUF_MAX 4096 + +static void prn_char(char *print_buf, size_t *print_buf_i, char c) { + if (c == '\n') { + prn_char(print_buf, print_buf_i, '\r'); + } + + if (*print_buf_i < (PRINT_BUF_MAX - 1)) { + print_buf[(*print_buf_i)++] = c; + } + + print_buf[*print_buf_i] = 0; +} + +static void prn_str(char *print_buf, size_t *print_buf_i, const char *string) { + size_t i; + + for (i = 0; string[i]; i++) { + prn_char(print_buf, print_buf_i, string[i]); + } +} + +static void prn_nstr(char *print_buf, size_t *print_buf_i, const char *string, size_t len) { + size_t i; + + for (i = 0; i < len; i++) { + prn_char(print_buf, print_buf_i, string[i]); + } +} + +static void prn_i(char *print_buf, size_t *print_buf_i, int64_t x) { + int i; + char buf[21] = {0}; + + if (!x) { + prn_char(print_buf, print_buf_i, '0'); + return; + } + + int sign = x < 0; + uint64_t ux = sign ? (uint64_t)0 - (uint64_t)x : (uint64_t)x; + + for (i = 19; ux; i--) { + buf[i] = (ux % 10) + 0x30; + ux = ux / 10; + } + if (sign) + buf[i] = '-'; + else + i++; + + prn_str(print_buf, print_buf_i, buf + i); +} + +static void prn_ui(char *print_buf, size_t *print_buf_i, uint64_t x) { + int i; + char buf[21] = {0}; + + if (!x) { + prn_char(print_buf, print_buf_i, '0'); + return; + } + + for (i = 19; x; i--) { + buf[i] = (x % 10) + 0x30; + x = x / 10; + } + + i++; + prn_str(print_buf, print_buf_i, buf + i); +} + +static void prn_x(char *print_buf, size_t *print_buf_i, uint64_t x) { + int i; + char buf[17] = {0}; + + if (!x) { + prn_str(print_buf, print_buf_i, "0x0"); + return; + } + + for (i = 15; x; i--) { + buf[i] = base_digits[(x % 16)]; + x = x / 16; + } + + i++; + prn_str(print_buf, print_buf_i, "0x"); + prn_str(print_buf, print_buf_i, buf + i); +} + +void print(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vprint(fmt, args); + va_end(args); +} + +static char print_buf[PRINT_BUF_MAX]; + +void vprint(const char *fmt, va_list args) { + size_t print_buf_i = 0; + + for (;;) { + while (*fmt && *fmt != '%') { + prn_char(print_buf, &print_buf_i, *fmt++); + } + + if (!*fmt++) + goto out; + + switch (*fmt++) { + case 's': { + char *str = (char *)va_arg(args, const char *); + if (!str) + prn_str(print_buf, &print_buf_i, "(null)"); + else + prn_str(print_buf, &print_buf_i, str); } + break; + case 'S': { + char *str = (char *)va_arg(args, const char *); + size_t str_len = va_arg(args, size_t); + if (!str) + prn_str(print_buf, &print_buf_i, "(null)"); + else + prn_nstr(print_buf, &print_buf_i, str, str_len); } + break; + case 'd': + prn_i(print_buf, &print_buf_i, (int64_t)va_arg(args, int32_t)); + break; + case 'u': + prn_ui(print_buf, &print_buf_i, (uint64_t)va_arg(args, uint32_t)); + break; + case 'x': + prn_x(print_buf, &print_buf_i, (uint64_t)va_arg(args, uint32_t)); + break; + case 'D': + prn_i(print_buf, &print_buf_i, va_arg(args, int64_t)); + break; + case 'U': + prn_ui(print_buf, &print_buf_i, va_arg(args, uint64_t)); + break; + case 'X': + prn_x(print_buf, &print_buf_i, va_arg(args, uint64_t)); + break; + case 'p': + prn_x(print_buf, &print_buf_i, va_arg(args, uintptr_t)); + break; + case 'c': { + char c = (char)va_arg(args, int); + prn_char(print_buf, &print_buf_i, c); } + break; + case '#': { + bool printed = false; + char *str = (char *)va_arg(args, const char *); + for (int i = (int)strlen(str) - 1; i >= 0; i--) { + if (str[i] != '#') { + continue; + } + + prn_nstr(print_buf, &print_buf_i, str, i); + printed = true; + break; + } + + if (!printed) { + prn_str(print_buf, &print_buf_i, str); + } + + break; + } + default: + prn_char(print_buf, &print_buf_i, '?'); + break; + } + } + +out: + if (!quiet) { +#if defined (BIOS) + if (stage3_loaded) { +#endif + FOR_TERM(flanterm_write(TERM, print_buf, print_buf_i)); +#if defined (BIOS) + } else { + s2_print(print_buf, print_buf_i); + } +#endif + } + + for (size_t i = 0; i < print_buf_i; i++) { +#if defined (__x86_64__) || defined (__i386__) + if (E9_OUTPUT) { + outb(0xe9, print_buf[i]); + } +#endif +#if defined (BIOS) + if (stage3_loaded && ((!quiet && serial) || COM_OUTPUT)) { + if (!isprint(print_buf[i])) { + switch (print_buf[i]) { + case '\r': case '\n': case '\e': break; + default: continue; + } + } + serial_out(print_buf[i]); + } +#endif + } +} diff --git a/limine/common/lib/rand.c b/limine/common/lib/rand.c new file mode 100644 index 0000000..6d34ce1 --- /dev/null +++ b/limine/common/lib/rand.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO: Find where this mersenne twister implementation is inspired from +// and properly credit the original author(s). + +static bool rand_initialised = false; + +#define n ((int)624) +#define m ((int)397) +#define matrix_a ((uint32_t)0x9908b0df) +#define msb ((uint32_t)0x80000000) +#define lsbs ((uint32_t)0x7fffffff) + +static uint32_t *status; +static int ctr; + +static void init_rand(void) { + uint32_t seed = ((uint32_t)0xc597060c * (uint32_t)rdtsc()) + * ((uint32_t)0xce86d624) + ^ ((uint32_t)0xee0da130 * (uint32_t)rdtsc()); + + // TODO(qookie): aarch64 also has an optional HW random number generator +#if defined (__x86_64__) || defined(__i386__) + uint32_t eax, ebx, ecx, edx; + + // Check for rdseed + if (cpuid(0x07, 0, &eax, &ebx, &ecx, &edx) && (ebx & (1 << 18))) { + seed *= (seed ^ rdseed(uint32_t)); + } else if (cpuid(0x01, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 30))) { + seed *= (seed ^ rdrand(uint32_t)); + } +#endif + + status = ext_mem_alloc(n * sizeof(uint32_t)); + + srand(seed); + + rand_initialised = true; +} + +void srand(uint32_t s) { + status[0] = s; + for (ctr = 1; ctr < n; ctr++) + status[ctr] = (1812433253 * (status[ctr - 1] ^ (status[ctr - 1] >> 30)) + ctr); +} + +uint32_t rand32(void) { + if (!rand_initialised) + init_rand(); + + const uint32_t mag01[2] = {0, matrix_a}; + + if (ctr >= n) { + for (int kk = 0; kk < n - m; kk++) { + uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs); + status[kk] = status[kk + m] ^ (y >> 1) ^ mag01[y & 1]; + } + + for (int kk = n - m; kk < n - 1; kk++) { + uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs); + status[kk] = status[kk + (m - n)] ^ (y >> 1) ^ mag01[y & 1]; + } + + uint32_t y = (status[n - 1] & msb) | (status[0] & lsbs); + status[n - 1] = status[m - 1] ^ (y >> 1) ^ mag01[y & 1]; + + ctr = 0; + } + + uint32_t res = status[ctr++]; + + res ^= (res >> 11); + res ^= (res << 7) & 0x9d2c5680; + res ^= (res << 15) & 0xefc60000; + res ^= (res >> 18); + + return res; +} + +uint64_t rand64(void) { + return (((uint64_t)rand32()) << 32) | (uint64_t)rand32(); +} diff --git a/limine/common/lib/rand.h b/limine/common/lib/rand.h new file mode 100644 index 0000000..ff259f5 --- /dev/null +++ b/limine/common/lib/rand.h @@ -0,0 +1,11 @@ +#ifndef LIB__RAND_H__ +#define LIB__RAND_H__ + +#include + +void srand(uint32_t s); + +uint32_t rand32(void); +uint64_t rand64(void); + +#endif diff --git a/limine/common/lib/real.h b/limine/common/lib/real.h new file mode 100644 index 0000000..564de08 --- /dev/null +++ b/limine/common/lib/real.h @@ -0,0 +1,34 @@ +#ifndef LIB__REAL_H__ +#define LIB__REAL_H__ + +#include +#include + +#define rm_seg(x) ((uint16_t)(((int)(x) & 0xffff0) >> 4)) +#define rm_off(x) ((uint16_t)(((int)(x) & 0x0000f) >> 0)) + +#define rm_desegment(seg, off) (((uint32_t)(seg) << 4) + (uint32_t)(off)) + +#define EFLAGS_CF (1 << 0) +#define EFLAGS_ZF (1 << 6) + +struct rm_regs { + uint16_t gs; + uint16_t fs; + uint16_t es; + uint16_t ds; + uint32_t eflags; + uint32_t ebp; + uint32_t edi; + uint32_t esi; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t eax; +} __attribute__((packed)); + +void rm_int(uint8_t int_no, struct rm_regs *out_regs, struct rm_regs *in_regs); + +noreturn void rm_hcf(void); + +#endif diff --git a/limine/common/lib/real.s2.asm_bios_ia32 b/limine/common/lib/real.s2.asm_bios_ia32 new file mode 100644 index 0000000..15bfabd --- /dev/null +++ b/limine/common/lib/real.s2.asm_bios_ia32 @@ -0,0 +1,168 @@ +section .realmode + +global rm_hcf +rm_hcf: + ; Load BIOS IVT + lidt [.rm_idt] + + ; Jump to real mode + jmp 0x08:.bits16 + .bits16: + bits 16 + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov eax, cr0 + btr ax, 0 + mov cr0, eax + jmp 0x00:.cszero + .cszero: + xor ax, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + sti + .hang: + hlt + jmp .hang + bits 32 + + .rm_idt: dw 0x3ff + dd 0 + +global rm_int +rm_int: + ; Self-modifying code: int $int_no + mov al, byte [esp+4] + mov byte [.int_no], al + + ; Save out_regs + mov eax, dword [esp+8] + mov dword [.out_regs], eax + + ; Save in_regs + mov eax, dword [esp+12] + mov dword [.in_regs], eax + + ; Save GDT in case BIOS overwrites it + sgdt [.gdt] + + ; Save IDT + sidt [.idt] + + ; Load BIOS IVT + lidt [.rm_idt] + + ; Save non-scratch GPRs + push ebx + push esi + push edi + push ebp + + ; Jump to real mode + jmp 0x08:.bits16 + .bits16: + bits 16 + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov eax, cr0 + and al, 0xfe + mov cr0, eax + jmp 0x00:.cszero + .cszero: + xor ax, ax + mov ss, ax + + ; Load in_regs + mov dword [ss:.esp], esp + mov esp, dword [ss:.in_regs] + pop gs + pop fs + pop es + pop ds + popfd + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + mov esp, dword [ss:.esp] + + sti + + ; Indirect interrupt call + db 0xcd + .int_no: + db 0 + + cli + + ; Load out_regs + mov dword [ss:.esp], esp + mov esp, dword [ss:.out_regs] + lea esp, [esp + 10*4] + push eax + push ebx + push ecx + push edx + push esi + push edi + push ebp + pushfd + push ds + push es + push fs + push gs + mov esp, dword [ss:.esp] + + ; Restore GDT + o32 lgdt [ss:.gdt] + + ; Restore IDT + o32 lidt [ss:.idt] + + ; Jump back to pmode + mov eax, cr0 + or al, 1 + mov cr0, eax + jmp 0x18:.bits32 + .bits32: + bits 32 + mov ax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; Restore non-scratch GPRs + pop ebp + pop edi + pop esi + pop ebx + + ; Exit + ret + +align 16 + .esp: dd 0 + .out_regs: dd 0 + .in_regs: dd 0 + .gdt: dq 0 + .idt: dq 0 + .rm_idt: dw 0x3ff + dd 0 + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/lib/sleep.asm_bios_ia32 b/limine/common/lib/sleep.asm_bios_ia32 new file mode 100644 index 0000000..fdd120a --- /dev/null +++ b/limine/common/lib/sleep.asm_bios_ia32 @@ -0,0 +1,157 @@ +section .realmode + +int_08_ticks_counter: dd 0 +int_08_callback: dd 0 + +int_08_isr: + bits 16 + pushf + inc dword [cs:int_08_ticks_counter] + popf + jmp far [cs:int_08_callback] + bits 32 + +extern getchar_internal + +global _pit_sleep_and_quit_on_keypress +_pit_sleep_and_quit_on_keypress: + ; Hook int 0x08 + mov edx, dword [0x08*4] + mov dword [int_08_callback], edx + mov dword [0x08*4], int_08_isr + + ; pit_ticks in edx + mov edx, dword [esp+4] + + mov dword [int_08_ticks_counter], 0 + + ; Save GDT in case BIOS overwrites it + sgdt [.gdt] + + ; Save IDT + sidt [.idt] + + ; Load BIOS IVT + lidt [.rm_idt] + + ; Save non-scratch GPRs + push ebx + push esi + push edi + push ebp + + ; Jump to real mode + jmp 0x08:.bits16 + .bits16: + bits 16 + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov eax, cr0 + and al, 0xfe + mov cr0, eax + jmp 0x00:.cszero + .cszero: + xor ax, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + sti + + mov byte [.mods], 0 + mov byte [.ascii], 0 + mov byte [.scan], 0 + + .loop: + cmp dword [int_08_ticks_counter], edx + je .done + + push ecx + push edx + mov ah, 0x01 + xor al, al + int 0x16 + pop edx + pop ecx + + jz .loop + + ; on keypress + xor ax, ax + int 0x16 + mov byte [.ascii], al + mov byte [.scan], ah + + mov ax, 0x0200 + int 0x16 + test al, 0x04 + jz .done + + ; ctrl handling + mov byte [.mods], 0x04 + add byte [.ascii], 0x60 + + .done: + cli + + ; Restore GDT + o32 lgdt [ss:.gdt] + + ; Restore IDT + o32 lidt [ss:.idt] + + ; Jump back to pmode + mov ebx, cr0 + or bl, 1 + mov cr0, ebx + jmp 0x18:.bits32 + .bits32: + bits 32 + mov bx, 0x20 + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + mov ss, bx + + ; Restore non-scratch GPRs + pop ebp + pop edi + pop esi + pop ebx + + ; Dehook int 0x08 + mov edx, dword [int_08_callback] + mov dword [0x08*4], edx + + cmp byte [.scan], 0 + je .fail + + push dword [.mods] + push dword [.ascii] + push dword [.scan] + call getchar_internal + add esp, 3*4 + + ret + + .fail: + xor eax, eax + ret + + .gdt: dq 0 + .idt: dq 0 + .rm_idt: dw 0x3ff + dd 0 + + .mods: dd 0 + .ascii: dd 0 + .scan: dd 0 + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/lib/spinup.asm_aarch64 b/limine/common/lib/spinup.asm_aarch64 new file mode 100644 index 0000000..d683def --- /dev/null +++ b/limine/common/lib/spinup.asm_aarch64 @@ -0,0 +1,113 @@ +#include + +.section .text + +// noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr, +// uint64_t mair, uint64_t tcr, uint64_t ttbr0, +// uint64_t ttbr1, uint64_t direct_map_offset) +// Potentially drop to EL1 from EL2 (and also disable trapping to EL2), then +// configure EL1 state and jump to kernel. + +.global enter_in_el1 +enter_in_el1: + msr spsel, #0 + mov sp, x1 + + PICK_EL x8, 0f, 2f +0: + // Switch to the new page tables + + // Point the EL1t handler to the continuation, such that after we page fault, + // execution continues and the kernel is entered. + adrp x8, 1f + add x8, x8, #:lo12:1f + add x8, x8, x7 + msr vbar_el1, x8 + isb + dsb sy + isb + + // Switch the page table registers + msr mair_el1, x3 + msr tcr_el1, x4 + msr ttbr0_el1, x5 + msr ttbr1_el1, x6 + msr sctlr_el1, x2 + isb + dsb sy + isb + + // Jump to the higher half mapping in case we didn't immediately crash + br x8 + +// Alignment required by VBAR register +.align 11 +1: + // Zero out VBAR to avoid confusion + msr vbar_el1, xzr + + // Enter kernel in EL1 + mov x8, #0x3c4 + msr spsr_el1, x8 + msr elr_el1, x0 + + mov x0, xzr + ZERO_REGS_EXCEPT_X0 + + eret + +2: + // Check HCR_EL2.E2H + mrs x8, hcr_el2 + tbnz x8, #34, 3f + + // Configure EL1 state (normal silicon) + msr mair_el1, x3 + msr tcr_el1, x4 + msr ttbr0_el1, x5 + msr ttbr1_el1, x6 + msr sctlr_el1, x2 + dsb sy + isb + b 4f + +3: + // Configure EL1 state (apple silicon) + msr s3_5_c10_c2_0, x3 // MAIR_EL12 + msr s3_5_c2_c0_2, x4 // TCR_EL12 + msr s3_5_c2_c0_0, x5 // TTBR0_EL12 + msr s3_5_c2_c0_1, x6 // TTBR1_EL12 + msr s3_5_c1_c0_0, x2 // SCTLR_EL12 + dsb sy + isb + +4: + + // Configure EL2-specific state for EL1 + + // Don't trap counters to EL2 + mrs x8, cnthctl_el2 + orr x8, x8, #3 + msr cnthctl_el2, x8 + msr cntvoff_el2, xzr + + // Enable AArch64 in EL1 + ldr x8, =0x80000002 + msr hcr_el2, x8 + + // Don't trap FP/SIMD to EL2 + mov x8, #0x33FF + msr cptr_el2, x8 + msr hstr_el2, xzr + + // Enter kernel in EL1 + mov x8, #0x3c4 + msr spsr_el2, x8 + msr elr_el2, x0 + + mov x0, xzr + ZERO_REGS_EXCEPT_X0 + + eret + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/lib/spinup.asm_bios_ia32 b/limine/common/lib/spinup.asm_bios_ia32 new file mode 100644 index 0000000..53bc298 --- /dev/null +++ b/limine/common/lib/spinup.asm_bios_ia32 @@ -0,0 +1,49 @@ +section .rodata + +invalid_idt: + dd 0, 0 + +section .text + +extern flush_irqs + +global common_spinup +bits 32 +common_spinup: + cli + + lidt [invalid_idt] + + call flush_irqs + + xor eax, eax + lldt ax + + ; We don't need the return address + add esp, 4 + + ; Get function address + pop edi + + ; We don't need the argument count + add esp, 4 + + mov eax, 0x00000011 + mov cr0, eax + + xor eax, eax + mov cr4, eax + mov cr3, eax + + ; Clear TSS busy bit and load TR with base 0, limit 0 + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + add esp, 8 + mov byte [eax + 0x3d], 0x89 + mov ax, 0x38 + ltr ax + + call edi + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/lib/spinup.asm_loongarch64 b/limine/common/lib/spinup.asm_loongarch64 new file mode 100644 index 0000000..5793327 --- /dev/null +++ b/limine/common/lib/spinup.asm_loongarch64 @@ -0,0 +1,110 @@ +.section .text + +#define PAGE_SHIFT 12 +#define PT_SHIFT (PAGE_SHIFT - 3) +#define PT_BASE(level) (PAGE_SHIFT + PT_SHIFT * (level)) + +#define MAKE_PWCL(Dir2_width, Dir2_base, Dirl_width, Dirl_base, PTwidth, PTbase) \ + ((Dir2_width) << 25) | ((Dir2_base) << 20) | ((Dirl_width) << 15) | \ + ((Dirl_base) << 10) | ((PTwidth) << 5) | ((PTbase) << 0) + +#define MAKE_PWCH(Dir4_width, Dir4_base, Dir3_width, Dir3_base) \ + ((Dir4_width) << 18) | ((Dir4_base) << 12) | ((Dir3_width) << 6) | \ + ((Dir3_base) << 0) + +#define CSR_CRMD 0x00 +#define CSR_EENTRY 0xc +#define CSR_PGDL 0x19 +#define CSR_PGDH 0x1a +#define CSR_PGD 0x1b +#define CSR_PWCL 0x1c +#define CSR_PWCH 0x1d +#define CSR_STLBPS 0x1e +#define CSR_TLBRENTRY 0x88 +#define CSR_TLBRSAVE 0x8b +#define CSR_TLBREHI 0x8e +#define CSR_MERRENTRY 0x93 +#define CSR_DMW0 0x180 +#define CSR_DMW1 0x181 +#define CSR_DMW2 0x182 +#define CSR_DMW3 0x183 + +.global loongarch_spinup +loongarch_spinup: + li.d $t0, 0b010001 // MAT=01, PLV1..3=0, PLV0=1 + csrwr $t0, CSR_DMW0 + csrwr $zero, CSR_DMW1 + csrwr $zero, CSR_DMW2 + csrwr $zero, CSR_DMW3 + + li.d $t0, 0b010110000 // DATF=01, DATM=01, PG=1, DA=0, IE=0, PLV=00 + csrwr $t0, CSR_CRMD + + invtlb 0, $zero, $zero + li.d $t0, PAGE_SHIFT + csrwr $t0, CSR_STLBPS + csrwr $t0, CSR_TLBREHI + + csrwr $a2, CSR_PGDL + csrwr $a3, CSR_PGDH + + li.d $t0, MAKE_PWCL(PT_SHIFT, PT_BASE(2), PT_SHIFT, PT_BASE(1), PT_SHIFT, PT_BASE(0)) + csrwr $t0, CSR_PWCL + li.d $t0, MAKE_PWCH(0, 0, PT_SHIFT, PT_BASE(3)) + csrwr $t0, CSR_PWCH + + la $t0, loongarch_handle_refill + csrwr $t0, CSR_TLBRENTRY + + csrwr $zero, CSR_EENTRY + csrwr $zero, CSR_MERRENTRY + + move $t0, $a0 + move $sp, $a1 + + move $ra, $zero + move $tp, $zero + move $a0, $zero + move $a1, $zero + move $a2, $zero + move $a3, $zero + move $a4, $zero + move $a5, $zero + move $a6, $zero + move $a7, $zero + move $t1, $zero + move $t2, $zero + move $t3, $zero + move $t4, $zero + move $t5, $zero + move $t6, $zero + move $t7, $zero + move $t8, $zero + move $fp, $zero + move $s0, $zero + move $s1, $zero + move $s2, $zero + move $s3, $zero + move $s4, $zero + move $s5, $zero + move $s6, $zero + move $s7, $zero + move $s8, $zero + + jirl $zero, $t0, 0 + +.global loongarch_handle_refill +.align 12 +loongarch_handle_refill: + csrwr $t0, CSR_TLBRSAVE + csrrd $t0, CSR_PGD + lddir $t0, $t0, 3 + lddir $t0, $t0, 2 + lddir $t0, $t0, 1 + ldpte $t0, 0 + ldpte $t0, 1 + tlbfill + csrrd $t0, CSR_TLBRSAVE + ertn + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/lib/spinup.asm_riscv64 b/limine/common/lib/spinup.asm_riscv64 new file mode 100644 index 0000000..8e964ee --- /dev/null +++ b/limine/common/lib/spinup.asm_riscv64 @@ -0,0 +1,55 @@ + +.section .text + +.global riscv_spinup +riscv_spinup: +.option norelax + csrci sstatus, 0x2 + csrw sie, zero + + lla t0, 0f + add t0, t0, a3 + csrw stvec, t0 + csrw satp, a2 + sfence.vma + unimp +.align 4 +0: + csrw stvec, zero + + mv t0, a0 + mv sp, a1 + + mv a0, zero + mv a1, zero + mv a2, zero + mv a3, zero + mv a4, zero + mv a5, zero + mv a6, zero + mv a7, zero + mv s0, zero + mv s1, zero + mv s2, zero + mv s3, zero + mv s4, zero + mv s5, zero + mv s6, zero + mv s7, zero + mv s8, zero + mv s9, zero + mv s10, zero + mv s11, zero + mv t1, zero + mv t2, zero + mv t3, zero + mv t4, zero + mv t5, zero + mv t6, zero + mv tp, zero + mv gp, zero + mv ra, zero + + jr t0 + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/lib/spinup.asm_uefi_ia32 b/limine/common/lib/spinup.asm_uefi_ia32 new file mode 100644 index 0000000..397e4d8 --- /dev/null +++ b/limine/common/lib/spinup.asm_uefi_ia32 @@ -0,0 +1,74 @@ +extern _GLOBAL_OFFSET_TABLE_ + +extern gdt + +section .text + +extern flush_irqs + +global common_spinup +bits 32 +common_spinup: + cli + + push 0 + push 0 + lidt [esp] + add esp, 8 + + call .get_got + .get_got: + pop ebx + add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc + + lgdt [ebx + gdt wrt ..gotoff] + + push dword 0x18 + call .p1 + .p1: + pop eax + add eax, 6 + push eax + retfd + + .flush_cs: + mov eax, 0x20 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + call flush_irqs + + xor eax, eax + lldt ax + + ; We don't need the return address + add esp, 4 + + ; Get function address + pop edi + + ; We don't need the argument count + add esp, 4 + + mov eax, 0x00000011 + mov cr0, eax + + xor eax, eax + mov cr4, eax + mov cr3, eax + + ; Clear TSS busy bit and load TR with base 0, limit 0 + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + add esp, 8 + mov byte [eax + 0x3d], 0x89 + mov ax, 0x38 + ltr ax + + call edi + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/lib/spinup.asm_uefi_x86_64 b/limine/common/lib/spinup.asm_uefi_x86_64 new file mode 100644 index 0000000..a2bf7ec --- /dev/null +++ b/limine/common/lib/spinup.asm_uefi_x86_64 @@ -0,0 +1,111 @@ +section .rodata + +invalid_idt: + dq 0, 0 + +section .text + +extern flush_irqs + +%macro push32 1 + sub rsp, 4 + mov dword [rsp], %1 +%endmacro + +extern gdt + +global common_spinup +bits 64 +common_spinup: + cli + + lgdt [rel gdt] + lidt [rel invalid_idt] + + lea rbx, [rel .reload_cs] + + push 0x28 + push rbx + retfq +.reload_cs: + mov eax, 0x30 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + push r8 + push r9 + push rcx + push rdx + push rsi + push rdi + call flush_irqs + pop rdi + pop rsi + pop rdx + pop rcx + pop r9 + pop r8 + + mov rbp, rsp + + sub esi, 4 + jle .no_stack_args + +.push_stack_args: + dec esi + mov eax, [rbp + 8 + rsi*8] + push32 eax + test esi, esi + jnz .push_stack_args + +.no_stack_args: + push32 r9d + push32 r8d + push32 ecx + push32 edx + + lea rbx, [rel .go_32] + + push 0x18 + push rbx + retfq + +bits 32 +.go_32: + mov eax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + xor eax, eax + lldt ax + + mov eax, 0x00000011 + mov cr0, eax + + mov ecx, 0xc0000080 + xor eax, eax + xor edx, edx + wrmsr + + xor eax, eax + mov cr4, eax + mov cr3, eax + + ; Clear TSS busy bit and load TR with base 0, limit 0 + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + add esp, 8 + mov byte [eax + 0x3d], 0x89 + mov ax, 0x38 + ltr ax + + call edi + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/lib/stb_image.c b/limine/common/lib/stb_image.c new file mode 100644 index 0000000..419ab75 --- /dev/null +++ b/limine/common/lib/stb_image.c @@ -0,0 +1,6 @@ +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-function" + +#define STB_IMAGE_IMPLEMENTATION + +#include diff --git a/limine/common/lib/term.c b/limine/common/lib/term.c new file mode 100644 index 0000000..cc9fc00 --- /dev/null +++ b/limine/common/lib/term.c @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (BIOS) +int current_video_mode = -1; +#endif + +struct flanterm_context **terms = NULL; +size_t terms_i = 0; + +int term_backend = _NOT_READY; + +void term_notready(void) { + for (size_t i = 0; i < terms_i; i++) { + struct flanterm_context *term = terms[i]; + + term->deinit(term, pmm_free_size_t); + } + + pmm_free(terms, terms_i * sizeof(void *)); + + terms_i = 0; + terms = NULL; + + term_backend = _NOT_READY; +} + +// --- fallback --- + +#if defined (BIOS) +static void fallback_raw_putchar(struct flanterm_context *ctx, uint8_t c) { + (void)ctx; + struct rm_regs r = {0}; + r.eax = 0x0e00 | c; + rm_int(0x10, &r, &r); +} + +static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y); +static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y); + +static void fallback_clear(struct flanterm_context *ctx, bool move) { + (void)ctx; + size_t x, y; + fallback_get_cursor_pos(NULL, &x, &y); + struct rm_regs r = {0}; + rm_int(0x11, &r, &r); + switch ((r.eax >> 4) & 3) { + case 0: + r.eax = 3; + break; + case 1: + r.eax = 1; + break; + case 2: + r.eax = 3; + break; + case 3: + r.eax = 7; + break; + } + rm_int(0x10, &r, &r); + if (move) { + x = y = 0; + } + fallback_set_cursor_pos(NULL, x, y); +} + +static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) { + (void)ctx; + struct rm_regs r = {0}; + r.eax = 0x0200; + r.ebx = 0; + r.edx = (y << 8) + x; + rm_int(0x10, &r, &r); +} + +static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) { + (void)ctx; + struct rm_regs r = {0}; + r.eax = 0x0300; + r.ebx = 0; + rm_int(0x10, &r, &r); + *x = r.edx & 0xff; + *y = r.edx >> 8; +} + +static void fallback_scroll(struct flanterm_context *ctx) { + (void)ctx; + size_t x, y; + fallback_get_cursor_pos(NULL, &x, &y); + fallback_set_cursor_pos(NULL, ctx->cols - 1, ctx->rows - 1); + fallback_raw_putchar(NULL, ' '); + fallback_set_cursor_pos(NULL, x, y); +} + +#elif defined (UEFI) + +static size_t cursor_x = 0, cursor_y = 0; + +static void fallback_scroll(struct flanterm_context *ctx) { + (void)ctx; + UINTN uefi_x_size, uefi_y_size; + gST->ConOut->QueryMode(gST->ConOut, gST->ConOut->Mode->Mode, &uefi_x_size, &uefi_y_size); + gST->ConOut->SetCursorPosition(gST->ConOut, uefi_x_size - 1, uefi_y_size - 1); + CHAR16 string[2]; + string[0] = ' '; + string[1] = 0; + gST->ConOut->OutputString(gST->ConOut, string); + gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y); +} + +static void fallback_raw_putchar(struct flanterm_context *ctx, uint8_t c) { + if (!ctx->scroll_enabled && cursor_x == ctx->cols - 1 && cursor_y == ctx->rows - 1) { + return; + } + gST->ConOut->EnableCursor(gST->ConOut, true); + CHAR16 string[2]; + string[0] = c; + string[1] = 0; + gST->ConOut->OutputString(gST->ConOut, string); + if (++cursor_x >= ctx->cols) { + cursor_x = 0; + if (++cursor_y >= ctx->rows) { + cursor_y--; + } + } + gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y); +} + +static void fallback_clear(struct flanterm_context *ctx, bool move) { + (void)ctx; + gST->ConOut->ClearScreen(gST->ConOut); + if (move) { + cursor_x = cursor_y = 0; + } + gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y); +} + +static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) { + (void)ctx; + if (x >= ctx->cols || y >= ctx->rows) { + return; + } + gST->ConOut->SetCursorPosition(gST->ConOut, x, y); + cursor_x = x; + cursor_y = y; +} + +static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) { + (void)ctx; + *x = cursor_x; + *y = cursor_y; +} + +static UINTN ansi_colours[] = { + EFI_BLACK, + EFI_RED, + EFI_GREEN, + EFI_YELLOW, + EFI_BLUE, + EFI_MAGENTA, + EFI_CYAN, + EFI_LIGHTGRAY +}; + +static UINTN conout_current_fg, conout_current_bg; + +static void fallback_set_text_fg(struct flanterm_context *ctx, size_t fg) { + (void)ctx; + conout_current_fg = ansi_colours[fg]; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +static void fallback_set_text_bg(struct flanterm_context *ctx, size_t bg) { + (void)ctx; + conout_current_bg = ansi_colours[bg]; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +static void fallback_set_text_fg_bright(struct flanterm_context *ctx, size_t fg) { + (void)ctx; + conout_current_fg = ansi_colours[fg] | EFI_BRIGHT; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +static void fallback_set_text_bg_bright(struct flanterm_context *ctx, size_t bg) { + (void)ctx; + // bg does not support bright + conout_current_bg = ansi_colours[bg]; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +static void fallback_set_text_fg_default(struct flanterm_context *ctx) { + (void)ctx; + conout_current_fg = EFI_LIGHTGRAY; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +static void fallback_set_text_bg_default(struct flanterm_context *ctx) { + (void)ctx; + conout_current_bg = EFI_BLACK; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +static void fallback_swap_palette(struct flanterm_context *ctx) { + (void)ctx; + UINTN tmp = conout_current_bg; + conout_current_bg = conout_current_fg; + conout_current_fg = tmp; + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg)); +} + +#endif + +static bool dummy_handle(void) { + return true; +} + +void term_fallback(void) { + term_notready(); + + terms = ext_mem_alloc(sizeof(void *)); + terms_i = 1; + + terms[0] = ext_mem_alloc(sizeof(struct flanterm_context)); + + struct flanterm_context *term = terms[0]; + +#if defined (UEFI) + if (!efi_boot_services_exited) { +#endif + + fallback_clear(NULL, true); + + term->set_text_fg = (void *)dummy_handle; + term->set_text_bg = (void *)dummy_handle; + term->set_text_fg_bright = (void *)dummy_handle; + term->set_text_bg_bright = (void *)dummy_handle; + term->set_text_fg_rgb = (void *)dummy_handle; + term->set_text_bg_rgb = (void *)dummy_handle; + term->set_text_fg_default = (void *)dummy_handle; + term->set_text_bg_default = (void *)dummy_handle; + term->move_character = (void *)dummy_handle; + term->revscroll = (void *)dummy_handle; + term->swap_palette = (void *)dummy_handle; + term->save_state = (void *)dummy_handle; + term->restore_state = (void *)dummy_handle; + term->double_buffer_flush = (void *)dummy_handle; + term->full_refresh = (void *)dummy_handle; + term->deinit = (void *)dummy_handle; + + term->raw_putchar = fallback_raw_putchar; + term->clear = fallback_clear; + term->set_cursor_pos = fallback_set_cursor_pos; + term->get_cursor_pos = fallback_get_cursor_pos; + term->scroll = fallback_scroll; + term->cols = 80; + term->rows = 24; + term_backend = FALLBACK; + flanterm_context_reinit(term); +#if defined (UEFI) + + term->set_text_fg = fallback_set_text_fg; + term->set_text_bg = fallback_set_text_bg; + term->set_text_fg_bright = fallback_set_text_fg_bright; + term->set_text_bg_bright = fallback_set_text_bg_bright; + term->set_text_fg_default = fallback_set_text_fg_default; + term->set_text_bg_default = fallback_set_text_bg_default; + term->swap_palette = fallback_swap_palette; + + term->set_text_fg_default(term); + term->set_text_bg_default(term); + } else { + if (fb_fbs_count == 0) { + goto fail; + } + + terms[0] = flanterm_fb_init(ext_mem_alloc_size_t, pmm_free_size_t, + (void *)(uintptr_t)fb_fbs[0].framebuffer_addr, fb_fbs[0].framebuffer_width, + fb_fbs[0].framebuffer_height, fb_fbs[0].framebuffer_pitch, + fb_fbs[0].red_mask_size, fb_fbs[0].red_mask_shift, + fb_fbs[0].green_mask_size, fb_fbs[0].green_mask_shift, + fb_fbs[0].blue_mask_size, fb_fbs[0].blue_mask_shift, + NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, 0, 0, 1, + 0, 0, + 0, + FLANTERM_FB_ROTATE_0 + ); + } + + return; + +fail: + pmm_free(terms[0], sizeof(struct flanterm_context)); + pmm_free(terms, sizeof(void *)); + terms_i = 0; + terms = NULL; +#endif +} diff --git a/limine/common/lib/term.h b/limine/common/lib/term.h new file mode 100644 index 0000000..18fd4dd --- /dev/null +++ b/limine/common/lib/term.h @@ -0,0 +1,59 @@ +#ifndef LIB__TERM_H__ +#define LIB__TERM_H__ + +#include +#include +#include +#include + +enum { + _NOT_READY, + GTERM, + TEXTMODE, + FALLBACK +}; + +#if defined (BIOS) +extern int current_video_mode; +#endif + +extern struct flanterm_context **terms; +extern size_t terms_i; + +extern int term_backend; + +#define TERM_CTX_SIZE ((uint64_t)(-1)) +#define TERM_CTX_SAVE ((uint64_t)(-2)) +#define TERM_CTX_RESTORE ((uint64_t)(-3)) +#define TERM_FULL_REFRESH ((uint64_t)(-4)) +#define TERM_OOB_OUTPUT_GET ((uint64_t)(-10)) +#define TERM_OOB_OUTPUT_SET ((uint64_t)(-11)) + +#define FOR_TERM(...) do { \ + for (size_t FOR_TERM_i = 0; FOR_TERM_i < terms_i; FOR_TERM_i++) { \ + struct flanterm_context *TERM = terms[FOR_TERM_i]; \ + __VA_ARGS__ \ + ; \ + } \ +} while (0) + +static inline void reset_term(void) { + for (size_t i = 0; i < terms_i; i++) { + struct flanterm_context *term = terms[i]; + + print("\e[2J\e[H"); + flanterm_context_reinit(term); + term->cursor_enabled = true; + term->double_buffer_flush(term); + } +} + +static inline void set_cursor_pos_helper(size_t x, size_t y) { + print("\e[%u;%uH", (int)y + 1, (int)x + 1); +} + +void term_notready(void); +void term_fallback(void); +void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count); + +#endif diff --git a/limine/common/lib/time.c b/limine/common/lib/time.c new file mode 100644 index 0000000..da82dbb --- /dev/null +++ b/limine/common/lib/time.c @@ -0,0 +1,75 @@ +#include +#include +#include +#if defined (BIOS) +# include +#elif defined (UEFI) +# include +#endif +#include + +// Julian date calculation from https://en.wikipedia.org/wiki/Julian_day +static int get_jdn(int days, int months, int years) { + return (1461 * (years + 4800 + (months - 14)/12))/4 + (367 * + (months - 2 - 12 * ((months - 14)/12)))/12 - + (3 * ((years + 4900 + (months - 14)/12)/100))/4 + + days - 32075; +} + +static uint64_t get_unix_epoch(uint8_t seconds, uint8_t minutes, uint8_t hours, + uint8_t days, uint8_t months, uint16_t years) { + uint64_t jdn_current = get_jdn(days, months, years); + uint64_t jdn_1970 = get_jdn(1, 1, 1970); + + uint64_t jdn_diff = jdn_current - jdn_1970; + + return (jdn_diff * (60 * 60 * 24)) + hours * 3600 + minutes * 60 + seconds; +} + +#if defined (BIOS) +uint64_t time(void) { + struct rm_regs r; + +again: + r = (struct rm_regs){0}; + r.eax = 0x0400; + rm_int(0x1a, &r, &r); + + uint8_t day = bcd_to_int( r.edx & 0x00ff); + uint8_t month = bcd_to_int((r.edx & 0xff00) >> 8); + uint16_t year = bcd_to_int( r.ecx & 0x00ff) + + /* century */ bcd_to_int((r.ecx & 0xff00) >> 8) * 100; + + r = (struct rm_regs){0}; + r.eax = 0x0200; + rm_int(0x1a, &r, &r); + + uint8_t second = bcd_to_int((r.edx & 0xff00) >> 8); + uint8_t minute = bcd_to_int( r.ecx & 0x00ff); + uint8_t hour = bcd_to_int((r.ecx & 0xff00) >> 8); + + // Check RTC day wraparound + r = (struct rm_regs){0}; + r.eax = 0x0400; + rm_int(0x1a, &r, &r); + if (bcd_to_int(r.edx & 0x00ff) != day) { + goto again; + } + + return get_unix_epoch(second, minute, hour, day, month, year); +} +#endif + +#if defined (UEFI) +uint64_t time(void) { + EFI_TIME time; + EFI_STATUS status = gRT->GetTime(&time, NULL); + + if (status != 0) { + return 0; + } + + return get_unix_epoch(time.Second, time.Minute, time.Hour, + time.Day, time.Month, time.Year); +} +#endif diff --git a/limine/common/lib/time.h b/limine/common/lib/time.h new file mode 100644 index 0000000..b59b1fa --- /dev/null +++ b/limine/common/lib/time.h @@ -0,0 +1,8 @@ +#ifndef LIB__TIME_H__ +#define LIB__TIME_H__ + +#include + +uint64_t time(void); + +#endif diff --git a/limine/common/lib/trace.h b/limine/common/lib/trace.h new file mode 100644 index 0000000..9c3f3bb --- /dev/null +++ b/limine/common/lib/trace.h @@ -0,0 +1,8 @@ +#ifndef LIB__TRACE_H__ +#define LIB__TRACE_H__ + +#include + +void print_stacktrace(size_t *base_ptr); + +#endif diff --git a/limine/common/lib/trace.s2.c b/limine/common/lib/trace.s2.c new file mode 100644 index 0000000..2018c81 --- /dev/null +++ b/limine/common/lib/trace.s2.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (BIOS) +extern symbol stage2_map; +#elif defined (UEFI) +extern symbol __slide; +#endif + +extern symbol full_map; + +static char *trace_address(size_t *off, size_t addr) { + char *limine_map; + +#if defined (BIOS) + if (!stage3_loaded) + limine_map = stage2_map; + else + limine_map = full_map; +#elif defined (UEFI) + limine_map = full_map; + + addr -= (size_t)__slide; +#endif + + uintptr_t prev_addr = 0; + char *prev_sym = NULL; + + for (size_t i = 0;;) { + if (*((uintptr_t *)&limine_map[i]) >= addr) { + *off = addr - prev_addr; + return prev_sym; + } + prev_addr = *((uintptr_t *)&limine_map[i]); + i += sizeof(uintptr_t); + prev_sym = &limine_map[i]; + while (limine_map[i++] != 0); + } +} + +void print_stacktrace(size_t *base_ptr) { + if (base_ptr == NULL) { + asm volatile ( +#if defined (__i386__) + "movl %%ebp, %0" +#elif defined (__x86_64__) + "movq %%rbp, %0" +#elif defined (__aarch64__) + "mov %0, x29" +#elif defined (__riscv) + "mv %0, fp; addi %0, %0, -16" +#elif defined (__loongarch64) + "move %0, $fp; addi.d %0, %0, -16" +#endif + : "=r"(base_ptr) + :: "memory" + ); + } + print("Stacktrace:\n"); + for (;;) { + size_t old_bp = base_ptr[0]; + size_t ret_addr = base_ptr[1]; + if (!ret_addr) + break; + size_t off; + char *name = trace_address(&off, ret_addr); + if (name) + print(" [%p] <%s+%p>\n", ret_addr, name, off); + else + print(" [%p]\n", ret_addr); + if (!old_bp) + break; +#if defined (__riscv) || defined (__loongarch64) + base_ptr = (void *)(old_bp - 16); +#else + base_ptr = (void*)old_bp; +#endif + } + print("End of trace. "); +} diff --git a/limine/common/lib/uri.c b/limine/common/lib/uri.c new file mode 100644 index 0000000..f390d2c --- /dev/null +++ b/limine/common/lib/uri.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// A URI takes the form of: resource(root):/path#hash +// The following function splits up a URI into its components. +// Note: Returns pointers into a static buffer. Callers must copy values +// if they need to persist across multiple uri_resolve() calls. +bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash) { + #define URI_BUF_SIZE 4096 + static char buf[URI_BUF_SIZE]; + + size_t length = strlen(uri) + 1; + if (length > URI_BUF_SIZE) { + panic(true, "uri_resolve: URI too long (max %u)", URI_BUF_SIZE - 1); + } + memcpy(buf, uri, length); + uri = buf; + + *resource = *root = *path = *hash = NULL; + + // Get resource + for (size_t i = 0; ; i++) { + if (strlen(uri + i) < 1) + return false; + + if (!strncmp(uri + i, "(", 1)) { + *resource = uri; + uri[i] = 0; + uri += i + 1; + break; + } + } + + // Get root + for (size_t i = 0; ; i++) { + if (strlen(uri + i) < 3) + return false; + + if (!strncmp(uri + i, "):/", 3)) { + *root = uri; + uri[i] = 0; + uri += i + 3; + break; + } + } + + // Get path + if (*uri == 0) + return false; + *path = uri; + + // Get hash + for (int i = (int)strlen(uri) - 1; i >= 0; i--) { + if (uri[i] != '#') { + continue; + } + + uri[i++] = 0; + + if (hash != NULL) { + *hash = uri + i; + } + + if (strlen(uri + i) != 128) { + panic(true, "Blake2b hash must be 128 characters long"); + return false; + } + + // Validate all 128 characters are valid hexadecimal + for (size_t j = 0; j < 128; j++) { + char c = uri[i + j]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + panic(true, "Blake2b hash contains invalid character at position %d", (int)j); + return false; + } + } + + break; + } + + return true; +} + +static bool parse_bios_partition(char *loc, int *drive, int *partition) { + uint64_t val; + + for (size_t i = 0; ; i++) { + if (loc[i] == 0) + return false; + + if (loc[i] == ':') { + loc[i] = 0; + if (*loc == 0) { + panic(true, "Drive number cannot be omitted for hdd():/ and odd():/"); + } else { + val = strtoui(loc, NULL, 10); + if (val < 1 || val > 256) { + panic(true, "Drive number outside range 1-256"); + } + *drive = val; + } + loc += i + 1; + break; + } + } + + val = strtoui(loc, NULL, 10); + if (val > 256) { + panic(true, "Partition number outside range 0-256"); + } + *partition = val; + + return true; +} + +static struct file_handle *uri_hdd_dispatch(char *loc, char *path) { + int drive, partition; + + if (!parse_bios_partition(loc, &drive, &partition)) + return NULL; + + struct volume *volume = volume_get_by_coord(false, drive, partition); + + if (volume == NULL) + return NULL; + + return fopen(volume, path); +} + +static struct file_handle *uri_odd_dispatch(char *loc, char *path) { + int drive, partition; + + if (!parse_bios_partition(loc, &drive, &partition)) + return NULL; + + struct volume *volume = volume_get_by_coord(true, drive, partition); + + if (volume == NULL) + return NULL; + + return fopen(volume, path); +} + +static struct file_handle *uri_guid_dispatch(char *guid_str, char *path) { + struct guid guid; + if (!string_to_guid_be(&guid, guid_str)) + return NULL; + + struct volume *volume = volume_get_by_guid(&guid); + if (volume == NULL) { + if (!string_to_guid_mixed(&guid, guid_str)) + return NULL; + + volume = volume_get_by_guid(&guid); + if (volume == NULL) + return NULL; + } + + return fopen(volume, path); +} + +static struct file_handle *uri_fslabel_dispatch(char *fslabel, char *path) { + struct volume *volume = volume_get_by_fslabel(fslabel); + if (volume == NULL) { + return NULL; + } + + return fopen(volume, path); +} + +static struct file_handle *uri_tftp_dispatch(char *root, char *path) { + uint32_t ip; + if (!strcmp(root, "")) { + ip = 0; + } else { + if (inet_pton(root, &ip)) { + panic(true, "tftp: Invalid ipv4 address: %s", root); + } + } + + struct file_handle *ret; + if ((ret = tftp_open(boot_volume, root, path)) == NULL) { + return NULL; + } + + return ret; +} + +static struct file_handle *uri_boot_dispatch(char *s_part, char *path) { + if (boot_volume->pxe) + return uri_tftp_dispatch(s_part, path); + + int partition; + + if (s_part[0] != '\0') { + uint64_t val = strtoui(s_part, NULL, 10); + if (val > 256) { + panic(true, "Partition number outside range 0-256"); + } + partition = val; + } else { + partition = boot_volume->partition; + } + + struct volume *volume = volume_get_by_coord(boot_volume->is_optical, + boot_volume->index, partition); + if (volume == NULL) + return NULL; + + return fopen(volume, path); +} + +struct file_handle *uri_open(char *uri) { + struct file_handle *ret; + + char *resource = NULL, *root = NULL, *path = NULL, *hash = NULL; + if (!uri_resolve(uri, &resource, &root, &path, &hash)) { + return NULL; + } + + if (resource == NULL) { + panic(true, "No resource specified for URI `%#`.", uri); + } + + if (!strcmp(resource, "hdd")) { + ret = uri_hdd_dispatch(root, path); + } else if (!strcmp(resource, "odd")) { + ret = uri_odd_dispatch(root, path); + } else if (!strcmp(resource, "boot")) { + ret = uri_boot_dispatch(root, path); + } else if (!strcmp(resource, "guid")) { + ret = uri_guid_dispatch(root, path); + } else if (!strcmp(resource, "uuid")) { + ret = uri_guid_dispatch(root, path); + } else if (!strcmp(resource, "fslabel")) { + ret = uri_fslabel_dispatch(root, path); + } else if (!strcmp(resource, "tftp")) { + ret = uri_tftp_dispatch(root, path); + } else { + panic(true, "Resource `%s` not valid.", resource); + } + + if (hash != NULL && ret != NULL) { + uint8_t out_buf[BLAKE2B_OUT_BYTES]; +#if defined (UEFI) && defined (__x86_64__) + void *file_buf = freadall_mode(ret, MEMMAP_BOOTLOADER_RECLAIMABLE, true); +#else + void *file_buf = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE); +#endif + blake2b(out_buf, file_buf, ret->size); + uint8_t hash_buf[BLAKE2B_OUT_BYTES]; + + for (size_t i = 0; i < sizeof(hash_buf); i++) { + hash_buf[i] = digit_to_int(hash[i * 2]) << 4 | digit_to_int(hash[i * 2 + 1]); + } + + if (memcmp(hash_buf, out_buf, sizeof(out_buf)) != 0) { + if (hash_mismatch_panic) { + panic(true, "Blake2b hash for URI `%#` does not match!", uri); + } else { + print("WARNING: Blake2b hash for URI `%#` does not match!\n" + " Press Y to continue, press any other key to return to menu...", uri); + + char ch = getchar(); + if (ch != 'Y' && ch != 'y') { + menu(false); + } + print("\n"); + } + } + } + + return ret; +} diff --git a/limine/common/lib/uri.h b/limine/common/lib/uri.h new file mode 100644 index 0000000..d15a549 --- /dev/null +++ b/limine/common/lib/uri.h @@ -0,0 +1,10 @@ +#ifndef LIB__URI_H__ +#define LIB__URI_H__ + +#include +#include + +bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash); +struct file_handle *uri_open(char *uri); + +#endif diff --git a/limine/common/libc-compat/stdlib.h b/limine/common/libc-compat/stdlib.h new file mode 100644 index 0000000..8e01eb2 --- /dev/null +++ b/limine/common/libc-compat/stdlib.h @@ -0,0 +1,6 @@ +#ifndef LIBC_COMPAT__STDLIB_H__ +#define LIBC_COMPAT__STDLIB_H__ + +unsigned long strtoul(const char *str, char **end, int base); + +#endif diff --git a/limine/common/libc-compat/string.h b/limine/common/libc-compat/string.h new file mode 100644 index 0000000..df3fa05 --- /dev/null +++ b/limine/common/libc-compat/string.h @@ -0,0 +1,23 @@ +#ifndef LIBC_COMPAT__STRING_H__ +#define LIBC_COMPAT__STRING_H__ + +#include + +void *memset(void *, int, size_t); +void *memcpy(void *restrict, const void *restrict, size_t); +int memcmp(const void *, const void *, size_t); +void *memmove(void *, const void *, size_t); +void *memchr(const void *, int, size_t); + +char *strcpy(char *, const char *); +char *strncpy(char *, const char *, size_t); +char *strchr(const char *, int); +char *strrchr(const char *, int); +size_t strlen(const char *); +size_t strnlen(const char *, size_t); +int strcmp(const char *, const char *); +int strcasecmp(const char *, const char *); +int strncmp(const char *, const char *, size_t); +int strncasecmp(const char *, const char *, size_t); + +#endif diff --git a/limine/common/linker_bios.ld.in b/limine/common/linker_bios.ld.in new file mode 100644 index 0000000..7154ec9 --- /dev/null +++ b/limine/common/linker_bios.ld.in @@ -0,0 +1,113 @@ +OUTPUT_FORMAT(elf32-i386) +ENTRY(_start) + +PHDRS +{ + text_s2 PT_LOAD FLAGS(0x05); + rodata_s2 PT_LOAD FLAGS(0x04); + data_s2 PT_LOAD FLAGS(0x06); + text_s3 PT_LOAD FLAGS(0x05); + rodata_s3 PT_LOAD FLAGS(0x04); + data_s3 PT_LOAD FLAGS(0x06); +} + +SECTIONS +{ + . = 0xf000; + + .text.stage2 : { + *(.entry) + *(.realmode) + *.s2.o(.text .text.*) + } :text_s2 + + .rodata.stage2 : { + *.s2.o(.rodata .rodata.*) + + build_id_s2 = .; + KEEP(*build-id.s2.o(*)) + +#ifdef LINKER_STAGE2ONLY + /* stage2 missing symbols overrides */ + stage2_map = .; + stage3_common = .; + build_id_s3 = .; + full_map = .; + getchar = .; + menu = .; + booting_from_editor = .; + flanterm_write = .; + term_backend = .; + term_fallback = .; + term_notready = .; + terms = .; + terms_i = .; + usec_at_bootloader_entry = .; + serial_out = .; + stage3_addr = .; +#else +#ifdef LINKER_NOS2MAP + stage2_map = .; +#else + *(.stage2_map) +#endif +#endif + } :rodata_s2 + + .data.stage2 : { + s2_data_begin = .; + *.s2.o(.data .data.*) + s2_data_end = .; + } :data_s2 + + .no_unwind.stage2 : { + *.s2.o(.no_unwind) + } :data_s2 + +#ifndef LINKER_STAGE2ONLY + .text.stage3 : { + stage3_addr = .; + *(.text .text.*) + } :text_s3 + + .rodata.stage3 : { + *(.rodata .rodata.*) + + build_id_s3 = .; + KEEP(*build-id.s3.o(*)) + +#ifdef LINKER_NOMAP + full_map = .; +#else + *(.full_map) +#endif + } :rodata_s3 + + .data.stage3 : { + data_begin = .; + *(.data .data.*) + data_end = .; + } :data_s3 + + .no_unwind.stage3 : { + *(.no_unwind) + } :data_s3 +#endif + + .note.gnu.build-id : { + *(.note.gnu.build-id) + limine_bios_sys_size = . - 0xf000; + } :data_s3 + + .bss : { + bss_begin = .; + *(.bss .bss.*) + *(COMMON) + bss_end = .; + } :data_s3 + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + } +} diff --git a/limine/common/linker_uefi_aarch64.ld.in b/limine/common/linker_uefi_aarch64.ld.in new file mode 100644 index 0000000..760ddac --- /dev/null +++ b/limine/common/linker_uefi_aarch64.ld.in @@ -0,0 +1,79 @@ +OUTPUT_FORMAT(elf64-littleaarch64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(0x05); + rodata PT_LOAD FLAGS(0x04); + data PT_LOAD FLAGS(0x06); + dynamic PT_DYNAMIC FLAGS(0x06); +} + +SECTIONS +{ + . = 0; + __slide = .; + __image_base = ABSOLUTE(.); + __image_size = ABSOLUTE(__image_end - __image_base); + + .text : { + KEEP(*(.pe_header)) + + . = ALIGN(0x1000); + + __text_start = ABSOLUTE(.); + *(.text .text.*) + } :text + + . = ALIGN(0x1000); + __text_end = ABSOLUTE(.); + __text_size = ABSOLUTE(__text_end - __text_start); + + .rodata : { + __reloc_start = ABSOLUTE(.); + *(.dummy_reloc) + + . = ALIGN(0x1000); + __reloc_end = ABSOLUTE(.); + __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); + + __data_start = ABSOLUTE(.); + *(.rodata .rodata.*) + +#ifdef LINKER_NOMAP + full_map = .; +#else + *(.full_map) +#endif + } :rodata + + .data : { + data_begin = .; + *(.data .data.*) + } :data + + .bss : { + *(.bss .bss.*) + *(COMMON) + data_end = .; + } :data + + .no_unwind : { + *(.no_unwind) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + __data_end = ABSOLUTE(ALIGN(0x1000)); + __data_size = ABSOLUTE(__data_end - __data_start); + + __image_end = ABSOLUTE(ALIGN(0x1000)); + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + *(.interp) + } +} diff --git a/limine/common/linker_uefi_ia32.ld.in b/limine/common/linker_uefi_ia32.ld.in new file mode 100644 index 0000000..c0cc0f4 --- /dev/null +++ b/limine/common/linker_uefi_ia32.ld.in @@ -0,0 +1,79 @@ +OUTPUT_FORMAT(elf32-i386) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(0x05); + rodata PT_LOAD FLAGS(0x04); + data PT_LOAD FLAGS(0x06); + dynamic PT_DYNAMIC FLAGS(0x06); +} + +SECTIONS +{ + . = 0; + __slide = .; + __image_base = ABSOLUTE(.); + __image_size = ABSOLUTE(__image_end - __image_base); + + .text : { + KEEP(*(.pe_header)) + + . = ALIGN(0x1000); + + __text_start = ABSOLUTE(.); + *(.text .text.*) + } :text + + . = ALIGN(0x1000); + __text_end = ABSOLUTE(.); + __text_size = ABSOLUTE(__text_end - __text_start); + + .rodata : { + __reloc_start = ABSOLUTE(.); + *(.dummy_reloc) + + . = ALIGN(0x1000); + __reloc_end = ABSOLUTE(.); + __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); + + __data_start = ABSOLUTE(.); + *(.rodata .rodata.*) + +#ifdef LINKER_NOMAP + full_map = .; +#else + *(.full_map) +#endif + } :rodata + + .data : { + data_begin = .; + *(.data .data.*) + } :data + + .bss : { + *(.bss .bss.*) + *(COMMON) + data_end = .; + } :data + + .no_unwind : { + *(.no_unwind) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + __data_end = ABSOLUTE(ALIGN(0x1000)); + __data_size = ABSOLUTE(__data_end - __data_start); + + __image_end = ABSOLUTE(ALIGN(0x1000)); + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + *(.interp) + } +} diff --git a/limine/common/linker_uefi_loongarch64.ld.in b/limine/common/linker_uefi_loongarch64.ld.in new file mode 100644 index 0000000..3946af5 --- /dev/null +++ b/limine/common/linker_uefi_loongarch64.ld.in @@ -0,0 +1,79 @@ +OUTPUT_FORMAT(elf64-loongarch) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(0x05); + rodata PT_LOAD FLAGS(0x04); + data PT_LOAD FLAGS(0x06); + dynamic PT_DYNAMIC FLAGS(0x06); +} + +SECTIONS +{ + . = 0; + __slide = .; + __image_base = ABSOLUTE(.); + __image_size = ABSOLUTE(__image_end - __image_base); + + .text : { + KEEP(*(.pe_header)) + + . = ALIGN(0x1000); + + __text_start = ABSOLUTE(.); + *(.text .text.*) + } :text + + . = ALIGN(0x1000); + __text_end = ABSOLUTE(.); + __text_size = ABSOLUTE(__text_end - __text_start); + + .rodata : { + __reloc_start = ABSOLUTE(.); + *(.dummy_reloc) + + . = ALIGN(0x1000); + __reloc_end = ABSOLUTE(.); + __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); + + __data_start = ABSOLUTE(.); + *(.rodata .rodata.*) + +#ifdef LINKER_NOMAP + full_map = .; +#else + *(.full_map) +#endif + } :rodata + + .data : { + data_begin = .; + *(.data .data.*) + } :data + + .bss : { + *(.bss .bss.*) + *(COMMON) + data_end = .; + } :data + + .no_unwind : { + *(.no_unwind) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + __data_end = ABSOLUTE(ALIGN(0x1000)); + __data_size = ABSOLUTE(__data_end - __data_start); + + __image_end = ABSOLUTE(ALIGN(0x1000)); + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + *(.interp) + } +} diff --git a/limine/common/linker_uefi_riscv64.ld.in b/limine/common/linker_uefi_riscv64.ld.in new file mode 100644 index 0000000..1432a5f --- /dev/null +++ b/limine/common/linker_uefi_riscv64.ld.in @@ -0,0 +1,83 @@ +OUTPUT_FORMAT(elf64-littleriscv) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(0x05); + rodata PT_LOAD FLAGS(0x04); + data PT_LOAD FLAGS(0x06); + dynamic PT_DYNAMIC FLAGS(0x06); +} + +SECTIONS +{ + . = 0; + __slide = .; + __image_base = ABSOLUTE(.); + __image_size = ABSOLUTE(__image_end - __image_base); + + .text : { + KEEP(*(.pe_header)) + + . = ALIGN(0x1000); + + __text_start = ABSOLUTE(.); + *(.text .text.*) + } :text + + . = ALIGN(0x1000); + __text_end = ABSOLUTE(.); + __text_size = ABSOLUTE(__text_end - __text_start); + + .rodata : { + __reloc_start = ABSOLUTE(.); + *(.dummy_reloc) + + . = ALIGN(0x1000); + __reloc_end = ABSOLUTE(.); + __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); + + __data_start = ABSOLUTE(.); + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.sdata2 .sdata2.*) + +#ifdef LINKER_NOMAP + full_map = .; +#else + *(.full_map) +#endif + } :rodata + + .data : { + data_begin = .; + *(.data .data.*) + *(.sdata .sdata.*) + } :data + + .bss : { + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + data_end = .; + } :data + + .no_unwind : { + *(.no_unwind) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + __data_end = ABSOLUTE(ALIGN(0x1000)); + __data_size = ABSOLUTE(__data_end - __data_start); + + __image_end = ABSOLUTE(ALIGN(0x1000)); + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + *(.interp) + } +} diff --git a/limine/common/linker_uefi_x86_64.ld.in b/limine/common/linker_uefi_x86_64.ld.in new file mode 100644 index 0000000..db5f176 --- /dev/null +++ b/limine/common/linker_uefi_x86_64.ld.in @@ -0,0 +1,79 @@ +OUTPUT_FORMAT(elf64-x86-64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(0x05); + rodata PT_LOAD FLAGS(0x04); + data PT_LOAD FLAGS(0x06); + dynamic PT_DYNAMIC FLAGS(0x06); +} + +SECTIONS +{ + . = 0; + __slide = .; + __image_base = ABSOLUTE(.); + __image_size = ABSOLUTE(__image_end - __image_base); + + .text : { + KEEP(*(.pe_header)) + + . = ALIGN(0x1000); + + __text_start = ABSOLUTE(.); + *(.text .text.*) + } :text + + . = ALIGN(0x1000); + __text_end = ABSOLUTE(.); + __text_size = ABSOLUTE(__text_end - __text_start); + + .rodata : { + __reloc_start = ABSOLUTE(.); + *(.dummy_reloc) + + . = ALIGN(0x1000); + __reloc_end = ABSOLUTE(.); + __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); + + __data_start = ABSOLUTE(.); + *(.rodata .rodata.*) + +#ifdef LINKER_NOMAP + full_map = .; +#else + *(.full_map) +#endif + } :rodata + + .data : { + data_begin = .; + *(.data .data.*) + } :data + + .bss : { + *(.bss .bss.*) + *(COMMON) + data_end = .; + } :data + + .no_unwind : { + *(.no_unwind) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + __data_end = ABSOLUTE(ALIGN(0x1000)); + __data_size = ABSOLUTE(__data_end - __data_start); + + __image_end = ABSOLUTE(ALIGN(0x1000)); + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + *(.interp) + } +} diff --git a/limine/common/menu.c b/limine/common/menu.c new file mode 100644 index 0000000..22a89eb --- /dev/null +++ b/limine/common/menu.c @@ -0,0 +1,1312 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (UEFI) +EFI_GUID limine_efi_vendor_guid = + { 0x513ee0d0, 0x6e43, 0xcb05, { 0xb2, 0x72, 0xf1, 0x46, 0xa2, 0xfc, 0xb8, 0x8a } }; +#endif + +#define EDITOR_MAX_BUFFER_SIZE 4096 +#define TOK_KEY 0 +#define TOK_EQUALS 1 +#define TOK_VALUE 2 +#define TOK_BADKEY 3 +#define TOK_COMMENT 4 + +static char interface_help_colour[] = "\e[32m"; +static char interface_help_colour_bright[] = "\e[92m"; + +static char *menu_branding = NULL; +static char *menu_branding_colour = NULL; +no_unwind bool booting_from_editor = false; +static no_unwind bool booting_from_blank = false; +static no_unwind char saved_orig_entry[EDITOR_MAX_BUFFER_SIZE]; +static no_unwind char saved_title[64]; + +static size_t get_line_offset(size_t *displacement, size_t index, const char *buffer) { + size_t offset = 0; + size_t _index = index; + for (size_t i = 0; buffer[i]; i++) { + if (!_index--) + break; + if (buffer[i] == '\n') + offset = i + 1; + } + if (displacement) + *displacement = index - offset; + return offset; +} + +static size_t get_line_length(size_t index, const char *buffer) { + size_t i; + for (i = index; buffer[i] != '\n' && buffer[i] != 0; i++); + return i - index; +} + +static size_t get_next_line(size_t index, const char *buffer) { + if (buffer[index] == 0) + return index; + size_t displacement; + get_line_offset(&displacement, index, buffer); + while (buffer[index] != '\n') { + if (buffer[index] == 0) + return index; + index++; + } + index++; + size_t next_line_length = get_line_length(index, buffer); + if (displacement > next_line_length) + displacement = next_line_length; + return index + displacement; +} + +static size_t get_prev_line(size_t index, const char *buffer) { + size_t offset, displacement, prev_line_offset, prev_line_length; + offset = get_line_offset(&displacement, index, buffer); + if (offset) { + prev_line_offset = get_line_offset(NULL, offset - 1, buffer); + prev_line_length = get_line_length(prev_line_offset, buffer); + if (displacement > prev_line_length) + displacement = prev_line_length; + return prev_line_offset + displacement; + } + return offset; +} + +static const char *VALID_KEYS[] = { + "COMMENT", + "PROTOCOL", + "CMDLINE", + "PATH", + "KERNEL_CMDLINE", + "KERNEL_PATH", + "INITRD_PATH", + "MODULE_PATH", + "MODULE_STRING", + "MODULE_CMDLINE", + "RESOLUTION", + "TEXTMODE", + "KASLR", + "RANDOMISE_HHDM_BASE", + "RANDOMIZE_HHDM_BASE", + "PAGING_MODE", + "MAX_PAGING_MODE", + "MIN_PAGING_MODE", + "DRIVE", + "PARTITION", + "MBR_ID", + "GPT_GUID", + "GPT_UUID", + "IMAGE_PATH", + "DTB_PATH", + NULL +}; + +static bool validation_enabled = true; +static bool invalid_syntax = false; + +static int validate_line(const char *buffer) { + if (!validation_enabled) return TOK_KEY; + if (buffer[0] == '#') + return TOK_COMMENT; + char keybuf[64]; + size_t i; + for (i = 0; buffer[i] && i < 64; i++) { + if (buffer[i] == ':') goto found_equals; + keybuf[i] = buffer[i]; + } +fail: + if (i < 64) keybuf[i] = 0; + if (keybuf[0] == '\n' || (!keybuf[0] && buffer[0] != ':')) return TOK_KEY; // blank line is valid + invalid_syntax = true; + return TOK_BADKEY; +found_equals: + if (i < 64) keybuf[i] = 0; + for (i = 0; VALID_KEYS[i]; i++) { + if (!strcasecmp(keybuf, VALID_KEYS[i])) { + return TOK_KEY; + } + } + goto fail; +} + +static void putchar_tokencol(int type, char c) { + switch (type) { + case TOK_KEY: + print("\e[36m%c\e[0m", c); + break; + case TOK_EQUALS: + print("\e[32m%c\e[0m", c); + break; + default: + case TOK_VALUE: + print("\e[39m%c\e[0m", c); + break; + case TOK_BADKEY: + print("\e[31m%c\e[0m", c); + break; + case TOK_COMMENT: + print("\e[33m%c\e[0m", c); + break; + } +} + +static bool editor_no_term_reset = false; + +char *config_entry_editor(const char *title, const char *orig_entry) { + if (terms[0]->cols < 40 || terms[0]->rows < 16) { + return NULL; + } + + FOR_TERM(TERM->autoflush = false); + + FOR_TERM(TERM->cursor_enabled = true); + + print("\e[2J\e[H"); + + if (booting_from_editor) { + orig_entry = saved_orig_entry; + title = saved_title; + } + + size_t cursor_offset = 0; + size_t entry_size = strlen(orig_entry); + size_t _window_size = terms[0]->rows - 8; + size_t window_offset = 0; + size_t line_size = terms[0]->cols - 2; + + bool display_overflow_error = false; + + // Skip leading newlines + while (*orig_entry == '\n') { + orig_entry++; + entry_size--; + } + + if (entry_size >= EDITOR_MAX_BUFFER_SIZE) { + panic(true, "Entry is too big to be edited."); + } + + bool syntax_highlighting_enabled = true; + char *syntax_highlighting_enabled_config = config_get_value(NULL, 0, "EDITOR_HIGHLIGHTING"); + if (syntax_highlighting_enabled_config != NULL + && strcmp(syntax_highlighting_enabled_config, "no") == 0) { + syntax_highlighting_enabled = false; + } + + validation_enabled = true; + char *validation_enabled_config = config_get_value(NULL, 0, "EDITOR_VALIDATION"); + if (validation_enabled_config != NULL + && strcmp(validation_enabled_config, "no") == 0) { + validation_enabled = false; + } + + char *buffer = ext_mem_alloc(EDITOR_MAX_BUFFER_SIZE); + memcpy(buffer, orig_entry, entry_size); + buffer[entry_size] = 0; + +refresh: + invalid_syntax = false; + + print("\e[2J\e[H"); + FOR_TERM(TERM->cursor_enabled = false); + { + size_t x, y; + print("\n"); + terms[0]->get_cursor_pos(terms[0], &x, &y); + set_cursor_pos_helper(terms[0]->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y); + print("\e[3%sm%s\e[0m", menu_branding_colour, menu_branding); + print("\n\n"); + } + + print(" %sESC\e[0m Discard and Exit %sF10\e[0m Boot\n\n", interface_help_colour, interface_help_colour); + + print(serial ? "/" : "┌"); + for (size_t i = 0; i < terms[0]->cols - 2; i++) { + switch (i) { + case 1: case 2: case 3: + if (window_offset > 0) { + print(serial ? "^" : "↑"); + break; + } + // FALLTHRU + default: { + size_t title_length = strlen(title); + if (i == (terms[0]->cols / 2) - DIV_ROUNDUP(title_length, 2) - 1 - 1) { + print(serial ? "|%s|" : "┤%s├", title); + i += (title_length + 2) - 1; + } else { + print(serial ? "-" : "─"); + } + } + } + } + size_t tmpx, tmpy; + + terms[0]->get_cursor_pos(terms[0], &tmpx, &tmpy); + print(serial ? "\\" : "┐"); + set_cursor_pos_helper(0, tmpy + 1); + print(serial ? "|" : "│"); + + size_t cursor_x, cursor_y; + size_t current_line = 0, line_offset = 0, window_size = _window_size; + bool printed_cursor = false; + bool printed_early = false; + int token_type = validate_line(buffer); + size_t tab_space_count = 0; + for (size_t i = 0; ; i++) { + // tab + if (buffer[i] == '\t') { + tab_space_count = 8 - (line_offset % 8); + goto tab_part; + } + + // newline + if (buffer[i] == '\n' + && current_line < window_offset + window_size + && current_line >= window_offset) { + size_t x, y; + terms[0]->get_cursor_pos(terms[0], &x, &y); + if (i == cursor_offset) { + cursor_x = x; + cursor_y = y; + printed_cursor = true; + } + set_cursor_pos_helper(terms[0]->cols - 1, y); + if (current_line == window_offset + window_size - 1) { + terms[0]->get_cursor_pos(terms[0], &tmpx, &tmpy); + print(serial ? "|" : "│"); + set_cursor_pos_helper(0, tmpy + 1); + print(serial ? "\\" : "└"); + } else { + terms[0]->get_cursor_pos(terms[0], &tmpx, &tmpy); + print(serial ? "|" : "│"); + set_cursor_pos_helper(0, tmpy + 1); + print(serial ? "|" : "│"); + } + line_offset = 0; + token_type = validate_line(buffer + i + 1); + current_line++; + continue; + } + + // switch to token type 1 if equals sign + if (token_type == TOK_KEY && buffer[i] == ':') token_type = TOK_EQUALS; + +tab_part: + if (buffer[i] != 0 && line_offset % line_size == line_size - 1) { + if (current_line < window_offset + window_size + && current_line >= window_offset) { + if (i == cursor_offset && !printed_cursor) { + terms[0]->get_cursor_pos(terms[0], &cursor_x, &cursor_y); + printed_cursor = true; + } + if (syntax_highlighting_enabled) { + putchar_tokencol(token_type, tab_space_count ? ' ' : buffer[i]); + } else { + print("%c", tab_space_count ? ' ' : buffer[i]); + } + if (tab_space_count != 0) { + tab_space_count--; + } + printed_early = true; + size_t x, y; + terms[0]->get_cursor_pos(terms[0], &x, &y); + if (y == terms[0]->rows - 3) { + print(serial ? ">" : "→"); + set_cursor_pos_helper(0, y + 1); + print(serial ? "\\" : "└"); + } else { + print(serial ? ">" : "→"); + set_cursor_pos_helper(0, y + 1); + print(serial ? "<" : "←"); + } + } + if (window_size > 0) { + window_size--; + } + } + + if (i == cursor_offset + && current_line < window_offset + window_size + && current_line >= window_offset + && !printed_cursor) { + terms[0]->get_cursor_pos(terms[0], &cursor_x, &cursor_y); + printed_cursor = true; + } + + if (buffer[i] == 0 || current_line >= window_offset + window_size) { + if (!printed_cursor) { + if (i <= cursor_offset) { + window_offset++; + goto refresh; + } + if (i > cursor_offset) { + window_offset--; + goto refresh; + } + } + break; + } + + if (buffer[i] == '\n') { + line_offset = 0; + token_type = validate_line(buffer + i + 1); + current_line++; + continue; + } + + if (current_line >= window_offset) { + line_offset++; + + // syntax highlighting + if (!printed_early) { + if (syntax_highlighting_enabled) { + putchar_tokencol(token_type, tab_space_count ? ' ' : buffer[i]); + } else { + print("%c", tab_space_count ? ' ' : buffer[i]); + } + + if (tab_space_count != 0) { + tab_space_count--; + } + } + + printed_early = false; + + // switch to token type 2 after equals sign + if (token_type == TOK_EQUALS) token_type = TOK_VALUE; + + } + + if (tab_space_count != 0) { + goto tab_part; + } + } + + // syntax error alert + if (validation_enabled) { + size_t x, y; + terms[0]->get_cursor_pos(terms[0], &x, &y); + set_cursor_pos_helper(0, terms[0]->rows - 1); + FOR_TERM(TERM->scroll_enabled = false); + if (invalid_syntax) { + print("\e[31mConfiguration is INVALID.\e[0m"); + } else { + print("\e[32mConfiguration is valid.\e[0m"); + } + FOR_TERM(TERM->scroll_enabled = true); + set_cursor_pos_helper(x, y); + } + + if (current_line - window_offset < window_size) { + size_t x, y; + for (size_t i = 0; i < (window_size - (current_line - window_offset)) - 1; i++) { + terms[0]->get_cursor_pos(terms[0], &x, &y); + set_cursor_pos_helper(terms[0]->cols - 1, y); + print(serial ? "|" : "│"); + set_cursor_pos_helper(0, y + 1); + print(serial ? "|" : "│"); + } + terms[0]->get_cursor_pos(terms[0], &x, &y); + set_cursor_pos_helper(terms[0]->cols - 1, y); + print(serial ? "|" : "│"); + set_cursor_pos_helper(0, y + 1); + print(serial ? "\\" : "└"); + } + + for (size_t i = 0; i < terms[0]->cols - 2; i++) { + switch (i) { + case 1: case 2: case 3: + if (current_line - window_offset >= window_size) { + print(serial ? "v" : "↓"); + break; + } + // FALLTHRU + default: + print(serial ? "-" : "─"); + } + } + terms[0]->get_cursor_pos(terms[0], &tmpx, &tmpy); + print(serial ? "/" : "┘"); + set_cursor_pos_helper(0, tmpy + 1); + + if (display_overflow_error) { + FOR_TERM(TERM->scroll_enabled = false); + print("\e[31mText buffer not big enough, delete something instead."); + FOR_TERM(TERM->scroll_enabled = true); + display_overflow_error = false; + } + + // Hack to redraw the cursor + set_cursor_pos_helper(cursor_x, cursor_y); + FOR_TERM(TERM->cursor_enabled = true); + + FOR_TERM(TERM->double_buffer_flush(TERM)); + + int c = getchar(); + size_t buffer_len = strlen(buffer); + switch (c) { + case GETCHAR_CURSOR_DOWN: + cursor_offset = get_next_line(cursor_offset, buffer); + break; + case GETCHAR_CURSOR_UP: + cursor_offset = get_prev_line(cursor_offset, buffer); + break; + case GETCHAR_CURSOR_LEFT: + if (cursor_offset) { + cursor_offset--; + } + break; + case GETCHAR_CURSOR_RIGHT: + if (cursor_offset < buffer_len) { + cursor_offset++; + } + break; + case GETCHAR_HOME: { + size_t displacement; + get_line_offset(&displacement, cursor_offset, buffer); + cursor_offset -= displacement; + break; + } + case GETCHAR_END: { + cursor_offset += get_line_length(cursor_offset, buffer); + break; + } + case '\b': + if (cursor_offset) { + cursor_offset--; + case GETCHAR_DELETE: + for (size_t i = cursor_offset; i < buffer_len; i++) { + buffer[i] = buffer[i+1]; + if (!buffer[i]) + break; + } + } + break; + case GETCHAR_F10: + memcpy(saved_orig_entry, buffer, buffer_len); + saved_orig_entry[buffer_len] = 0; + size_t title_len = strlen(title); + if (title_len >= sizeof(saved_title)) { + title_len = sizeof(saved_title) - 1; + } + memcpy(saved_title, title, title_len); + saved_title[title_len] = 0; + editor_no_term_reset ? editor_no_term_reset = false : reset_term(); + booting_from_editor = true; + return buffer; + case GETCHAR_ESCAPE: + pmm_free(buffer, EDITOR_MAX_BUFFER_SIZE); + editor_no_term_reset ? editor_no_term_reset = false : reset_term(); + booting_from_editor = false; + return NULL; + default: + if (buffer_len < EDITOR_MAX_BUFFER_SIZE - 1) { + if (isprint(c) || c == '\n' || c == '\t') { + for (size_t i = buffer_len; ; i--) { + buffer[i+1] = buffer[i]; + if (i == cursor_offset) + break; + } + buffer[cursor_offset++] = c; + } + } else { + display_overflow_error = true; + } + break; + } + + goto refresh; +} + +static size_t print_tree(size_t offset, size_t window, const char *shift, size_t level, size_t base_index, size_t selected_entry, + struct menu_entry *current_entry, + struct menu_entry **selected_menu_entry, + size_t *max_len, size_t *max_height) { + size_t max_entries = 0; + + bool no_print = false; + size_t dummy_max_len = 0; + if (max_len == NULL) { + max_len = &dummy_max_len; + } + size_t dummy_max_height = 0; + if (max_height == NULL) { + max_height = &dummy_max_height; + } + if (!level) { + *max_len = 0; + *max_height = 0; + } + if (shift == NULL) { + no_print = true; + } + + for (;;) { + size_t cur_len = 0; + if (current_entry == NULL) + break; + if (current_entry->sub == NULL) { + bool skip_entry = false; + char *cur_entry_protocol = config_get_value(current_entry->body, 0, "PROTOCOL"); + if (cur_entry_protocol) { +#if defined (UEFI) + if (strcmp(cur_entry_protocol, "bios") == 0 + || strcmp(cur_entry_protocol, "bios_chainload") == 0) { +#elif defined (BIOS) + if (strcmp(cur_entry_protocol, "efi") == 0 + || strcmp(cur_entry_protocol, "uefi") == 0 + || strcmp(cur_entry_protocol, "efi_chainload") == 0) { +#endif + skip_entry = true; + } + if (skip_entry) { + current_entry = current_entry->next; + continue; + } + } + } + if (!no_print && base_index + max_entries < offset) { + goto skip_line; + } + if (!no_print && base_index + max_entries >= offset + window) { + goto skip_line; + } + if (!no_print) print("%s", shift); + if (level) { + for (size_t i = level - 1; i > 0; i--) { + struct menu_entry *actual_parent = current_entry; + for (size_t j = 0; j < i; j++) + actual_parent = actual_parent->parent; + if (actual_parent->next != NULL) { + if (!no_print) print(serial ? " |" : " │"); + } else { + if (!no_print) print(" "); + } + cur_len += 2; + } + if (current_entry->next == NULL) { + if (!no_print) print(serial ? " `" : " └"); + } else { + if (!no_print) print(serial ? " |" : " ├"); + } + cur_len += 2; + } + if (current_entry->sub) { + if (!no_print) print(current_entry->expanded ? "[-]" : "[+]"); + } else if (level) { + if (!no_print) print(serial ? "-->" : "──►"); + } else { + if (!no_print) print(" "); + } + cur_len += 3; + if (base_index + max_entries == selected_entry) { + *selected_menu_entry = current_entry; + if (!no_print) print("\e[7m"); + } + if (!no_print) print(" %s \e[27m\n", current_entry->name); + (*max_height)++; + cur_len += 1 + strlen(current_entry->name) + 1; +skip_line: + if (current_entry->sub && current_entry->expanded) { + max_entries += print_tree(offset, window, shift, level + 1, base_index + max_entries + 1, + selected_entry, + current_entry->sub, + selected_menu_entry, + max_len, max_height); + } + max_entries++; + current_entry = current_entry->next; + if (cur_len > *max_len) { + *max_len = cur_len; + } + } + return max_entries; +} + +static struct memmap_entry *rewound_memmap = NULL; +static size_t rewound_memmap_entries = 0; +static no_unwind uint8_t *rewound_data; +#if defined (BIOS) +static no_unwind uint8_t *rewound_s2_data; +static no_unwind uint8_t *rewound_bss; +#endif + +extern symbol data_begin; +extern symbol data_end; +#if defined (BIOS) +extern symbol s2_data_begin; +extern symbol s2_data_end; +extern symbol bss_begin; +extern symbol bss_end; +#endif + +static void menu_init_term(void) { + // If there is GRAPHICS config key and the value is "yes", enable graphics +#if defined (BIOS) + char *graphics = config_get_value(NULL, 0, "GRAPHICS"); +#elif defined (UEFI) + char *graphics = "yes"; +#endif + + if (graphics == NULL || strcmp(graphics, "no") != 0) { + size_t req_width = 0, req_height = 0, req_bpp = 0; + + char *menu_resolution = config_get_value(NULL, 0, "INTERFACE_RESOLUTION"); + if (menu_resolution != NULL) + parse_resolution(&req_width, &req_height, &req_bpp, menu_resolution); + + if (!quiet && !gterm_init(NULL, NULL, NULL, req_width, req_height)) { +#if defined (BIOS) + vga_textmode_init(true); +#elif defined (UEFI) + serial = true; + term_fallback(); +#endif + } + } else { +#if defined (BIOS) + if (!quiet) { + vga_textmode_init(true); + } +#endif + } +} + +#if defined(UEFI) +bool reboot_to_fw_ui_supported(void) { + uint64_t os_indications_supported; + UINTN size = sizeof(os_indications_supported); + EFI_GUID global_variable = EFI_GLOBAL_VARIABLE; + EFI_STATUS status = gRT->GetVariable(L"OsIndicationsSupported", &global_variable, NULL, &size, &os_indications_supported); + if (status == EFI_SUCCESS && size == sizeof(os_indications_supported)) { + return (os_indications_supported & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0; + } + return false; +} + +noreturn void reboot_to_fw_ui(void) { + reset_term(); + print("Rebooting to the firmware setup...\n"); + + uint64_t os_indications; + UINTN size = sizeof(os_indications); + EFI_GUID global_variable = EFI_GLOBAL_VARIABLE; + EFI_STATUS status = gRT->GetVariable(L"OsIndications", &global_variable, NULL, &size, &os_indications); + if (status != EFI_SUCCESS || size != sizeof(os_indications)) { + if (status == EFI_NOT_FOUND) { + os_indications = 0; + goto not_found; + } + + panic(true, "Failed to get OsIndications variable, status=%X", status); + } + +not_found:; + uint64_t new_os_indications = os_indications | EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + status = gRT->SetVariable(L"OsIndications", &global_variable, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(new_os_indications), &new_os_indications); + + if (status != EFI_SUCCESS) { + panic(true, "Failed to set OsIndications variable, status=%X", status); + } + + gRT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); + panic(true, "Failed to reboot to firmware UI"); +} +#endif + +noreturn void _menu(bool first_run) { + size_t data_size = (uintptr_t)data_end - (uintptr_t)data_begin; +#if defined (BIOS) + size_t s2_data_size = (uintptr_t)s2_data_end - (uintptr_t)s2_data_begin; + size_t bss_size = (uintptr_t)bss_end - (uintptr_t)bss_begin; +#endif + + if (rewound_memmap != NULL) { + memcpy(data_begin, rewound_data, data_size); +#if defined (BIOS) + memcpy(s2_data_begin, rewound_s2_data, s2_data_size); + memcpy(bss_begin, rewound_bss, bss_size); +#endif + memcpy(memmap, rewound_memmap, rewound_memmap_entries * sizeof(struct memmap_entry)); + memmap_entries = rewound_memmap_entries; + } else { + rewound_data = ext_mem_alloc(data_size); +#if defined (BIOS) + rewound_s2_data = ext_mem_alloc(s2_data_size); + rewound_bss = ext_mem_alloc(bss_size); +#endif + /* addition due to allocation potentially adding new memory map entries */ + rewound_memmap = ext_mem_alloc((memmap_entries + 16) * sizeof(struct memmap_entry)); + memcpy(rewound_memmap, memmap, memmap_entries * sizeof(struct memmap_entry)); + rewound_memmap_entries = memmap_entries; + memcpy(rewound_data, data_begin, data_size); +#if defined (BIOS) + memcpy(rewound_s2_data, s2_data_begin, s2_data_size); + memcpy(rewound_bss, bss_begin, bss_size); +#endif + } + + term_fallback(); + + if (bad_config == false) { + if (!init_config_smbios()) { + +#if defined (UEFI) + if (init_config_disk(boot_volume)) { +#endif + volume_iterate_parts(boot_volume, + if (!init_config_disk(_PART)) { + boot_volume = _PART; + break; + } + ); +#if defined (UEFI) + } +#endif + } + } + +#if defined (__riscv) + init_riscv(NULL); +#endif + + char *quiet_str = config_get_value(NULL, 0, "QUIET"); + quiet = quiet_str != NULL && strcmp(quiet_str, "yes") == 0; + + char *verbose_str = config_get_value(NULL, 0, "VERBOSE"); + verbose = verbose_str != NULL && strcmp(verbose_str, "yes") == 0; + + char *serial_str = config_get_value(NULL, 0, "SERIAL"); + serial = +#if defined (UEFI) + is_efi_serial_present() && +#endif + serial_str != NULL && strcmp(serial_str, "yes") == 0; + +#if defined (BIOS) + if (serial) { + char *baudrate_s = config_get_value(NULL, 0, "SERIAL_BAUDRATE"); + if (baudrate_s == NULL) { + serial_baudrate = 115200; + } else { + serial_baudrate = strtoui(baudrate_s, NULL, 10); + if (serial_baudrate == 0 || serial_baudrate > 115200) { + serial_baudrate = 115200; + } + } + } +#endif + + char *hash_mismatch_panic_str = config_get_value(NULL, 0, "HASH_MISMATCH_PANIC"); + hash_mismatch_panic = hash_mismatch_panic_str == NULL || strcmp(hash_mismatch_panic_str, "yes") == 0; + + char *randomise_mem_str = config_get_value(NULL, 0, "RANDOMISE_MEMORY"); + if (randomise_mem_str == NULL) + randomise_mem_str = config_get_value(NULL, 0, "RANDOMIZE_MEMORY"); + bool randomise_mem = randomise_mem_str != NULL && strcmp(randomise_mem_str, "yes") == 0; + if (randomise_mem) { + pmm_randomise_memory(); + } + + char *editor_enabled_str = config_get_value(NULL, 0, "EDITOR_ENABLED"); + if (editor_enabled_str != NULL) { + editor_enabled = strcmp(editor_enabled_str, "yes") == 0; + } + + char *help_hidden_str = config_get_value(NULL, 0, "INTERFACE_HELP_HIDDEN"); + if (help_hidden_str != NULL) { + help_hidden = strcmp(help_hidden_str, "yes") == 0; + } + + char *interface_help_colour_str = config_get_value(NULL, 0, "INTERFACE_HELP_COLOUR"); + if (interface_help_colour_str == NULL) { + interface_help_colour_str = config_get_value(NULL, 0, "INTERFACE_HELP_COLOR"); + } + if (interface_help_colour_str != NULL && interface_help_colour_str[0] != '\0') { + interface_help_colour[3] = interface_help_colour_str[0]; + interface_help_colour_bright[3] = interface_help_colour_str[0]; + } + + { + char *tmp = config_get_value(NULL, 0, "INTERFACE_BRANDING"); + if (tmp != NULL) { + size_t len = strlen(tmp) + 1; + menu_branding = ext_mem_alloc(len); + memcpy(menu_branding, tmp, len); + } + } + if (menu_branding == NULL) { +#if defined (BIOS) + { + uint32_t eax, ebx, ecx, edx; + if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) { + menu_branding = "Limine " LIMINE_VERSION " (ia-32, BIOS)"; + } else { + menu_branding = "Limine " LIMINE_VERSION " (x86-64, BIOS)"; + } + } +#elif defined (UEFI) +#if defined (__i386__) + { + uint32_t eax, ebx, ecx, edx; + if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) { + menu_branding = "Limine " LIMINE_VERSION " (ia-32, UEFI32)"; + } else { + menu_branding = "Limine " LIMINE_VERSION " (x86-64, UEFI32)"; + } + } +#else + menu_branding = "Limine " LIMINE_VERSION " (" +#if defined (__x86_64__) + "x86-64" +#elif defined (__riscv) + "riscv64" +#elif defined (__aarch64__) + "aarch64" +#elif defined (__loongarch64) + "loongarch64" +#endif + ", UEFI)"; +#endif +#endif + } + + { + char *tmp = config_get_value(NULL, 0, "INTERFACE_BRANDING_COLOUR"); + if (tmp == NULL) + tmp = config_get_value(NULL, 0, "INTERFACE_BRANDING_COLOR"); + if (tmp != NULL) { + size_t len = strlen(tmp) + 1; + menu_branding_colour = ext_mem_alloc(len); + memcpy(menu_branding_colour, tmp, len); + } else { + menu_branding_colour = "6"; + } + } + + bool skip_timeout = false; + struct menu_entry *selected_menu_entry = NULL; + + size_t selected_entry = 0; + + char *default_entry = config_get_value(NULL, 0, "DEFAULT_ENTRY"); + if (default_entry != NULL) { + selected_entry = strtoui(default_entry, NULL, 10); + if (selected_entry) + selected_entry--; + } + +#if defined (UEFI) + char *remember_last = config_get_value(NULL, 0, "REMEMBER_LAST_ENTRY"); + if (remember_last != NULL && strcasecmp(remember_last, "yes") == 0) { + UINTN getvar_size = sizeof(size_t); + size_t last; + if (gRT->GetVariable(L"LimineLastBootedEntry", + &limine_efi_vendor_guid, + NULL, + &getvar_size, + &last) == 0 && getvar_size == sizeof(size_t)) { + selected_entry = last; + } + } +#endif + + // Use print tree to load up selected_menu_entry and determine if the + // default entry is valid. + size_t max_entries = print_tree(0, 0, NULL, 0, 0, selected_entry, menu_tree, &selected_menu_entry, NULL, NULL); + if (selected_entry >= max_entries) { + selected_entry = 0; + } + + size_t timeout = 5; + char *timeout_config = config_get_value(NULL, 0, "TIMEOUT"); + if (timeout_config != NULL) { + if (!strcmp(timeout_config, "no")) + skip_timeout = true; + else + timeout = strtoui(timeout_config, NULL, 10); + } + +#if defined(UEFI) + bool reboot_to_firmware_supported = reboot_to_fw_ui_supported(); +#endif + + if (!first_run) { + quiet = false; + skip_timeout = true; + } + + if (!skip_timeout && !timeout) { + if (max_entries == 0 || selected_menu_entry->sub != NULL) { + quiet = false; + print("Default entry is not valid or directory, booting to menu.\n"); + skip_timeout = true; + } else { + goto autoboot; + } + } + + menu_init_term(); + + if (terms[0]->cols < 40 || terms[0]->rows < 16) { + // Terminal too small for menu, fall back to text console +#if defined (BIOS) + vga_textmode_init(true); +#elif defined (UEFI) + serial = true; + term_fallback(); +#endif + } + + size_t tree_offset = 0; + +refresh: + if (selected_entry >= tree_offset + terms[0]->rows - 8) { + tree_offset = selected_entry - (terms[0]->rows - 9); + } + if (selected_entry < tree_offset) { + tree_offset = selected_entry; + } + + FOR_TERM(TERM->autoflush = false); + + FOR_TERM(TERM->cursor_enabled = false); + + print("\e[2J\e[H"); + { + size_t x, y; + print("\n"); + terms[0]->get_cursor_pos(terms[0], &x, &y); + set_cursor_pos_helper(terms[0]->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y); + print("\e[3%sm%s\e[0m", menu_branding_colour, menu_branding); + print("\n\n\n\n"); + } + + if (max_entries == 0) { + if (quiet) { + quiet = false; + menu_init_term(); + } + const char *msg; + if (config_ready) { + msg = "[config file contains no valid entries]"; + } else { + msg = "[config file not found]"; + } + set_cursor_pos_helper(terms[0]->cols / 2 - strlen(msg) / 2, terms[0]->rows / 2); + print("%s\n", msg); + } + + size_t max_tree_len, max_tree_height; + max_entries = print_tree(tree_offset, terms[0]->rows - 8, NULL, 0, 0, selected_entry, menu_tree, + &selected_menu_entry, &max_tree_len, &max_tree_height); + + if (max_entries != 0) { + size_t half_cols = terms[0]->cols / 2; + size_t half_tree = DIV_ROUNDUP(max_tree_len, 2); + size_t tree_prefix_len = (half_cols > half_tree + 2) ? (half_cols - half_tree - 2) : 0; + char *tree_prefix = ext_mem_alloc(tree_prefix_len + 1); + memset(tree_prefix, ' ', tree_prefix_len); + + if (max_tree_height > terms[0]->rows - 10) { + max_tree_height = terms[0]->rows - 10; + } + + set_cursor_pos_helper(0, terms[0]->rows / 2 - max_tree_height / 2); + + max_entries = print_tree(tree_offset, terms[0]->rows - 8, tree_prefix, 0, 0, selected_entry, menu_tree, + &selected_menu_entry, NULL, NULL); + + pmm_free(tree_prefix, tree_prefix_len + 1); + } + + { + size_t x, y; + terms[0]->get_cursor_pos(terms[0], &x, &y); + + if (max_entries != 0) { + if (tree_offset > 0) { + set_cursor_pos_helper(terms[0]->cols / 2 - 1, 4); + print(serial ? "^^^" : "↑↑↑"); + } + + if (tree_offset + (terms[0]->rows - 8) < max_entries) { + set_cursor_pos_helper(terms[0]->cols / 2 - 1, terms[0]->rows - 3); + print(serial ? "vvv" : "↓↓↓"); + } + } + + if (!help_hidden) { + set_cursor_pos_helper(0, 3); + if (max_entries != 0) { + if (selected_menu_entry->sub == NULL) { + print(" %sARROWS\e[0m Select %sENTER\e[0m Boot %s%s", + interface_help_colour, interface_help_colour, interface_help_colour, + editor_enabled ? "E\e[0m Edit" : "\e[0m"); + } else { + print(" %sARROWS\e[0m Select %sENTER\e[0m %s", + interface_help_colour, interface_help_colour, + selected_menu_entry->expanded ? "Collapse" : "Expand"); + } + } +#if defined(UEFI) + if (reboot_to_firmware_supported) { + set_cursor_pos_helper(terms[0]->cols - (editor_enabled ? 37 : 20), 3); + print("%sS\e[0m Firmware Setup", interface_help_colour); + } +#endif + if (editor_enabled) { + set_cursor_pos_helper(terms[0]->cols - 17, 3); + print("%sB\e[0m Blank Entry", interface_help_colour); + } + } + set_cursor_pos_helper(x, y); + } + + if (max_entries == 0 || selected_menu_entry->sub != NULL) + skip_timeout = true; + + int c; + + if (skip_timeout == false) { + print("\n\n"); + for (size_t i = timeout; i; i--) { + set_cursor_pos_helper(0, terms[0]->rows - 1); + FOR_TERM(TERM->scroll_enabled = false); + print("\e[2K%sBooting automatically in %s%U%s, press any key to stop the countdown...\e[0m", + interface_help_colour, interface_help_colour_bright, (uint64_t)i, interface_help_colour); + FOR_TERM(TERM->scroll_enabled = true); + FOR_TERM(TERM->double_buffer_flush(TERM)); + if ((c = pit_sleep_and_quit_on_keypress(1))) { + skip_timeout = true; + if (quiet) { + quiet = false; + menu_init_term(); + goto timeout_aborted; + } + print("\e[2K"); + FOR_TERM(TERM->double_buffer_flush(TERM)); + goto timeout_aborted; + } + } + goto autoboot; + } + + set_cursor_pos_helper(0, terms[0]->rows - 1); + if (max_entries != 0 && selected_menu_entry->comment != NULL) { + FOR_TERM(TERM->scroll_enabled = false); + print("\e[36m%s\e[0m", selected_menu_entry->comment); + FOR_TERM(TERM->scroll_enabled = true); + } + + if (booting_from_editor) { + if (booting_from_blank) { + goto editor_blank; + } + goto editor; + } + + FOR_TERM(TERM->double_buffer_flush(TERM)); + + for (;;) { + c = getchar(); +timeout_aborted: + if (max_entries == 0) { + switch (c) { + case 'b': case 'B': case 's': case 'S': + break; + default: + continue; + + } + } + switch (c) { + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': { + int ent = (c - '0') - 1; + if (ent < (int)max_entries) { + selected_entry = ent; + print_tree(0, 0, NULL, 0, 0, selected_entry, menu_tree, + &selected_menu_entry, NULL, NULL); + goto autoboot; + } + goto refresh; + } + case GETCHAR_HOME: + selected_entry = 0; + goto refresh; + case GETCHAR_END: + selected_entry = max_entries - 1; + goto refresh; + case GETCHAR_CURSOR_UP: + if (selected_entry == 0) + selected_entry = max_entries - 1; + else + selected_entry--; + goto refresh; + case GETCHAR_CURSOR_DOWN: + if (++selected_entry == max_entries) + selected_entry = 0; + goto refresh; + case GETCHAR_CURSOR_RIGHT: + case '\n': + case ' ': + autoboot: + if (max_entries == 0) { + break; + } + if (selected_menu_entry->sub != NULL) { + selected_menu_entry->expanded = !selected_menu_entry->expanded; + goto refresh; + } + if (!quiet) { + if (term_backend == FALLBACK) { + if (!gterm_init(NULL, NULL, NULL, 0, 0)) { +#if defined (BIOS) + vga_textmode_init(true); +#elif defined (UEFI) + serial = true; + term_fallback(); +#endif + } + } else { + reset_term(); + } + } + +#if defined (UEFI) + gRT->SetVariable(L"LimineLastBootedEntry", + &limine_efi_vendor_guid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(size_t), + &selected_entry); +#endif + + boot(selected_menu_entry->body); + case 'e': + case 'E': { + if (editor_enabled) { + editor: + if (max_entries == 0 || selected_menu_entry->sub != NULL) { + break; + } + editor_no_term_reset = true; + char *new_body = config_entry_editor(selected_menu_entry->name, selected_menu_entry->body); + if (new_body == NULL) + goto refresh; + selected_menu_entry->body = new_body; + goto autoboot; + } + break; + } +#if defined(UEFI) + case 's': + case 'S': { + if (reboot_to_firmware_supported) { + reboot_to_fw_ui(); + } + break; + } +#endif + case 'b': + case 'B': { + if (editor_enabled) { + editor_blank: + booting_from_blank = true; + char *new_entry = config_entry_editor("Blank Entry", ""); + if (new_entry != NULL) { + config_ready = true; + boot(new_entry); + } + booting_from_blank = false; + goto refresh; + } + break; + } + } + } +} + +noreturn void boot(char *config) { +#if defined (__riscv) + init_riscv(config); +#endif + + char *cmdline; + { + char *tmp = config_get_value(config, 0, "KERNEL_CMDLINE"); + if (!tmp) { + tmp = config_get_value(config, 0, "CMDLINE"); + } + if (tmp) { + size_t len = strlen(tmp) + 1; + cmdline = ext_mem_alloc(len); + memcpy(cmdline, tmp, len); + } else { + cmdline = ""; + } + } + + char *proto = config_get_value(config, 0, "PROTOCOL"); + if (proto == NULL) { + panic(true, "Boot protocol not specified for this entry"); + } + + if (!strcmp(proto, "limine")) { + limine_load(config, cmdline); + } else if (!strcmp(proto, "linux")) { + linux_load(config, cmdline); + } else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) { +#if defined (__x86_64__) || defined (__i386__) + multiboot1_load(config, cmdline); +#else + quiet = false; + print("Multiboot 1 is not available on non-x86 architectures.\n\n"); +#endif + } else if (!strcmp(proto, "multiboot2")) { +#if defined (__x86_64__) || defined (__i386__) + multiboot2_load(config, cmdline); +#else + quiet = false; + print("Multiboot 2 is not available on non-x86 architectures.\n\n"); +#endif +#if defined (BIOS) + } else if (!strcmp(proto, "bios_chainload") + || !strcmp(proto, "bios")) { +#elif defined (UEFI) + } else if (!strcmp(proto, "efi_chainload") + || !strcmp(proto, "efi") + || !strcmp(proto, "uefi")) { +#endif + chainload(config, cmdline); + } + + panic(true, "Unsupported protocol specified."); +} diff --git a/limine/common/menu.h b/limine/common/menu.h new file mode 100644 index 0000000..38f48f6 --- /dev/null +++ b/limine/common/menu.h @@ -0,0 +1,20 @@ +#ifndef MENU_H__ +#define MENU_H__ + +#include +#include + +#if defined(UEFI) +bool reboot_to_fw_ui_supported(void); +noreturn void reboot_to_fw_ui(void); +#endif + +noreturn void menu(bool first_run); + +noreturn void boot(char *config); + +char *config_entry_editor(const char *title, const char *orig_entry); + +extern bool booting_from_editor; + +#endif diff --git a/limine/common/menu_thunk.asm_aarch64 b/limine/common/menu_thunk.asm_aarch64 new file mode 100644 index 0000000..5fcff87 --- /dev/null +++ b/limine/common/menu_thunk.asm_aarch64 @@ -0,0 +1,27 @@ +.section .data + +.align 3 +stack_at_first_entry: + .quad 0 + +.section .text + +.global menu +.extern _menu + +menu: + adrp x8, stack_at_first_entry + ldr x9, [x8, :lo12:stack_at_first_entry] + cbz x9, 1f + mov sp, x9 + b 2f +1: + mov x9, sp + str x9, [x8, :lo12:stack_at_first_entry] +2: + mov x30, xzr + mov x29, xzr + + b _menu + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/menu_thunk.asm_ia32 b/limine/common/menu_thunk.asm_ia32 new file mode 100644 index 0000000..08af227 --- /dev/null +++ b/limine/common/menu_thunk.asm_ia32 @@ -0,0 +1,29 @@ +section .text + +global menu +extern _menu +menu: + pop eax + call .L1 +.L1: + pop eax + add eax, .L3 - .L1 + cmp dword [eax], 0 + jne .L2 + mov [eax], esp + jmp .L4 + +.L2: + mov edi, [esp] + mov esp, [eax] + push edi + jmp .L4 + +.L3: + dq 0 + +.L4: + push 0 + jmp _menu + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/menu_thunk.asm_loongarch64 b/limine/common/menu_thunk.asm_loongarch64 new file mode 100644 index 0000000..831c566 --- /dev/null +++ b/limine/common/menu_thunk.asm_loongarch64 @@ -0,0 +1,25 @@ +.section .data + +.align 3 +stack_at_first_entry: + .quad 0 + +.section .text + +.global menu +.extern _menu + +menu: + la $t0, stack_at_first_entry + ld.d $t1, $t0, 0 + beqz $t1, 1f + move $sp, $t1 + b 2f +1: + st.d $sp, $t0, 0 +2: + move $fp, $r0 + move $ra, $r0 + b _menu + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/menu_thunk.asm_riscv64 b/limine/common/menu_thunk.asm_riscv64 new file mode 100644 index 0000000..75d6301 --- /dev/null +++ b/limine/common/menu_thunk.asm_riscv64 @@ -0,0 +1,24 @@ +.section .data + +.p2align 3 +stack_at_first_entry: + .8byte 0 + +.section .text + +.global menu +.extern _menu + +menu: +.option norelax + lla t0, stack_at_first_entry + ld t1, (t0) + beqz t1, 1f + mv sp, t1 + j 2f +1: sd sp, (t0) +2: mv fp, zero + mv ra, zero + j _menu + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/menu_thunk.asm_x86_64 b/limine/common/menu_thunk.asm_x86_64 new file mode 100644 index 0000000..506c177 --- /dev/null +++ b/limine/common/menu_thunk.asm_x86_64 @@ -0,0 +1,23 @@ +section .data + +stack_at_first_entry: + dq 0 + +section .text + +global menu +extern _menu +menu: + xor eax, eax + cmp [rel stack_at_first_entry], rax + jne .L1 + mov [rel stack_at_first_entry], rsp + jmp .L2 +.L1: + mov rsp, [rel stack_at_first_entry] +.L2: + push 0 + push 0 + jmp _menu + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/mm/mtrr.c b/limine/common/mm/mtrr.c new file mode 100644 index 0000000..b00e171 --- /dev/null +++ b/limine/common/mm/mtrr.c @@ -0,0 +1,140 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include + +static bool mtrr_supported(void) { + uint32_t eax, ebx, ecx, edx; + + if (!cpuid(1, 0, &eax, &ebx, &ecx, &edx)) + return false; + + return !!(edx & (1 << 12)); +} + +uint64_t *saved_mtrrs = NULL; + +void mtrr_save(void) { + if (!mtrr_supported()) { + return; + } + + uint64_t ia32_mtrrcap = rdmsr(0xfe); + + uint8_t var_reg_count = ia32_mtrrcap & 0xff; + + if (saved_mtrrs == NULL) { + saved_mtrrs = ext_mem_alloc(( + (var_reg_count * 2) /* variable MTRRs, 2 MSRs each */ + + 11 /* 11 fixed MTRRs */ + + 1 /* 1 default type MTRR */ + ) * sizeof(uint64_t)); + } + + /* save variable range MTRRs */ + for (uint8_t i = 0; i < var_reg_count * 2; i += 2) { + saved_mtrrs[i] = rdmsr(0x200 + i); + saved_mtrrs[i + 1] = rdmsr(0x200 + i + 1); + } + + /* save fixed range MTRRs */ + saved_mtrrs[var_reg_count * 2 + 0] = rdmsr(0x250); + saved_mtrrs[var_reg_count * 2 + 1] = rdmsr(0x258); + saved_mtrrs[var_reg_count * 2 + 2] = rdmsr(0x259); + saved_mtrrs[var_reg_count * 2 + 3] = rdmsr(0x268); + saved_mtrrs[var_reg_count * 2 + 4] = rdmsr(0x269); + saved_mtrrs[var_reg_count * 2 + 5] = rdmsr(0x26a); + saved_mtrrs[var_reg_count * 2 + 6] = rdmsr(0x26b); + saved_mtrrs[var_reg_count * 2 + 7] = rdmsr(0x26c); + saved_mtrrs[var_reg_count * 2 + 8] = rdmsr(0x26d); + saved_mtrrs[var_reg_count * 2 + 9] = rdmsr(0x26e); + saved_mtrrs[var_reg_count * 2 + 10] = rdmsr(0x26f); + + /* save MTRR default type */ + saved_mtrrs[var_reg_count * 2 + 11] = rdmsr(0x2ff); + + /* make sure that the saved MTRR default has MTRRs off */ + saved_mtrrs[var_reg_count * 2 + 11] &= ~((uint64_t)1 << 11); +} + +void mtrr_restore(void) { + if (!mtrr_supported()) { + return; + } + + uint64_t ia32_mtrrcap = rdmsr(0xfe); + + uint8_t var_reg_count = ia32_mtrrcap & 0xff; + + if (saved_mtrrs == NULL) { + panic(true, "mtrr: Attempted restore without prior save"); + } + + /* according to the Intel SDM 12.11.7.2 "MemTypeSet() Function", + we need to follow this procedure before changing MTRR set up */ + + /* save old cr0 and then enable the CD flag and disable the NW flag */ + uintptr_t old_cr0; + asm volatile ("mov %%cr0, %0" : "=r"(old_cr0) :: "memory"); + uintptr_t new_cr0 = (old_cr0 | (1 << 30)) & ~((uintptr_t)1 << 29); + asm volatile ("mov %0, %%cr0" :: "r"(new_cr0) : "memory"); + + /* then invalidate the caches */ + asm volatile ("wbinvd" ::: "memory"); + + /* do a cr3 read/write to flush the TLB */ + uintptr_t cr3; + asm volatile ("mov %%cr3, %0" : "=r"(cr3) :: "memory"); + asm volatile ("mov %0, %%cr3" :: "r"(cr3) : "memory"); + + /* disable the MTRRs */ + uint64_t mtrr_def = rdmsr(0x2ff); + mtrr_def &= ~((uint64_t)1 << 11); + wrmsr(0x2ff, mtrr_def); + + /* restore variable range MTRRs */ + for (uint8_t i = 0; i < var_reg_count * 2; i += 2) { + wrmsr(0x200 + i, saved_mtrrs[i]); + wrmsr(0x200 + i + 1, saved_mtrrs[i + 1]); + } + + /* restore fixed range MTRRs */ + wrmsr(0x250, saved_mtrrs[var_reg_count * 2 + 0]); + wrmsr(0x258, saved_mtrrs[var_reg_count * 2 + 1]); + wrmsr(0x259, saved_mtrrs[var_reg_count * 2 + 2]); + wrmsr(0x268, saved_mtrrs[var_reg_count * 2 + 3]); + wrmsr(0x269, saved_mtrrs[var_reg_count * 2 + 4]); + wrmsr(0x26a, saved_mtrrs[var_reg_count * 2 + 5]); + wrmsr(0x26b, saved_mtrrs[var_reg_count * 2 + 6]); + wrmsr(0x26c, saved_mtrrs[var_reg_count * 2 + 7]); + wrmsr(0x26d, saved_mtrrs[var_reg_count * 2 + 8]); + wrmsr(0x26e, saved_mtrrs[var_reg_count * 2 + 9]); + wrmsr(0x26f, saved_mtrrs[var_reg_count * 2 + 10]); + + /* restore MTRR default type */ + wrmsr(0x2ff, saved_mtrrs[var_reg_count * 2 + 11]); + + /* now do the opposite of the cache disable and flush from above */ + + /* re-enable MTRRs */ + mtrr_def = rdmsr(0x2ff); + mtrr_def |= (1 << 11); + wrmsr(0x2ff, mtrr_def); + + /* do a cr3 read/write to flush the TLB */ + asm volatile ("mov %%cr3, %0" : "=r"(cr3) :: "memory"); + asm volatile ("mov %0, %%cr3" :: "r"(cr3) : "memory"); + + /* then invalidate the caches */ + asm volatile ("wbinvd" ::: "memory"); + + /* restore old value of cr0 */ + asm volatile ("mov %0, %%cr0" :: "r"(old_cr0) : "memory"); +} + +#endif diff --git a/limine/common/mm/mtrr.h b/limine/common/mm/mtrr.h new file mode 100644 index 0000000..9cead12 --- /dev/null +++ b/limine/common/mm/mtrr.h @@ -0,0 +1,11 @@ +#ifndef MM__MTRR_H__ +#define MM__MTRR_H__ + +#if defined (__x86_64__) || defined (__i386__) + +void mtrr_save(void); +void mtrr_restore(void); + +#endif + +#endif diff --git a/limine/common/mm/pmm.c b/limine/common/mm/pmm.c new file mode 100644 index 0000000..123c156 --- /dev/null +++ b/limine/common/mm/pmm.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include + +static bool full_overlap_check(uint64_t base1, uint64_t top1, + uint64_t base2, uint64_t top2) { + return ((base1 >= base2 && base1 < top2) + && (top1 > base2 && top1 <= top2)); +} + +bool check_usable_memory(uint64_t base, uint64_t top) { + uint64_t overlap_remaining = top - base; + + for (size_t i = 0; i < memmap_entries; i++) { + if (memmap[i].type != MEMMAP_USABLE +#if defined (UEFI) + && memmap[i].type != MEMMAP_EFI_RECLAIMABLE +#endif + && memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE + && memmap[i].type != MEMMAP_KERNEL_AND_MODULES) { + continue; + } + + uint64_t memmap_top = memmap[i].base + memmap[i].length; + + if (full_overlap_check(base, top, memmap[i].base, memmap_top)) { + return true; + } + + // Count how many bytes from a real-RAM entry overlap our range + if (memmap[i].base < top && memmap_top > base) { + uint64_t overlap_bottom = memmap[i].base > base ? memmap[i].base : base; + uint64_t overlap_top = memmap_top < top ? memmap_top : top; + + uint64_t overlap_size = overlap_top - overlap_bottom; + overlap_remaining -= overlap_size; + + if (overlap_remaining == 0) { + return true; + } + } + } + + return false; +} + +void pmm_randomise_memory(void) { + print("pmm: Randomising memory contents..."); + + for (size_t i = 0; i < memmap_entries; i++) { + if (memmap[i].type != MEMMAP_USABLE) + continue; + +#if defined (BIOS) + // We're not going to randomise memory above 4GiB from protected mode, + // are we? + if (memmap[i].base >= 0x100000000) { + continue; + } +#endif + + uint8_t *ptr = (void *)(uintptr_t)memmap[i].base; + size_t len = memmap[i].length; + + for (size_t j = 0;;) { + uint32_t random = rand32(); + uint8_t *rnd_data = (void *)&random; + if (j >= len) + break; + ptr[j++] = rnd_data[0]; + if (j >= len) + break; + ptr[j++] = rnd_data[1]; + if (j >= len) + break; + ptr[j++] = rnd_data[2]; + if (j >= len) + break; + ptr[j++] = rnd_data[3]; + } + } + + print("\n"); +} diff --git a/limine/common/mm/pmm.h b/limine/common/mm/pmm.h new file mode 100644 index 0000000..e3bc404 --- /dev/null +++ b/limine/common/mm/pmm.h @@ -0,0 +1,79 @@ +#ifndef MM__PMM_H__ +#define MM__PMM_H__ + +#include +#include +#include + +struct memmap_entry { + uint64_t base; + uint64_t length; + uint32_t type; + uint32_t unused; +}; + +#define MEMMAP_USABLE 1 +#define MEMMAP_RESERVED 2 +#define MEMMAP_ACPI_RECLAIMABLE 3 +#define MEMMAP_ACPI_NVS 4 +#define MEMMAP_BAD_MEMORY 5 +#define MEMMAP_BOOTLOADER_RECLAIMABLE 0x1000 +#define MEMMAP_KERNEL_AND_MODULES 0x1001 +#define MEMMAP_FRAMEBUFFER 0x1002 +#define MEMMAP_RESERVED_MAPPED 0x1003 +#define MEMMAP_EFI_RECLAIMABLE 0x2000 + +struct meminfo { + size_t uppermem; + size_t lowermem; +}; + +struct meminfo mmap_get_info(size_t mmap_count, struct memmap_entry *mmap); + +#if defined (BIOS) +extern struct memmap_entry memmap[]; +extern size_t memmap_entries; +#endif + +#if defined (UEFI) +extern struct memmap_entry *memmap; +extern size_t memmap_entries; + +extern struct memmap_entry *untouched_memmap; +extern size_t untouched_memmap_entries; +#endif + +extern bool allocations_disallowed; + +void init_memmap(void); +struct memmap_entry *get_memmap(size_t *entries); +struct memmap_entry *get_raw_memmap(size_t *entry_count); +void print_memmap(struct memmap_entry *mm, size_t size); +uint64_t pmm_check_type(uint64_t addr); +bool memmap_alloc_range_in(struct memmap_entry *m, size_t *_count, + uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool do_panic, bool simulation, bool new_entry); +bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool panic, bool simulation, bool new_entry); +void pmm_randomise_memory(void); + +void *ext_mem_alloc_size_t(size_t count); +void *ext_mem_alloc(uint64_t count); +void *ext_mem_alloc_type(uint64_t count, uint32_t type); +void *ext_mem_alloc_type_aligned(uint64_t count, uint32_t type, size_t alignment); +void *ext_mem_alloc_type_aligned_mode(uint64_t count, uint32_t type, size_t alignment, bool allow_high_allocs); + +void *conv_mem_alloc(uint64_t count); + +void pmm_free_size_t(void *ptr, size_t length); +void pmm_free(void *ptr, uint64_t length); +void *pmm_realloc(void *old_ptr, uint64_t old_size, uint64_t new_size); + +#if defined (UEFI) +void pmm_release_uefi_mem(void); +#endif + +bool check_usable_memory(uint64_t base, uint64_t top); +void pmm_sanitise_entries(struct memmap_entry *m, size_t *_count, bool align_entries); + +extern bool pmm_sanitiser_keep_first_page; + +#endif diff --git a/limine/common/mm/pmm.s2.c b/limine/common/mm/pmm.s2.c new file mode 100644 index 0000000..6cc74cb --- /dev/null +++ b/limine/common/mm/pmm.s2.c @@ -0,0 +1,885 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined (UEFI) +# include +#endif + +#define PAGE_SIZE 4096 + +#if defined (BIOS) +extern symbol bss_end; +#endif + +bool allocations_disallowed = true; + +void *conv_mem_alloc(uint64_t count) { + static uint64_t base = 4096; + + if (allocations_disallowed) + panic(false, "Memory allocations disallowed"); + + count = ALIGN_UP(count, 4096); + + for (;;) { + if (base + count > 0x100000) + panic(false, "Conventional memory allocation failed"); + + if (memmap_alloc_range(base, count, MEMMAP_BOOTLOADER_RECLAIMABLE, MEMMAP_USABLE, false, false, false)) { + void *ret = (void *)(uintptr_t)base; + // Zero out allocated space + memset(ret, 0, count); + base += count; + + pmm_sanitise_entries(memmap, &memmap_entries, false); + + return ret; + } + + base += 4096; + } +} + +#if defined (BIOS) +#define memmap_max_entries ((size_t)512) + +struct memmap_entry memmap[memmap_max_entries]; +size_t memmap_entries = 0; +#endif + +#if defined (UEFI) +static size_t memmap_max_entries; + +struct memmap_entry *memmap; +size_t memmap_entries = 0; + +struct memmap_entry *untouched_memmap; +size_t untouched_memmap_entries = 0; +#endif + +static const char *memmap_type(uint32_t type) { + switch (type) { + case MEMMAP_USABLE: + return "Usable RAM"; + case MEMMAP_RESERVED: + return "Reserved"; + case MEMMAP_RESERVED_MAPPED: + return "Reserved (Mapped)"; + case MEMMAP_ACPI_RECLAIMABLE: + return "ACPI reclaimable"; + case MEMMAP_ACPI_NVS: + return "ACPI NVS"; + case MEMMAP_BAD_MEMORY: + return "Bad memory"; + case MEMMAP_FRAMEBUFFER: + return "Framebuffer"; + case MEMMAP_BOOTLOADER_RECLAIMABLE: + return "Bootloader reclaimable"; + case MEMMAP_KERNEL_AND_MODULES: + return "Kernel/Modules"; + case MEMMAP_EFI_RECLAIMABLE: + return "EFI reclaimable"; + default: + return "???"; + } +} + +void print_memmap(struct memmap_entry *mm, size_t size) { + for (size_t i = 0; i < size; i++) { + print("[%X -> %X] : %X <%s (%x)>\n", + mm[i].base, + mm[i].base + mm[i].length, + mm[i].length, + memmap_type(mm[i].type), mm[i].type); + } +} + +static bool align_entry(uint64_t *base, uint64_t *length) { + if (*length < PAGE_SIZE) + return false; + + uint64_t orig_base = *base; + + *base = ALIGN_UP(*base, PAGE_SIZE); + + *length -= (*base - orig_base); + *length = ALIGN_DOWN(*length, PAGE_SIZE); + + if (*length == 0) + return false; + + return true; +} + +#if defined (BIOS) +bool pmm_sanitiser_keep_first_page = false; +#else +bool pmm_sanitiser_keep_first_page = true; +#endif + +void pmm_sanitise_entries(struct memmap_entry *m, size_t *_count, bool align_entries) { + size_t count = *_count; + + for (size_t i = 0; i < count; i++) { + if (m[i].type != MEMMAP_USABLE) + continue; + + // Check if the entry overlaps other entries + for (size_t j = 0; j < count; j++) { + if (j == i) + continue; + + uint64_t base = m[i].base; + uint64_t length = m[i].length; + uint64_t top = base + length; + + uint64_t res_base = m[j].base; + uint64_t res_length = m[j].length; + uint64_t res_top = res_base + res_length; + + // Non-usable entry fully contains usable entry + if (res_base <= base && res_top >= top) { + m[i].base = top; + m[i].length = 0; + break; + } + + if ( (res_base >= base && res_base < top) + && (res_top >= base && res_top < top) ) { + // TODO actually handle splitting off usable chunks + panic(false, "A non-usable memory map entry is inside a usable section."); + } + + if (res_base >= base && res_base < top) { + top = res_base; + } + + if (res_top >= base && res_top < top) { + base = res_top; + } + + m[i].base = base; + m[i].length = top - base; + } + + if (!m[i].length + || (align_entries && !align_entry(&m[i].base, &m[i].length))) { + // Remove i from memmap + if (i < count - 1) { + m[i] = m[count - 1]; + } + count--; i--; + } + } + + // Remove 0 length usable entries and usable entries below 0x1000 + for (size_t i = 0; i < count; i++) { + if (m[i].type != MEMMAP_USABLE) + continue; + + if (!pmm_sanitiser_keep_first_page && m[i].base < 0x1000) { + uint64_t entry_top; + if (__builtin_add_overflow(m[i].base, m[i].length, &entry_top) || + entry_top <= 0x1000) { + goto del_mm1; + } + + m[i].length -= 0x1000 - m[i].base; + m[i].base = 0x1000; + } + + if (m[i].length == 0) { +del_mm1: + // Remove i from memmap + if (i < count - 1) { + m[i] = m[count - 1]; + } + count--; i--; + } + } + + // Sort the entries + for (size_t p = 0; p + 1 < count; p++) { + uint64_t min = m[p].base; + size_t min_index = p; + for (size_t i = p; i < count; i++) { + if (m[i].base < min) { + min = m[i].base; + min_index = i; + } + } + struct memmap_entry min_e = m[min_index]; + m[min_index] = m[p]; + m[p] = min_e; + } + + // Merge contiguous bootloader-reclaimable, reserved (mapped), usable entries + for (size_t i = 0; i + 1 < count; i++) { + if (m[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE + && m[i].type != MEMMAP_RESERVED_MAPPED + && m[i].type != MEMMAP_USABLE) + continue; + + if (m[i+1].type == m[i].type + && m[i+1].base == m[i].base + m[i].length) { + m[i].length += m[i+1].length; + + // Eradicate from memmap + for (size_t j = i + 2; j < count; j++) { + m[j - 1] = m[j]; + } + count--; + i--; + } + } + + *_count = count; +} + +#if defined (UEFI) +static void pmm_reclaim_uefi_mem(struct memmap_entry *m, size_t *_count, bool raw); +#endif + +struct memmap_entry *get_memmap(size_t *entries) { +#if defined (UEFI) + if (efi_boot_services_exited == false) { + panic(true, "get_memmap called whilst in boot services"); + } + + pmm_reclaim_uefi_mem(memmap, &memmap_entries, false); +#endif + + pmm_sanitise_entries(memmap, &memmap_entries, true); + + *entries = memmap_entries; + + allocations_disallowed = true; + + return memmap; +} + +#if defined (BIOS) +void init_memmap(void) { + for (size_t i = 0; i < e820_entries; i++) { + if (memmap_entries == memmap_max_entries) { + panic(false, "Memory map exhausted."); + } + + memmap[memmap_entries] = e820_map[i]; + + uint64_t top = memmap[memmap_entries].base + memmap[memmap_entries].length; + + if (memmap[memmap_entries].type == MEMMAP_USABLE) { + if (memmap[memmap_entries].base >= EBDA && memmap[memmap_entries].base < 0x100000) { + if (top <= 0x100000) + continue; + + memmap[memmap_entries].length -= 0x100000 - memmap[memmap_entries].base; + memmap[memmap_entries].base = 0x100000; + } + + if (top > EBDA && top <= 0x100000) { + memmap[memmap_entries].length -= top - EBDA; + } + } + + memmap_entries++; + } + + pmm_sanitise_entries(memmap, &memmap_entries, false); + + // Allocate bootloader itself + memmap_alloc_range(4096, + ALIGN_UP((uintptr_t)bss_end, 4096) - 4096, MEMMAP_BOOTLOADER_RECLAIMABLE, 0, true, false, false); + + pmm_sanitise_entries(memmap, &memmap_entries, false); + + allocations_disallowed = false; +} +#endif + +#if defined (UEFI) +static struct memmap_entry *recl; + +extern symbol __slide, __image_base, __image_end; + +void init_memmap(void) { + EFI_STATUS status; + + EFI_MEMORY_DESCRIPTOR tmp_mmap[1]; + efi_mmap_size = sizeof(tmp_mmap); + UINTN mmap_key = 0; + + gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); + + memmap_max_entries = (efi_mmap_size / efi_desc_size) + 512; + + efi_mmap_size += 4096; + + status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&efi_mmap); + if (status) { + goto fail; + } + + status = gBS->AllocatePool(EfiLoaderData, memmap_max_entries * sizeof(struct memmap_entry), (void **)&memmap); + if (status) { + gBS->FreePool(efi_mmap); + goto fail; + } + + status = gBS->AllocatePool(EfiLoaderData, memmap_max_entries * sizeof(struct memmap_entry), (void **)&untouched_memmap); + if (status) { + gBS->FreePool(efi_mmap); + gBS->FreePool(memmap); + goto fail; + } + + status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); + if (status) { + gBS->FreePool(efi_mmap); + gBS->FreePool(memmap); + gBS->FreePool(untouched_memmap); + goto fail; + } + + size_t entry_count = efi_mmap_size / efi_desc_size; + + for (size_t i = 0; i < entry_count; i++) { + EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size; + + if (entry->NumberOfPages == 0) { + continue; + } + + uint32_t our_type; + switch (entry->Type) { + case EfiReservedMemoryType: + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + case EfiUnusableMemory: + case EfiMemoryMappedIO: + case EfiMemoryMappedIOPortSpace: + case EfiPalCode: + default: + our_type = MEMMAP_RESERVED; break; + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + our_type = MEMMAP_EFI_RECLAIMABLE; break; + case EfiACPIReclaimMemory: + our_type = MEMMAP_ACPI_RECLAIMABLE; break; + case EfiACPIMemoryNVS: + our_type = MEMMAP_ACPI_NVS; break; + case EfiConventionalMemory: + our_type = MEMMAP_USABLE; break; + } + + uint64_t base = entry->PhysicalStart; + uint64_t length; + if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &length)) { + continue; + } + + memmap[memmap_entries].base = base; + memmap[memmap_entries].length = length; + memmap[memmap_entries].type = our_type; + + memmap_entries++; + } + + pmm_sanitise_entries(memmap, &memmap_entries, false); + + allocations_disallowed = false; + + // Let's leave 64MiB to the firmware below 4GiB + for (size_t i = 0; i < 64; i++) { + ext_mem_alloc_type(0x100000, MEMMAP_EFI_RECLAIMABLE); + } + + memcpy(untouched_memmap, memmap, memmap_entries * sizeof(struct memmap_entry)); + untouched_memmap_entries = memmap_entries; + + // Now own all the usable entries + for (size_t i = 0; i < untouched_memmap_entries; i++) { + if (untouched_memmap[i].type != MEMMAP_USABLE) + continue; + + EFI_PHYSICAL_ADDRESS base = untouched_memmap[i].base; + +#if defined (__i386__) + if (untouched_memmap[i].base + untouched_memmap[i].length > 0x100000000) { + continue; + } +#endif + + status = gBS->AllocatePages(AllocateAddress, EfiLoaderCode, + untouched_memmap[i].length / 4096, &base); + + if (status) { + for (size_t j = 0; j < untouched_memmap[i].length; j += 4096) { + base = untouched_memmap[i].base + j; + status = gBS->AllocatePages(AllocateAddress, EfiLoaderCode, 1, &base); + if (status) { + memmap_alloc_range(base, 4096, MEMMAP_EFI_RECLAIMABLE, MEMMAP_USABLE, true, false, false); + } + } + } + } + + memcpy(untouched_memmap, memmap, memmap_entries * sizeof(struct memmap_entry)); + untouched_memmap_entries = memmap_entries; + + // Allocate bootloader itself + size_t image_size = ALIGN_UP((uintptr_t)__image_end - (uintptr_t)__image_base, 4096); + + memmap_alloc_range((uintptr_t)__slide, (uintptr_t)image_size, + MEMMAP_BOOTLOADER_RECLAIMABLE, 0, true, false, true); + + pmm_sanitise_entries(memmap, &memmap_entries, false); + + recl = ext_mem_alloc(1024 * sizeof(struct memmap_entry)); + + return; + +fail: + panic(false, "pmm: Failure initialising memory map"); +} + +static void pmm_reclaim_uefi_mem(struct memmap_entry *m, size_t *_count, bool raw) { + size_t count = *_count; + + size_t recl_i = 0; + + for (size_t i = 0; i < count; i++) { + if (m[i].type == MEMMAP_EFI_RECLAIMABLE) { + recl[recl_i++] = m[i]; + } + } + + for (size_t ri = 0; ri < recl_i; ri++) { + struct memmap_entry *r = &recl[ri]; + + // Punch holes in our EFI reclaimable entry for every EFI area which is + // boot services or conventional that fits within + size_t efi_mmap_entry_count = efi_mmap_size / efi_desc_size; + for (size_t i = 0; i < efi_mmap_entry_count; i++) { + EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size; + + uint64_t base = r->base; + uint64_t top = base + r->length; + uint64_t efi_base = entry->PhysicalStart; + uint64_t efi_size; + if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &efi_size)) { + continue; // Skip malformed entry + } + + if (efi_base < base) { + if (efi_size <= base - efi_base) + continue; + efi_size -= base - efi_base; + efi_base = base; + } + + uint64_t efi_top = efi_base + efi_size; + + if (efi_top > top) { + if (efi_size <= efi_top - top) + continue; + efi_size -= efi_top - top; + efi_top = top; + } + + // Sanity check + if (!(efi_base >= base && efi_base < top + && efi_top > base && efi_top <= top)) + continue; + + uint32_t our_type; + switch (entry->Type) { + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + if (raw) { + our_type = MEMMAP_USABLE; + } else { + our_type = MEMMAP_BOOTLOADER_RECLAIMABLE; + } + break; + case EfiConventionalMemory: + our_type = MEMMAP_USABLE; break; + case EfiACPIReclaimMemory: + our_type = MEMMAP_ACPI_RECLAIMABLE; break; + case EfiACPIMemoryNVS: + our_type = MEMMAP_ACPI_NVS; break; + default: + our_type = MEMMAP_RESERVED; break; + } + + memmap_alloc_range_in(m, &count, efi_base, efi_size, our_type, 0, true, false, false); + } + } + + allocations_disallowed = true; + + pmm_sanitise_entries(m, &count, false); + + *_count = count; +} + +void pmm_release_uefi_mem(void) { + EFI_STATUS status; + + for (size_t i = 0; i < untouched_memmap_entries; i++) { + if (untouched_memmap[i].type != MEMMAP_USABLE + && untouched_memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE) { + continue; + } + + status = gBS->FreePages(untouched_memmap[i].base, untouched_memmap[i].length / 4096); + + if (status) { + panic(false, "pmm: FreePages failure (%X)", (uint64_t)status); + } + } + + allocations_disallowed = true; +} +#endif + +#if defined (BIOS) +struct memmap_entry *get_raw_memmap(size_t *entry_count) { + *entry_count = e820_entries; + return e820_map; +} +#endif + +#if defined (UEFI) +struct memmap_entry *get_raw_memmap(size_t *entry_count) { + if (efi_boot_services_exited == false) { + panic(true, "get_raw_memmap called whilst in boot services"); + } + + bool old_skfp = pmm_sanitiser_keep_first_page; + pmm_sanitiser_keep_first_page = true; + pmm_reclaim_uefi_mem(untouched_memmap, &untouched_memmap_entries, true); + pmm_sanitiser_keep_first_page = old_skfp; + + *entry_count = untouched_memmap_entries; + return untouched_memmap; +} +#endif + +void pmm_free_size_t(void *ptr, size_t length) { + pmm_free(ptr, length); +} + +void pmm_free(void *ptr, uint64_t count) { + count = ALIGN_UP(count, 4096); + if (allocations_disallowed) + panic(false, "Memory allocations disallowed"); + memmap_alloc_range((uintptr_t)ptr, count, MEMMAP_USABLE, 0, false, false, true); +} + +void *pmm_realloc(void *old_ptr, uint64_t old_size, uint64_t new_size) { + if (new_size == 0) { + if (old_ptr != NULL) { + pmm_free(old_ptr, old_size); + } + return NULL; + } + if (old_ptr == NULL) { + return ext_mem_alloc(new_size); + } + + void *new_ptr = ext_mem_alloc(new_size); + + memcpy(new_ptr, old_ptr, MIN(new_size, old_size)); + + pmm_free(old_ptr, old_size); + + return new_ptr; +} + +void *ext_mem_alloc_size_t(size_t count) { + return ext_mem_alloc(count); +} + +void *ext_mem_alloc(uint64_t count) { + return ext_mem_alloc_type(count, MEMMAP_BOOTLOADER_RECLAIMABLE); +} + +void *ext_mem_alloc_type(uint64_t count, uint32_t type) { + return ext_mem_alloc_type_aligned(count, type, 4096); +} + +void *ext_mem_alloc_type_aligned(uint64_t count, uint32_t type, size_t alignment) { + return ext_mem_alloc_type_aligned_mode(count, type, alignment, false); +} + +// Allocate memory top down. +void *ext_mem_alloc_type_aligned_mode(uint64_t count, uint32_t type, size_t alignment, bool allow_high_allocs) { +#if !defined (__x86_64__) && !defined (__i386__) + (void)allow_high_allocs; +#endif + + count = ALIGN_UP(count, alignment); + + if (allocations_disallowed) + panic(false, "Memory allocations disallowed"); + +#if defined(__x86_64__) || defined(__i386__) + // Try below 4GiB first to avoid relying on firmware identity-mapping + // memory above 4GiB (some buggy UEFI implementations don't). + uint64_t limit = 0x100000000; + +again: +#else + uint64_t limit = UINT64_MAX; +#endif + + for (int i = memmap_entries - 1; i >= 0; i--) { + if (memmap[i].type != 1) + continue; + + int64_t entry_base = (int64_t)(memmap[i].base); + int64_t entry_top = (int64_t)(memmap[i].base + memmap[i].length); + + if ((uint64_t)entry_top > limit) { + entry_top = (int64_t)limit; + if (entry_base >= entry_top) + continue; + } + + int64_t alloc_base = ALIGN_DOWN(entry_top - (int64_t)count, alignment); + + // This entry is too small for us. + if (alloc_base < entry_base) + continue; + + // We now reserve the range we need. + int64_t aligned_length = entry_top - alloc_base; + memmap_alloc_range((uint64_t)alloc_base, (uint64_t)aligned_length, type, MEMMAP_USABLE, true, false, false); + + void *ret; + +#if defined (__i386__) + if (!allow_high_allocs) { +#endif + ret = (void *)(size_t)alloc_base; + + // Zero out allocated space + memset(ret, 0, count); +#if defined (__i386__) + } else { + static uint64_t above64_ret; + above64_ret = alloc_base; + ret = &above64_ret; + } +#endif + + pmm_sanitise_entries(memmap, &memmap_entries, false); + + return ret; + } + +#if defined(__x86_64__) || defined(__i386__) + if (allow_high_allocs && limit < UINT64_MAX) { + limit = UINT64_MAX; + goto again; + } +#endif + + panic(false, "High memory allocator: Out of memory"); +} + +/// Compute and returns the amount of upper and lower memory till +/// the first hole. +struct meminfo mmap_get_info(size_t mmap_count, struct memmap_entry *mmap) { + struct meminfo info = {0}; + + // Find contiguous usable memory from address 0 (lower) and 1 MiB (upper) + // by iteratively extending a frontier. Handles unsorted entries. + uint64_t lower_end = 0; + uint64_t upper_end = 0x100000; + bool progress; + + do { + progress = false; + for (size_t i = 0; i < mmap_count; i++) { + if (mmap[i].type != MEMMAP_USABLE) + continue; + uint64_t base = mmap[i].base; + uint64_t top = base + mmap[i].length; + if (base <= lower_end && top > lower_end) { + lower_end = top; + progress = true; + } + if (base <= upper_end && top > upper_end) { + upper_end = top; + progress = true; + } + } + } while (progress); + + if (lower_end > 0x100000) + lower_end = 0x100000; + + info.lowermem = lower_end; + info.uppermem = upper_end - 0x100000; + + return info; +} + +static bool pmm_new_entry(struct memmap_entry *m, size_t *_count, + uint64_t base, uint64_t length, uint32_t type) { + size_t count = *_count; + + uint64_t top; + if (__builtin_add_overflow(base, length, &top)) { + panic(false, "pmm: Integer overflow in memory range calculation"); + } + + // Handle overlapping new entries. + for (size_t i = 0; i < count; i++) { + uint64_t entry_base = m[i].base; + uint64_t entry_top; + if (__builtin_add_overflow(m[i].base, m[i].length, &entry_top)) { + continue; // Skip malformed entries + } + + // Full overlap + if (base <= entry_base && top >= entry_top) { + // Remove overlapped entry + if (i < count - 1) { + m[i] = m[count - 1]; + } + count--; + i--; + continue; + } + + // Partial overlap (bottom) + if (base <= entry_base && top < entry_top && top > entry_base) { + // Entry gets bottom shaved off + m[i].base += top - entry_base; + m[i].length -= top - entry_base; + continue; + } + + // Partial overlap (top) + if (base > entry_base && base < entry_top && top >= entry_top) { + // Entry gets top shaved off + m[i].length -= entry_top - base; + continue; + } + + // Nested (pain) + if (base > entry_base && top < entry_top) { + // Entry gets top shaved off first + m[i].length -= entry_top - base; + + // Now we need to create a new entry + if (count >= memmap_max_entries) + panic(false, "Memory map exhausted."); + + struct memmap_entry *new_entry = &m[count++]; + + new_entry->type = m[i].type; + new_entry->base = top; + new_entry->length = entry_top - top; + + continue; + } + } + + if (count >= memmap_max_entries) + panic(false, "Memory map exhausted."); + + struct memmap_entry *target = &m[count++]; + + target->type = type; + target->base = base; + target->length = length; + + *_count = count; + return true; +} + +uint64_t pmm_check_type(uint64_t addr) { + for (size_t i = 0; i < memmap_entries; i++) { + uint64_t entry_base = memmap[i].base; + uint64_t entry_top = memmap[i].base + memmap[i].length; + + if (addr >= entry_base && addr < entry_top) { + return memmap[i].type; + } + } + + return (uint64_t)-1; +} + +bool memmap_alloc_range_in(struct memmap_entry *m, size_t *_count, + uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool do_panic, bool simulation, bool new_entry) { + size_t count = *_count; + + if (length == 0) + return true; + + if (simulation && new_entry) { + return true; + } + + uint64_t top; + if (__builtin_add_overflow(base, length, &top)) { + if (do_panic) + panic(false, "Memory allocation overflow."); + return false; + } + + for (size_t i = 0; i < count; i++) { + if (overlay_type != 0 && m[i].type != overlay_type) + continue; + + uint64_t entry_base = m[i].base; + uint64_t entry_top; + if (__builtin_add_overflow(m[i].base, m[i].length, &entry_top)) + continue; + + if (base >= entry_base && base < entry_top && top <= entry_top) { + if (simulation) + return true; + + if (pmm_new_entry(m, &count, base, length, type) == true) { + goto success; + } + } + } + + if (!new_entry && do_panic) + panic(false, "Memory allocation failure."); + + if (!new_entry) { + return false; + } + + if (pmm_new_entry(m, &count, base, length, type) == false) { + return false; + } + +success: + pmm_sanitise_entries(m, &count, false); + *_count = count; + return true; +} + +bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool do_panic, bool simulation, bool new_entry) { + return memmap_alloc_range_in(memmap, &memmap_entries, base, length, type, overlay_type, do_panic, simulation, new_entry); +} diff --git a/limine/common/mm/vmm.c b/limine/common/mm/vmm.c new file mode 100644 index 0000000..106f47e --- /dev/null +++ b/limine/common/mm/vmm.c @@ -0,0 +1,555 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PT_SIZE ((uint64_t)0x1000) + +typedef uint64_t pt_entry_t; + +// Maps level indexes to the page size for that level. +_Static_assert(VMM_MAX_LEVEL <= 5, "6-level paging not supported"); +static uint64_t page_sizes[5] = { + 0x1000, + 0x200000, + 0x40000000, + 0x8000000000, + 0x1000000000000, +}; + +static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level, + uint64_t virt, enum page_size desired_sz, + size_t level_idx, size_t entry); + +void map_pages(pagemap_t pagemap, uint64_t virt, uint64_t phys, uint64_t flags, uint64_t count) { + if (virt % 0x1000 != 0 || phys % 0x1000 != 0 || count % 0x1000 != 0) { + panic(true, "vmm: Misaligned call to map_pages()"); + } + + for (uint64_t i = 0; i < count; ) { + if (((phys + i) & (0x40000000 - 1)) == 0 && ((virt + i) & (0x40000000 - 1)) == 0 && count - i >= 0x40000000) { + map_page(pagemap, virt + i, phys + i, flags, Size1GiB); + i += 0x40000000; + continue; + } + if (((phys + i) & (0x200000 - 1)) == 0 && ((virt + i) & (0x200000 - 1)) == 0 && count - i >= 0x200000) { + map_page(pagemap, virt + i, phys + i, flags, Size2MiB); + i += 0x200000; + continue; + } + map_page(pagemap, virt + i, phys + i, flags, Size4KiB); + i += 0x1000; + } +} + +#if defined (__x86_64__) || defined (__i386__) + +#define PT_FLAG_VALID ((uint64_t)1 << 0) +#define PT_FLAG_WRITE ((uint64_t)1 << 1) +#define PT_FLAG_USER ((uint64_t)1 << 2) +#define PT_FLAG_LARGE ((uint64_t)1 << 7) +#define PT_FLAG_NX ((uint64_t)1 << 63) +#define PT_PADDR_MASK ((uint64_t)0x0000FFFFFFFFF000) + +#define PT_TABLE_FLAGS (PT_FLAG_VALID | PT_FLAG_WRITE | PT_FLAG_USER) +#define PT_IS_TABLE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_LARGE)) == PT_FLAG_VALID) +#define PT_IS_LARGE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_LARGE)) == (PT_FLAG_VALID | PT_FLAG_LARGE)) +#define PT_TO_VMM_FLAGS(x) ((x) & (PT_FLAG_WRITE | PT_FLAG_NX | VMM_FLAG_FB)) + +#define pte_new(addr, flags) ((pt_entry_t)(addr) | (flags)) +#define pte_addr(pte) ((pte) & PT_PADDR_MASK) + +pagemap_t new_pagemap(int paging_mode) { + pagemap_t pagemap; + pagemap.levels = paging_mode == PAGING_MODE_X86_64_5LVL ? 5 : 4; + pagemap.top_level = ext_mem_alloc(PT_SIZE); + return pagemap; +} + +static bool is_1gib_page_supported(void) { + // Cache the cpuid result :^) + static bool CACHE_INIT = false; + static bool CACHE = false; + + if (!CACHE_INIT) { + // Check if 1GiB pages are supported: + uint32_t eax, ebx, ecx, edx; + + CACHE = cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) && ((edx & 1 << 26) == 1 << 26); + CACHE_INIT = true; + + printv("paging: 1GiB pages are %s!\n", CACHE ? "supported" : "not supported"); + } + + return CACHE; +} + +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size pg_size) { + // Calculate the indices in the various tables using the virtual address + size_t pml5_entry = (virt_addr & ((uint64_t)0x1ff << 48)) >> 48; + size_t pml4_entry = (virt_addr & ((uint64_t)0x1ff << 39)) >> 39; + size_t pml3_entry = (virt_addr & ((uint64_t)0x1ff << 30)) >> 30; + size_t pml2_entry = (virt_addr & ((uint64_t)0x1ff << 21)) >> 21; + size_t pml1_entry = (virt_addr & ((uint64_t)0x1ff << 12)) >> 12; + + pt_entry_t *pml5, *pml4, *pml3, *pml2, *pml1; + + static bool pat_supported = false, pat_supported_got = false; + if (!pat_supported_got) { + uint32_t eax, ebx, ecx, edx; + if (cpuid(1, 0, &eax, &ebx, &ecx, &edx) && (edx & (1 << 16))) { + pat_supported = true; + } + pat_supported_got = true; + } + + flags |= PT_FLAG_VALID; // Always present + if ((flags & VMM_FLAG_FB) && !pat_supported) { + flags &= ~(uint64_t)VMM_FLAG_FB; + } + + // Paging levels + switch (pagemap.levels) { + case 5: + pml5 = pagemap.top_level; + goto level5; + case 4: + pml4 = pagemap.top_level; + goto level4; + default: + __builtin_unreachable(); + } + +level5: + pml4 = get_next_level(pagemap, pml5, virt_addr, pg_size, 4, pml5_entry); +level4: + pml3 = get_next_level(pagemap, pml4, virt_addr, pg_size, 3, pml4_entry); + + if (pg_size == Size1GiB) { + // Check if 1GiB pages are available. + if (is_1gib_page_supported()) { + pml3[pml3_entry] = (pt_entry_t)(phys_addr | flags | PT_FLAG_LARGE); + } else { + // If 1GiB pages are not supported then emulate it by splitting them into + // 2MiB pages. + for (uint64_t i = 0; i < 0x40000000; i += 0x200000) { + map_page(pagemap, virt_addr + i, phys_addr + i, flags, Size2MiB); + } + } + + return; + } + + pml2 = get_next_level(pagemap, pml3, virt_addr, pg_size, 2, pml3_entry); + + if (pg_size == Size2MiB) { + pml2[pml2_entry] = (pt_entry_t)(phys_addr | flags | PT_FLAG_LARGE); + return; + } + + pml1 = get_next_level(pagemap, pml2, virt_addr, pg_size, 1, pml2_entry); + + // PML1 wants PAT bit at 7 instead of 12 + if (flags & ((uint64_t)1 << 12)) { + flags &= ~((uint64_t)1 << 12); + flags |= ((uint64_t)1 << 7); + } + + pml1[pml1_entry] = (pt_entry_t)(phys_addr | flags); +} + +#elif defined (__aarch64__) + +// Here we operate under the assumption that 4K pages are supported by the CPU. +// This appears to be guaranteed by UEFI, as section 2.3.6 "AArch64 Platforms" +// states that the primary processor core configuration includes 4K translation +// granules (TCR_EL1.TG0 = 0). +// Support for 4K pages also implies 2M, 1G and 512G blocks. + +// Sanity check that 4K pages are supported. +void vmm_assert_4k_pages(void) { + uint64_t aa64mmfr0; + asm volatile ("mrs %0, id_aa64mmfr0_el1" : "=r"(aa64mmfr0)); + + if (((aa64mmfr0 >> 28) & 0b1111) == 0b1111) { + panic(false, "vmm: CPU does not support 4K pages, please make a bug report about this."); + } +} + +#define PT_FLAG_VALID ((uint64_t)1 << 0) +#define PT_FLAG_TABLE ((uint64_t)1 << 1) +#define PT_FLAG_4K_PAGE ((uint64_t)1 << 1) +#define PT_FLAG_BLOCK ((uint64_t)0 << 1) +#define PT_FLAG_USER ((uint64_t)1 << 6) +#define PT_FLAG_READONLY ((uint64_t)1 << 7) +#define PT_FLAG_INNER_SH ((uint64_t)3 << 8) +#define PT_FLAG_ACCESS ((uint64_t)1 << 10) +#define PT_FLAG_XN ((uint64_t)1 << 54) +#define PT_FLAG_WB ((uint64_t)0 << 2) +#define PT_FLAG_FB ((uint64_t)1 << 2) +#define PT_PADDR_MASK ((uint64_t)0x0000FFFFFFFFF000) + +#define PT_TABLE_FLAGS (PT_FLAG_VALID | PT_FLAG_TABLE) + +#define PT_IS_TABLE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_TABLE)) == (PT_FLAG_VALID | PT_FLAG_TABLE)) +#define PT_IS_LARGE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_TABLE)) == PT_FLAG_VALID) +#define PT_TO_VMM_FLAGS(x) (pt_to_vmm_flags_internal(x)) + +#define pte_new(addr, flags) ((pt_entry_t)(addr) | (flags)) +#define pte_addr(pte) ((pte) & PT_PADDR_MASK) + +static uint64_t pt_to_vmm_flags_internal(pt_entry_t entry) { + uint64_t flags = 0; + + if (!(entry & PT_FLAG_READONLY)) + flags |= VMM_FLAG_WRITE; + if (entry & PT_FLAG_XN) + flags |= VMM_FLAG_NOEXEC; + if (entry & PT_FLAG_FB) + flags |= VMM_FLAG_FB; + + return flags; +} + +pagemap_t new_pagemap(int paging_mode) { + pagemap_t pagemap; + pagemap.levels = paging_mode == PAGING_MODE_AARCH64_5LVL ? 5 : 4; + pagemap.top_level[0] = ext_mem_alloc(PT_SIZE); + pagemap.top_level[1] = ext_mem_alloc(PT_SIZE); + return pagemap; +} + +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size pg_size) { + // Calculate the indices in the various tables using the virtual address + size_t pml5_entry = (virt_addr & ((uint64_t)0xf << 48)) >> 48; + size_t pml4_entry = (virt_addr & ((uint64_t)0x1ff << 39)) >> 39; + size_t pml3_entry = (virt_addr & ((uint64_t)0x1ff << 30)) >> 30; + size_t pml2_entry = (virt_addr & ((uint64_t)0x1ff << 21)) >> 21; + size_t pml1_entry = (virt_addr & ((uint64_t)0x1ff << 12)) >> 12; + + pt_entry_t *pml5, *pml4, *pml3, *pml2, *pml1; + + bool is_higher_half = virt_addr & ((uint64_t)1 << 63); + + uint64_t real_flags = PT_FLAG_VALID | PT_FLAG_INNER_SH | PT_FLAG_ACCESS | PT_FLAG_WB; + if (!(flags & VMM_FLAG_WRITE)) + real_flags |= PT_FLAG_READONLY; + if (flags & VMM_FLAG_NOEXEC) + real_flags |= PT_FLAG_XN; + if (flags & VMM_FLAG_FB) + real_flags |= PT_FLAG_FB; + + // Paging levels + switch (pagemap.levels) { + case 5: + pml5 = pagemap.top_level[is_higher_half]; + goto level5; + case 4: + pml4 = pagemap.top_level[is_higher_half]; + goto level4; + default: + __builtin_unreachable(); + } + +level5: + pml4 = get_next_level(pagemap, pml5, virt_addr, pg_size, 4, pml5_entry); +level4: + pml3 = get_next_level(pagemap, pml4, virt_addr, pg_size, 3, pml4_entry); + + if (pg_size == Size1GiB) { + pml3[pml3_entry] = (pt_entry_t)(phys_addr | real_flags | PT_FLAG_BLOCK); + return; + } + + pml2 = get_next_level(pagemap, pml3, virt_addr, pg_size, 2, pml3_entry); + + if (pg_size == Size2MiB) { + pml2[pml2_entry] = (pt_entry_t)(phys_addr | real_flags | PT_FLAG_BLOCK); + return; + } + + pml1 = get_next_level(pagemap, pml2, virt_addr, pg_size, 1, pml2_entry); + + pml1[pml1_entry] = (pt_entry_t)(phys_addr | real_flags | PT_FLAG_4K_PAGE); +} + +#elif defined (__riscv) + +#define PT_FLAG_VALID ((uint64_t)1 << 0) +#define PT_FLAG_READ ((uint64_t)1 << 1) +#define PT_FLAG_WRITE ((uint64_t)1 << 2) +#define PT_FLAG_EXEC ((uint64_t)1 << 3) +#define PT_FLAG_USER ((uint64_t)1 << 4) +#define PT_FLAG_ACCESSED ((uint64_t)1 << 6) +#define PT_FLAG_DIRTY ((uint64_t)1 << 7) +#define PT_FLAG_PBMT_NC ((uint64_t)1 << 62) +#define PT_PADDR_MASK ((uint64_t)0x003ffffffffffc00) + +#define PT_FLAG_RWX (PT_FLAG_READ | PT_FLAG_WRITE | PT_FLAG_EXEC) + +#define PT_TABLE_FLAGS PT_FLAG_VALID +#define PT_IS_TABLE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_RWX)) == PT_FLAG_VALID) +#define PT_IS_LARGE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_RWX)) > PT_FLAG_VALID) +#define PT_TO_VMM_FLAGS(x) (pt_to_vmm_flags_internal(x)) + +#define pte_new(addr, flags) (((pt_entry_t)(addr) >> 2) | (flags)) +#define pte_addr(pte) (((pte) & PT_PADDR_MASK) << 2) + +static uint64_t pt_to_vmm_flags_internal(pt_entry_t entry) { + uint64_t flags = 0; + + if (entry & PT_FLAG_WRITE) + flags |= VMM_FLAG_WRITE; + if (!(entry & PT_FLAG_EXEC)) + flags |= VMM_FLAG_NOEXEC; + if (entry & PT_FLAG_PBMT_NC) + flags |= VMM_FLAG_FB; + + return flags; +} + +uint64_t paging_mode_higher_half(int paging_mode) { + switch (paging_mode) { + case PAGING_MODE_RISCV_SV39: + return 0xffffffc000000000; + case PAGING_MODE_RISCV_SV48: + return 0xffff800000000000; + case PAGING_MODE_RISCV_SV57: + return 0xff00000000000000; + default: + panic(false, "paging_mode_higher_half: invalid mode"); + } +} + +int paging_mode_va_bits(int paging_mode) { + switch (paging_mode) { + case PAGING_MODE_RISCV_SV39: + return 39; + case PAGING_MODE_RISCV_SV48: + return 48; + case PAGING_MODE_RISCV_SV57: + return 57; + default: + panic(false, "paging_mode_va_bits: invalid mode"); + } +} + +int vmm_max_paging_mode(void) +{ + if (bsp_hart == NULL || !(bsp_hart->flags & RISCV_HART_HAS_MMU)) { + panic(false, "vmm: BSP hart does not advertise MMU support"); + } + + return PAGING_MODE_RISCV_SV39 + bsp_hart->mmu_type; +} + +static pt_entry_t pbmt_nc = 0; + +pagemap_t new_pagemap(int paging_mode) { + pagemap_t pagemap; + pagemap.paging_mode = paging_mode; + pagemap.max_page_size = paging_mode - 6; + pagemap.top_level = ext_mem_alloc(PT_SIZE); + + if (riscv_check_isa_extension("svpbmt", NULL, NULL)) { + printv("riscv: Svpbmt extension is supported.\n"); + pbmt_nc = PT_FLAG_PBMT_NC; + } + + return pagemap; +} + +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size) { + // Truncate the requested page size to the maximum supported. + if (page_size > pagemap.max_page_size) + page_size = pagemap.max_page_size; + + // Convert VMM_FLAG_* into PT_FLAG_*. + // Set the ACCESSED and DIRTY flags to avoid faults. + pt_entry_t ptflags = PT_FLAG_VALID | PT_FLAG_READ | PT_FLAG_ACCESSED | PT_FLAG_DIRTY; + if (flags & VMM_FLAG_WRITE) + ptflags |= PT_FLAG_WRITE; + if (!(flags & VMM_FLAG_NOEXEC)) + ptflags |= PT_FLAG_EXEC; + if (flags & VMM_FLAG_FB) + ptflags |= pbmt_nc; + + // Start at the highest level. + // The values of `enum page_size` map to the level index at which that size is mapped. + int level = pagemap.max_page_size; + pt_entry_t *table = pagemap.top_level; + for (;;) { + int index = (virt_addr >> (12 + 9 * level)) & 0x1ff; + + // Stop when we reach the level for the requested page size. + if (level == (int)page_size) { + table[index] = pte_new(phys_addr, ptflags); + break; + } + + table = get_next_level(pagemap, table, virt_addr, page_size, level, index); + level--; + } +} + +#elif defined (__loongarch64) + +#define INVALID_PAGE 0 + +#define PT_FLAG_VALID ((uint64_t)1 << 0) +#define PT_FLAG_DIRTY ((uint64_t)1 << 1) +#define PT_FLAG_MAT_CC ((uint64_t)1 << 4) +#define PT_FLAG_MAT_WUC ((uint64_t)1 << 5) +#define PT_FLAG_GLOBAL ((uint64_t)1 << 6) +#define PT_FLAG_HUGE ((uint64_t)1 << 6) +#define PT_FLAG_WRITE ((uint64_t)1 << 8) +#define PT_FLAG_HGLOBAL ((uint64_t)1 << 12) +#define PT_FLAG_NX ((uint64_t)1 << 62) +#define PT_PADDR_MASK ((uint64_t)0x0000FFFFFFFFF000) +#define PT_PADDR_HMASK ((uint64_t)0x0000FFFFFF000000) + +#define PT_TABLE_FLAGS 0 +#define PT_IS_TABLE(x) ((level_idx > 0) && (((x) & PT_FLAG_VALID) == 0) && ((x) != INVALID_PAGE)) +#define PT_IS_LARGE(x) (((x) & (PT_FLAG_HGLOBAL | PT_FLAG_HUGE)) == (PT_FLAG_HGLOBAL | PT_FLAG_HUGE)) +#define PT_TO_VMM_FLAGS(x) (pt_to_vmm_flags_internal(x)) + +#define pte_new(addr, flags) (pt_entry_t)((addr) | (flags)) + +static inline uint64_t pte_addr(uint64_t pte) { + if (PT_IS_LARGE(pte)) { + return pte & PT_PADDR_HMASK; + } + return pte & PT_PADDR_MASK; +} + +static uint64_t pt_to_vmm_flags_internal(pt_entry_t entry) { + uint64_t flags = 0; + + if (entry & PT_FLAG_WRITE) + flags |= VMM_FLAG_WRITE; + if (entry & PT_FLAG_NX) + flags |= VMM_FLAG_NOEXEC; + if (entry & PT_FLAG_MAT_WUC) + flags |= VMM_FLAG_FB; + + return flags; +} + +pagemap_t new_pagemap(int paging_mode) { + (void)paging_mode; + + uint32_t cpucfg_val; + + asm volatile ( + "cpucfg %0, %1" + : "=r"(cpucfg_val) + : "r"(1) + ); + + uint32_t valen = ((cpucfg_val >> 12) & 0xff) + 1; + if (valen < 48) { + panic(true, "vmm: VALEN values < 48 not currently supported"); + } + + (void)paging_mode; + pagemap_t pagemap; + pagemap.pgd[0] = ext_mem_alloc(PT_SIZE); + pagemap.pgd[1] = ext_mem_alloc(PT_SIZE); + return pagemap; +} + +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size pg_size) { + size_t pml4_entry = (virt_addr & ((uint64_t)0x1ff << 39)) >> 39; + size_t pml3_entry = (virt_addr & ((uint64_t)0x1ff << 30)) >> 30; + size_t pml2_entry = (virt_addr & ((uint64_t)0x1ff << 21)) >> 21; + size_t pml1_entry = (virt_addr & ((uint64_t)0x1ff << 12)) >> 12; + + pt_entry_t *pml4, *pml3, *pml2, *pml1; + + bool is_higher_half = virt_addr & ((uint64_t)1 << 63); + + uint64_t real_flags = PT_FLAG_VALID | PT_FLAG_GLOBAL; + if (flags & VMM_FLAG_WRITE) + real_flags |= PT_FLAG_DIRTY | PT_FLAG_WRITE; + if (flags & VMM_FLAG_NOEXEC) + real_flags |= PT_FLAG_NX; + if (flags & VMM_FLAG_FB) + real_flags |= PT_FLAG_MAT_WUC; + else + real_flags |= PT_FLAG_MAT_CC; + + pml4 = pagemap.pgd[is_higher_half]; + + pml3 = get_next_level(pagemap, pml4, virt_addr, pg_size, 3, pml4_entry); + + if (pg_size == Size1GiB) { + pml3[pml3_entry] = pte_new(phys_addr, real_flags | PT_FLAG_HGLOBAL | PT_FLAG_HUGE); + return; + } + + pml2 = get_next_level(pagemap, pml3, virt_addr, pg_size, 2, pml3_entry); + + if (pg_size == Size2MiB) { + pml2[pml2_entry] = pte_new(phys_addr, real_flags | PT_FLAG_HGLOBAL | PT_FLAG_HUGE); + return; + } + + pml1 = get_next_level(pagemap, pml2, virt_addr, pg_size, 1, pml2_entry); + + pml1[pml1_entry] = pte_new(phys_addr, real_flags); +} + +#else +#error Unknown architecture +#endif + +static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level, + uint64_t virt, enum page_size desired_sz, + size_t level_idx, size_t entry) { + pt_entry_t *ret; + + if (PT_IS_TABLE(current_level[entry])) { + ret = (pt_entry_t *)(size_t)pte_addr(current_level[entry]); + } else { + if (PT_IS_LARGE(current_level[entry])) { + // We are replacing an existing large page with a smaller page. + // Split the previous mapping into mappings of the newly requested size + // before performing the requested map operation. + + + if ((level_idx >= VMM_MAX_LEVEL) || (level_idx == 0)) + panic(false, "Unexpected level in get_next_level"); + if (desired_sz >= VMM_MAX_LEVEL) + panic(false, "Unexpected page size in get_next_level"); + + uint64_t old_page_size = page_sizes[level_idx]; + uint64_t new_page_size = page_sizes[desired_sz]; + + // Save all the information from the old entry at this level + uint64_t old_flags = PT_TO_VMM_FLAGS(current_level[entry]); + uint64_t old_phys = pte_addr(current_level[entry]); + uint64_t old_virt = virt & ~(old_page_size - 1); + + if (old_phys & (old_page_size - 1)) + panic(false, "Unexpected page table entry address in get_next_level"); + + // Allocate a table for the next level + ret = ext_mem_alloc(PT_SIZE); + current_level[entry] = pte_new((size_t)ret, PT_TABLE_FLAGS); + + // Recreate the old mapping with smaller pages + for (uint64_t i = 0; i < old_page_size; i += new_page_size) { + map_page(pagemap, old_virt + i, old_phys + i, old_flags, desired_sz); + } + } else { + // Allocate a table for the next level + ret = ext_mem_alloc(PT_SIZE); + current_level[entry] = pte_new((size_t)ret, PT_TABLE_FLAGS); + } + } + + return ret; +} diff --git a/limine/common/mm/vmm.h b/limine/common/mm/vmm.h new file mode 100644 index 0000000..01cf7e6 --- /dev/null +++ b/limine/common/mm/vmm.h @@ -0,0 +1,166 @@ +#ifndef MM__VMM_H__ +#define MM__VMM_H__ + +#include +#include + +#if defined (__x86_64__) || defined (__i386__) + +#define VMM_FLAG_WRITE ((uint64_t)1 << 1) +#define VMM_FLAG_NOEXEC ((uint64_t)1 << 63) +#define VMM_FLAG_FB (((uint64_t)1 << 3) | ((uint64_t)1 << 12)) + +#define VMM_MAX_LEVEL 3 + +#define PAGING_MODE_X86_64_4LVL 0 +#define PAGING_MODE_X86_64_5LVL 1 + +#define PAGING_MODE_MIN PAGING_MODE_X86_64_4LVL +#define PAGING_MODE_MAX PAGING_MODE_X86_64_5LVL + +#define paging_mode_va_bits(mode) ((mode) ? 57 : 48) + +static inline uint64_t paging_mode_higher_half(int paging_mode) { + if (paging_mode == PAGING_MODE_X86_64_5LVL) { + return 0xff00000000000000; + } else { + return 0xffff800000000000; + } +} + +typedef struct { + int levels; + void *top_level; +} pagemap_t; + +enum page_size { + Size4KiB, + Size2MiB, + Size1GiB +}; + +pagemap_t new_pagemap(int lv); +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size); + +#elif defined (__aarch64__) + +// We use fake flags here because these don't properly map onto the +// aarch64 flags. +#define VMM_FLAG_WRITE ((uint64_t)1 << 0) +#define VMM_FLAG_NOEXEC ((uint64_t)1 << 1) +#define VMM_FLAG_FB ((uint64_t)1 << 2) + +#define VMM_MAX_LEVEL 3 + +#define PAGING_MODE_AARCH64_4LVL 0 +#define PAGING_MODE_AARCH64_5LVL 1 + +#define PAGING_MODE_MIN PAGING_MODE_AARCH64_4LVL +#define PAGING_MODE_MAX PAGING_MODE_AARCH64_5LVL + +#define paging_mode_va_bits(mode) ((mode) ? 53 : 49) + +static inline uint64_t paging_mode_higher_half(int paging_mode) { + if (paging_mode == PAGING_MODE_AARCH64_5LVL) { + return 0xffe0000000000000; + } else { + return 0xffff000000000000; + } +} + +typedef struct { + int levels; + void *top_level[2]; +} pagemap_t; + +enum page_size { + Size4KiB, + Size2MiB, + Size1GiB +}; + +void vmm_assert_4k_pages(void); +pagemap_t new_pagemap(int lv); +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size); + +#elif defined (__riscv) + +// We use fake flags here because these don't properly map onto the +// RISC-V flags. +#define VMM_FLAG_WRITE ((uint64_t)1 << 0) +#define VMM_FLAG_NOEXEC ((uint64_t)1 << 1) +#define VMM_FLAG_FB ((uint64_t)1 << 2) + +#define VMM_MAX_LEVEL 5 + +#define PAGING_MODE_RISCV_SV39 8 +#define PAGING_MODE_RISCV_SV48 9 +#define PAGING_MODE_RISCV_SV57 10 + +#define PAGING_MODE_MIN PAGING_MODE_RISCV_SV39 +#define PAGING_MODE_MAX PAGING_MODE_RISCV_SV57 + +int paging_mode_va_bits(int paging_mode); + +enum page_size { + Size4KiB, + Size2MiB, + Size1GiB, + Size512GiB, + Size256TiB +}; + +typedef struct { + enum page_size max_page_size; + int paging_mode; + void *top_level; +} pagemap_t; + +uint64_t paging_mode_higher_half(int paging_mode); +int vmm_max_paging_mode(void); +pagemap_t new_pagemap(int paging_mode); +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size); + +#elif defined (__loongarch64) + +#define paging_mode_va_bits(mode) 48 + +static inline uint64_t paging_mode_higher_half(int paging_mode) { + (void)paging_mode; + return 0xffff800000000000; +} + +// We use fake flags here because these don't properly map onto the +// LoongArch flags. +#define VMM_FLAG_WRITE ((uint64_t)1 << 0) +#define VMM_FLAG_NOEXEC ((uint64_t)1 << 1) +#define VMM_FLAG_FB ((uint64_t)1 << 2) + +#define VMM_MAX_LEVEL 3 + +#define PAGING_MODE_LOONGARCH64_4LVL 0 + +#define PAGING_MODE_MIN PAGING_MODE_LOONGARCH64_4LVL +#define PAGING_MODE_MAX PAGING_MODE_LOONGARCH64_4LVL + +enum page_size { + Size4KiB, + Size2MiB, + Size1GiB +}; + +typedef struct { + void *pgd[2]; +} pagemap_t; + +pagemap_t new_pagemap(int paging_mode); +void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size); + +#else +#error Unknown architecture +#endif + +int vmm_max_paging_mode(void); +void map_pages(pagemap_t pagemap, uint64_t virt, uint64_t phys, uint64_t flags, uint64_t count); + +#endif diff --git a/limine/common/protos/chainload.c b/limine/common/protos/chainload.c new file mode 100644 index 0000000..a5512f4 --- /dev/null +++ b/limine/common/protos/chainload.c @@ -0,0 +1,367 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined (UEFI) +# include +#endif + +#if defined (BIOS) + +__attribute__((noinline, section(".realmode"))) +noreturn static void spinup(uint8_t drive, void *buf) { + struct idtr real_mode_idt; + real_mode_idt.limit = 0x3ff; + real_mode_idt.ptr = 0; + + asm volatile ( + "cli\n\t" + "cld\n\t" + + // Safe stack location + "mov $0x7c00, %%esp\n\t" + + // move buffer to final location + "mov $0x7c00, %%edi\n\t" + "mov $512, %%ecx\n\t" + "rep movsb\n\t" + + "lidt (%%eax)\n\t" + + "pushl $0x08\n\t" + "pushl $1f\n\t" + "lret\n\t" + "1: .code16\n\t" + "movw $0x10, %%ax\n\t" + "movw %%ax, %%ds\n\t" + "movw %%ax, %%es\n\t" + "movw %%ax, %%fs\n\t" + "movw %%ax, %%gs\n\t" + "movw %%ax, %%ss\n\t" + "movl %%cr0, %%eax\n\t" + "andb $0xfe, %%al\n\t" + "movl %%eax, %%cr0\n\t" + "movl $1f, %%eax\n\t" + "pushw $0\n\t" + "pushw %%ax\n\t" + "lret\n\t" + "1:\n\t" + "xorw %%ax, %%ax\n\t" + "movw %%ax, %%ds\n\t" + "movw %%ax, %%es\n\t" + "movw %%ax, %%fs\n\t" + "movw %%ax, %%gs\n\t" + "movw %%ax, %%ss\n\t" + + "sti\n\t" + + "pushw $0\n\t" + "pushw $0x7c00\n\t" + "lret\n\t" + + ".code32\n\t" + : + : "a" (&real_mode_idt), "d" (drive), "S"(buf) + : "memory" + ); + + __builtin_unreachable(); +} + +noreturn void chainload(char *config, char *cmdline) { + (void)cmdline; + + uint64_t val; + + int part; { + char *part_config = config_get_value(config, 0, "PARTITION"); + if (part_config == NULL) { + part = 0; + } else { + val = strtoui(part_config, NULL, 10); + if (val > 256) { + panic(true, "bios: BIOS partition number outside range 0-256"); + } + part = val; + } + } + int drive; { + char *drive_config = config_get_value(config, 0, "DRIVE"); + if (drive_config == NULL) { + drive = boot_volume->index; + } else { + val = strtoui(drive_config, NULL, 10); + if (val < 1 || val > 256) { + panic(true, "bios: BIOS drive number outside range 1-256"); + } + drive = val; + } + } + + struct volume *p = volume_get_by_coord(false, drive, part); + if (p == NULL && config_get_value(config, 0, "GPT_GUID") == NULL + && config_get_value(config, 0, "GPT_UUID") == NULL + && config_get_value(config, 0, "MBR_ID") == NULL) { + panic(true, "bios: Specified drive/partition not found"); + } + + char *gpt_guid_s = config_get_value(config, 0, "GPT_GUID"); + if (gpt_guid_s == NULL) { + gpt_guid_s = config_get_value(config, 0, "GPT_UUID"); + } + if (gpt_guid_s != NULL) { + struct guid guid; + if (!string_to_guid_be(&guid, gpt_guid_s)) { + panic(true, "bios: Malformed GUID"); + } + + p = volume_get_by_guid(&guid); + if (p == NULL) { + if (!string_to_guid_mixed(&guid, gpt_guid_s)) { + panic(true, "bios: Malformed GUID"); + } + + p = volume_get_by_guid(&guid); + } + + if (p == NULL) { + panic(true, "bios: No matching GPT drive for GPT_GUID found"); + } + + if (p->partition != 0) { + panic(true, "bios: GPT_GUID is that of a partition, not a drive"); + } + + p = volume_get_by_coord(false, p->index, part); + + if (p == NULL) { + panic(true, "bios: Partition specified is not valid"); + } + + goto load; + } + + char *mbr_id_s = config_get_value(config, 0, "MBR_ID"); + if (mbr_id_s != NULL) { + uint32_t mbr_id = strtoui(mbr_id_s, NULL, 16); + + for (size_t i = 0; i < volume_index_i; i++) { + p = volume_index[i]; + + if (!is_valid_mbr(p)) { + continue; + } + + uint32_t mbr_id_1; + if (!volume_read(p, &mbr_id_1, 0x1b8, sizeof(uint32_t))) { + continue; + } + + if (mbr_id_1 == mbr_id) { + p = volume_get_by_coord(false, p->index, part); + + if (p == NULL) { + panic(true, "bios: Partition specified is not valid"); + } + + goto load; + } + } + + panic(true, "bios: No matching MBR ID found"); + } + +load: + vga_textmode_init(false); + + void *buf = ext_mem_alloc(512); + + if (!volume_read(p, buf, 0, 512)) { + panic(true, "bios: Failed to read boot sector"); + } + + uint16_t *boot_sig = (uint16_t *)(buf + 0x1fe); + + if (*boot_sig != 0xaa55) { + panic(true, "bios: Volume is not bootable"); + } + + spinup(p->drive, buf); +} + +#elif defined (UEFI) + +static EFI_DEVICE_PATH_PROTOCOL *build_relative_efi_file_path(struct file_handle *image) { + // The file path stored in EFI_LOADED_IMAGE_PROTOCOL::FilePath is + // expected to be relative to the EFI_LOADED_IMAGE_PROTOCOL::DeviceHandle. + // For this reason the EFI_DEVICE_PATH_PROTOCOL of the efi_part_handle + // is not used as a prefix. This likely also means that the returned + // path cannot be given to gBS->LoadImage() directly. + + size_t original_path_chars = strlen(image->path); + + size_t efi_file_path_alloc_len = (original_path_chars + 1) * sizeof(CHAR16); + CHAR16 *efi_file_path = ext_mem_alloc(efi_file_path_alloc_len); + + bool leading_slash = true; + size_t j = 0; + for (size_t i = 0; i < original_path_chars; i++) { + if (image->path[i] == '/' && leading_slash) { + continue; + } + leading_slash = false; + efi_file_path[j++] = image->path[i] == '/' ? '\\' : image->path[i]; + } + efi_file_path[j] = 0; + + + size_t efi_file_path_len = ((j + 1) * sizeof(CHAR16)); + size_t path_item_len = sizeof(EFI_DEVICE_PATH_PROTOCOL) + efi_file_path_len; + size_t end_item_len = sizeof(EFI_DEVICE_PATH_PROTOCOL); + size_t alloc_len = path_item_len + end_item_len; + + EFI_DEVICE_PATH_PROTOCOL *device_path; + EFI_STATUS status = gBS->AllocatePool(EfiLoaderData, alloc_len, (void **)&device_path); + if (status) { + panic(true, "efi: AllocatePool() failure (%x)", status); + } + + FILEPATH_DEVICE_PATH *path_item = (FILEPATH_DEVICE_PATH *)device_path; + path_item->Header.Type = MEDIA_DEVICE_PATH; + path_item->Header.SubType = MEDIA_FILEPATH_DP; + path_item->Header.Length[0] = path_item_len; + path_item->Header.Length[1] = path_item_len >> 8; + memcpy(&path_item->PathName, efi_file_path, efi_file_path_len); + + EFI_DEVICE_PATH_PROTOCOL *end_item = (void *)device_path + path_item_len; + end_item->Type = END_DEVICE_PATH_TYPE; + end_item->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end_item->Length[0] = end_item_len; + end_item->Length[1] = end_item_len >> 8; + + pmm_free(efi_file_path, efi_file_path_alloc_len); + return device_path; +} + +noreturn void chainload(char *config, char *cmdline) { + char *image_path = config_get_value(config, 0, "PATH"); + if (image_path == NULL) { + image_path = config_get_value(config, 0, "IMAGE_PATH"); + } + if (image_path == NULL) { + panic(true, "efi: Image path not specified"); + } + + struct file_handle *image; + if ((image = uri_open(image_path)) == NULL) + panic(true, "efi: Failed to open image with path `%s`. Is the path correct?", image_path); + + EFI_STATUS status; + + EFI_HANDLE efi_part_handle = image->efi_part_handle; + + void *ptr = freadall(image, MEMMAP_RESERVED); + size_t image_size = image->size; + + memmap_alloc_range_in(untouched_memmap, &untouched_memmap_entries, + (uintptr_t)ptr, ALIGN_UP(image_size, 4096), + MEMMAP_RESERVED, MEMMAP_USABLE, true, false, true); + + EFI_DEVICE_PATH_PROTOCOL *efi_file_path = build_relative_efi_file_path(image); + + fclose(image); + term_notready(); + + size_t req_width = 0, req_height = 0, req_bpp = 0; + + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + + struct fb_info *fbinfo; + size_t fb_count; + fb_init(&fbinfo, &fb_count, req_width, req_height, req_bpp); + + size_t cmdline_len = strlen(cmdline); + CHAR16 *new_cmdline; + status = gBS->AllocatePool(EfiLoaderData, (cmdline_len + 1) * sizeof(CHAR16), (void **)&new_cmdline); + if (status) { + panic(true, "efi: Allocation failure"); + } + for (size_t i = 0; i < cmdline_len + 1; i++) { + new_cmdline[i] = cmdline[i]; + } + + pmm_release_uefi_mem(); + + MEMMAP_DEVICE_PATH memdev_path[2]; + + memdev_path[0].Header.Type = HARDWARE_DEVICE_PATH; + memdev_path[0].Header.SubType = HW_MEMMAP_DP; + memdev_path[0].Header.Length[0] = sizeof(MEMMAP_DEVICE_PATH); + memdev_path[0].Header.Length[1] = sizeof(MEMMAP_DEVICE_PATH) >> 8; + + memdev_path[0].MemoryType = EfiLoaderCode; + memdev_path[0].StartingAddress = (uintptr_t)ptr; + memdev_path[0].EndingAddress = (uintptr_t)ptr + image_size; + + memdev_path[1].Header.Type = END_DEVICE_PATH_TYPE; + memdev_path[1].Header.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + memdev_path[1].Header.Length[0] = sizeof(EFI_DEVICE_PATH); + memdev_path[1].Header.Length[1] = sizeof(EFI_DEVICE_PATH) >> 8; + + EFI_HANDLE new_handle = 0; + + status = gBS->LoadImage(0, efi_image_handle, + (EFI_DEVICE_PATH *)memdev_path, + ptr, image_size, &new_handle); + if (status) { + panic(false, "efi: LoadImage failure (%X)", (uint64_t)status); + } + + EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; + + EFI_LOADED_IMAGE_PROTOCOL *new_handle_loaded_image = NULL; + status = gBS->HandleProtocol(new_handle, &loaded_img_prot_guid, + (void **)&new_handle_loaded_image); + if (status) { + panic(false, "efi: HandleProtocol failure (%X)", (uint64_t)status); + } + + if (efi_part_handle != 0) { + new_handle_loaded_image->DeviceHandle = efi_part_handle; + } + + new_handle_loaded_image->FilePath = efi_file_path; + + new_handle_loaded_image->LoadOptionsSize = (cmdline_len + 1) * sizeof(CHAR16); + new_handle_loaded_image->LoadOptions = new_cmdline; + + bli_on_boot(); + + UINTN exit_data_size = 0; + CHAR16 *exit_data = NULL; + EFI_STATUS exit_status = gBS->StartImage(new_handle, &exit_data_size, &exit_data); + + status = gBS->Exit(efi_image_handle, exit_status, exit_data_size, exit_data); + if (status) { + panic(false, "efi: Exit failure (%X)", (uint64_t)status); + } + + __builtin_unreachable(); +} + +#endif diff --git a/limine/common/protos/chainload.h b/limine/common/protos/chainload.h new file mode 100644 index 0000000..93759bc --- /dev/null +++ b/limine/common/protos/chainload.h @@ -0,0 +1,8 @@ +#ifndef PROTOS__CHAINLOAD_H__ +#define PROTOS__CHAINLOAD_H__ + +#include + +noreturn void chainload(char *config, char *cmdline); + +#endif diff --git a/limine/common/protos/limine.c b/limine/common/protos/limine.c new file mode 100644 index 0000000..fc2a220 --- /dev/null +++ b/limine/common/protos/limine.c @@ -0,0 +1,1746 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LIMINE_NO_POINTERS +#include +#include + +enum executable_format { + EXECUTABLE_FORMAT_ELF, + EXECUTABLE_FORMAT_PE, +}; + +static enum executable_format detect_kernel_format(uint8_t *kernel, size_t kernel_size) { + if (elf_bits(kernel) != -1) { + return EXECUTABLE_FORMAT_ELF; + } else if (pe_bits(kernel, kernel_size) != -1) { + return EXECUTABLE_FORMAT_PE; + } else { + panic(true, "limine: Unknown kernel executable format"); + } +} + +#define SUPPORTED_BASE_REVISION 5 + +#define MAX_REQUESTS 128 + +#define MEMMAP_MAX 1024 + +static int paging_mode; + +static uint64_t get_hhdm_span_top(int base_revision) { + uint64_t ret = base_revision >= 3 ? 0 : 0x100000000; + for (size_t i = 0; i < memmap_entries; i++) { + if (((base_revision >= 1 && base_revision < 3) || base_revision >= 4) && ( + memmap[i].type == MEMMAP_RESERVED + || memmap[i].type == MEMMAP_BAD_MEMORY)) { + continue; + } + + if (base_revision == 3 && ( + memmap[i].type != MEMMAP_USABLE + && memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE + && memmap[i].type != MEMMAP_KERNEL_AND_MODULES + && memmap[i].type != MEMMAP_FRAMEBUFFER + && memmap[i].type != MEMMAP_EFI_RECLAIMABLE)) { + continue; + } + + uint64_t base = memmap[i].base; + uint64_t length = memmap[i].length; + uint64_t top = base + length; + + if (base_revision < 3 && base < 0x100000000) { + base = 0x100000000; + } + + if (base >= top) { + continue; + } + + uint64_t aligned_top = ALIGN_UP(top, 0x40000000); + + if (aligned_top > ret) { + ret = aligned_top; + } + } + + return ret; +} + +#if defined (__i386__) +static pagemap_t build_identity_map(void) { + pagemap_t pagemap = new_pagemap(paging_mode); + + map_pages(pagemap, 0, 0, VMM_FLAG_WRITE, 0x100000000); + + size_t _memmap_entries = memmap_entries; + struct memmap_entry *_memmap = + ext_mem_alloc(_memmap_entries * sizeof(struct memmap_entry)); + for (size_t i = 0; i < _memmap_entries; i++) { + _memmap[i] = memmap[i]; + } + + for (size_t i = 0; i < _memmap_entries; i++) { + if (_memmap[i].type != MEMMAP_USABLE + && _memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE + && _memmap[i].type != MEMMAP_KERNEL_AND_MODULES + && _memmap[i].type != MEMMAP_FRAMEBUFFER + && _memmap[i].type != MEMMAP_EFI_RECLAIMABLE) { + continue; + } + + uint64_t base = _memmap[i].base; + uint64_t length = _memmap[i].length; + uint64_t top = base + length; + + if (base < 0x100000000) { + base = 0x100000000; + } + + if (base >= top) { + continue; + } + + uint64_t aligned_base = ALIGN_DOWN(base, 0x1000); + uint64_t aligned_top = ALIGN_UP(top, 0x1000); + uint64_t aligned_length = aligned_top - aligned_base; + + map_pages(pagemap, aligned_base, aligned_base, VMM_FLAG_WRITE, aligned_length); + } + + return pagemap; +} + +void limine_memcpy_to_64_asm(int paging_mode, void *pagemap, uint64_t dst, void *src, size_t count); + +static void limine_memcpy_to_64(uint64_t dst, void *src, size_t count) { + static bool identity_map_ready = false; + static pagemap_t identity_map; + + if (!identity_map_ready) { + identity_map = build_identity_map(); + identity_map_ready = true; + } + + limine_memcpy_to_64_asm(paging_mode, identity_map.top_level, dst, src, count); +} +#endif + +static pagemap_t build_pagemap(int base_revision, + bool nx, struct mem_range *ranges, size_t ranges_count, + uint64_t physical_base, uint64_t virtual_base, + uint64_t direct_map_offset) { + pagemap_t pagemap = new_pagemap(paging_mode); + + if (ranges_count == 0) { + panic(true, "limine: ranges_count == 0"); + } + + for (size_t i = 0; i < ranges_count; i++) { + uint64_t virt = ranges[i].base; + uint64_t phys; + + if (virt & ((uint64_t)1 << 63)) { + phys = physical_base + (virt - virtual_base); + } else { + panic(false, "limine: Virtual address of a PHDR in lower half"); + } + + uint64_t pf = + (ranges[i].permissions & MEM_RANGE_X ? 0 : (nx ? VMM_FLAG_NOEXEC : 0)) | + (ranges[i].permissions & MEM_RANGE_W ? VMM_FLAG_WRITE : 0); + + map_pages(pagemap, virt, phys, pf, ranges[i].length); + } + + // Map 0x1000->4GiB range to identity if base revision == 0 + if (base_revision == 0) { + map_pages(pagemap, 0x1000, 0x1000, VMM_FLAG_WRITE, 0x100000000 - 0x1000); + } + + // Map 0->4GiB range to HHDM if base revision < 3 + if (base_revision < 3) { + map_pages(pagemap, direct_map_offset, 0, VMM_FLAG_WRITE, 0x100000000); + } + + size_t _memmap_entries = memmap_entries; + struct memmap_entry *_memmap = + ext_mem_alloc(_memmap_entries * sizeof(struct memmap_entry)); + for (size_t i = 0; i < _memmap_entries; i++) + _memmap[i] = memmap[i]; + + // Map all free memory regions to the higher half direct map offset + for (size_t i = 0; i < _memmap_entries; i++) { + if (((base_revision >= 1 && base_revision < 3) || base_revision >= 4) && ( + _memmap[i].type == MEMMAP_RESERVED + || _memmap[i].type == MEMMAP_BAD_MEMORY)) { + continue; + } + + if (base_revision == 3 && ( + _memmap[i].type != MEMMAP_USABLE + && _memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE + && _memmap[i].type != MEMMAP_KERNEL_AND_MODULES + && _memmap[i].type != MEMMAP_FRAMEBUFFER + && _memmap[i].type != MEMMAP_EFI_RECLAIMABLE)) { + continue; + } + + uint64_t base = _memmap[i].base; + uint64_t length = _memmap[i].length; + uint64_t top = base + length; + + if (base_revision < 3 && base < 0x100000000) { + base = 0x100000000; + } + + if (base >= top) { + continue; + } + + uint64_t aligned_base = ALIGN_DOWN(base, 0x1000); + uint64_t aligned_top = ALIGN_UP(top, 0x1000); + uint64_t aligned_length = aligned_top - aligned_base; + + if (base_revision == 0) { + map_pages(pagemap, aligned_base, aligned_base, VMM_FLAG_WRITE, aligned_length); + } + map_pages(pagemap, direct_map_offset + aligned_base, aligned_base, VMM_FLAG_WRITE, aligned_length); + } + + // Map the framebuffer with appropriate permissions + for (size_t i = 0; i < _memmap_entries; i++) { + if (_memmap[i].type != MEMMAP_FRAMEBUFFER) { + continue; + } + + uint64_t base = _memmap[i].base; + uint64_t length = _memmap[i].length; + uint64_t top = base + length; + + uint64_t aligned_base = ALIGN_DOWN(base, 0x1000); + uint64_t aligned_top = ALIGN_UP(top, 0x1000); + uint64_t aligned_length = aligned_top - aligned_base; + + if (base_revision == 0) { + map_pages(pagemap, aligned_base, aligned_base, VMM_FLAG_WRITE | VMM_FLAG_FB, aligned_length); + } + map_pages(pagemap, direct_map_offset + aligned_base, aligned_base, VMM_FLAG_WRITE | VMM_FLAG_FB, aligned_length); + } + + // XXX we do this as a quick and dirty way to switch to the higher half +#if defined (__x86_64__) || defined (__i386__) + if (base_revision >= 1) { + map_pages(pagemap, 0, 0, VMM_FLAG_WRITE, 0x100000000); + } +#endif + + return pagemap; +} + +#if defined (__x86_64__) || defined (__i386__) +extern symbol limine_spinup_32; +#elif defined (__aarch64__) + +#define LIMINE_SCTLR ((1 << 29) /* Res1 */ \ + | (1 << 28) /* Res1 */ \ + | (1 << 23) /* Res1 */ \ + | (1 << 22) /* Res1 */ \ + | (1 << 20) /* Res1 */ \ + | (1 << 12) /* I-Cache */ \ + | (1 << 11) /* Res1 */ \ + | (1 << 8) /* Res1 */ \ + | (1 << 7) /* Res1 */ \ + | (1 << 4) /* SP0 Alignment check */ \ + | (1 << 3) /* SP Alignment check */ \ + | (1 << 2) /* D-Cache */ \ + | (1 << 0)) /* MMU */ \ + +#define LIMINE_MAIR(fb) ( ((uint64_t)0b11111111 << 0) /* Normal WB RW-allocate non-transient */ \ + | ((uint64_t)(fb) << 8) ) /* Framebuffer type */ + +#define LIMINE_TCR(tsz, pa) ( ((uint64_t)(pa) << 32) /* Intermediate address size */ \ + | ((uint64_t)2 << 30) /* TTBR1 4K granule */ \ + | ((uint64_t)2 << 28) /* TTBR1 Outer shareable */ \ + | ((uint64_t)1 << 26) /* TTBR1 Outer WB RW-Allocate */ \ + | ((uint64_t)1 << 24) /* TTBR1 Inner WB RW-Allocate */ \ + | ((uint64_t)(tsz) << 16) /* Address bits in TTBR1 */ \ + /* TTBR0 4K granule */ \ + | ((uint64_t)2 << 12) /* TTBR0 Outer shareable */ \ + | ((uint64_t)1 << 10) /* TTBR0 Outer WB RW-Allocate */ \ + | ((uint64_t)1 << 8) /* TTBR0 Inner WB RW-Allocate */ \ + | ((uint64_t)(tsz) << 0)) /* Address bits in TTBR0 */ + +#elif defined (__riscv) +#elif defined (__loongarch64) +#else +#error Unknown architecture +#endif + +static uint64_t physical_base, virtual_base, slide, direct_map_offset; +static size_t requests_count; +static void **requests; + +static void set_paging_mode(bool randomise_hhdm_base) { + direct_map_offset = paging_mode_higher_half(paging_mode); + if (randomise_hhdm_base) { + // A quarter of the higher half of wiggle room for KASLR, align to 1GiB steps. + uint64_t mask = ((uint64_t)1 << (paging_mode_va_bits(paging_mode) - 3)) - 1; + direct_map_offset += (rand64() & ~((uint64_t)0x40000000 - 1)) & mask; + } +} + +static uint64_t reported_addr(void *addr) { + return (uint64_t)(uintptr_t)addr + direct_map_offset; +} + +#if defined (__i386__) +static uint64_t reported_addr_64(uint64_t addr) { + return addr + direct_map_offset; +} +#endif + +#define get_phys_addr(addr) ({ \ + __auto_type get_phys_addr__addr = (addr); \ + uintptr_t get_phys_addr__r; \ + if (get_phys_addr__addr & ((uint64_t)1 << 63)) { \ + get_phys_addr__r = physical_base + (get_phys_addr__addr - virtual_base); \ + } else { \ + get_phys_addr__r = get_phys_addr__addr; \ + } \ + get_phys_addr__r; \ +}) + +static struct limine_file get_file(struct file_handle *file, char *cmdline, bool kernel) { + struct limine_file ret = {0}; + + if (file->pxe) { + ret.media_type = LIMINE_MEDIA_TYPE_TFTP; + + ret.tftp_ip = file->pxe_ip; + ret.tftp_port = file->pxe_port; + } else { + struct volume *vol = file->vol; + + if (vol->is_optical) { + ret.media_type = LIMINE_MEDIA_TYPE_OPTICAL; + } + + ret.partition_index = vol->partition; + + ret.mbr_disk_id = mbr_get_id(vol->backing_dev ?: vol); + + if (vol->guid_valid) { + memcpy(&ret.part_uuid, &vol->guid, sizeof(struct limine_uuid)); + } + + if (vol->part_guid_valid) { + memcpy(&ret.gpt_part_uuid, &vol->part_guid, sizeof(struct limine_uuid)); + } + + struct guid gpt_disk_uuid; + if (gpt_get_guid(&gpt_disk_uuid, vol->backing_dev ?: vol) == true) { + memcpy(&ret.gpt_disk_uuid, &gpt_disk_uuid, sizeof(struct limine_uuid)); + } + } + + char *path = ext_mem_alloc(file->path_len); + memcpy(path, file->path, file->path_len); + + ret.path = reported_addr(path); + + void *freadall_ret = freadall_mode(file, MEMMAP_KERNEL_AND_MODULES, !kernel +#if defined (__i386__) + , limine_memcpy_to_64 +#endif + ); +#if defined (__i386__) + ret.address = kernel ? reported_addr(freadall_ret) : reported_addr_64(*(uint64_t *)freadall_ret); +#else + ret.address = reported_addr(freadall_ret); +#endif + + ret.size = file->size; + + ret.string = reported_addr(cmdline); + + return ret; +} + +static void *_get_request(uint64_t id[4]) { + for (size_t i = 0; i < requests_count; i++) { + uint64_t *p = requests[i]; + + if (p[2] != id[2]) { + continue; + } + if (p[3] != id[3]) { + continue; + } + + return p; + } + + return NULL; +} + +#define get_request(REQ) _get_request((uint64_t[4])REQ) + +#define FEAT_START do { +#define FEAT_END } while (0); + +noreturn void limine_load(char *config, char *cmdline) { +#if defined (__x86_64__) || defined (__i386__) + uint32_t eax, ebx, ecx, edx; +#endif + + char *kernel_path = config_get_value(config, 0, "PATH"); + if (kernel_path == NULL) { + kernel_path = config_get_value(config, 0, "KERNEL_PATH"); + } + if (kernel_path == NULL) { + panic(true, "limine: Executable path not specified"); + } + + print("limine: Loading executable `%#`...\n", kernel_path); + + struct file_handle *kernel_file; + if ((kernel_file = uri_open(kernel_path)) == NULL) + panic(true, "limine: Failed to open executable with path `%#`. Is the path correct?", kernel_path); + + char *k_path_copy = ext_mem_alloc(strlen(kernel_path) + 1); + strcpy(k_path_copy, kernel_path); + char *k_resource = NULL, *k_root = NULL, *k_path = NULL, *k_hash = NULL; + uri_resolve(k_path_copy, &k_resource, &k_root, &k_path, &k_hash); + // Copy k_resource and k_root since uri_resolve returns pointers to a static + // buffer that gets overwritten by subsequent uri_open/uri_resolve calls + k_resource = strdup(k_resource); + k_root = strdup(k_root); + char *k_path_ = ext_mem_alloc(strlen(k_path) + 2); + k_path_[0] = '/'; + strcpy(k_path_ + 1, k_path); + k_path = k_path_; + for (size_t i = strlen(k_path) - 1; ; i--) { + if (k_path[i] == '/' || i == 1) { + k_path[i] = 0; + break; + } + k_path[i] = 0; + } + + uint8_t *kernel = freadall(kernel_file, MEMMAP_BOOTLOADER_RECLAIMABLE); + + char *kaslr_s = config_get_value(config, 0, "KASLR"); + bool kaslr = false; + if (kaslr_s != NULL && strcmp(kaslr_s, "yes") == 0) { + kaslr = true; + } + + // ELF loading + uint64_t entry_point = 0; + struct mem_range *ranges; + uint64_t ranges_count; + + uint64_t image_size_before_bss; + bool is_reloc; + + enum executable_format kernel_format = detect_kernel_format(kernel, kernel_file->size); + switch (kernel_format) { + case EXECUTABLE_FORMAT_ELF: + if (!elf64_load(kernel, kernel_file->size, &entry_point, &slide, + MEMMAP_KERNEL_AND_MODULES, kaslr, + &ranges, &ranges_count, + &physical_base, &virtual_base, NULL, + &image_size_before_bss, + &is_reloc)) { + panic(true, "limine: ELF64 load failure"); + } + break; + case EXECUTABLE_FORMAT_PE: + if (!pe64_load(kernel, kernel_file->size, &entry_point, &slide, + MEMMAP_KERNEL_AND_MODULES, kaslr, + &ranges, &ranges_count, + &physical_base, &virtual_base, NULL, + &image_size_before_bss, + &is_reloc)) { + panic(true, "limine: PE64 load failure"); + } + break; + } + + kaslr = kaslr && is_reloc; + + uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER; + uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; + + // Determine base revision + uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(0); + int base_revision = 0; + bool base_revision_found = false; + uint64_t *base_rev_p1_ptr = NULL; + uint64_t *base_rev_p2_ptr = NULL; + for (size_t i = 0; i < ALIGN_DOWN(image_size_before_bss, 8); i += 8) { + uint64_t *p = (void *)(uintptr_t)physical_base + i; + + // Check if start marker hit + if (p[0] == limine_requests_start_marker[0] && p[1] == limine_requests_start_marker[1] + && p[2] == limine_requests_start_marker[2] && p[3] == limine_requests_start_marker[3]) { + base_revision = 0; + base_revision_found = false; + base_rev_p1_ptr = NULL; + base_rev_p2_ptr = NULL; + continue; + } + + // Check if end marker hit + if (p[0] == limine_requests_end_marker[0] && p[1] == limine_requests_end_marker[1]) { + break; + } + + if (p[0] == limine_base_revision[0] && p[1] == limine_base_revision[1]) { + if (base_revision_found) { + panic(true, "limine: Duplicated base revision tag"); + } + base_revision_found = true; + base_revision = p[2]; + if (p[2] <= SUPPORTED_BASE_REVISION) { + // Set to 0 to mean "supported" + base_rev_p2_ptr = &p[2]; + } else { + base_revision = SUPPORTED_BASE_REVISION; + } + base_rev_p1_ptr = &p[1]; + } + } + if (base_rev_p1_ptr != NULL) { + *base_rev_p1_ptr = base_revision; + } + if (base_rev_p2_ptr != NULL) { + *base_rev_p2_ptr = 0; + } + + // Load requests + uint64_t *limine_reqs = NULL; + requests = ext_mem_alloc(MAX_REQUESTS * sizeof(void *)); + requests_count = 0; + if (base_revision == 0 && kernel_format == EXECUTABLE_FORMAT_ELF && elf64_load_section(kernel, kernel_file->size, &limine_reqs, ".limine_reqs", 0, slide)) { + for (size_t i = 0; ; i++) { + if (i >= MAX_REQUESTS) { + panic(true, "limine: Maximum requests exceeded"); + } + if (limine_reqs[i] == 0) { + break; + } + requests[i] = (void *)(uintptr_t)((limine_reqs[i] - virtual_base) + physical_base); + requests_count++; + } + } else { + uint64_t common_magic[2] = { LIMINE_COMMON_MAGIC }; + for (size_t i = 0; i < ALIGN_DOWN(image_size_before_bss, 8); i += 8) { + uint64_t *p = (void *)(uintptr_t)physical_base + i; + + // Check if start marker hit + if (p[0] == limine_requests_start_marker[0] && p[1] == limine_requests_start_marker[1] + && p[2] == limine_requests_start_marker[2] && p[3] == limine_requests_start_marker[3]) { + requests_count = 0; + continue; + } + + // Check if end marker hit + if (p[0] == limine_requests_end_marker[0] && p[1] == limine_requests_end_marker[1]) { + break; + } + + if (p[0] != common_magic[0]) { + continue; + } + if (p[1] != common_magic[1]) { + continue; + } + + if (requests_count == MAX_REQUESTS) { + panic(true, "limine: Maximum requests exceeded"); + } + + // Check for a conflict + if (_get_request(p) != NULL) { + panic(true, "limine: Conflict detected for request ID %X %X", p[2], p[3]); + } + + requests[requests_count++] = p; + } + } + +#if defined (__x86_64__) || defined (__i386__) + // Check if 64 bit CPU + if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) { + panic(true, "limine: This CPU does not support 64-bit mode."); + } +#endif + + uint64_t hhdm_span_top = get_hhdm_span_top(base_revision); + +#if defined (__x86_64__) || defined (__i386__) + uint64_t maxphyaddr; + if (!cpuid(0x80000008, 0, &eax, &ebx, &ecx, &edx)) { + maxphyaddr = 36; + } else { + maxphyaddr = eax & 0xff; + } + + if (maxphyaddr > 64) { + panic(true, "limine: MAXPHYADDR > 64"); + } + if (maxphyaddr < 64 && hhdm_span_top > (uint64_t)1 << maxphyaddr) { + panic(true, "limine: Top of HHDM exceeds maximum allowable MAXPHYADDR value"); + } +#endif + + printv("limine: Physical base: %X\n", physical_base); + printv("limine: Virtual base: %X\n", virtual_base); + printv("limine: Slide: %X\n", slide); + printv("limine: ELF entry point: %X\n", entry_point); + printv("limine: Base revision: %u\n", base_revision); + printv("limine: Requests count: %U\n", (uint64_t)requests_count); + printv("limine: Top of HHDM: %X\n", hhdm_span_top); + + // Paging Mode + int max_supported_paging_mode, min_supported_paging_mode; + +#if defined (__x86_64__) || defined (__i386__) + max_supported_paging_mode = PAGING_MODE_X86_64_4LVL; + if (cpuid(0x00000007, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 16))) { + printv("limine: CPU has 5-level paging support\n"); + max_supported_paging_mode = PAGING_MODE_X86_64_5LVL; + } + min_supported_paging_mode = PAGING_MODE_X86_64_4LVL; + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + min_supported_paging_mode = PAGING_MODE_X86_64_5LVL; + if (min_supported_paging_mode > max_supported_paging_mode) { + goto hhdm_fail; + } + } + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + goto hhdm_fail; + } +#elif defined (__aarch64__) + max_supported_paging_mode = PAGING_MODE_AARCH64_4LVL; + min_supported_paging_mode = PAGING_MODE_AARCH64_4LVL; + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + goto hhdm_fail; + } + // TODO(qookie): aarch64 also has optional 5 level paging when using 4K pages +#elif defined (__riscv) + max_supported_paging_mode = vmm_max_paging_mode(); + min_supported_paging_mode = PAGING_MODE_RISCV_SV39; + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + min_supported_paging_mode = PAGING_MODE_RISCV_SV48; + if (min_supported_paging_mode > max_supported_paging_mode) { + goto hhdm_fail; + } + } + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + min_supported_paging_mode = PAGING_MODE_RISCV_SV57; + if (min_supported_paging_mode > max_supported_paging_mode) { + goto hhdm_fail; + } + } + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + goto hhdm_fail; + } +#elif defined (__loongarch64) + max_supported_paging_mode = PAGING_MODE_LOONGARCH64_4LVL; + min_supported_paging_mode = PAGING_MODE_LOONGARCH64_4LVL; + if (hhdm_span_top >= (uint64_t)1 << (paging_mode_va_bits(min_supported_paging_mode) - 2)) { + goto hhdm_fail; + } +#else +#error Unknown architecture +#endif + + if (0) { +hhdm_fail: + panic(true, "limine: Unable to allocate higher half direct map (too much memory?)"); + } + + char *user_paging_mode_s = config_get_value(config, 0, "PAGING_MODE"); + + int user_max_paging_mode = PAGING_MODE_MAX; + + char *user_max_paging_mode_s; + if (user_paging_mode_s != NULL) { + user_max_paging_mode_s = user_paging_mode_s; + } else { + user_max_paging_mode_s = config_get_value(config, 0, "MAX_PAGING_MODE"); + } + if (user_max_paging_mode_s != NULL) { +#if defined (__x86_64__) || defined (__i386__) + if (strcasecmp(user_max_paging_mode_s, "4level") == 0) { + user_max_paging_mode = PAGING_MODE_X86_64_4LVL; + } else if (strcasecmp(user_max_paging_mode_s, "5level") == 0) { + user_max_paging_mode = PAGING_MODE_X86_64_5LVL; + } +#elif defined (__aarch64__) + if (strcasecmp(user_max_paging_mode_s, "4level") == 0) { + user_max_paging_mode = PAGING_MODE_AARCH64_4LVL; + } else if (strcasecmp(user_max_paging_mode_s, "5level") == 0) { + user_max_paging_mode = PAGING_MODE_AARCH64_5LVL; + } +#elif defined (__riscv) + if (strcasecmp(user_max_paging_mode_s, "sv39") == 0) { + user_max_paging_mode = PAGING_MODE_RISCV_SV39; + } else if (strcasecmp(user_max_paging_mode_s, "sv48") == 0) { + user_max_paging_mode = PAGING_MODE_RISCV_SV48; + } else if (strcasecmp(user_max_paging_mode_s, "sv57") == 0) { + user_max_paging_mode = PAGING_MODE_RISCV_SV57; + } +#elif defined (__loongarch64) + if (strcasecmp(user_max_paging_mode_s, "4level") == 0) { + user_max_paging_mode = PAGING_MODE_LOONGARCH64_4LVL; + } +#endif + else { + panic(true, "limine: Invalid MAX_PAGING_MODE: `%s`", user_max_paging_mode_s); + } + } + + int user_min_paging_mode = PAGING_MODE_MIN; + + char *user_min_paging_mode_s; + if (user_paging_mode_s != NULL) { + user_min_paging_mode_s = user_paging_mode_s; + } else { + user_min_paging_mode_s = config_get_value(config, 0, "MIN_PAGING_MODE"); + } + if (user_min_paging_mode_s != NULL) { +#if defined (__x86_64__) || defined (__i386__) + if (strcasecmp(user_min_paging_mode_s, "4level") == 0) { + user_min_paging_mode = PAGING_MODE_X86_64_4LVL; + } else if (strcasecmp(user_min_paging_mode_s, "5level") == 0) { + user_min_paging_mode = PAGING_MODE_X86_64_5LVL; + } +#elif defined (__aarch64__) + if (strcasecmp(user_min_paging_mode_s, "4level") == 0) { + user_min_paging_mode = PAGING_MODE_AARCH64_4LVL; + } else if (strcasecmp(user_min_paging_mode_s, "5level") == 0) { + user_min_paging_mode = PAGING_MODE_AARCH64_5LVL; + } +#elif defined (__riscv) + if (strcasecmp(user_min_paging_mode_s, "sv39") == 0) { + user_min_paging_mode = PAGING_MODE_RISCV_SV39; + } else if (strcasecmp(user_min_paging_mode_s, "sv48") == 0) { + user_min_paging_mode = PAGING_MODE_RISCV_SV48; + } else if (strcasecmp(user_min_paging_mode_s, "sv57") == 0) { + user_min_paging_mode = PAGING_MODE_RISCV_SV57; + } +#elif defined (__loongarch64) + if (strcasecmp(user_min_paging_mode_s, "4level") == 0) { + user_min_paging_mode = PAGING_MODE_LOONGARCH64_4LVL; + } +#endif + else { + panic(true, "limine: Invalid MIN_PAGING_MODE: `%s`", user_min_paging_mode_s); + } + } + + if (user_max_paging_mode < user_min_paging_mode) { + panic(true, "limine: MAX_PAGING_MODE is lower than MIN_PAGING_MODE"); + } + + if (user_max_paging_mode < max_supported_paging_mode) { + if (user_max_paging_mode < min_supported_paging_mode) { + panic(true, "limine: User set MAX_PAGING_MODE less than minimum supported paging mode"); + } + max_supported_paging_mode = user_max_paging_mode; + } + if (user_min_paging_mode > min_supported_paging_mode) { + if (user_min_paging_mode > max_supported_paging_mode) { + panic(true, "limine: User set MIN_PAGING_MODE greater than maximum supported paging mode"); + } + min_supported_paging_mode = user_min_paging_mode; + } + +#if defined (__x86_64__) || defined (__i386__) + paging_mode = PAGING_MODE_X86_64_4LVL; +#elif defined (__riscv) + paging_mode = max_supported_paging_mode >= PAGING_MODE_RISCV_SV48 ? PAGING_MODE_RISCV_SV48 : PAGING_MODE_RISCV_SV39; +#elif defined (__aarch64__) + paging_mode = PAGING_MODE_AARCH64_4LVL; +#elif defined (__loongarch64) + paging_mode = PAGING_MODE_LOONGARCH64_4LVL; +#endif + +#if defined (__riscv) +#define paging_mode_limine_to_vmm(x) (PAGING_MODE_RISCV_SV39 + (x)) +#define paging_mode_vmm_to_limine(x) ((x) - PAGING_MODE_RISCV_SV39) +#else +#define paging_mode_limine_to_vmm(x) (x) +#define paging_mode_vmm_to_limine(x) (x) +#endif + + bool paging_mode_set = false; + bool randomise_hhdm_base = false; +FEAT_START + struct limine_paging_mode_request *pm_request = get_request(LIMINE_PAGING_MODE_REQUEST_ID); + if (pm_request == NULL) + break; + + uint64_t target_mode = pm_request->mode; + paging_mode = paging_mode_limine_to_vmm(target_mode); + + int kern_min_mode = PAGING_MODE_MIN, kern_max_mode = paging_mode; + if (pm_request->revision >= 1) { + kern_min_mode = (int)paging_mode_limine_to_vmm(pm_request->min_mode); + kern_max_mode = (int)paging_mode_limine_to_vmm(pm_request->max_mode); + } + + if (paging_mode > max_supported_paging_mode) { + paging_mode = max_supported_paging_mode; + } + if (paging_mode < min_supported_paging_mode) { + paging_mode = min_supported_paging_mode; + } + + if (kern_max_mode < kern_min_mode) { + panic(true, "limine: Executable's paging max_mode lower than min_mode"); + } + + if (paging_mode > kern_max_mode) { + if (kern_max_mode < min_supported_paging_mode) { + panic(true, "limine: Executable's maximum supported paging mode lower than minimum allowable paging mode"); + } + paging_mode = kern_max_mode; + } + if (paging_mode < kern_min_mode) { + if (kern_min_mode > max_supported_paging_mode) { + panic(true, "limine: Executable's minimum supported paging mode higher than maximum allowable paging mode"); + } + paging_mode = kern_min_mode; + } + + char *randomise_hhdm_base_s = config_get_value(config, 0, "RANDOMISE_HHDM_BASE"); + if (randomise_hhdm_base_s == NULL) { + randomise_hhdm_base_s = config_get_value(config, 0, "RANDOMIZE_HHDM_BASE"); + } + if (randomise_hhdm_base_s == NULL) { + randomise_hhdm_base = kaslr; + } else { + randomise_hhdm_base = strcasecmp(randomise_hhdm_base_s, "yes") == 0; + } + + set_paging_mode(randomise_hhdm_base); + paging_mode_set = true; + + struct limine_paging_mode_response *pm_response = + ext_mem_alloc(sizeof(struct limine_paging_mode_response)); + + pm_response->mode = paging_mode_vmm_to_limine(paging_mode); + pm_request->response = reported_addr(pm_response); +FEAT_END + + if (!paging_mode_set) { + set_paging_mode(randomise_hhdm_base); + } + +#if defined (__aarch64__) + uint64_t aa64mmfr0; + asm volatile ("mrs %0, id_aa64mmfr0_el1" : "=r" (aa64mmfr0)); + + uint64_t pa = aa64mmfr0 & 0xF; + + uint64_t tsz = 64 - (paging_mode_va_bits(paging_mode) - 1); +#endif + + struct limine_file *kf = ext_mem_alloc(sizeof(struct limine_file)); + *kf = get_file(kernel_file, cmdline, true); + fclose(kernel_file); + + // Entry point feature +FEAT_START + struct limine_entry_point_request *entrypoint_request = get_request(LIMINE_ENTRY_POINT_REQUEST_ID); + if (entrypoint_request == NULL) { + break; + } + + entry_point = entrypoint_request->entry; + + printv("limine: Entry point at %X\n", entry_point); + + struct limine_entry_point_response *entrypoint_response = + ext_mem_alloc(sizeof(struct limine_entry_point_response)); + + entrypoint_request->response = reported_addr(entrypoint_response); +FEAT_END + + // x86-64 Keep IOMMU feature +#if defined (__x86_64__) || defined (__i386__) + bool keep_iommu = false; +FEAT_START + struct limine_x86_64_keep_iommu_request *keep_iommu_request = + get_request(LIMINE_X86_64_KEEP_IOMMU_REQUEST_ID); + if (keep_iommu_request == NULL) { + break; + } + + struct limine_x86_64_keep_iommu_response *keep_iommu_response = + ext_mem_alloc(sizeof(struct limine_x86_64_keep_iommu_response)); + + keep_iommu_request->response = reported_addr(keep_iommu_response); + keep_iommu = true; +FEAT_END +#endif + + // Bootloader info feature +FEAT_START + struct limine_bootloader_info_request *bootloader_info_request = get_request(LIMINE_BOOTLOADER_INFO_REQUEST_ID); + if (bootloader_info_request == NULL) { + break; // next feature + } + + struct limine_bootloader_info_response *bootloader_info_response = + ext_mem_alloc(sizeof(struct limine_bootloader_info_response)); + + bootloader_info_response->name = reported_addr("Limine"); + bootloader_info_response->version = reported_addr(LIMINE_VERSION); + + bootloader_info_request->response = reported_addr(bootloader_info_response); +FEAT_END + + // Executable Command Line feature +FEAT_START + struct limine_executable_cmdline_request *executable_cmdline_request = get_request(LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID); + if (executable_cmdline_request == NULL) { + break; // next feature + } + + struct limine_executable_cmdline_response *executable_cmdline_response = + ext_mem_alloc(sizeof(struct limine_executable_cmdline_response)); + + executable_cmdline_response->cmdline = reported_addr(cmdline); + + executable_cmdline_request->response = reported_addr(executable_cmdline_response); +FEAT_END + + // Firmware type feature +FEAT_START + struct limine_firmware_type_request *firmware_type_request = get_request(LIMINE_FIRMWARE_TYPE_REQUEST_ID); + if (firmware_type_request == NULL) { + break; // next feature + } + + struct limine_firmware_type_response *firmware_type_response = + ext_mem_alloc(sizeof(struct limine_firmware_type_response)); + + firmware_type_response->firmware_type = +#if defined (UEFI) +#if defined (__i386__) + LIMINE_FIRMWARE_TYPE_EFI32 +#else + LIMINE_FIRMWARE_TYPE_EFI64 +#endif +#else + LIMINE_FIRMWARE_TYPE_X86BIOS +#endif + ; + + firmware_type_request->response = reported_addr(firmware_type_response); +FEAT_END + + // Executable address feature +FEAT_START + struct limine_executable_address_request *executable_address_request = get_request(LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID); + if (executable_address_request == NULL) { + break; // next feature + } + + struct limine_executable_address_response *executable_address_response = + ext_mem_alloc(sizeof(struct limine_executable_address_response)); + + executable_address_response->physical_base = physical_base; + executable_address_response->virtual_base = virtual_base; + + executable_address_request->response = reported_addr(executable_address_response); +FEAT_END + + // HHDM feature +FEAT_START + struct limine_hhdm_request *hhdm_request = get_request(LIMINE_HHDM_REQUEST_ID); + if (hhdm_request == NULL) { + break; // next feature + } + + struct limine_hhdm_response *hhdm_response = + ext_mem_alloc(sizeof(struct limine_hhdm_response)); + + hhdm_response->offset = direct_map_offset; + + hhdm_request->response = reported_addr(hhdm_response); +FEAT_END + + // RSDP feature +FEAT_START + struct limine_rsdp_request *rsdp_request = get_request(LIMINE_RSDP_REQUEST_ID); + if (rsdp_request == NULL) { + break; // next feature + } + + void *rsdp = acpi_get_rsdp(); + if (rsdp == NULL) { + break; + } + + struct limine_rsdp_response *rsdp_response = + ext_mem_alloc(sizeof(struct limine_rsdp_response)); + + rsdp_response->address = (base_revision <= 2 || base_revision >= 4) ? reported_addr(rsdp) : (uintptr_t)rsdp; + + rsdp_request->response = reported_addr(rsdp_response); +FEAT_END + + // SMBIOS feature +FEAT_START + struct limine_smbios_request *smbios_request = get_request(LIMINE_SMBIOS_REQUEST_ID); + if (smbios_request == NULL) { + break; // next feature + } + + void *smbios_entry_32 = NULL, *smbios_entry_64 = NULL; + acpi_get_smbios(&smbios_entry_32, &smbios_entry_64); + if (smbios_entry_32 == NULL && smbios_entry_64 == NULL) { + break; + } + + struct limine_smbios_response *smbios_response = + ext_mem_alloc(sizeof(struct limine_smbios_response)); + + if (smbios_entry_32) { + smbios_response->entry_32 = (base_revision <= 2 || base_revision >= 5) ? reported_addr(smbios_entry_32) : (uintptr_t)smbios_entry_32; + } + if (smbios_entry_64) { + smbios_response->entry_64 = (base_revision <= 2 || base_revision >= 5) ? reported_addr(smbios_entry_64) : (uintptr_t)smbios_entry_64; + } + + smbios_request->response = reported_addr(smbios_response); +FEAT_END + +#if defined (UEFI) + // EFI system table feature +FEAT_START + struct limine_efi_system_table_request *est_request = get_request(LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID); + if (est_request == NULL) { + break; // next feature + } + + struct limine_efi_system_table_response *est_response = + ext_mem_alloc(sizeof(struct limine_efi_system_table_response)); + + est_response->address = (base_revision <= 2 || base_revision >= 5) ? reported_addr(gST) : (uintptr_t)gST; + + est_request->response = reported_addr(est_response); +FEAT_END +#endif + + // Device tree blob feature +FEAT_START + struct limine_dtb_request *dtb_request = get_request(LIMINE_DTB_REQUEST_ID); + if (dtb_request == NULL) { + break; // next feature + } + + void *dtb = get_device_tree_blob(config, 0); + + if (dtb) { + // Delete all /memory@... nodes. + // The executable must use the given UEFI memory map instead. + while (true) { + int offset = fdt_subnode_offset_namelen(dtb, 0, "memory@", 7); + + if (offset == -FDT_ERR_NOTFOUND) { + break; + } + + if (offset < 0) { + panic(true, "limine: failed to find node: '%s'", fdt_strerror(offset)); + } + + int ret = fdt_del_node(dtb, offset); + if (ret < 0) { + panic(true, "limine: failed to delete memory node: '%s'", fdt_strerror(ret)); + } + } + + struct limine_dtb_response *dtb_response = + ext_mem_alloc(sizeof(struct limine_dtb_response)); + dtb_response->dtb_ptr = reported_addr(dtb); + dtb_request->response = reported_addr(dtb_response); + } +FEAT_END + + // Stack size + uint64_t stack_size = 65536; +FEAT_START + struct limine_stack_size_request *stack_size_request = get_request(LIMINE_STACK_SIZE_REQUEST_ID); + if (stack_size_request == NULL) { + break; // next feature + } + + struct limine_stack_size_response *stack_size_response = + ext_mem_alloc(sizeof(struct limine_stack_size_response)); + + if (stack_size_request->stack_size > stack_size) { + stack_size = stack_size_request->stack_size; + } + + stack_size_request->response = reported_addr(stack_size_response); +FEAT_END + + // Executable file +FEAT_START + struct limine_executable_file_request *executable_file_request = get_request(LIMINE_EXECUTABLE_FILE_REQUEST_ID); + if (executable_file_request == NULL) { + break; // next feature + } + + struct limine_executable_file_response *executable_file_response = + ext_mem_alloc(sizeof(struct limine_executable_file_response)); + + executable_file_response->executable_file = reported_addr(kf); + + executable_file_request->response = reported_addr(executable_file_response); +FEAT_END + + // Modules +FEAT_START + struct limine_module_request *module_request = get_request(LIMINE_MODULE_REQUEST_ID); + if (module_request == NULL) { + break; // next feature + } + + size_t module_count; + for (module_count = 0; ; module_count++) { + char *module_file = config_get_value(config, module_count, "MODULE_PATH"); + if (module_file == NULL) + break; + } + + if (module_request->revision >= 1) { + module_count += module_request->internal_module_count; + } + + if (module_count == 0) { + break; + } + + struct limine_module_response *module_response = + ext_mem_alloc(sizeof(struct limine_module_response)); + + module_response->revision = 2; + + struct limine_file *modules = ext_mem_alloc(module_count * sizeof(struct limine_file)); + + size_t final_module_count = 0; + for (size_t i = 0; i < module_count; i++) { + char *module_path; + char *module_cmdline; + bool module_required = true; + bool module_path_allocated = false; + + if (module_request->revision >= 1 && i < module_request->internal_module_count) { + uint64_t *internal_modules = (void *)get_phys_addr(module_request->internal_modules); + struct limine_internal_module *internal_module = (void *)get_phys_addr(internal_modules[i]); + + module_path = (char *)get_phys_addr(internal_module->path); + module_cmdline = (char *)get_phys_addr(internal_module->string); + + if (internal_module->flags & LIMINE_INTERNAL_MODULE_COMPRESSED) { + panic(true, "limine: Compressed internal modules no longer supported"); + } + + // Validate path length to prevent buffer overflow + size_t k_resource_len = strlen(k_resource); + size_t k_root_len = strlen(k_root); + size_t module_path_len = strlen(module_path); + size_t k_path_len = strlen(k_path); + // Format: k_resource + "(" + k_root + "):" + k_path + "/" + module_path + null + size_t total_len = k_resource_len + 1 + k_root_len + 2 + k_path_len + 1 + module_path_len + 1; + if (total_len > 1024) { + panic(true, "limine: Internal module path too long"); + } + + char *module_path_abs = ext_mem_alloc(1024); + char *module_path_abs_p = module_path_abs; + memcpy(module_path_abs_p, k_resource, k_resource_len); + module_path_abs_p += k_resource_len; + *module_path_abs_p++ = '('; + memcpy(module_path_abs_p, k_root, k_root_len); + module_path_abs_p += k_root_len; + memcpy(module_path_abs_p, "):", 2); + module_path_abs_p += 2; + size_t remaining_size = 1024 - (module_path_abs_p - module_path_abs); + if (!get_absolute_path(module_path_abs_p, module_path, k_path, remaining_size)) { + panic(true, "limine: Internal module path too long"); + } + + module_path = module_path_abs; + module_path_allocated = true; + + module_required = internal_module->flags & LIMINE_INTERNAL_MODULE_REQUIRED; + } else { + size_t config_index = i - (module_request->revision >= 1 ? module_request->internal_module_count : 0); + + // Try MODULE_STRING first, then fall back to MODULE_CMDLINE + struct conf_tuple conf_tuple = + config_get_tuple(config, config_index, "MODULE_PATH", "MODULE_STRING"); + + module_path = conf_tuple.value1; + module_cmdline = conf_tuple.value2; + + if (module_cmdline == NULL) { + conf_tuple = config_get_tuple(config, config_index, "MODULE_PATH", "MODULE_CMDLINE"); + module_cmdline = conf_tuple.value2; + } + + // Copy cmdline since conf_tuple uses static buffers + module_cmdline = module_cmdline ? strdup(module_cmdline) : ""; + } + + print("limine: Loading module `%#`...\n", module_path); + + struct file_handle *f; + if ((f = uri_open(module_path)) == NULL) { + if (module_required) { + panic(true, "limine: Failed to open module with path `%#`. Is the path correct?", module_path); + } + printv("limine: Warning: Non-required internal module `%#` not found\n", module_path); + if (module_path_allocated) { + pmm_free(module_path, 1024); + } + continue; + } + if (module_path_allocated) { + pmm_free(module_path, 1024); + } + + struct limine_file *l = &modules[final_module_count++]; + *l = get_file(f, module_cmdline, false); + + fclose(f); + } + + uint64_t *modules_list = ext_mem_alloc(final_module_count * sizeof(uint64_t)); + for (size_t i = 0; i < final_module_count; i++) { + modules_list[i] = reported_addr(&modules[i]); + } + + module_response->module_count = final_module_count; + module_response->modules = reported_addr(modules_list); + + module_request->response = reported_addr(module_response); +FEAT_END + + size_t req_width = 0, req_height = 0, req_bpp = 0; + + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) { + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + } + + struct fb_info *fbs; + size_t fbs_count; + + term_notready(); + + fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + if (fbs_count == 0) { + goto no_fb; + } + + for (size_t i = 0; i < fbs_count; i++) { + memmap_alloc_range(fbs[i].framebuffer_addr, + (uint64_t)fbs[i].framebuffer_pitch * fbs[i].framebuffer_height, + MEMMAP_FRAMEBUFFER, 0, false, false, true); + } + + // Framebuffer feature +FEAT_START + struct limine_framebuffer_request *framebuffer_request = get_request(LIMINE_FRAMEBUFFER_REQUEST_ID); + if (framebuffer_request == NULL) { + break; // next feature + } + + if (fbs_count == 0) { + break; + } + + struct limine_framebuffer *fbp = ext_mem_alloc(fbs_count * sizeof(struct limine_framebuffer)); + + struct limine_framebuffer_response *framebuffer_response = + ext_mem_alloc(sizeof(struct limine_framebuffer_response)); + + framebuffer_response->revision = 1; + + uint64_t *fb_list = ext_mem_alloc(fbs_count * sizeof(uint64_t)); + + for (size_t i = 0; i < fbs_count; i++) { + uint64_t *modes_list = ext_mem_alloc(fbs[i].mode_count * sizeof(uint64_t)); + for (size_t j = 0; j < fbs[i].mode_count; j++) { + fbs[i].mode_list[j].memory_model = LIMINE_FRAMEBUFFER_RGB; + modes_list[j] = reported_addr(&fbs[i].mode_list[j]); + } + fbp[i].modes = reported_addr(modes_list); + fbp[i].mode_count = fbs[i].mode_count; + + if (fbs[i].edid != NULL) { + fbp[i].edid_size = sizeof(struct edid_info_struct); + fbp[i].edid = reported_addr(fbs[i].edid); + } + + fbp[i].memory_model = LIMINE_FRAMEBUFFER_RGB; + fbp[i].address = reported_addr((void *)(uintptr_t)fbs[i].framebuffer_addr); + fbp[i].width = fbs[i].framebuffer_width; + fbp[i].height = fbs[i].framebuffer_height; + fbp[i].bpp = fbs[i].framebuffer_bpp; + fbp[i].pitch = fbs[i].framebuffer_pitch; + fbp[i].red_mask_size = fbs[i].red_mask_size; + fbp[i].red_mask_shift = fbs[i].red_mask_shift; + fbp[i].green_mask_size = fbs[i].green_mask_size; + fbp[i].green_mask_shift = fbs[i].green_mask_shift; + fbp[i].blue_mask_size = fbs[i].blue_mask_size; + fbp[i].blue_mask_shift = fbs[i].blue_mask_shift; + + fb_list[i] = reported_addr(&fbp[i]); + } + + framebuffer_response->framebuffer_count = fbs_count; + framebuffer_response->framebuffers = reported_addr(fb_list); + + framebuffer_request->response = reported_addr(framebuffer_response); +FEAT_END + +no_fb: + // Boot time feature +FEAT_START + struct limine_date_at_boot_request *date_at_boot_request = get_request(LIMINE_DATE_AT_BOOT_REQUEST_ID); + if (date_at_boot_request == NULL) { + break; // next feature + } + + struct limine_date_at_boot_response *date_at_boot_response = + ext_mem_alloc(sizeof(struct limine_date_at_boot_response)); + + date_at_boot_response->timestamp = time(); + + date_at_boot_request->response = reported_addr(date_at_boot_response); +FEAT_END + + // Wrap-up stuff before memmap close +#if defined (__x86_64__) || defined (__i386__) + struct gdtr *local_gdt = ext_mem_alloc(sizeof(struct gdtr)); + local_gdt->limit = gdt.limit; + uint64_t local_gdt_base = (uint64_t)gdt.ptr; + local_gdt_base += direct_map_offset; + local_gdt->ptr = local_gdt_base; +#if defined (__i386__) + local_gdt->ptr_hi = local_gdt_base >> 32; +#endif +#endif + +#if defined (__aarch64__) + // Find the most restrictive caching mode from all framebuffers to use + uint64_t fb_attr = (uint64_t)-1; + + for (size_t i = 0; i < fbs_count; i++) { + int el = current_el(); + uint64_t res; + + // Figure out the caching mode used for this particular framebuffer + if (el == 1) { + asm volatile ( + "at s1e1w, %1\n\t" + "isb\n\t" + "mrs %0, par_el1" + : "=r"(res) + : "r"(fbs[i].framebuffer_addr) + : "memory"); + } else if (el == 2) { + asm volatile ( + "at s1e2w, %1\n\t" + "isb\n\t" + "mrs %0, par_el1" + : "=r"(res) + : "r"(fbs[i].framebuffer_addr) + : "memory"); + } else { + panic(false, "Unexpected EL in limine_load"); + } + + if (res & 1) + panic(false, "Address translation for framebuffer failed"); + + uint64_t new_attr = res >> 56; + + // Use whatever we find first + if (fb_attr == (uint64_t)-1) + fb_attr = new_attr; + // Prefer Device memory over Normal memory + else if ((fb_attr & 0b11110000) && !(new_attr & 0b11110000)) + fb_attr = new_attr; + // Prefer tighter Device memory (lower values) + else if (!(fb_attr & 0b11110000) && !(new_attr & 0b11110000) && fb_attr > new_attr) + fb_attr = new_attr; + // Use Normal non-cacheable otherwise (avoid trying to figure out how to downgrade inner vs outer). + else if ((fb_attr & 0b11110000) && (new_attr & 0b11110000)) + fb_attr = 0b01000100; // Inner&outer Non-cacheable + // Otherwise do nothing (fb_attr is already more restrictive than new_attr). + } + + // If no framebuffers are found, just zero out the MAIR entry + if (fb_attr == (uint64_t)-1) + fb_attr = 0; +#endif + + void *stack = ext_mem_alloc(stack_size) + stack_size; + + bool nx_available = true; +#if defined (__x86_64__) || defined (__i386__) + // Check if we have NX + if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 20))) { + nx_available = false; + } +#endif + + // Bootloader Performance + // rdtsc_usec depends on EFI boot services +FEAT_START + if (usec_at_bootloader_entry == 0) { + break; + } + + struct limine_bootloader_performance_request *perf_request = get_request(LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID); + if (perf_request == NULL) { + break; + } + + struct limine_bootloader_performance_response *perf_response = + ext_mem_alloc(sizeof(struct limine_bootloader_performance_response)); + + perf_response->reset_usec = 0; + perf_response->init_usec = usec_at_bootloader_entry; + perf_response->exec_usec = rdtsc_usec(); + perf_request->response = reported_addr(perf_response); +FEAT_END + +#if defined (UEFI) + efi_exit_boot_services(); +#endif + + // EFI memory map +#if defined (UEFI) +FEAT_START + struct limine_efi_memmap_request *efi_memmap_request = get_request(LIMINE_EFI_MEMMAP_REQUEST_ID); + if (efi_memmap_request == NULL) { + break; // next feature + } + + struct limine_efi_memmap_response *efi_memmap_response = + ext_mem_alloc(sizeof(struct limine_efi_memmap_response)); + + efi_memmap_response->memmap = reported_addr(efi_mmap); + efi_memmap_response->memmap_size = efi_mmap_size; + efi_memmap_response->desc_size = efi_desc_size; + efi_memmap_response->desc_version = efi_desc_ver; + + efi_memmap_request->response = reported_addr(efi_memmap_response); +FEAT_END +#endif + + if (base_revision < 3) { + pmm_sanitiser_keep_first_page = false; + pmm_sanitise_entries(memmap, &memmap_entries, true); + } + + if (base_revision >= 4) { + acpi_map_tables(); + if (base_revision >= 5) { + smbios_map_tables(); +#if defined (UEFI) + efi_map_runtime_entries(); +#endif + } + pmm_sanitise_entries(memmap, &memmap_entries, true); + } + + pagemap_t pagemap = {0}; + pagemap = build_pagemap(base_revision, nx_available, ranges, ranges_count, + physical_base, virtual_base, direct_map_offset); + + // MP +FEAT_START + struct limine_mp_request *mp_request = get_request(LIMINE_MP_REQUEST_ID); + if (mp_request == NULL) { + break; // next feature + } + + struct limine_mp_info *mp_info; + size_t cpu_count; +#if defined (__x86_64__) || defined (__i386__) + smp_configure_apic = base_revision >= 5; + uint32_t bsp_lapic_id; + mp_info = init_smp(&cpu_count, &bsp_lapic_id, + paging_mode, + pagemap, mp_request->flags & LIMINE_MP_REQUEST_X86_64_X2APIC, nx_available, + direct_map_offset, true); +#elif defined (__aarch64__) + uint64_t bsp_mpidr; + + mp_info = init_smp(config, &cpu_count, &bsp_mpidr, + pagemap, LIMINE_MAIR(fb_attr), LIMINE_TCR(tsz, pa), LIMINE_SCTLR, + direct_map_offset); +#elif defined (__riscv) + mp_info = init_smp(&cpu_count, pagemap, direct_map_offset); +#elif defined (__loongarch64) + cpu_count = 0; + mp_info = NULL; // TODO: LoongArch MP +#else +#error Unknown architecture +#endif + + if (mp_info == NULL) { + break; + } + + for (size_t i = 0; i < cpu_count; i++) { +#if defined (__x86_64__) || defined (__i386__) + if (mp_info[i].lapic_id == bsp_lapic_id) { + continue; + } +#elif defined (__aarch64__) + if (mp_info[i].mpidr == bsp_mpidr) { + continue; + } +#elif defined (__riscv) + if (mp_info[i].hartid == bsp_hartid) { + continue; + } +#elif defined (__loongarch64) +#else +#error Unknown architecture +#endif + + void *cpu_stack = ext_mem_alloc(stack_size) + stack_size; + mp_info[i].reserved = reported_addr(cpu_stack); + } + + struct limine_mp_response *mp_response = + ext_mem_alloc(sizeof(struct limine_mp_response)); + +#if defined (__x86_64__) || defined (__i386__) + mp_response->flags |= + (mp_request->flags & LIMINE_MP_REQUEST_X86_64_X2APIC) && x2apic_check() ? LIMINE_MP_RESPONSE_X86_64_X2APIC : 0; + mp_response->bsp_lapic_id = bsp_lapic_id; +#elif defined (__aarch64__) + mp_response->bsp_mpidr = bsp_mpidr; +#elif defined (__riscv) + mp_response->bsp_hartid = bsp_hartid; +#elif defined (__loongarch64) +#else +#error Unknown architecture +#endif + + uint64_t *mp_list = ext_mem_alloc(cpu_count * sizeof(uint64_t)); + for (size_t i = 0; i < cpu_count; i++) { + mp_list[i] = reported_addr(&mp_info[i]); + } + + mp_response->cpu_count = cpu_count; + mp_response->cpus = reported_addr(mp_list); + + mp_request->response = reported_addr(mp_response); +FEAT_END + +#if defined (__x86_64__) || defined (__i386__) + // If there was no MP request, the kernel has no way to tell us it supports + // x2APIC. Try to disable it as a courtesy, but do not panic if we cannot + // since the kernel may be able to deal with it itself. + if (get_request(LIMINE_MP_REQUEST_ID) == NULL + && (rdmsr(0x1b) & (1 << 10))) { + if (x2apic_disable()) { + printv("limine: Firmware had x2APIC enabled, reverted to xAPIC mode\n"); + } else { + printv("limine: Firmware has x2APIC enabled and it could not be disabled\n"); + } + } +#endif + +#if defined(__riscv) + // RISC-V BSP Hart ID +FEAT_START + struct limine_riscv_bsp_hartid_request *bsp_request = get_request(LIMINE_RISCV_BSP_HARTID_REQUEST_ID); + if (bsp_request == NULL) { + break; + } + struct limine_riscv_bsp_hartid_response *bsp_response = ext_mem_alloc(sizeof(struct limine_riscv_bsp_hartid_response)); + bsp_response->bsp_hartid = bsp_hartid; + bsp_request->response = reported_addr(bsp_response); +FEAT_END +#endif + + // Memmap +FEAT_START + struct limine_memmap_request *memmap_request = get_request(LIMINE_MEMMAP_REQUEST_ID); + struct limine_memmap_response *memmap_response; + struct limine_memmap_entry *_memmap; + uint64_t *memmap_list; + + if (memmap_request != NULL) { + memmap_response = ext_mem_alloc(sizeof(struct limine_memmap_response)); + _memmap = ext_mem_alloc(sizeof(struct limine_memmap_entry) * MEMMAP_MAX); + memmap_list = ext_mem_alloc(MEMMAP_MAX * sizeof(uint64_t)); + } + + size_t mmap_entries; + struct memmap_entry *mmap = get_memmap(&mmap_entries); + + if (memmap_request == NULL) { + break; // next feature + } + + if (mmap_entries > MEMMAP_MAX) { + panic(false, "limine: Too many memmap entries"); + } + + for (size_t i = 0; i < mmap_entries; i++) { + _memmap[i].base = mmap[i].base; + _memmap[i].length = mmap[i].length; + + switch (mmap[i].type) { + case MEMMAP_USABLE: + _memmap[i].type = LIMINE_MEMMAP_USABLE; + break; + case MEMMAP_RESERVED_MAPPED: + _memmap[i].type = LIMINE_MEMMAP_RESERVED_MAPPED; + break; + case MEMMAP_ACPI_RECLAIMABLE: + _memmap[i].type = LIMINE_MEMMAP_ACPI_RECLAIMABLE; + break; + case MEMMAP_ACPI_NVS: + _memmap[i].type = LIMINE_MEMMAP_ACPI_NVS; + break; + case MEMMAP_BAD_MEMORY: + _memmap[i].type = LIMINE_MEMMAP_BAD_MEMORY; + break; + case MEMMAP_BOOTLOADER_RECLAIMABLE: + _memmap[i].type = LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE; + break; + case MEMMAP_KERNEL_AND_MODULES: + _memmap[i].type = LIMINE_MEMMAP_EXECUTABLE_AND_MODULES; + break; + case MEMMAP_FRAMEBUFFER: + _memmap[i].type = LIMINE_MEMMAP_FRAMEBUFFER; + break; + default: + case MEMMAP_RESERVED: + _memmap[i].type = LIMINE_MEMMAP_RESERVED; + break; + } + } + + for (size_t i = 0; i < mmap_entries; i++) { + memmap_list[i] = reported_addr(&_memmap[i]); + } + + memmap_response->entry_count = mmap_entries; + memmap_response->entries = reported_addr(memmap_list); + + memmap_request->response = reported_addr(memmap_response); +FEAT_END + +#if defined (__x86_64__) || defined (__i386__) +#if defined (BIOS) + // If we're going 64, we might as well call this BIOS interrupt + // to tell the BIOS that we are entering Long Mode, since it is in + // the specification. + struct rm_regs r = {0}; + r.eax = 0xec00; + r.ebx = 0x02; // Long mode only + rm_int(0x15, &r, &r); +#endif + + if (!keep_iommu) { + iommu_disable_all(); + } + + pic_mask_all(); + io_apic_mask_all(base_revision >= 5); + + if (base_revision >= 5 && lapic_check()) { + lapic_configure_bsp(); + } + + irq_flush_type = IRQ_PIC_APIC_FLUSH; + + uint64_t reported_stack = reported_addr(stack); + + common_spinup(limine_spinup_32, 11, + paging_mode, (uint32_t)(uintptr_t)pagemap.top_level, + (uint32_t)entry_point, (uint32_t)(entry_point >> 32), + (uint32_t)reported_stack, (uint32_t)(reported_stack >> 32), + (uint32_t)(uintptr_t)local_gdt, nx_available, + (uint32_t)direct_map_offset, (uint32_t)(direct_map_offset >> 32), + (uint32_t)base_revision + ); +#elif defined (__aarch64__) + vmm_assert_4k_pages(); + + uint64_t reported_stack = reported_addr(stack); + + enter_in_el1(entry_point, reported_stack, LIMINE_SCTLR, LIMINE_MAIR(fb_attr), LIMINE_TCR(tsz, pa), + (uint64_t)pagemap.top_level[0], + (uint64_t)pagemap.top_level[1], + direct_map_offset); +#elif defined (__riscv) + uint64_t reported_stack = reported_addr(stack); + uint64_t satp = make_satp(pagemap.paging_mode, pagemap.top_level); + + riscv_spinup(entry_point, reported_stack, satp, direct_map_offset); +#elif defined (__loongarch64) + uint64_t reported_stack = reported_addr(stack); + + loongarch_spinup(entry_point, reported_stack, (uint64_t)pagemap.pgd[0], (uint64_t)pagemap.pgd[1], + direct_map_offset); +#else +#error Unknown architecture +#endif +} diff --git a/limine/common/protos/limine.h b/limine/common/protos/limine.h new file mode 100644 index 0000000..54cd788 --- /dev/null +++ b/limine/common/protos/limine.h @@ -0,0 +1,8 @@ +#ifndef PROTOS__LIMINE_H__ +#define PROTOS__LIMINE_H__ + +#include + +noreturn void limine_load(char *config, char *cmdline); + +#endif diff --git a/limine/common/protos/limine_32.asm_x86 b/limine/common/protos/limine_32.asm_x86 new file mode 100644 index 0000000..75819e4 --- /dev/null +++ b/limine/common/protos/limine_32.asm_x86 @@ -0,0 +1,138 @@ +bits 32 + +section .text + +global limine_spinup_32 +limine_spinup_32: + ; If available, set PAT as: + ; PAT0 -> WB (06) + ; PAT1 -> WT (04) + ; PAT2 -> UC- (07) + ; PAT3 -> UC (00) + ; PAT4 -> WP (05) + ; PAT5 -> WC (01) + mov eax, 1 + xor ecx, ecx + cpuid + test edx, 1 << 16 + jz .no_pat + mov eax, 0x00070406 + mov edx, 0x00000105 + mov ecx, 0x277 + wrmsr + .no_pat: + + ; Enable CR4.LA57 + cmp dword [esp+4], 0 ; level5pg + je .no_la57 + mov eax, cr4 + bts eax, 12 + mov cr4, eax + .no_la57: + + ; Enable CR0.WP + mov eax, cr0 + bts eax, 16 + mov cr0, eax + + cld + + mov eax, [esp+8] ; pagemap_top_lv + mov cr3, eax + + ; Enable CR4.PAE + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + ; Set EFER (LME + NX if available), all other bits cleared + mov ecx, 0xc0000080 + xor edx, edx + mov eax, 1 << 8 + cmp dword [esp+32], 0 ; nx_available + je .no_nx + or eax, 1 << 11 + .no_nx: + wrmsr + + ; Enable CR0.PG + mov eax, cr0 + bts eax, 31 + mov cr0, eax + + ; Go 64 + push 0x28 + call .p1 + .p1: + add dword [esp], .mode64 - .p1 + retfd + +bits 64 + .mode64: + mov eax, 0x30 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + ; Load 64-bit GDT + mov eax, [rsp+28] ; local_gdt + lgdt [rax] + + ; Jump to higher half + mov rax, qword [rsp+36] + add rsp, rax + call .p2 + .p2: + add qword [rsp], .hh - .p2 + add qword [rsp], rax + retq + .hh: + + cmp dword [rsp+44], 1 + jb .no_unmap_lower_half + + ; Unmap lower half entirely + mov rsi, cr3 + lea rdi, [rsi + rax] + mov rcx, 256 + xor rax, rax + rep stosq + mov cr3, rsi + + .no_unmap_lower_half: + + ; Push fake return address + mov rsi, [rsp+20] ; stack + sub rsi, 8 + mov qword [rsi], 0 + + ; Prepare iretq frame + mov rax, qword [rsp+12] ; entry_point + push 0x30 + push rsi + push 0x2 + push 0x28 + push rax + + ; Zero out all GPRs + xor eax, eax + xor ebx, ebx + xor ecx, ecx + xor edx, edx + xor esi, esi + xor edi, edi + xor ebp, ebp + xor r8d, r8d + xor r9d, r9d + xor r10d, r10d + xor r11d, r11d + xor r12d, r12d + xor r13d, r13d + xor r14d, r14d + xor r15d, r15d + + iretq + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/protos/limine_asm.asm_ia32 b/limine/common/protos/limine_asm.asm_ia32 new file mode 100644 index 0000000..a83cc9d --- /dev/null +++ b/limine/common/protos/limine_asm.asm_ia32 @@ -0,0 +1,278 @@ +%ifdef UEFI +extern _GLOBAL_OFFSET_TABLE_ +%endif + +section .data + +local_gdt: + dq 0 + + dq 0x00209b0000000000 + dq 0x0000930000000000 + .top: + +local_gdt_ptr: + dw local_gdt.top - local_gdt - 1 + dd local_gdt + dd 0 + +old_gdt_ptr: + dw 0 + dq 0 + +old_cs: + dq 0 + +interrupt_state: + dd 0 + +old_ds: + dq 0 +old_es: + dq 0 +old_fs: + dq 0 +old_gs: + dq 0 +old_ss: + dq 0 + +%ifdef UEFI +paging_state: + dd 0 + +pae_state: + dd 0 + +old_pagemap: + dd 0 +%endif + +section .text + +; void limine_memcpy_to_64_asm(int paging_mode, void *pagemap, uint64_t dst, void *src, size_t count); + +global limine_memcpy_to_64_asm +limine_memcpy_to_64_asm: + push ebp + mov ebp, esp + + pusha + +%ifdef UEFI + call .get_got + .get_got: + pop ebx + add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc +%endif + + pushfd + mov eax, dword [esp] + add esp, 4 + and eax, 0x200 +%ifdef UEFI + mov dword [ebx + interrupt_state wrt ..gotoff], eax +%else + mov dword [interrupt_state], eax +%endif + + cli + +%ifdef UEFI + ; Disable paging if needed and save old pagemap + mov eax, cr0 + and eax, (1 << 31) + mov dword [ebx + paging_state wrt ..gotoff], eax + mov eax, cr0 + btr eax, 31 + mov cr0, eax + + mov eax, cr3 + mov dword [ebx + old_pagemap wrt ..gotoff], eax +%endif + + mov eax, [ebp+12] ; pagemap + mov cr3, eax + + ; Enable CR4.LA57 + cmp dword [ebp+8], 0 ; paging_mode + je .no_la57 + mov eax, cr4 + bts eax, 12 + mov cr4, eax + .no_la57: + + ; Enable CR4.PAE +%ifdef UEFI + mov eax, cr4 + and eax, (1 << 5) + mov [ebx + pae_state wrt ..gotoff], eax +%endif + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + ; Enable EFER.LME + mov ecx, 0xc0000080 + rdmsr + bts eax, 8 + wrmsr + + ; Enable CR0.PG + mov eax, cr0 + bts eax, 31 + mov cr0, eax + + ; Save old segments +%ifdef UEFI + mov [ebx + old_ss wrt ..gotoff], ss + mov [ebx + old_gs wrt ..gotoff], gs + mov [ebx + old_fs wrt ..gotoff], fs + mov [ebx + old_es wrt ..gotoff], es + mov [ebx + old_ds wrt ..gotoff], ds +%else + mov [old_ss], ss + mov [old_gs], gs + mov [old_fs], fs + mov [old_es], es + mov [old_ds], ds +%endif + + ; Save old CS + mov eax, cs +%ifdef UEFI + mov dword [ebx + old_cs wrt ..gotoff], eax +%else + mov dword [old_cs], eax +%endif + + ; Save old GDT +%ifdef UEFI + sgdt [ebx + old_gdt_ptr wrt ..gotoff] +%else + sgdt [old_gdt_ptr] +%endif + + ; Load new GDT +%ifdef UEFI + lgdt [ebx + local_gdt_ptr wrt ..gotoff] +%else + lgdt [local_gdt_ptr] +%endif + + ; Go 64 + push 0x08 + call .p1 + .p1: + add dword [esp], .mode64 - .p1 + retf + +bits 64 + .mode64: + mov eax, 0x10 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + mov rdi, qword [rbp+16] + mov esi, dword [rbp+24] + mov ecx, dword [rbp+28] + + rep movsb + + ; Restore old GDT +%ifdef UEFI + lgdt [ebx + old_gdt_ptr wrt ..gotoff] +%else + lgdt [abs old_gdt_ptr] +%endif + + ; Restore old segments +%ifdef UEFI + mov ds, [ebx + old_ds wrt ..gotoff] + mov es, [ebx + old_es wrt ..gotoff] + mov fs, [ebx + old_fs wrt ..gotoff] + mov gs, [ebx + old_gs wrt ..gotoff] + mov ss, [ebx + old_ss wrt ..gotoff] +%else + mov ds, [abs old_ds] + mov es, [abs old_es] + mov fs, [abs old_fs] + mov gs, [abs old_gs] + mov ss, [abs old_ss] +%endif + + ; Go 32 +%ifdef UEFI + push qword [ebx + old_cs wrt ..gotoff] +%else + push qword [abs old_cs] +%endif + call .p2 + .p2: + add qword [rsp], .mode32 - .p2 + retfq + +bits 32 + .mode32: + ; Disable CR0.PG + mov eax, cr0 + btr eax, 31 + mov cr0, eax + + ; Disable EFER.LME + mov ecx, 0xc0000080 + rdmsr + btr eax, 8 + wrmsr + + ; Disable CR4.PAE +%ifdef UEFI + cmp dword [ebx + pae_state wrt ..gotoff], 0 + jne .no_disable_pae +%endif + mov eax, cr4 + btr eax, 5 + mov cr4, eax +%ifdef UEFI + .no_disable_pae: +%endif + + ; Disable CR4.LA57 + mov eax, cr4 + btr eax, 12 + mov cr4, eax + + ; Invalidate pagemap + xor eax, eax + mov cr3, eax + +%ifdef UEFI + mov eax, dword [ebx + old_pagemap wrt ..gotoff] + mov cr3, eax + + cmp dword [ebx + paging_state wrt ..gotoff], 0 + je .no_paging + mov eax, cr0 + bts eax, 31 + mov cr0, eax + .no_paging: +%endif + +%ifdef UEFI + cmp dword [ebx + interrupt_state wrt ..gotoff], 0 +%else + cmp dword [interrupt_state], 0 +%endif + je .no_ints + sti + .no_ints: + + popa + pop ebp + + ret + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/protos/linux.h b/limine/common/protos/linux.h new file mode 100644 index 0000000..f214c25 --- /dev/null +++ b/limine/common/protos/linux.h @@ -0,0 +1,73 @@ +#ifndef PROTOS__LINUX_H__ +#define PROTOS__LINUX_H__ + +#include + +struct screen_info { + uint8_t orig_x; /* 0x00 */ + uint8_t orig_y; /* 0x01 */ + uint16_t ext_mem_k; /* 0x02 */ + uint16_t orig_video_page; /* 0x04 */ + uint8_t orig_video_mode; /* 0x06 */ + uint8_t orig_video_cols; /* 0x07 */ + uint8_t flags; /* 0x08 */ + uint8_t unused2; /* 0x09 */ + uint16_t orig_video_ega_bx;/* 0x0a */ + uint16_t unused3; /* 0x0c */ + uint8_t orig_video_lines; /* 0x0e */ + uint8_t orig_video_isVGA; /* 0x0f */ + uint16_t orig_video_points;/* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + uint16_t lfb_width; /* 0x12 */ + uint16_t lfb_height; /* 0x14 */ + uint16_t lfb_depth; /* 0x16 */ + uint32_t lfb_base; /* 0x18 */ + uint32_t lfb_size; /* 0x1c */ + uint16_t cl_magic, cl_offset; /* 0x20 */ + uint16_t lfb_linelength; /* 0x24 */ + uint8_t red_size; /* 0x26 */ + uint8_t red_pos; /* 0x27 */ + uint8_t green_size; /* 0x28 */ + uint8_t green_pos; /* 0x29 */ + uint8_t blue_size; /* 0x2a */ + uint8_t blue_pos; /* 0x2b */ + uint8_t rsvd_size; /* 0x2c */ + uint8_t rsvd_pos; /* 0x2d */ + uint16_t vesapm_seg; /* 0x2e */ + uint16_t vesapm_off; /* 0x30 */ + uint16_t pages; /* 0x32 */ + uint16_t vesa_attributes; /* 0x34 */ + uint32_t capabilities; /* 0x36 */ + uint32_t ext_lfb_base; /* 0x3a */ + uint8_t _reserved[2]; /* 0x3e */ +} __attribute__((packed)); + +#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ +#define VIDEO_TYPE_CGA 0x11 /* CGA Display */ +#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ +#define VIDEO_TYPE_EGAC 0x21 /* EGA in Color Mode */ +#define VIDEO_TYPE_VGAC 0x22 /* VGA+ in Color Mode */ +#define VIDEO_TYPE_VLFB 0x23 /* VESA VGA in graphic mode */ + +#define VIDEO_TYPE_PICA_S3 0x30 /* ACER PICA-61 local S3 video */ +#define VIDEO_TYPE_MIPS_G364 0x31 /* MIPS Magnum 4000 G364 video */ +#define VIDEO_TYPE_SGI 0x33 /* Various SGI graphics hardware */ + +#define VIDEO_TYPE_TGAC 0x40 /* DEC TGA */ + +#define VIDEO_TYPE_SUN 0x50 /* Sun frame buffer. */ +#define VIDEO_TYPE_SUNPCI 0x51 /* Sun PCI based frame buffer. */ + +#define VIDEO_TYPE_PMAC 0x60 /* PowerMacintosh frame buffer. */ + +#define VIDEO_TYPE_EFI 0x70 /* EFI graphic mode */ + +#define VIDEO_FLAGS_NOCURSOR (1 << 0) /* The video mode has no cursor set */ + +#define VIDEO_CAPABILITY_SKIP_QUIRKS (1 << 0) +#define VIDEO_CAPABILITY_64BIT_BASE (1 << 1) /* Frame buffer base is 64-bit */ + +noreturn void linux_load(char *config, char *cmdline); + +#endif diff --git a/limine/common/protos/linux_32.asm_x86 b/limine/common/protos/linux_32.asm_x86 new file mode 100644 index 0000000..3756cb5 --- /dev/null +++ b/limine/common/protos/linux_32.asm_x86 @@ -0,0 +1,69 @@ +section .data + +align 16 +linux_gdt: + dq 0 + + dq 0 + + dw 0xffff + dw 0x0000 + db 0x00 + db 10011011b + db 11001111b + db 0x00 + + dw 0xffff + dw 0x0000 + db 0x00 + db 10010011b + db 11001111b + db 0x00 + + .end: + +align 16 +linux_gdt_ptr: + dw (linux_gdt.end - linux_gdt) - 1 + dd 0 + +bits 32 + +section .text + +global linux_spinup +linux_spinup: + call .p0 + .p0: + pop eax + lea ebx, [eax - (linux_spinup.p0 - linux_gdt_ptr)] + lea ecx, [eax - (linux_spinup.p0 - linux_gdt)] + mov [ebx+2], ecx + + lgdt [ebx] + + push 0x10 + call .p1 + .p1: + add dword [esp], .fj - .p1 + retfd + + .fj: + mov eax, 0x18 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + xor ebp, ebp + xor edi, edi + xor ebx, ebx + + mov esi, [esp+8] ; boot_params + + cld + + jmp [esp+4] + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/protos/linux_64.asm_uefi_x86_64 b/limine/common/protos/linux_64.asm_uefi_x86_64 new file mode 100644 index 0000000..bca9b46 --- /dev/null +++ b/limine/common/protos/linux_64.asm_uefi_x86_64 @@ -0,0 +1,62 @@ +section .data + +align 16 +linux_gdt64: + dq 0 + + dq 0 + + dw 0xffff + dw 0x0000 + db 0x00 + db 10011011b + db 10101111b + db 0x00 + + dw 0xffff + dw 0x0000 + db 0x00 + db 10010011b + db 10001111b + db 0x00 + + .end: + +align 16 +linux_gdt64_ptr: + dw (linux_gdt64.end - linux_gdt64) - 1 + dq linux_gdt64 + +section .text + +bits 64 + +global linux_spinup64 +linux_spinup64: + cli + cld + + lgdt [rel linux_gdt64_ptr] + + lea rbx, [rel .fj] + push 0x10 + push rbx + retfq + + .fj: + mov eax, 0x18 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + mov rax, rdi + + xor ebp, ebp + xor edi, edi + xor ebx, ebx + + jmp rax + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/protos/linux_risc.c b/limine/common/protos/linux_risc.c new file mode 100644 index 0000000..c1c0956 --- /dev/null +++ b/limine/common/protos/linux_risc.c @@ -0,0 +1,473 @@ +#if defined(__riscv) || defined(__aarch64__) || defined(__loongarch__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// The following definitions and struct were copied and adapted from Linux +// kernel headers released under GPL-2.0 WITH Linux-syscall-note +// allowing their inclusion in non GPL compliant code. + +#if defined(__riscv) || defined(__aarch64__) +struct linux_header { + uint32_t code0; + uint32_t code1; + uint64_t text_offset; + uint64_t image_size; + uint64_t flags; + uint32_t version; + uint32_t res1; + uint64_t res2; + uint64_t res3; // originally 'magic' field, deprecated + uint32_t magic2; + uint32_t res4; +} __attribute__((packed)); +#elif defined(__loongarch__) +struct linux_header { + uint32_t mz; + uint32_t res0; + uint64_t kernel_entry; + uint64_t image_size; + uint64_t load_offset; + uint64_t res1; + uint64_t res2; + uint64_t res3; + uint32_t magic2; // LINUX_PE_MAGIC + uint32_t pe_offset; +} __attribute__((packed)); +#else +#error "Unknown architecture" +#endif + +struct linux_efi_memreserve { + int size; + int count; + uint64_t next; +}; + +struct linux_efi_boot_memmap { + UINTN map_size; + UINTN desc_size; + uint32_t desc_ver; + UINTN map_key; + UINTN buff_size; + EFI_MEMORY_DESCRIPTOR descs[]; +}; + +struct linux_efi_initrd { + UINTN base; + UINTN size; +}; + +// End of Linux code + +struct boot_param { + void *kernel_base; + size_t kernel_size; + void *module_base; + size_t module_size; + char *cmdline; + void *dtb; + struct linux_efi_boot_memmap *memmap; +}; + +#if defined(__riscv) +#define LINUX_HEADER_MAGIC2 0x05435352 +#define LINUX_HEADER_MAJOR_VER(ver) (((ver) >> 16) & 0xffff) +#define LINUX_HEADER_MINOR_VER(ver) (((ver) >> 0) & 0xffff) +#elif defined(__aarch64__) +#define LINUX_HEADER_MAGIC2 0x644d5241 +#elif defined(__loongarch__) +#define LINUX_HEADER_MAGIC2 0x818223cd +#endif + +static const char *verify_kernel(struct linux_header *header) { + if (header->magic2 != LINUX_HEADER_MAGIC2) { + return "kernel header magic does not match"; + } + + // riscv-specific version requirements +#if defined(__riscv) + printv("linux: boot protocol version %d.%d\n", + LINUX_HEADER_MAJOR_VER(header->version), + LINUX_HEADER_MINOR_VER(header->version)); + if (LINUX_HEADER_MAJOR_VER(header->version) == 0 + && LINUX_HEADER_MINOR_VER(header->version) < 2) { + return "linux: protocols < 0.2 are not supported"; + } +#endif + + return NULL; +} + +static void load_module(struct boot_param *p, char *config) { + char *module_path = config_get_value(config, 0, "MODULE_PATH"); + if (module_path) { + print("linux: Loading module `%#`...\n", module_path); + + struct file_handle *module_file = uri_open(module_path); + if (!module_file) { + panic(true, "linux: failed to open module `%s`. Is the path correct?", module_path); + } + + p->module_size = module_file->size; + p->module_base = ext_mem_alloc_type_aligned( + ALIGN_UP(p->module_size, 4096), + MEMMAP_KERNEL_AND_MODULES, 4096); + + fread(module_file, p->module_base, 0, p->module_size); + fclose(module_file); + printv("linux: loaded module `%s` at %p, size %U\n", module_path, p->module_base, (uint64_t)p->module_size); + } +} + +static void prepare_device_tree_blob(struct boot_param *p) { + void *dtb = p->dtb; + int ret; + + // Delete all /memory@... nodes. Linux will use the given UEFI memory map + // instead. + while (true) { + int offset = fdt_subnode_offset_namelen(dtb, 0, "memory@", 7); + + if (offset == -FDT_ERR_NOTFOUND) { + break; + } + + if (offset < 0) { + panic(true, "linux: failed to find node: '%s'", fdt_strerror(offset)); + } + + ret = fdt_del_node(dtb, offset); + if (ret < 0) { + panic(true, "linux: failed to delete memory node: '%s'", fdt_strerror(ret)); + } + } + + if (p->module_base) { + ret = fdt_set_chosen_uint64(dtb, "linux,initrd-start", (uint64_t)p->module_base); + if (ret < 0) { + panic(true, "linux: cannot set initrd parameter: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint64(dtb, "linux,initrd-end", (uint64_t)(p->module_base + p->module_size)); + if (ret < 0) { + panic(true, "linux: cannot set initrd parameter: '%s'", fdt_strerror(ret)); + } + } + + // Set the kernel command line arguments. + ret = fdt_set_chosen_string(dtb, "bootargs", p->cmdline); + if (ret < 0) { + panic(true, "linux: failed to set bootargs: '%s'", fdt_strerror(ret)); + } + + // Tell Linux about the UEFI memory map and system table. + ret = fdt_set_chosen_uint64(dtb, "linux,uefi-system-table", (uint64_t)gST); + if (ret < 0) { + panic(true, "linux: failed to set UEFI system table pointer: '%s'", fdt_strerror(ret)); + } + + // This property is not required by mainline Linux, but is required by + // Debian (and derivative) kernels, because Debian has a patch that adds + // this flag, and the existing logic that deals with it will just outright + // fail if any of the properties is missing. We don't care about Debian's + // hardening or whatever, so just always report that secure boot is off. + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-secure-boot", 0); + if (ret < 0) { + panic(true, "linux: failed to set UEFI secure boot state: '%s'", fdt_strerror(ret)); + } +} + +static void add_framebuffer(struct fb_info *fb) { + struct screen_info *screen_info; + + EFI_STATUS alloc_ret = gBS->AllocatePool(EfiLoaderData, sizeof(*screen_info), (void **)&screen_info); + if (alloc_ret != EFI_SUCCESS) { + panic(true, "linux: failed to allocate screen info table"); + } + memset(screen_info, 0, sizeof(*screen_info)); + + screen_info->capabilities = VIDEO_CAPABILITY_64BIT_BASE | VIDEO_CAPABILITY_SKIP_QUIRKS; + screen_info->flags = VIDEO_FLAGS_NOCURSOR; + screen_info->lfb_base = (uint32_t)fb->framebuffer_addr; + screen_info->ext_lfb_base = (uint32_t)(fb->framebuffer_addr >> 32); + screen_info->lfb_size = fb->framebuffer_pitch * fb->framebuffer_height; + screen_info->lfb_width = fb->framebuffer_width; + screen_info->lfb_height = fb->framebuffer_height; + screen_info->lfb_depth = fb->framebuffer_bpp; + screen_info->lfb_linelength = fb->framebuffer_pitch; + screen_info->red_size = fb->red_mask_size; + screen_info->red_pos = fb->red_mask_shift; + screen_info->green_size = fb->green_mask_size; + screen_info->green_pos = fb->green_mask_shift; + screen_info->blue_size = fb->blue_mask_size; + screen_info->blue_pos = fb->blue_mask_shift; + + screen_info->orig_video_isVGA = VIDEO_TYPE_EFI; + + EFI_GUID screen_info_table_guid = {0xe03fc20a, 0x85dc, 0x406e, {0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95}}; + EFI_STATUS ret = gBS->InstallConfigurationTable(&screen_info_table_guid, screen_info); + + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install screen info configuration table: '%X'", (uint64_t)ret); + } +} + +static void prepare_efi_tables(struct boot_param *p, char *config) { + (void)p; + EFI_STATUS ret = 0; + + { + size_t req_width = 0, req_height = 0, req_bpp = 0; + + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) { + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + } + + struct fb_info *fbs; + size_t fbs_count; + + term_notready(); + + fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + + // TODO(qookie): Let the user pick a framebuffer if there's > 1 + if (fbs_count > 0) { + add_framebuffer(&fbs[0]); + } + } + + + { + struct linux_efi_memreserve *rsv; + + ret = gBS->AllocatePool(EfiLoaderData, sizeof(*rsv), (void **)&rsv); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to allocate memory reservation table"); + } + memset(rsv, 0, sizeof(*rsv)); + + rsv->size = 0; + rsv->count = 0; + rsv->next = 0; + + EFI_GUID memreserve_table_guid = {0x888eb0c6, 0x8ede, 0x4ff5, {0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2}}; + ret = gBS->InstallConfigurationTable(&memreserve_table_guid, rsv); + + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install memory reservation configuration table: '%X'", (uint64_t)ret); + } + } + + if (p->module_base) { + struct linux_efi_initrd *initrd_table; + + ret = gBS->AllocatePool(EfiLoaderData, sizeof(*initrd_table), (void **)&initrd_table); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to allocate Linux initrd table"); + } + + initrd_table->base = (UINTN)p->module_base; + initrd_table->size = p->module_size; + + EFI_GUID initrd_table_guid = { 0x5568e427, 0x68fc, 0x4f3d, { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}; + ret = gBS->InstallConfigurationTable(&initrd_table_guid, initrd_table); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install initrd\n"); + } + } + + { + size_t buff_size = sizeof(struct linux_efi_boot_memmap) + efi_mmap_size + 4096; + + ret = gBS->AllocatePool(EfiLoaderData, buff_size, (void **)&p->memmap); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to allocate UEFI memory map"); + } + + p->memmap->buff_size = buff_size; + + EFI_GUID memmap_table_guid = { 0x800f683f, 0xd08b, 0x423a, { 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4}}; + ret = gBS->InstallConfigurationTable(&memmap_table_guid, p->memmap); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install UEFI memory map"); + } + } + + efi_exit_boot_services(); +} + +static void prepare_mmap(struct boot_param *p) { + { + void *dtb = p->dtb; + int ret = fdt_set_chosen_uint64(dtb, "linux,uefi-mmap-start", (uint64_t)efi_mmap); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map pointer: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-size", efi_mmap_size); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map size: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-desc-size", efi_desc_size); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map descriptor size: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-desc-ver", efi_desc_ver); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map descriptor version: '%s'", fdt_strerror(ret)); + } + } + + p->memmap->map_size = efi_mmap_size; + p->memmap->desc_size = efi_desc_size; + p->memmap->desc_ver = efi_desc_ver; + p->memmap->map_key = efi_mmap_key; + + size_t efi_mmap_entry_count = efi_mmap_size / efi_desc_size; + for (size_t i = 0; i < efi_mmap_entry_count; i++) { + EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size; + + if (entry->Attribute & EFI_MEMORY_RUNTIME) { + // LoongArch kernel requires the virtual address stays in the + // privileged, direct-mapped window + #if defined(__loongarch__) + entry->VirtualStart = entry->PhysicalStart | (0x8ULL << 60); + #else + entry->VirtualStart = entry->PhysicalStart; + #endif + } + } + + memcpy(&p->memmap->descs, efi_mmap, efi_mmap_size); + + EFI_STATUS status = gRT->SetVirtualAddressMap(efi_mmap_size, efi_desc_size, efi_desc_ver, efi_mmap); + if (status != EFI_SUCCESS) { + panic(false, "linux: failed to set UEFI virtual address map: '%X'", (uint64_t)status); + } +} + +noreturn static void jump_to_kernel(struct boot_param *p) { +#if defined(__riscv) + printv("linux: bsp hart %U, device tree blob at %p\n", (uint64_t)bsp_hartid, p->dtb); + + void (*kernel_entry)(uint64_t hartid, uint64_t dtb) = p->kernel_base; + asm ("csrci sstatus, 0x2\n\t" + "csrw sie, zero\n\t"); + kernel_entry(bsp_hartid, (uint64_t)p->dtb); +#elif defined(__aarch64__) + printv("linux: device tree blob at %p\n", p->dtb); + + void (*kernel_entry)(uint64_t dtb, uint64_t res0, uint64_t res1, uint64_t res2) = p->kernel_base; + asm ("msr daifset, 0xF"); + kernel_entry((uint64_t)p->dtb, 0, 0, 0); +#elif defined(__loongarch__) +// LoongArch kernel used to store virtual address in header.kernel_entry +// clearing the high 16bits ensures compatibility +#define TO_PHYS(addr) ((addr) & ((1ULL << 48) - 1)) +#define CSR_DMW_PLV0 1ULL +#define CSR_DMW0_VSEG 0x8000ULL +#define CSR_DMW0_BASE (CSR_DMW0_VSEG << 48) +#define CSR_DMW0_INIT (CSR_DMW0_BASE | CSR_DMW_PLV0) +#define CSR_DMW1_MAT (1 << 4) +#define CSR_DMW1_VSEG 0x9000ULL +#define CSR_DMW1_BASE (CSR_DMW1_VSEG << 48) +#define CSR_DMW1_INIT (CSR_DMW1_BASE | CSR_DMW1_MAT | CSR_DMW_PLV0) +#define CSR_DMW2_VSEG 0xa000ULL +#define CSR_DMW2_MAT (2 << 4) +#define CSR_DMW2_BASE (CSR_DMW2_VSEG << 48) +#define CSR_DMW2_INIT (CSR_DMW2_BASE | CSR_DMW2_MAT | CSR_DMW_PLV0) +#define CSR_DMW3_INIT 0 + + struct linux_header *header = p->kernel_base; + void (*kernel_entry)(uint64_t efi_boot, uint64_t cmdline, uint64_t st); + kernel_entry = p->kernel_base + (TO_PHYS(header->kernel_entry) - header->load_offset); + + asm volatile ("csrxchg $r0, %0, 0x0" :: "r" (0x4) : "memory"); + asm volatile ("csrwr %0, 0x180" :: "r" (CSR_DMW0_INIT) : "memory"); + asm volatile ("csrwr %0, 0x181" :: "r" (CSR_DMW1_INIT) : "memory"); + asm volatile ("csrwr %0, 0x182" :: "r" (CSR_DMW2_INIT) : "memory"); + asm volatile ("csrwr %0, 0x183" :: "r" (CSR_DMW3_INIT) : "memory"); + kernel_entry(1, (uint64_t)p->cmdline, (uint64_t)gST); +#endif + __builtin_unreachable(); +} + +noreturn void linux_load(char *config, char *cmdline) { + struct boot_param p; + memset(&p, 0, sizeof(p)); + p.cmdline = cmdline; + p.dtb = get_device_tree_blob(config, 0x1000); + + struct file_handle *kernel_file; + + char *kernel_path = config_get_value(config, 0, "PATH"); + if (kernel_path == NULL) { + kernel_path = config_get_value(config, 0, "KERNEL_PATH"); + } + if (kernel_path == NULL) { + panic(true, "linux: Kernel path not specified"); + } + + print("linux: Loading kernel `%#`...\n", kernel_path); + + if ((kernel_file = uri_open(kernel_path)) == NULL) { + panic(true, "linux: failed to open kernel `%s`. Is the path correct?", kernel_path); + } + + p.kernel_size = kernel_file->size; + + if (p.kernel_size < sizeof(struct linux_header)) { + panic(true, "linux: kernel too small to contain a valid header"); + } + + struct linux_header tmp_hdr; + fread(kernel_file, &tmp_hdr, 0, sizeof(tmp_hdr)); + + const char *reason = verify_kernel(&tmp_hdr); + if (reason) + panic(true, "linux: invalid kernel image: %s", reason); + + // Use image_size from kernel header for total memory including BSS + size_t kernel_alloc_size = p.kernel_size; + if (tmp_hdr.image_size > kernel_alloc_size) { + kernel_alloc_size = tmp_hdr.image_size; + } + + p.kernel_base = ext_mem_alloc_type_aligned( + ALIGN_UP(kernel_alloc_size, 4096), + MEMMAP_KERNEL_AND_MODULES, 2 * 1024 * 1024); + fread(kernel_file, p.kernel_base, 0, p.kernel_size); + fclose(kernel_file); + printv("linux: loaded kernel `%s` at %p, size %U\n", kernel_path, p.kernel_base, (uint64_t)p.kernel_size); + + load_module(&p, config); + + prepare_device_tree_blob(&p); + + prepare_efi_tables(&p, config); + + prepare_mmap(&p); + + jump_to_kernel(&p); +} + +#endif diff --git a/limine/common/protos/linux_x86.c b/limine/common/protos/linux_x86.c new file mode 100644 index 0000000..ec5520c --- /dev/null +++ b/limine/common/protos/linux_x86.c @@ -0,0 +1,664 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +noreturn void linux_spinup(void *entry, void *boot_params); +#if defined (UEFI) && defined (__x86_64__) + noreturn void linux_spinup64(void *entry, void *boot_params); +#endif + +// The following definitions and struct were copied and adapted from Linux +// kernel headers released under GPL-2.0 WITH Linux-syscall-note +// allowing their inclusion in non GPL compliant code. + +#define EDD_MBR_SIG_MAX 16 +#define E820_MAX_ENTRIES_ZEROPAGE 128 +#define EDDMAXNR 6 + +struct setup_header { + uint8_t setup_sects; + uint16_t root_flags; + uint32_t syssize; + uint16_t ram_size; + uint16_t vid_mode; + uint16_t root_dev; + uint16_t boot_flag; + uint16_t jump; + uint32_t header; + uint16_t version; + uint32_t realmode_swtch; + uint16_t start_sys_seg; + uint16_t kernel_version; + uint8_t type_of_loader; + uint8_t loadflags; + uint16_t setup_move_size; + uint32_t code32_start; + uint32_t ramdisk_image; + uint32_t ramdisk_size; + uint32_t bootsect_kludge; + uint16_t heap_end_ptr; + uint8_t ext_loader_ver; + uint8_t ext_loader_type; + uint32_t cmd_line_ptr; + uint32_t initrd_addr_max; + uint32_t kernel_alignment; + uint8_t relocatable_kernel; + uint8_t min_alignment; + uint16_t xloadflags; + uint32_t cmdline_size; + uint32_t hardware_subarch; + uint64_t hardware_subarch_data; + uint32_t payload_offset; + uint32_t payload_length; + uint64_t setup_data; + uint64_t pref_address; + uint32_t init_size; + uint32_t handover_offset; + uint32_t kernel_info_offset; +} __attribute__((packed)); + +struct apm_bios_info { + uint16_t version; + uint16_t cseg; + uint32_t offset; + uint16_t cseg_16; + uint16_t dseg; + uint16_t flags; + uint16_t cseg_len; + uint16_t cseg_16_len; + uint16_t dseg_len; +}; + +struct ist_info { + uint32_t signature; + uint32_t command; + uint32_t event; + uint32_t perf_level; +}; + +struct sys_desc_table { + uint16_t length; + uint8_t table[14]; +}; + +struct olpc_ofw_header { + uint32_t ofw_magic; /* OFW signature */ + uint32_t ofw_version; + uint32_t cif_handler; /* callback into OFW */ + uint32_t irq_desc_table; +} __attribute__((packed)); + +struct edid_info { + unsigned char dummy[128]; +}; + +struct efi_info { + uint32_t efi_loader_signature; + uint32_t efi_systab; + uint32_t efi_memdesc_size; + uint32_t efi_memdesc_version; + uint32_t efi_memmap; + uint32_t efi_memmap_size; + uint32_t efi_systab_hi; + uint32_t efi_memmap_hi; +}; + +struct boot_e820_entry { + uint64_t addr; + uint64_t size; + uint32_t type; +} __attribute__((packed)); + +struct edd_device_params { + uint16_t length; + uint16_t info_flags; + uint32_t num_default_cylinders; + uint32_t num_default_heads; + uint32_t sectors_per_track; + uint64_t number_of_sectors; + uint16_t bytes_per_sector; + uint32_t dpte_ptr; /* 0xFFFFFFFF for our purposes */ + uint16_t key; /* = 0xBEDD */ + uint8_t device_path_info_length; /* = 44 */ + uint8_t reserved2; + uint16_t reserved3; + uint8_t host_bus_type[4]; + uint8_t interface_type[8]; + union { + struct { + uint16_t base_address; + uint16_t reserved1; + uint32_t reserved2; + } __attribute__ ((packed)) isa; + struct { + uint8_t bus; + uint8_t slot; + uint8_t function; + uint8_t channel; + uint32_t reserved; + } __attribute__ ((packed)) pci; + /* pcix is same as pci */ + struct { + uint64_t reserved; + } __attribute__ ((packed)) ibnd; + struct { + uint64_t reserved; + } __attribute__ ((packed)) xprs; + struct { + uint64_t reserved; + } __attribute__ ((packed)) htpt; + struct { + uint64_t reserved; + } __attribute__ ((packed)) unknown; + } interface_path; + union { + struct { + uint8_t device; + uint8_t reserved1; + uint16_t reserved2; + uint32_t reserved3; + uint64_t reserved4; + } __attribute__ ((packed)) ata; + struct { + uint8_t device; + uint8_t lun; + uint8_t reserved1; + uint8_t reserved2; + uint32_t reserved3; + uint64_t reserved4; + } __attribute__ ((packed)) atapi; + struct { + uint16_t id; + uint64_t lun; + uint16_t reserved1; + uint32_t reserved2; + } __attribute__ ((packed)) scsi; + struct { + uint64_t serial_number; + uint64_t reserved; + } __attribute__ ((packed)) usb; + struct { + uint64_t eui; + uint64_t reserved; + } __attribute__ ((packed)) i1394; + struct { + uint64_t wwid; + uint64_t lun; + } __attribute__ ((packed)) fibre; + struct { + uint64_t identity_tag; + uint64_t reserved; + } __attribute__ ((packed)) i2o; + struct { + uint32_t array_number; + uint32_t reserved1; + uint64_t reserved2; + } __attribute__ ((packed)) raid; + struct { + uint8_t device; + uint8_t reserved1; + uint16_t reserved2; + uint32_t reserved3; + uint64_t reserved4; + } __attribute__ ((packed)) sata; + struct { + uint64_t reserved1; + uint64_t reserved2; + } __attribute__ ((packed)) unknown; + } device_path; + uint8_t reserved4; + uint8_t checksum; +} __attribute__ ((packed)); + +struct edd_info { + uint8_t device; + uint8_t version; + uint16_t interface_support; + uint16_t legacy_max_cylinder; + uint8_t legacy_max_head; + uint8_t legacy_sectors_per_track; + struct edd_device_params params; +} __attribute__ ((packed)); + +struct boot_params { + struct screen_info screen_info; /* 0x000 */ + struct apm_bios_info apm_bios_info; /* 0x040 */ + uint8_t _pad2[4]; /* 0x054 */ + uint64_t tboot_addr; /* 0x058 */ + struct ist_info ist_info; /* 0x060 */ + uint64_t acpi_rsdp_addr; /* 0x070 */ + uint8_t _pad3[8]; /* 0x078 */ + uint8_t hd0_info[16]; /* obsolete! */ /* 0x080 */ + uint8_t hd1_info[16]; /* obsolete! */ /* 0x090 */ + struct sys_desc_table sys_desc_table; /* obsolete! */ /* 0x0a0 */ + struct olpc_ofw_header olpc_ofw_header; /* 0x0b0 */ + uint32_t ext_ramdisk_image; /* 0x0c0 */ + uint32_t ext_ramdisk_size; /* 0x0c4 */ + uint32_t ext_cmd_line_ptr; /* 0x0c8 */ + uint8_t _pad4[116]; /* 0x0cc */ + struct edid_info edid_info; /* 0x140 */ + struct efi_info efi_info; /* 0x1c0 */ + uint32_t alt_mem_k; /* 0x1e0 */ + uint32_t scratch; /* Scratch field! */ /* 0x1e4 */ + uint8_t e820_entries; /* 0x1e8 */ + uint8_t eddbuf_entries; /* 0x1e9 */ + uint8_t edd_mbr_sig_buf_entries; /* 0x1ea */ + uint8_t kbd_status; /* 0x1eb */ + uint8_t secure_boot; /* 0x1ec */ + uint8_t _pad5[2]; /* 0x1ed */ + /* + * The sentinel is set to a nonzero value (0xff) in header.S. + * + * A bootloader is supposed to only take setup_header and put + * it into a clean boot_params buffer. If it turns out that + * it is clumsy or too generous with the buffer, it most + * probably will pick up the sentinel variable too. The fact + * that this variable then is still 0xff will let kernel + * know that some variables in boot_params are invalid and + * kernel should zero out certain portions of boot_params. + */ + uint8_t sentinel; /* 0x1ef */ + uint8_t _pad6[1]; /* 0x1f0 */ + struct setup_header hdr; /* setup header */ /* 0x1f1 */ + uint8_t _pad7[0x290-0x1f1-sizeof(struct setup_header)]; + uint32_t edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 0x290 */ + struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]; /* 0x2d0 */ + uint8_t _pad8[48]; /* 0xcd0 */ + struct edd_info eddbuf[EDDMAXNR]; /* 0xd00 */ + uint8_t _pad9[276]; /* 0xeec */ +} __attribute__((packed)); + +// End of Linux code + +noreturn void linux_load(char *config, char *cmdline) { + struct file_handle *kernel_file; + + char *kernel_path = config_get_value(config, 0, "PATH"); + if (kernel_path == NULL) { + kernel_path = config_get_value(config, 0, "KERNEL_PATH"); + } + if (kernel_path == NULL) { + panic(true, "linux: Kernel path not specified"); + } + + print("linux: Loading kernel `%#`...\n", kernel_path); + + if ((kernel_file = uri_open(kernel_path)) == NULL) + panic(true, "linux: Failed to open kernel with path `%#`. Is the path correct?", kernel_path); + + // Minimum size check: need at least 0x206 bytes for signature at 0x202 + if (kernel_file->size < 0x206) { + panic(true, "linux: Kernel file too small"); + } + +#if defined (UEFI) && defined (__x86_64__) + bool use_64_bit_proto = false; +#endif + + uint32_t signature; + fread(kernel_file, &signature, 0x202, sizeof(uint32_t)); + + // validate signature + if (signature != 0x53726448) { + panic(true, "linux: Invalid kernel signature"); + } + + size_t setup_code_size = 0; + fread(kernel_file, &setup_code_size, 0x1f1, 1); + + if (setup_code_size == 0) + setup_code_size = 4; + + setup_code_size *= 512; + + size_t real_mode_code_size = 512 + setup_code_size; + + if (real_mode_code_size > kernel_file->size) { + panic(true, "linux: Kernel file too small for real mode code"); + } + + struct boot_params *boot_params = ext_mem_alloc(sizeof(struct boot_params)); + + struct setup_header *setup_header = &boot_params->hdr; + + size_t setup_header_end = ({ + uint8_t x; + fread(kernel_file, &x, 0x201, 1); + 0x202 + x; + }); + + if (setup_header_end > kernel_file->size) { + panic(true, "linux: Kernel file too small for setup header"); + } + + fread(kernel_file, setup_header, 0x1f1, setup_header_end - 0x1f1); + + printv("linux: Boot protocol: %u.%u\n", + setup_header->version >> 8, setup_header->version & 0xff); + + if (setup_header->version < 0x203) { + panic(true, "linux: Protocols < 2.03 are not supported"); + } + + setup_header->cmd_line_ptr = (uint32_t)(uintptr_t)cmdline; + + // vid_mode. 0xffff means "normal" + setup_header->vid_mode = 0xffff; + + if (verbose) { + char *kernel_version = ext_mem_alloc(128); + if (setup_header->kernel_version != 0) { + size_t version_offset = (size_t)setup_header->kernel_version + 0x200; + if (version_offset + 128 <= kernel_file->size) { + fread(kernel_file, kernel_version, version_offset, 128); + kernel_version[127] = '\0'; + print("linux: Kernel version: %s\n", kernel_version); + } + } + pmm_free(kernel_version, 128); + } + + setup_header->type_of_loader = 0xff; + + if (!(setup_header->loadflags & (1 << 0))) { + panic(true, "linux: Kernels that load at 0x10000 are not supported"); + } + + setup_header->loadflags &= ~(1 << 5); // print early messages + + // load kernel + size_t kernel_data_size = kernel_file->size - real_mode_code_size; + size_t kernel_alloc_size = kernel_data_size; + if (setup_header->version >= 0x20a && setup_header->init_size > kernel_alloc_size) { + kernel_alloc_size = setup_header->init_size; + } + uintptr_t kernel_align = 0x100000; + if (setup_header->version >= 0x205 && setup_header->kernel_alignment > kernel_align) { + kernel_align = setup_header->kernel_alignment; + } + uintptr_t kernel_load_addr = ALIGN_UP(0x100000, kernel_align); + for (;;) { + if (memmap_alloc_range(kernel_load_addr, + ALIGN_UP(kernel_alloc_size, 4096), + MEMMAP_BOOTLOADER_RECLAIMABLE, MEMMAP_USABLE, false, false, false)) + break; + + if (kernel_load_addr >= 0xfff00000) { + panic(true, "linux: Failed to allocate memory for kernel"); + } + + kernel_load_addr += kernel_align; + } + + fread(kernel_file, (void *)kernel_load_addr, real_mode_code_size, kernel_file->size - real_mode_code_size); + + fclose(kernel_file); + + /////////////////////////////////////// + // Modules + /////////////////////////////////////// + size_t size_of_all_modules = 0; + + size_t module_count; + for (module_count = 0; ; module_count++) { + char *module_path = config_get_value(config, module_count, "MODULE_PATH"); + if (module_path == NULL) + break; + } + + if (module_count == 0) { + goto no_modules; + } + + struct file_handle **modules = ext_mem_alloc(module_count * sizeof(struct file_handle *)); + + for (size_t i = 0; ; i++) { + char *module_path = config_get_value(config, i, "MODULE_PATH"); + if (module_path == NULL) + break; + + print("linux: Loading module `%#`...\n", module_path); + + struct file_handle *module; + if ((module = uri_open(module_path)) == NULL) + panic(true, "linux: Failed to open module with path `%s`. Is the path correct?", module_path); + + if (__builtin_add_overflow(size_of_all_modules, module->size, &size_of_all_modules)) { + panic(true, "linux: Total module size overflow"); + } + + modules[i] = module; + } + + uintptr_t modules_mem_base; + + if (setup_header->version <= 0x202 || setup_header->initrd_addr_max == 0) { + modules_mem_base = 0x38000000; + } else { + modules_mem_base = (uintptr_t)setup_header->initrd_addr_max + 1; + } + + if (size_of_all_modules > modules_mem_base) { + panic(true, "linux: Total module size exceeds available address space"); + } + modules_mem_base -= size_of_all_modules; + modules_mem_base = ALIGN_DOWN(modules_mem_base, 0x100000); + + for (;;) { + if (modules_mem_base < 0x100000) { +#if defined (UEFI) && defined (__x86_64__) + if ((setup_header->xloadflags & 3) == 3) { + modules_mem_base = (uintptr_t)ext_mem_alloc_type_aligned_mode( + size_of_all_modules, + MEMMAP_BOOTLOADER_RECLAIMABLE, + 0x200000, + true + ); + use_64_bit_proto = true; + break; + } +#endif + panic(true, "linux: Failed to allocate memory for modules"); + } + + if (memmap_alloc_range(modules_mem_base, ALIGN_UP(size_of_all_modules, 0x100000), + MEMMAP_BOOTLOADER_RECLAIMABLE, MEMMAP_USABLE, false, false, false)) + break; + + modules_mem_base -= 0x100000; + } + + uintptr_t _modules_mem_base = modules_mem_base; + for (size_t i = 0; ; i++) { + char *module_path = config_get_value(config, i, "MODULE_PATH"); + if (module_path == NULL) + break; + + fread(modules[i], (void *)_modules_mem_base, 0, modules[i]->size); + + _modules_mem_base += modules[i]->size; + + fclose(modules[i]); + } + + pmm_free(modules, module_count * sizeof(struct file_handle *)); + + setup_header->ramdisk_image = (uint32_t)modules_mem_base; +#if defined (UEFI) && defined (__x86_64__) + boot_params->ext_ramdisk_image = (uint32_t)(modules_mem_base >> 32); +#endif + setup_header->ramdisk_size = (uint32_t)size_of_all_modules; +#if defined (UEFI) && defined (__x86_64__) + boot_params->ext_ramdisk_size = (uint32_t)(size_of_all_modules >> 32); +#endif + +no_modules:; + + /////////////////////////////////////// + // Video + /////////////////////////////////////// + + term_notready(); + + struct screen_info *screen_info = &boot_params->screen_info; + +#if defined (BIOS) + { + char *textmode_str = config_get_value(config, 0, "TEXTMODE"); + bool textmode = textmode_str != NULL && strcmp(textmode_str, "yes") == 0; + if (textmode) { + goto set_textmode; + } + } +#endif + + size_t req_width = 0, req_height = 0, req_bpp = 0; + + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + + struct fb_info *fbs; + size_t fbs_count; +#if defined (UEFI) + gop_force_16 = true; +#endif + fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + if (fbs_count == 0) { +#if defined (UEFI) + goto no_fb; +#elif defined (BIOS) +set_textmode:; + vga_textmode_init(false); + + screen_info->orig_video_mode = 3; + screen_info->orig_video_ega_bx = 3; + screen_info->orig_video_lines = 25; + screen_info->orig_video_cols = 80; + screen_info->orig_video_points = 16; + + screen_info->orig_video_isVGA = VIDEO_TYPE_VGAC; +#endif + } else { + screen_info->capabilities = VIDEO_CAPABILITY_64BIT_BASE | VIDEO_CAPABILITY_SKIP_QUIRKS; + screen_info->flags = VIDEO_FLAGS_NOCURSOR; + screen_info->lfb_base = (uint32_t)fbs[0].framebuffer_addr; + screen_info->ext_lfb_base = (uint32_t)(fbs[0].framebuffer_addr >> 32); + screen_info->lfb_size = fbs[0].framebuffer_pitch * fbs[0].framebuffer_height; + screen_info->lfb_width = fbs[0].framebuffer_width; + screen_info->lfb_height = fbs[0].framebuffer_height; + screen_info->lfb_depth = fbs[0].framebuffer_bpp; + screen_info->lfb_linelength = fbs[0].framebuffer_pitch; + screen_info->red_size = fbs[0].red_mask_size; + screen_info->red_pos = fbs[0].red_mask_shift; + screen_info->green_size = fbs[0].green_mask_size; + screen_info->green_pos = fbs[0].green_mask_shift; + screen_info->blue_size = fbs[0].blue_mask_size; + screen_info->blue_pos = fbs[0].blue_mask_shift; + + if (fbs[0].edid != NULL) { + memcpy(&boot_params->edid_info, fbs[0].edid, sizeof(struct edid_info_struct)); + } + +#if defined (BIOS) + screen_info->orig_video_isVGA = VIDEO_TYPE_VLFB; + screen_info->lfb_size = DIV_ROUNDUP(screen_info->lfb_size, 65536); +#elif defined (UEFI) + screen_info->orig_video_isVGA = VIDEO_TYPE_EFI; +#endif + } + +#if defined (UEFI) +no_fb:; +#endif + /////////////////////////////////////// + // RSDP + /////////////////////////////////////// + + boot_params->acpi_rsdp_addr = (uintptr_t)acpi_get_rsdp(); + + /////////////////////////////////////// + // UEFI + /////////////////////////////////////// +#if defined (UEFI) + efi_exit_boot_services(); + +#if defined (__x86_64__) + memcpy(&boot_params->efi_info.efi_loader_signature, "EL64", 4); +#elif defined (__i386__) + memcpy(&boot_params->efi_info.efi_loader_signature, "EL32", 4); +#endif + + boot_params->efi_info.efi_systab = (uint32_t)(uint64_t)(uintptr_t)gST; + boot_params->efi_info.efi_systab_hi = (uint32_t)((uint64_t)(uintptr_t)gST >> 32); + boot_params->efi_info.efi_memmap = (uint32_t)(uint64_t)(uintptr_t)efi_mmap; + boot_params->efi_info.efi_memmap_hi = (uint32_t)((uint64_t)(uintptr_t)efi_mmap >> 32); + boot_params->efi_info.efi_memmap_size = efi_mmap_size; + boot_params->efi_info.efi_memdesc_size = efi_desc_size; + boot_params->efi_info.efi_memdesc_version = efi_desc_ver; +#endif + + /////////////////////////////////////// + // e820 + /////////////////////////////////////// + + struct boot_e820_entry *e820_table = boot_params->e820_table; + + size_t mmap_entries; + struct memmap_entry *mmap = get_raw_memmap(&mmap_entries); + + for (size_t i = 0, j = 0; i < mmap_entries; i++) { + if (mmap[i].type >= 0x1000) { + continue; + } + if (j >= E820_MAX_ENTRIES_ZEROPAGE) { + panic(false, "linux: Too many E820 memory map entries"); + } + e820_table[j].addr = mmap[i].base; + e820_table[j].size = mmap[i].length; + e820_table[j].type = mmap[i].type; + j++; + boot_params->e820_entries = j; + } + + /////////////////////////////////////// + // Spin up + /////////////////////////////////////// + + // Commented out because Linux shouldn't need it and we don't want to + // introduce potential breakages or security weakening. + //iommu_disable_all(); + + irq_flush_type = IRQ_PIC_ONLY_FLUSH; + +#if defined (UEFI) && defined (__x86_64__) + if (use_64_bit_proto == true && (setup_header->xloadflags & 3) == 3) { + flush_irqs(); + linux_spinup64((void *)kernel_load_addr + 0x200, boot_params); + } +#endif + + common_spinup(linux_spinup, 2, (uint32_t)kernel_load_addr, + (uint32_t)(uintptr_t)boot_params); +} + +#endif diff --git a/limine/common/protos/multiboot.h b/limine/common/protos/multiboot.h new file mode 100644 index 0000000..00def66 --- /dev/null +++ b/limine/common/protos/multiboot.h @@ -0,0 +1,17 @@ +#ifndef PROTOS__MULTIBOOT_H__ +#define PROTOS__MULTIBOOT_H__ + +#include +#include + +struct mb_reloc_stub { + char jmp[4]; + uint32_t magic; + uint32_t entry_point; + uint32_t mb_info_target; +}; + +extern symbol multiboot_spinup_32; +extern symbol multiboot_reloc_stub, multiboot_reloc_stub_end; + +#endif diff --git a/limine/common/protos/multiboot1.c b/limine/common/protos/multiboot1.c new file mode 100644 index 0000000..d7c5c20 --- /dev/null +++ b/limine/common/protos/multiboot1.c @@ -0,0 +1,509 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIMINE_BRAND "Limine " LIMINE_VERSION + +#define MEMMAP_MAX 256 + +// Returns the size required to store the multiboot info. +static size_t get_multiboot1_info_size( + char *cmdline, + size_t modules_count, size_t modules_cmdlines_size, + uint32_t section_entry_size, uint32_t section_num +) { + return ALIGN_UP(sizeof(struct multiboot1_info), 16) + // base structure + ALIGN_UP(strlen(cmdline) + 1, 16) + // cmdline + ALIGN_UP(sizeof(LIMINE_BRAND), 16) + // bootloader brand + ALIGN_UP(section_entry_size * section_num, 16) + // ELF info + ALIGN_UP(sizeof(struct multiboot1_module) * modules_count, 16) + // modules count + ALIGN_UP(modules_cmdlines_size, 16) + // modules command lines + ALIGN_UP(sizeof(struct multiboot1_mmap_entry) * MEMMAP_MAX, 16); // memory map +} + +static void *mb1_info_alloc(void **mb1_info_raw, size_t size) { + void *ret = *mb1_info_raw; + *mb1_info_raw += ALIGN_UP(size, 16); + return ret; +} + +noreturn void multiboot1_load(char *config, char *cmdline) { + struct file_handle *kernel_file; + + char *kernel_path = config_get_value(config, 0, "PATH"); + if (kernel_path == NULL) { + kernel_path = config_get_value(config, 0, "KERNEL_PATH"); + } + if (kernel_path == NULL) { + panic(true, "multiboot1: Executable path not specified"); + } + + print("multiboot1: Loading executable `%#`...\n", kernel_path); + + if ((kernel_file = uri_open(kernel_path)) == NULL) + panic(true, "multiboot1: Failed to open executable with path `%#`. Is the path correct?", kernel_path); + + uint8_t *kernel = freadall(kernel_file, MEMMAP_KERNEL_AND_MODULES); + + size_t kernel_file_size = kernel_file->size; + + fclose(kernel_file); + + struct multiboot1_header header = {0}; + size_t header_offset = 0; + + // Per Multiboot spec, header must be within first 8192 bytes and 4-byte aligned. + // Ensure we don't read past end of file when checking magic or copying header. + size_t search_limit = 8192; + if (kernel_file_size < sizeof(struct multiboot1_header)) { + panic(true, "multiboot1: Kernel file too small to contain header"); + } + if (search_limit > kernel_file_size - sizeof(struct multiboot1_header)) { + search_limit = kernel_file_size - sizeof(struct multiboot1_header); + } + + for (header_offset = 0; header_offset <= search_limit; header_offset += 4) { + uint32_t v = *(uint32_t *)(kernel + header_offset); + + if (v == MULTIBOOT1_HEADER_MAGIC) { + memcpy(&header, kernel + header_offset, sizeof(header)); + break; + } + } + + if (header.magic != MULTIBOOT1_HEADER_MAGIC) { + panic(true, "multiboot1: Invalid magic"); + } + + if (header.magic + header.flags + header.checksum) + panic(true, "multiboot1: Header checksum is invalid"); + + bool section_hdr_info_valid = false; + struct elf_section_hdr_info section_hdr_info = {0}; + + uint64_t entry_point; + struct elsewhere_range *ranges; + uint64_t ranges_count = 1; + + if (header.flags & (1 << 16)) { + if (header.load_addr > header.header_addr) + panic(true, "multiboot1: Illegal load address"); + + size_t addr_diff = header.header_addr - header.load_addr; + if (addr_diff > header_offset) + panic(true, "multiboot1: Address tag offset underflow"); + + size_t load_src = header_offset - addr_diff; + + size_t load_size; + if (header.load_end_addr) { + if (header.load_end_addr < header.load_addr) + panic(true, "multiboot1: Load end address less than load address"); + load_size = header.load_end_addr - header.load_addr; + } else { + if (load_src > kernel_file_size) + panic(true, "multiboot1: Load source exceeds kernel file size"); + load_size = kernel_file_size - load_src; + } + + uint32_t bss_size = 0; + if (header.bss_end_addr) { + uintptr_t bss_addr = header.load_addr + load_size; + if (header.bss_end_addr < bss_addr) + panic(true, "multiboot1: Illegal bss end address"); + + bss_size = header.bss_end_addr - bss_addr; + } + + if (load_src + load_size > kernel_file_size) { + panic(true, "multiboot1: load_src + load_size exceeds kernel file size"); + } + + size_t full_size = load_size + bss_size; + + void *elsewhere = ext_mem_alloc(full_size); + + memcpy(elsewhere, kernel + load_src, load_size); + + entry_point = header.entry_addr; + + ranges = ext_mem_alloc(sizeof(struct elsewhere_range)); + + ranges->elsewhere = (uintptr_t)elsewhere; + ranges->target = header.load_addr; + ranges->length = full_size; + } else { + int bits = elf_bits(kernel); + + switch (bits) { + case 32: + if (!elf32_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges)) + panic(true, "multiboot1: ELF32 load failure"); + + section_hdr_info = elf32_section_hdr_info(kernel); + section_hdr_info_valid = true; + break; + case 64: { + if (!elf64_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges)) + panic(true, "multiboot1: ELF64 load failure"); + + section_hdr_info = elf64_section_hdr_info(kernel); + section_hdr_info_valid = true; + break; + } + default: + panic(true, "multiboot1: Invalid ELF file bitness"); + } + } + + size_t n_modules; + size_t modules_cmdlines_size = 0; + + for (n_modules = 0;; n_modules++) { + struct conf_tuple conf_tuple = config_get_tuple(config, n_modules, "MODULE_PATH", "MODULE_STRING"); + if (!conf_tuple.value1) break; + + char *module_cmdline = conf_tuple.value2; + if (!module_cmdline) module_cmdline = ""; + modules_cmdlines_size += ALIGN_UP(strlen(module_cmdline) + 1, 16); + } + + size_t mb1_info_size = get_multiboot1_info_size( + cmdline, + n_modules, + modules_cmdlines_size, + section_hdr_info_valid ? section_hdr_info.section_entry_size : 0, + section_hdr_info_valid ? section_hdr_info.num : 0 + ); + + // Realloc elsewhere ranges to include mb1 info, modules, and elf sections + struct elsewhere_range *new_ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * + (ranges_count + + 1 /* mb1 info range */ + + n_modules + + (section_hdr_info_valid ? section_hdr_info.num : 0))); + + memcpy(new_ranges, ranges, sizeof(struct elsewhere_range) * ranges_count); + pmm_free(ranges, sizeof(struct elsewhere_range) * ranges_count); + ranges = new_ranges; + + // GRUB allocates boot info at 0x10000, *except* if the kernel happens + // to overlap this region, then it gets moved to right after the + // kernel, or whichever PHDR happens to sit at 0x10000. + // Allocate it wherever, then move it to where GRUB puts it + // afterwards. + + // Elsewhere append mb1 info *after* kernel but *before* modules. + void *mb1_info_raw = ext_mem_alloc(mb1_info_size); + uint64_t mb1_info_final_loc = 0x10000; + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + mb1_info_raw, &mb1_info_final_loc, mb1_info_size)) { + panic(true, "multiboot1: Cannot allocate mb1 info"); + } + + size_t mb1_info_slide = (size_t)mb1_info_raw - mb1_info_final_loc; + + struct multiboot1_info *multiboot1_info = + mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_info)); + + if (section_hdr_info_valid == true) { + size_t section_table_size = (size_t)section_hdr_info.section_entry_size * section_hdr_info.num; + if (section_hdr_info.section_offset > kernel_file_size || + section_table_size > kernel_file_size - section_hdr_info.section_offset) { + panic(true, "multiboot1: ELF section headers out of bounds"); + } + + multiboot1_info->elf_sect.num = section_hdr_info.num; + multiboot1_info->elf_sect.size = section_hdr_info.section_entry_size; + multiboot1_info->elf_sect.shndx = section_hdr_info.str_section_idx; + + void *sections = mb1_info_alloc(&mb1_info_raw, section_table_size); + + multiboot1_info->elf_sect.addr = (uintptr_t)sections - mb1_info_slide; + + memcpy(sections, kernel + section_hdr_info.section_offset, section_table_size); + + int bits = elf_bits(kernel); + + for (size_t i = 0; i < section_hdr_info.num; i++) { + if (bits == 64) { + struct elf64_shdr *shdr = (void *)sections + i * section_hdr_info.section_entry_size; + + if (shdr->sh_addr != 0 || shdr->sh_size == 0) { + continue; + } + + if (shdr->sh_offset > kernel_file_size || + shdr->sh_size > kernel_file_size - shdr->sh_offset) { + continue; + } + + uint64_t section = (uint64_t)-1; /* no target preference, use top */ + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + kernel + shdr->sh_offset, §ion, shdr->sh_size)) { + panic(true, "multiboot1: Cannot allocate elf sections"); + } + + shdr->sh_addr = section; + } else { + struct elf32_shdr *shdr = (void *)sections + i * section_hdr_info.section_entry_size; + + if (shdr->sh_addr != 0 || shdr->sh_size == 0) { + continue; + } + + if (shdr->sh_offset > kernel_file_size || + shdr->sh_size > kernel_file_size - shdr->sh_offset) { + continue; + } + + uint64_t section = (uint64_t)-1; /* no target preference, use top */ + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + kernel + shdr->sh_offset, §ion, shdr->sh_size)) { + panic(true, "multiboot1: Cannot allocate elf sections"); + } + + shdr->sh_addr = section; + } + } + + multiboot1_info->flags |= (1 << 5); + } + + if (n_modules) { + struct multiboot1_module *mods = + mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_module) * n_modules); + + multiboot1_info->mods_count = n_modules; + multiboot1_info->mods_addr = (size_t)mods - mb1_info_slide; + + for (size_t i = 0; i < n_modules; i++) { + struct multiboot1_module *m = mods + i; + + struct conf_tuple conf_tuple = config_get_tuple(config, i, "MODULE_PATH", "MODULE_STRING"); + char *module_path = conf_tuple.value1; + if (module_path == NULL) + panic(true, "multiboot1: Module disappeared unexpectedly"); + + print("multiboot1: Loading module `%#`...\n", module_path); + + struct file_handle *f; + if ((f = uri_open(module_path)) == NULL) + panic(true, "multiboot1: Failed to open module with path `%#`. Is the path correct?", module_path); + + char *module_cmdline = conf_tuple.value2; + if (module_cmdline == NULL) { + module_cmdline = ""; + } + char *lowmem_modstr = mb1_info_alloc(&mb1_info_raw, strlen(module_cmdline) + 1); + strcpy(lowmem_modstr, module_cmdline); + + void *module_addr = freadall(f, MEMMAP_BOOTLOADER_RECLAIMABLE); + uint64_t module_target = (uint64_t)-1; /* no target preference, use top */ + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + module_addr, &module_target, f->size)) { + panic(true, "multiboot1: Cannot allocate module"); + } + + m->begin = module_target; + m->end = m->begin + f->size; + m->cmdline = (uint32_t)(size_t)lowmem_modstr - mb1_info_slide; + m->pad = 0; + + fclose(f); + + if (verbose) { + print("multiboot1: Requested module %u:\n", (uint32_t)i); + print(" Path: %s\n", module_path); + print(" String: \"%s\"\n", module_cmdline ?: ""); + print(" Begin: %x\n", m->begin); + print(" End: %x\n", m->end); + } + } + + multiboot1_info->flags |= (1 << 3); + } + + char *lowmem_cmdline = mb1_info_alloc(&mb1_info_raw, strlen(cmdline) + 1); + strcpy(lowmem_cmdline, cmdline); + multiboot1_info->cmdline = (uint32_t)(size_t)lowmem_cmdline - mb1_info_slide; + if (cmdline) + multiboot1_info->flags |= (1 << 2); + + char *bootload_name = LIMINE_BRAND; + char *lowmem_bootname = mb1_info_alloc(&mb1_info_raw, strlen(bootload_name) + 1); + strcpy(lowmem_bootname, bootload_name); + + multiboot1_info->bootloader_name = (uint32_t)(size_t)lowmem_bootname - mb1_info_slide; + multiboot1_info->flags |= (1 << 9); + + term_notready(); + + size_t req_width = 0; + size_t req_height = 0; + size_t req_bpp = 0; +#if defined (BIOS) + { + char *textmode_str = config_get_value(config, 0, "TEXTMODE"); + bool textmode = textmode_str != NULL && strcmp(textmode_str, "yes") == 0; + if (textmode) { + goto textmode; + } + } +#endif + + + if (header.flags & (1 << 2)) { + req_width = header.fb_width; + req_height = header.fb_height; + req_bpp = header.fb_bpp; + + if (header.fb_mode == 0) { +#if defined (UEFI) +modeset:; +#endif + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + + struct fb_info *fbs; + size_t fbs_count; + fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + if (fbs_count == 0) { +#if defined (UEFI) + goto skip_modeset; +#elif defined (BIOS) +textmode: + vga_textmode_init(false); + + multiboot1_info->fb_addr = 0xb8000; + multiboot1_info->fb_width = 80; + multiboot1_info->fb_height = 25; + multiboot1_info->fb_bpp = 16; + multiboot1_info->fb_pitch = 2 * 80; + multiboot1_info->fb_type = 2; +#endif + } else { + multiboot1_info->fb_addr = (uint64_t)fbs[0].framebuffer_addr; + multiboot1_info->fb_width = fbs[0].framebuffer_width; + multiboot1_info->fb_height = fbs[0].framebuffer_height; + multiboot1_info->fb_bpp = fbs[0].framebuffer_bpp; + multiboot1_info->fb_pitch = fbs[0].framebuffer_pitch; + multiboot1_info->fb_type = 1; + multiboot1_info->fb_red_mask_size = fbs[0].red_mask_size; + multiboot1_info->fb_red_mask_shift = fbs[0].red_mask_shift; + multiboot1_info->fb_green_mask_size = fbs[0].green_mask_size; + multiboot1_info->fb_green_mask_shift = fbs[0].green_mask_shift; + multiboot1_info->fb_blue_mask_size = fbs[0].blue_mask_size; + multiboot1_info->fb_blue_mask_shift = fbs[0].blue_mask_shift; + } + } else { +#if defined (UEFI) + print("multiboot1: Warning: Cannot use text mode with UEFI\n"); + goto modeset; +#elif defined (BIOS) + goto textmode; +#endif + } + + multiboot1_info->flags |= (1 << 12); + +#if defined (UEFI) +skip_modeset:; +#endif + } else { +#if defined (UEFI) + panic(true, "multiboot1: Cannot use text mode with UEFI."); +#elif defined (BIOS) + vga_textmode_init(false); +#endif + } + + // Load relocation stub where it won't get overwritten (hopefully) + size_t reloc_stub_size = (size_t)multiboot_reloc_stub_end - (size_t)multiboot_reloc_stub; + void *reloc_stub = ext_mem_alloc(reloc_stub_size); + memcpy(reloc_stub, multiboot_reloc_stub, reloc_stub_size); + +#if defined (UEFI) + efi_exit_boot_services(); +#endif + + size_t mb_mmap_count; + struct memmap_entry *raw_memmap = get_raw_memmap(&mb_mmap_count); + + if (mb_mmap_count > MEMMAP_MAX) { + panic(false, "multiboot1: Too many memory map entries."); + } + + size_t mb_mmap_len = mb_mmap_count * sizeof(struct multiboot1_mmap_entry); + struct multiboot1_mmap_entry *mmap = mb1_info_alloc(&mb1_info_raw, mb_mmap_len); + + // Multiboot is bad and passes raw memmap. We do the same to support it. + for (size_t i = 0; i < mb_mmap_count; i++) { + mmap[i].size = sizeof(struct multiboot1_mmap_entry) - 4; + mmap[i].addr = raw_memmap[i].base; + mmap[i].len = raw_memmap[i].length; + mmap[i].type = raw_memmap[i].type; + } + + struct meminfo memory_info = mmap_get_info(mb_mmap_count, raw_memmap); + + // Convert the uppermem and lowermem fields from bytes to + // KiB. + multiboot1_info->mem_lower = memory_info.lowermem / 1024; + multiboot1_info->mem_upper = memory_info.uppermem / 1024; + + multiboot1_info->mmap_length = mb_mmap_len; + multiboot1_info->mmap_addr = (uint32_t)(size_t)mmap - mb1_info_slide; + multiboot1_info->flags |= (1 << 0) | (1 << 6); + + if (rdmsr(0x1b) & (1 << 10)) { + if (x2apic_disable()) { + printv("multiboot1: Firmware had x2APIC enabled, reverted to xAPIC mode\n"); + } else { + printv("multiboot1: Firmware has x2APIC enabled and it could not be disabled\n"); + } + } + + iommu_disable_all(); + + irq_flush_type = IRQ_PIC_ONLY_FLUSH; + + common_spinup(multiboot_spinup_32, 6, + (uint32_t)(uintptr_t)reloc_stub, (uint32_t)0x2badb002, + (uint32_t)mb1_info_final_loc, (uint32_t)entry_point, + (uint32_t)(uintptr_t)ranges, (uint32_t)ranges_count); +} + +#endif diff --git a/limine/common/protos/multiboot1.h b/limine/common/protos/multiboot1.h new file mode 100644 index 0000000..e94e4f5 --- /dev/null +++ b/limine/common/protos/multiboot1.h @@ -0,0 +1,100 @@ +#ifndef PROTOS__MULTIBOOT1_H__ +#define PROTOS__MULTIBOOT1_H__ + +#include +#include +#include + +#define MULTIBOOT1_HEADER_MAGIC 0x1BADB002 + +struct multiboot1_header { + uint32_t magic; + uint32_t flags; + uint32_t checksum; + + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; + uint32_t entry_addr; + + uint32_t fb_mode; + uint32_t fb_width; + uint32_t fb_height; + uint32_t fb_bpp; +}; + +struct multiboot1_elf_sections { + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; +}; + +struct multiboot1_info { + uint32_t flags; + + uint32_t mem_lower; + uint32_t mem_upper; + + uint32_t boot_device; + + uint32_t cmdline; + + uint32_t mods_count; + uint32_t mods_addr; + + struct multiboot1_elf_sections elf_sect; + + uint32_t mmap_length; + uint32_t mmap_addr; + + uint32_t drives_length; + uint32_t drivers_addr; + + uint32_t rom_config_table; + + uint32_t bootloader_name; + + uint32_t apm_table; + + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; + + uint64_t fb_addr; + uint32_t fb_pitch; + uint32_t fb_width; + uint32_t fb_height; + uint8_t fb_bpp; + uint8_t fb_type; + uint16_t fb_reserved; + + uint8_t fb_red_mask_shift; + uint8_t fb_red_mask_size; + uint8_t fb_green_mask_shift; + uint8_t fb_green_mask_size; + uint8_t fb_blue_mask_shift; + uint8_t fb_blue_mask_size; +}; + +struct multiboot1_module { + uint32_t begin; + uint32_t end; + uint32_t cmdline; + uint32_t pad; +}; + +struct multiboot1_mmap_entry { + uint32_t size; + uint64_t addr; + uint64_t len; + uint32_t type; +} __attribute__((packed)); + +noreturn void multiboot1_load(char *config, char *cmdline); + +#endif diff --git a/limine/common/protos/multiboot2.c b/limine/common/protos/multiboot2.c new file mode 100644 index 0000000..3acbdba --- /dev/null +++ b/limine/common/protos/multiboot2.c @@ -0,0 +1,1024 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIMINE_BRAND "Limine " LIMINE_VERSION + +#define MEMMAP_MAX 256 +#define EFI_MEMMAP_MAX 1024 + +/// Returns the size required to store the multiboot2 info. +static size_t get_multiboot2_info_size( + char *cmdline, + size_t modules_size, + uint32_t section_entry_size, uint32_t section_num, + uint32_t smbios_tag_size +) { + return ALIGN_UP(sizeof(struct multiboot2_start_tag), MULTIBOOT_TAG_ALIGN) + // start + ALIGN_UP(sizeof(struct multiboot_tag_string) + strlen(cmdline) + 1, MULTIBOOT_TAG_ALIGN) + // cmdline + ALIGN_UP(sizeof(struct multiboot_tag_string) + sizeof(LIMINE_BRAND), MULTIBOOT_TAG_ALIGN) + // bootloader brand + ALIGN_UP(sizeof(struct multiboot_tag_framebuffer), MULTIBOOT_TAG_ALIGN) + // framebuffer + ALIGN_UP(sizeof(struct multiboot_tag_new_acpi) + sizeof(struct rsdp), MULTIBOOT_TAG_ALIGN) + // new ACPI info + ALIGN_UP(sizeof(struct multiboot_tag_old_acpi) + 20, MULTIBOOT_TAG_ALIGN) + // old ACPI info + ALIGN_UP(sizeof(struct multiboot_tag_elf_sections) + section_entry_size * section_num, MULTIBOOT_TAG_ALIGN) + // ELF info + ALIGN_UP(modules_size, MULTIBOOT_TAG_ALIGN) + // modules + ALIGN_UP(sizeof(struct multiboot_tag_load_base_addr), MULTIBOOT_TAG_ALIGN) + // load base address + ALIGN_UP(smbios_tag_size, MULTIBOOT_TAG_ALIGN) + // SMBIOS + ALIGN_UP(sizeof(struct multiboot_tag_basic_meminfo), MULTIBOOT_TAG_ALIGN) + // basic memory info + ALIGN_UP(sizeof(struct multiboot_tag_mmap) + sizeof(struct multiboot_mmap_entry) * MEMMAP_MAX, MULTIBOOT_TAG_ALIGN) + // MMAP + #if defined (UEFI) + ALIGN_UP(sizeof(struct multiboot_tag_efi_mmap) + (efi_desc_size * EFI_MEMMAP_MAX), MULTIBOOT_TAG_ALIGN) + // EFI MMAP + #if defined (__i386__) + ALIGN_UP(sizeof(struct multiboot_tag_efi32), MULTIBOOT_TAG_ALIGN) + // EFI system table 32 + ALIGN_UP(sizeof(struct multiboot_tag_efi32_ih), MULTIBOOT_TAG_ALIGN) + // EFI image handle 32 + #elif defined (__x86_64__) + ALIGN_UP(sizeof(struct multiboot_tag_efi64), MULTIBOOT_TAG_ALIGN) + // EFI system table 64 + ALIGN_UP(sizeof(struct multiboot_tag_efi64_ih), MULTIBOOT_TAG_ALIGN) + // EFI image handle 64 + #endif + #endif + ALIGN_UP(sizeof(struct multiboot_tag_network) + DHCP_ACK_PACKET_LEN, MULTIBOOT_TAG_ALIGN) + // network info + ALIGN_UP(sizeof(struct multiboot_tag), MULTIBOOT_TAG_ALIGN); // end +} + +#define append_tag(P, TAG) do { \ + (P) += ALIGN_UP((TAG)->size, MULTIBOOT_TAG_ALIGN); \ +} while (0) + +noreturn void multiboot2_load(char *config, char* cmdline) { + struct file_handle *kernel_file; + + char *kernel_path = config_get_value(config, 0, "PATH"); + if (kernel_path == NULL) { + kernel_path = config_get_value(config, 0, "KERNEL_PATH"); + } + if (kernel_path == NULL) { + panic(true, "multiboot2: Executable path not specified"); + } + + print("multiboot2: Loading executable `%#`...\n", kernel_path); + + if ((kernel_file = uri_open(kernel_path)) == NULL) + panic(true, "multiboot2: Failed to open executable with path `%#`. Is the path correct?", kernel_path); + + uint8_t *kernel = freadall(kernel_file, MEMMAP_KERNEL_AND_MODULES); + + size_t kernel_file_size = kernel_file->size; + + fclose(kernel_file); + + struct multiboot_header *header = NULL; + + // Per Multiboot2 spec, header must be within first 32768 bytes and 8-byte aligned. + // Ensure we don't read past end of file when checking magic. + size_t search_limit = MULTIBOOT_SEARCH; + if (kernel_file_size < sizeof(struct multiboot_header)) { + panic(true, "multiboot2: Kernel file too small to contain header"); + } + if (search_limit > kernel_file_size - sizeof(struct multiboot_header)) { + search_limit = kernel_file_size - sizeof(struct multiboot_header); + } + + for (size_t header_offset = 0; header_offset <= search_limit; header_offset += MULTIBOOT_HEADER_ALIGN) { + header = (void *)(kernel + header_offset); + + if (header->magic == MULTIBOOT2_HEADER_MAGIC) { + break; + } + } + + if (header == NULL || header->magic != MULTIBOOT2_HEADER_MAGIC) { + panic(true, "multiboot2: Invalid magic"); + } + + if (header->magic + header->architecture + header->checksum + header->header_length) { + panic(true, "multiboot2: Header checksum is invalid"); + } + + if (header->architecture != MULTIBOOT_ARCHITECTURE_I386) { + panic(true, "multiboot2: Unsupported architecture %u (expected i386)", header->architecture); + } + + size_t header_offset_in_file = (uintptr_t)header - (uintptr_t)kernel; + if (header->header_length > kernel_file_size - header_offset_in_file) { + panic(true, "multiboot2: Header length exceeds kernel file size"); + } + + struct multiboot_header_tag_address *addresstag = NULL; + struct multiboot_header_tag_framebuffer *fbtag = NULL; + + bool has_reloc_header = false; + struct multiboot_header_tag_relocatable reloc_tag = {0}; + + bool is_new_acpi_required = false; + bool is_old_acpi_required = false; + + bool is_elf_info_requested = false; + +#if defined (UEFI) + bool is_framebuffer_required = false; +#endif + + uint64_t entry_point = 0xffffffff; + + // Iterate through the entries... + for (struct multiboot_header_tag *tag = (struct multiboot_header_tag*)(header + 1); // header + 1 to skip the header struct. + tag < (struct multiboot_header_tag *)((uintptr_t)header + header->header_length) && tag->type != MULTIBOOT_HEADER_TAG_END; + tag = (struct multiboot_header_tag *)((uintptr_t)tag + ALIGN_UP(tag->size, MULTIBOOT_TAG_ALIGN))) { + if (tag->size == 0) { + break; + } + bool is_required = !(tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL); + switch (tag->type) { + case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: { + // Iterate the requests and check if they are supported by or not. + struct multiboot_header_tag_information_request *request = (void *)tag; + if (request->size < sizeof(struct multiboot_header_tag_information_request)) { + panic(true, "multiboot2: Invalid information request tag size"); + } + uint32_t size = (request->size - sizeof(struct multiboot_header_tag_information_request)) / sizeof(uint32_t); + + for (uint32_t i = 0; i < size; i++) { + uint32_t r = request->requests[i]; + + switch (r) { + // We already support the following requests: + case MULTIBOOT_TAG_TYPE_CMDLINE: + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + case MULTIBOOT_TAG_TYPE_MODULE: + case MULTIBOOT_TAG_TYPE_MMAP: + case MULTIBOOT_TAG_TYPE_SMBIOS: + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + break; + #if defined (UEFI) + case MULTIBOOT_TAG_TYPE_EFI_MMAP: + break; + case MULTIBOOT_TAG_TYPE_EFI32: + case MULTIBOOT_TAG_TYPE_EFI32_IH: + case MULTIBOOT_TAG_TYPE_EFI64: + case MULTIBOOT_TAG_TYPE_EFI64_IH: + break; + #endif + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + #if defined (UEFI) + is_framebuffer_required = is_required; + #endif + break; + case MULTIBOOT_TAG_TYPE_ACPI_NEW: + is_new_acpi_required = is_required; + break; + case MULTIBOOT_TAG_TYPE_ACPI_OLD: + is_old_acpi_required = is_required; + break; + case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: + is_elf_info_requested = is_required; + break; + case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR: + case MULTIBOOT_TAG_TYPE_NETWORK: + break; + default: + if (is_required) + panic(true, "multiboot2: Requested tag `%d` which is not supported", r); + break; + } + } + break; + } + case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS: { +#if defined (UEFI) + struct multiboot_header_tag_console_flags *flags = (void *)tag; + if ((flags->console_flags & (1 << 1)) && (flags->console_flags & (1 << 0))) { + panic(true, "multiboot2: OS requested EGA text mode, but UEFI does not support it"); + } +#endif + break; + } + case MULTIBOOT_HEADER_TAG_FRAMEBUFFER: { + fbtag = (void *)tag; + break; + } + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: { + struct multiboot_header_tag_entry_address *entrytag = (void *)tag; + entry_point = entrytag->entry_addr; + break; + } + case MULTIBOOT_HEADER_TAG_ADDRESS: { + addresstag = (void *)tag; + break; + } + // We always align the modules ;^) + case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: + break; + case MULTIBOOT_HEADER_TAG_EFI_BS: + print("multiboot2: Warning: EFI_BS tag requested but not supported\n"); + if (is_required) { + panic(true, "multiboot2: EFI_BS tag is required but not supported"); + } + break; + + case MULTIBOOT_HEADER_TAG_RELOCATABLE: { + has_reloc_header = true; + struct multiboot_header_tag_relocatable *reloc_tag_ptr = (void *)tag; + reloc_tag = *reloc_tag_ptr; + break; + } + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: { + // Ignore EFI64 entry address tag as we do not support it. + break; + } + + default: + if (is_required) { + if (tag->type <= 10 /* max specified ID */) { + panic(true, "multiboot2: Unsupported header tag type: %u\n", tag->type); + } else { + panic(true, "multiboot2: Unknown custom header tag type: %u\n", tag->type); + } + } else { + if (tag->type > 10) { + print("multiboot2: Unknown custom header tag type: %u\n", tag->type); + } + } + } + } + + bool section_hdr_info_valid = false; + struct elf_section_hdr_info section_hdr_info = {0}; + + struct elsewhere_range *ranges; + uint64_t ranges_count = 1; + + if (addresstag != NULL) { + size_t header_offset = (size_t)header - (size_t)kernel; + + uintptr_t load_src, load_addr; + if (addresstag->load_addr != (uint32_t)-1) { + if (addresstag->load_addr > addresstag->header_addr) { + panic(true, "multiboot2: Illegal load address"); + } + + size_t addr_diff = addresstag->header_addr - addresstag->load_addr; + if (addr_diff > header_offset) { + panic(true, "multiboot2: Address tag offset underflow"); + } + load_src = header_offset - addr_diff; + load_addr = addresstag->load_addr; + } else { + if (header_offset > addresstag->header_addr) { + panic(true, "multiboot2: Header offset exceeds header address"); + } + load_src = 0; + load_addr = addresstag->header_addr - header_offset; + } + + size_t load_size; + if (addresstag->load_end_addr != 0) { + if (addresstag->load_end_addr < load_addr) { + panic(true, "multiboot2: Load end address less than load address"); + } + load_size = addresstag->load_end_addr - load_addr; + } else { + if (load_src > kernel_file_size) { + panic(true, "multiboot2: Load source exceeds kernel file size"); + } + load_size = kernel_file_size - load_src; + } + + size_t bss_size = 0; + if (addresstag->bss_end_addr != 0) { + uintptr_t bss_addr = load_addr + load_size; + if (addresstag->bss_end_addr < bss_addr) { + panic(true, "multiboot2: Illegal bss end address"); + } + + bss_size = addresstag->bss_end_addr - bss_addr; + } + + if (load_src + load_size > kernel_file_size) { + panic(true, "multiboot2: load_src + load_size exceeds kernel file size"); + } + + size_t full_size = load_size + bss_size; + + void *elsewhere = ext_mem_alloc(full_size); + + memcpy(elsewhere, kernel + load_src, load_size); + + if (entry_point == 0xffffffff) { + panic(true, "multiboot2: Using address tag but entry address tag missing"); + } + + ranges = ext_mem_alloc(sizeof(struct elsewhere_range)); + + ranges->elsewhere = (uintptr_t)elsewhere; + ranges->target = load_addr; + ranges->length = full_size; + } else { + uint64_t e; + int bits = elf_bits(kernel); + + switch (bits) { + case 32: + if (!elf32_load_elsewhere(kernel, kernel_file_size, &e, &ranges)) + panic(true, "multiboot2: ELF32 load failure"); + + section_hdr_info = elf32_section_hdr_info(kernel); + section_hdr_info_valid = true; + break; + case 64: { + if (!elf64_load_elsewhere(kernel, kernel_file_size, &e, &ranges)) + panic(true, "multiboot2: ELF64 load failure"); + + section_hdr_info = elf64_section_hdr_info(kernel); + section_hdr_info_valid = true; + break; + } + default: + panic(true, "multiboot2: Invalid ELF file bitness"); + } + + if (entry_point == 0xffffffff) { + entry_point = e; + } + } + + int64_t reloc_slide = 0; + + if (has_reloc_header) { + if (reloc_tag.align == 0) { + panic(true, "multiboot2: Relocatable tag has align=0"); + } + + bool reloc_ascend; + uint64_t relocated_base; + + switch (reloc_tag.preference) { + default: + case 0: case 1: // No preference / prefer lowest + reloc_ascend = true; + relocated_base = ALIGN_UP(reloc_tag.min_addr, reloc_tag.align); + if (relocated_base + ranges->length > reloc_tag.max_addr) { + goto reloc_fail; + } + break; + case 2: // Prefer highest + reloc_ascend = false; + if (ranges->length > reloc_tag.max_addr) { + goto reloc_fail; + } + relocated_base = ALIGN_DOWN(reloc_tag.max_addr - ranges->length, reloc_tag.align); + if (relocated_base < reloc_tag.min_addr) { + goto reloc_fail; + } + break; + } + + for (;;) { + if (check_usable_memory(relocated_base, relocated_base + ranges->length)) { + break; + } + + if (reloc_ascend) { + relocated_base += reloc_tag.align; + if (relocated_base + ranges->length > reloc_tag.max_addr) { + goto reloc_fail; + } + } else { + if (relocated_base - reloc_tag.min_addr < reloc_tag.align) { + goto reloc_fail; + } + relocated_base -= reloc_tag.align; + } + } + + reloc_slide = (int64_t)relocated_base - ranges->target; + + entry_point += reloc_slide; + + ranges->target = relocated_base; + } + + if (!check_usable_memory(ranges->target, ranges->target + ranges->length)) { +reloc_fail: + panic(true, "multiboot2: Could not find viable load address for executable"); + } + + // Get the load base address (AKA the lowest target in the ranges) + uint64_t load_base_addr = ranges->target; + + size_t modules_size = 0; + size_t n_modules; + + for (n_modules = 0;; n_modules++) { + struct conf_tuple conf_tuple = config_get_tuple(config, n_modules, "MODULE_PATH", "MODULE_STRING"); + if (!conf_tuple.value1) break; + + char *module_cmdline = conf_tuple.value2; + if (!module_cmdline) module_cmdline = ""; + modules_size += ALIGN_UP(sizeof(struct multiboot_tag_module) + strlen(module_cmdline) + 1, MULTIBOOT_TAG_ALIGN); + } + + struct smbios_entry_point_32* smbios_entry_32 = NULL; + struct smbios_entry_point_64* smbios_entry_64 = NULL; + + acpi_get_smbios((void **)&smbios_entry_32, (void **)&smbios_entry_64); + + uint32_t smbios_tag_size = 0; + + if (smbios_entry_32 != NULL) + smbios_tag_size += ALIGN_UP(sizeof(struct multiboot_tag_smbios) + smbios_entry_32->length, MULTIBOOT_TAG_ALIGN); + if (smbios_entry_64 != NULL) + smbios_tag_size += ALIGN_UP(sizeof(struct multiboot_tag_smbios) + smbios_entry_64->length, MULTIBOOT_TAG_ALIGN); + + size_t mb2_info_size = get_multiboot2_info_size( + cmdline, + modules_size, + section_hdr_info_valid ? section_hdr_info.section_entry_size : 0, + section_hdr_info_valid ? section_hdr_info.num : 0, + smbios_tag_size + ); + + size_t info_idx = 0; + + // Realloc elsewhere ranges to include mb2 info, modules, and elf sections + struct elsewhere_range *new_ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * + (ranges_count + + 1 /* mb2 info range */ + + n_modules + + (section_hdr_info_valid ? section_hdr_info.num : 0))); + + memcpy(new_ranges, ranges, sizeof(struct elsewhere_range) * ranges_count); + pmm_free(ranges, sizeof(struct elsewhere_range) * ranges_count); + ranges = new_ranges; + + // GRUB allocates boot info at 0x10000, *except* if the kernel happens + // to overlap this region, then it gets moved to right after the + // kernel, or whichever PHDR happens to sit at 0x10000. + // Allocate it wherever, then move it to where GRUB puts it + // afterwards. + + // Elsewhere append mb2 info *after* kernel but *before* modules. + uint8_t *mb2_info = ext_mem_alloc(mb2_info_size); + uint64_t mb2_info_final_loc = 0x10000; + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + mb2_info, &mb2_info_final_loc, mb2_info_size)) { + panic(true, "multiboot2: Cannot allocate mb2 info"); + } + + struct multiboot2_start_tag *mbi_start = (struct multiboot2_start_tag *)mb2_info; + info_idx += sizeof(struct multiboot2_start_tag); + + ////////////////////////////////////////////// + // Create ELF info tag + ////////////////////////////////////////////// + if (section_hdr_info_valid == false) { + if (is_elf_info_requested) { + panic(true, "multiboot2: Cannot return ELF file information"); + } + } else { + size_t section_table_size = (size_t)section_hdr_info.section_entry_size * section_hdr_info.num; + if (section_hdr_info.section_offset > kernel_file_size || + section_table_size > kernel_file_size - section_hdr_info.section_offset) { + panic(true, "multiboot2: ELF section headers out of bounds"); + } + + uint32_t size = sizeof(struct multiboot_tag_elf_sections) + section_table_size; + struct multiboot_tag_elf_sections *tag = (struct multiboot_tag_elf_sections*)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_ELF_SECTIONS; + tag->size = size; + + tag->num = section_hdr_info.num; + tag->entsize = section_hdr_info.section_entry_size; + tag->shndx = section_hdr_info.str_section_idx; + + memcpy(tag->sections, kernel + section_hdr_info.section_offset, section_table_size); + + int bits = elf_bits(kernel); + + if ((bits == 64 && section_hdr_info.section_entry_size < sizeof(struct elf64_shdr)) || + (bits == 32 && section_hdr_info.section_entry_size < sizeof(struct elf32_shdr))) { + panic(true, "multiboot2: ELF section entry size too small"); + } + + for (size_t i = 0; i < section_hdr_info.num; i++) { + if (bits == 64) { + struct elf64_shdr *shdr = (void *)tag->sections + i * section_hdr_info.section_entry_size; + + if (shdr->sh_addr != 0) { + shdr->sh_addr += reloc_slide; + continue; + } + if (shdr->sh_size == 0) { + continue; + } + + if (shdr->sh_offset > kernel_file_size || + shdr->sh_size > kernel_file_size - shdr->sh_offset) { + continue; + } + + uint64_t section = (uint64_t)-1; /* no target preference, use top */ + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + kernel + shdr->sh_offset, §ion, shdr->sh_size)) { + panic(true, "multiboot2: Cannot allocate elf sections"); + } + + shdr->sh_addr = section; + } else { + struct elf32_shdr *shdr = (void *)tag->sections + i * section_hdr_info.section_entry_size; + + if (shdr->sh_addr != 0) { + shdr->sh_addr += (int32_t)reloc_slide; + continue; + } + if (shdr->sh_size == 0) { + continue; + } + + if (shdr->sh_offset > kernel_file_size || + shdr->sh_size > kernel_file_size - shdr->sh_offset) { + continue; + } + + uint64_t section = (uint64_t)-1; /* no target preference, use top */ + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + kernel + shdr->sh_offset, §ion, shdr->sh_size)) { + panic(true, "multiboot2: Cannot allocate elf sections"); + } + + shdr->sh_addr = section; + } + } + + append_tag(info_idx, tag); + } + + ////////////////////////////////////////////// + // Create load base address tag + ////////////////////////////////////////////// + if (has_reloc_header) { + uint32_t size = sizeof(struct multiboot_tag_load_base_addr); + struct multiboot_tag_load_base_addr *tag = (void *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR; + tag->size = size; + + tag->load_base_addr = load_base_addr; + + append_tag(info_idx, tag); + } + + ////////////////////////////////////////////// + // Create modules tag + ////////////////////////////////////////////// + for (size_t i = 0; i < n_modules; i++) { + struct conf_tuple conf_tuple = config_get_tuple(config, i, "MODULE_PATH", "MODULE_STRING"); + char *module_path = conf_tuple.value1; + if (!module_path) panic(true, "multiboot2: Module disappeared unexpectedly"); + + print("multiboot2: Loading module `%#`...\n", module_path); + + struct file_handle *f; + if ((f = uri_open(module_path)) == NULL) + panic(true, "multiboot2: Failed to open module with path `%#`. Is the path correct?", module_path); + + // Module commandline can be null, so we guard against that and make the + // string "". + char *module_cmdline = conf_tuple.value2; + if (!module_cmdline) module_cmdline = ""; + + void *module_addr = freadall(f, MEMMAP_BOOTLOADER_RECLAIMABLE); + uint64_t module_target = (uint64_t)-1; + + if (!elsewhere_append(true /* flexible target */, + ranges, &ranges_count, + module_addr, &module_target, f->size)) { + panic(true, "multiboot2: Cannot allocate module"); + } + + struct multiboot_tag_module *module_tag = (struct multiboot_tag_module *)(mb2_info + info_idx); + + module_tag->type = MULTIBOOT_TAG_TYPE_MODULE; + module_tag->size = sizeof(struct multiboot_tag_module) + strlen(module_cmdline) + 1; + module_tag->mod_start = module_target; + module_tag->mod_end = module_tag->mod_start + f->size; + strcpy(module_tag->cmdline, module_cmdline); // Copy over the command line + + fclose(f); + + if (verbose) { + print("multiboot2: Requested module %u:\n", (uint32_t)i); + print(" Path: %s\n", module_path); + print(" String: \"%s\"\n", module_cmdline ?: ""); + print(" Begin: %x\n", module_tag->mod_start); + print(" End: %x\n", module_tag->mod_end); + } + + append_tag(info_idx, module_tag); + } + + ////////////////////////////////////////////// + // Create command line tag + ////////////////////////////////////////////// + { + uint32_t size = sizeof(struct multiboot_tag_string) + strlen(cmdline) + 1; + struct multiboot_tag_string *tag = (struct multiboot_tag_string *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_CMDLINE; + tag->size = size; + + strcpy(tag->string, cmdline); + append_tag(info_idx, tag); + } + + ////////////////////////////////////////////// + // Create bootloader name tag + ////////////////////////////////////////////// + { + uint32_t size = sizeof(struct multiboot_tag_string) + sizeof(LIMINE_BRAND); + struct multiboot_tag_string *tag = (struct multiboot_tag_string *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME; + tag->size = size; + + strcpy(tag->string, LIMINE_BRAND); + append_tag(info_idx, tag); + } + + ////////////////////////////////////////////// + // Create EFI image handle tag + ////////////////////////////////////////////// +#if defined (UEFI) + { + #if defined (__i386__) + struct multiboot_tag_efi32_ih *tag = (struct multiboot_tag_efi32_ih *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_EFI32_IH; + tag->size = sizeof(struct multiboot_tag_efi32_ih); + #elif defined (__x86_64__) + struct multiboot_tag_efi64_ih *tag = (struct multiboot_tag_efi64_ih *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_EFI64_IH; + tag->size = sizeof(struct multiboot_tag_efi64_ih); + #endif + + tag->pointer = (uintptr_t)efi_image_handle; + append_tag(info_idx, tag); + } +#endif + + ////////////////////////////////////////////// + // Create framebuffer tag + ////////////////////////////////////////////// + { + struct multiboot_tag_framebuffer *tag = (struct multiboot_tag_framebuffer *)(mb2_info + info_idx); + + tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER; + + term_notready(); + + size_t req_width = 0; + size_t req_height = 0; + size_t req_bpp = 0; +#if defined (BIOS) + { + char *textmode_str = config_get_value(config, 0, "TEXTMODE"); + bool textmode = textmode_str != NULL && strcmp(textmode_str, "yes") == 0; + if (textmode) { + goto textmode; + } + } +#endif + + if (fbtag) { + req_width = fbtag->width; + req_height = fbtag->height; + req_bpp = fbtag->depth; + +#if defined (UEFI) +modeset:; +#endif + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + + struct fb_info *fbs; + size_t fbs_count; + fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + if (fbs_count == 0) { +#if defined (BIOS) +textmode: + vga_textmode_init(false); + + tag->common.framebuffer_addr = 0xb8000; + tag->common.framebuffer_pitch = 2 * 80; + tag->common.framebuffer_width = 80; + tag->common.framebuffer_height = 25; + tag->common.framebuffer_bpp = 16; + tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT; + tag->common.size = sizeof(struct multiboot_tag_framebuffer_common); +#elif defined (UEFI) + if (is_framebuffer_required) { + panic(true, "multiboot2: Failed to set video mode"); + } else { + goto skip_modeset; + } +#endif + } else { + tag->common.framebuffer_addr = fbs[0].framebuffer_addr; + tag->common.framebuffer_pitch = fbs[0].framebuffer_pitch; + tag->common.framebuffer_width = fbs[0].framebuffer_width; + tag->common.framebuffer_height = fbs[0].framebuffer_height; + tag->common.framebuffer_bpp = fbs[0].framebuffer_bpp; + tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB; // We only support RGB for VBE + tag->common.size = sizeof(struct multiboot_tag_framebuffer); + + tag->framebuffer_red_field_position = fbs[0].red_mask_shift; + tag->framebuffer_red_mask_size = fbs[0].red_mask_size; + tag->framebuffer_green_field_position = fbs[0].green_mask_shift; + tag->framebuffer_green_mask_size = fbs[0].green_mask_size; + tag->framebuffer_blue_field_position = fbs[0].blue_mask_shift; + tag->framebuffer_blue_mask_size = fbs[0].blue_mask_size; + } + } else { +#if defined (UEFI) + print("multiboot2: Warning: Cannot use text mode with UEFI\n"); + goto modeset; +#elif defined (BIOS) + goto textmode; +#endif + } + + append_tag(info_idx, &tag->common); + +#if defined (UEFI) +skip_modeset:; +#endif + } + + ////////////////////////////////////////////// + // Create new ACPI info tag + ////////////////////////////////////////////// + { + void *new_rsdp = acpi_get_rsdp_v2(); + + if (new_rsdp != NULL) { + uint32_t size = sizeof(struct multiboot_tag_new_acpi) + sizeof(struct rsdp); // XSDP is 36 bytes wide + struct multiboot_tag_new_acpi *tag = (struct multiboot_tag_new_acpi *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_ACPI_NEW; + tag->size = size; + + memcpy(tag->rsdp, new_rsdp, sizeof(struct rsdp)); + append_tag(info_idx, tag); + } else if (is_new_acpi_required) { + panic(true, "multiboot2: XSDP requested but not found"); + } + } + + ////////////////////////////////////////////// + // Create old ACPI info tag + ////////////////////////////////////////////// + { + void *old_rsdp = acpi_get_rsdp_v1(); + + if (old_rsdp != NULL) { + uint32_t size = sizeof(struct multiboot_tag_old_acpi) + 20; // RSDP is 20 bytes wide + struct multiboot_tag_old_acpi *tag = (struct multiboot_tag_old_acpi *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_ACPI_OLD; + tag->size = size; + + memcpy(tag->rsdp, old_rsdp, 20); + append_tag(info_idx, tag); + } else if (is_old_acpi_required) { + panic(true, "multiboot2: RSDP requested but not found"); + } + } + + ////////////////////////////////////////////// + // Create SMBIOS tag + ////////////////////////////////////////////// + { + // NOTE: The multiboot2 specification does not say anything about if both + // smbios 32 and 64 bit entry points are present, then we pass both of them + smbios + // support for grub2 is unimplemented. So, we are going to assume they expect us to + // pass both of them if available. Oh well... + if (smbios_entry_32 != NULL) { + struct multiboot_tag_smbios *tag = (struct multiboot_tag_smbios *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_SMBIOS; + tag->size = sizeof(struct multiboot_tag_smbios) + smbios_entry_32->length; + + tag->major = smbios_entry_32->major_version; + tag->minor = smbios_entry_32->minor_version; + + memset(tag->reserved, 0, 6); + memcpy(tag->tables, smbios_entry_32, smbios_entry_32->length); + + append_tag(info_idx, tag); + } + + if (smbios_entry_64 != NULL) { + struct multiboot_tag_smbios *tag = (struct multiboot_tag_smbios *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_SMBIOS; + tag->size = sizeof(struct multiboot_tag_smbios) + smbios_entry_64->length; + + tag->major = smbios_entry_64->major_version; + tag->minor = smbios_entry_64->minor_version; + + memset(tag->reserved, 0, 6); + memcpy(tag->tables, smbios_entry_64, smbios_entry_64->length); + + append_tag(info_idx, tag); + } + } + + ////////////////////////////////////////////// + // Create EFI system table info tag + ////////////////////////////////////////////// +#if defined (UEFI) + { + #if defined (__i386__) + uint32_t size = sizeof(struct multiboot_tag_efi32); + struct multiboot_tag_efi32 *tag = (void *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_EFI32; + #elif defined (__x86_64__) + uint32_t size = sizeof(struct multiboot_tag_efi64); + struct multiboot_tag_efi64 *tag = (void *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_EFI64; + #endif + + tag->size = size; + tag->pointer = (uintptr_t)gST; + + append_tag(info_idx, tag); + } +#endif + + // Load relocation stub where it won't get overwritten (hopefully) + size_t reloc_stub_size = (size_t)multiboot_reloc_stub_end - (size_t)multiboot_reloc_stub; + void *reloc_stub = ext_mem_alloc(reloc_stub_size); + memcpy(reloc_stub, multiboot_reloc_stub, reloc_stub_size); + +#if defined (UEFI) + efi_exit_boot_services(); +#endif + + size_t mb_mmap_count; + struct memmap_entry *raw_memmap = get_raw_memmap(&mb_mmap_count); + + ////////////////////////////////////////////// + // Create memory map tag + ////////////////////////////////////////////// + { + if (mb_mmap_count > MEMMAP_MAX) { + panic(false, "multiboot2: too many memory map entries"); + } + + // Create the normal memory map tag. + uint32_t mmap_size = sizeof(struct multiboot_tag_mmap) + sizeof(struct multiboot_mmap_entry) * mb_mmap_count; + struct multiboot_tag_mmap *mmap_tag = (struct multiboot_tag_mmap *)(mb2_info + info_idx); + + mmap_tag->type = MULTIBOOT_TAG_TYPE_MMAP; + mmap_tag->entry_size = sizeof(struct multiboot_mmap_entry); + mmap_tag->entry_version = 0; + mmap_tag->size = mmap_size; + + for (size_t i = 0; i < mb_mmap_count; i++) { + struct multiboot_mmap_entry *entry = &mmap_tag->entries[i]; + entry->addr = raw_memmap[i].base; + entry->len = raw_memmap[i].length; + entry->type = raw_memmap[i].type; + entry->zero = 0; + } + + append_tag(info_idx, mmap_tag); + } + + ////////////////////////////////////////////// + // Create basic memory info tag + ////////////////////////////////////////////// + { + struct meminfo meminfo = mmap_get_info(mb_mmap_count, raw_memmap); + struct multiboot_tag_basic_meminfo *tag = (struct multiboot_tag_basic_meminfo *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO; + tag->size = sizeof(struct multiboot_tag_basic_meminfo); + + // Convert the uppermem and lowermem fields from bytes to + // KiB. + tag->mem_upper = (uint32_t)(meminfo.uppermem / 1024); + tag->mem_lower = (uint32_t)(meminfo.lowermem / 1024); + + append_tag(info_idx, tag); + } + + ////////////////////////////////////////////// + // Create EFI memory map tag + ////////////////////////////////////////////// +#if defined (UEFI) + { + if ((efi_mmap_size / efi_desc_size) > EFI_MEMMAP_MAX) { + panic(false, "multiboot2: too many EFI memory map entries"); + } + + // Create the EFI memory map tag. + uint32_t size = sizeof(struct multiboot_tag_efi_mmap) + efi_mmap_size; + struct multiboot_tag_efi_mmap *mmap_tag = (struct multiboot_tag_efi_mmap *)(mb2_info + info_idx); + + mmap_tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP; + mmap_tag->descr_vers = efi_desc_ver; + mmap_tag->descr_size = efi_desc_size; + mmap_tag->size = size; + + // Copy over the EFI memory map. + memcpy(mmap_tag->efi_mmap, efi_mmap, efi_mmap_size); + append_tag(info_idx, mmap_tag); + } +#endif + + ////////////////////////////////////////////// + // Create network info tag + ////////////////////////////////////////////// + { + if (cached_dhcp_ack_valid) { + struct multiboot_tag_network *tag = (struct multiboot_tag_network *)(mb2_info + info_idx); + + tag->type = MULTIBOOT_TAG_TYPE_NETWORK; + tag->size = sizeof(struct multiboot_tag_network) + DHCP_ACK_PACKET_LEN; + + // Copy over the DHCP packet. + memcpy(tag->dhcpack, cached_dhcp_packet, DHCP_ACK_PACKET_LEN); + append_tag(info_idx, tag); + } + } + + ////////////////////////////////////////////// + // Create end tag + ////////////////////////////////////////////// + { + struct multiboot_tag *end_tag = (struct multiboot_tag *)(mb2_info + info_idx); + end_tag->type = MULTIBOOT_TAG_TYPE_END; + end_tag->size = sizeof(struct multiboot_tag); + + append_tag(info_idx, end_tag); + } + + mbi_start->size = info_idx; + mbi_start->reserved = 0x00; + + if (rdmsr(0x1b) & (1 << 10)) { + if (x2apic_disable()) { + printv("multiboot2: Firmware had x2APIC enabled, reverted to xAPIC mode\n"); + } else { + printv("multiboot2: Firmware has x2APIC enabled and it could not be disabled\n"); + } + } + + iommu_disable_all(); + + irq_flush_type = IRQ_PIC_ONLY_FLUSH; + + common_spinup(multiboot_spinup_32, 6, + (uint32_t)(uintptr_t)reloc_stub, (uint32_t)0x36d76289, + (uint32_t)mb2_info_final_loc, (uint32_t)entry_point, + (uint32_t)(uintptr_t)ranges, (uint32_t)ranges_count); +} + +#endif diff --git a/limine/common/protos/multiboot2.h b/limine/common/protos/multiboot2.h new file mode 100644 index 0000000..1965a7f --- /dev/null +++ b/limine/common/protos/multiboot2.h @@ -0,0 +1,418 @@ +/* multiboot2.h - Multiboot 2 header file. Copied from the multiboot2 specs / +/ Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PROTOS__MULTIBOOT2_H__ +#define PROTOS__MULTIBOOT2_H__ + +#include +#include + +noreturn void multiboot2_load(char *config, char *cmdline); + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 + +/* The magic field should contain this. */ +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000008 + +/* Flags set in the ’flags’ member of the multiboot header. */ + +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + +#define MULTIBOOT_ARCHITECTURE_I386 0 +#define MULTIBOOT_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + uint32_t magic; + + /* ISA */ + uint32_t architecture; + + /* Total header length. */ + uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + uint32_t checksum; +}; + +struct multiboot_header_tag +{ + uint16_t type; + uint16_t flags; + uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t requests[0]; +}; + +struct multiboot2_start_tag { + uint32_t size; + uint32_t reserved; +}; + +struct multiboot_header_tag_address +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; +}; + +struct multiboot_header_tag_entry_address +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t width; + uint32_t height; + uint32_t depth; +}; + +struct multiboot_header_tag_module_align +{ + uint16_t type; + uint16_t flags; + uint32_t size; +}; + +struct multiboot_header_tag_relocatable +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t min_addr; + uint32_t max_addr; + uint32_t align; + uint32_t preference; +}; + +struct multiboot_color +{ + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + uint64_t addr; + uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + uint32_t type; + uint32_t zero; +}; +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + uint32_t type; + uint32_t size; +}; + +struct multiboot_tag_string +{ + uint32_t type; + uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + uint32_t type; + uint32_t size; + uint32_t mod_start; + uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + uint32_t type; + uint32_t size; + uint32_t mem_lower; + uint32_t mem_upper; +}; + +struct multiboot_tag_bootdev +{ + uint32_t type; + uint32_t size; + uint32_t biosdev; + uint32_t slice; + uint32_t part; +}; + +struct multiboot_tag_mmap +{ + uint32_t type; + uint32_t size; + uint32_t entry_size; + uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; + +struct multiboot_vbe_info_block +{ + uint8_t external_specification[512]; +}; + +struct multiboot_vbe_mode_info_block +{ + uint8_t external_specification[256]; +}; + +struct multiboot_tag_vbe +{ + uint32_t type; + uint32_t size; + + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + uint32_t type; + uint32_t size; + + uint64_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + uint8_t framebuffer_type; + uint16_t reserved; +}; + +struct multiboot_tag_framebuffer +{ + struct multiboot_tag_framebuffer_common common; + + union + { + struct + { + uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; + }; + struct + { + uint8_t framebuffer_red_field_position; + uint8_t framebuffer_red_mask_size; + uint8_t framebuffer_green_field_position; + uint8_t framebuffer_green_mask_size; + uint8_t framebuffer_blue_field_position; + uint8_t framebuffer_blue_mask_size; + }; + }; +}; + +struct multiboot_tag_elf_sections +{ + uint32_t type; + uint32_t size; + uint32_t num; + uint32_t entsize; + uint32_t shndx; + char sections[0]; +}; + +struct multiboot_tag_apm +{ + uint32_t type; + uint32_t size; + uint16_t version; + uint16_t cseg; + uint32_t offset; + uint16_t cseg_16; + uint16_t dseg; + uint16_t flags; + uint16_t cseg_len; + uint16_t cseg_16_len; + uint16_t dseg_len; +}; + +struct multiboot_tag_efi32 +{ + uint32_t type; + uint32_t size; + uint32_t pointer; +}; + +struct multiboot_tag_efi64 +{ + uint32_t type; + uint32_t size; + uint64_t pointer; +}; + +struct multiboot_tag_smbios +{ + uint32_t type; + uint32_t size; + uint8_t major; + uint8_t minor; + uint8_t reserved[6]; + uint8_t tables[0]; +}; + +struct multiboot_tag_old_acpi +{ + uint32_t type; + uint32_t size; + uint8_t rsdp[0]; +}; + +struct multiboot_tag_new_acpi +{ + uint32_t type; + uint32_t size; + uint8_t rsdp[0]; +}; + +struct multiboot_tag_network +{ + uint32_t type; + uint32_t size; + uint8_t dhcpack[0]; +}; + +struct multiboot_tag_efi_mmap +{ + uint32_t type; + uint32_t size; + uint32_t descr_size; + uint32_t descr_vers; + uint8_t efi_mmap[0]; +}; + +struct multiboot_tag_efi32_ih +{ + uint32_t type; + uint32_t size; + uint32_t pointer; +}; + +struct multiboot_tag_efi64_ih +{ + uint32_t type; + uint32_t size; + uint64_t pointer; +}; + +struct multiboot_tag_load_base_addr +{ + uint32_t type; + uint32_t size; + uint32_t load_base_addr; +}; + +#endif diff --git a/limine/common/protos/multiboot_32.asm_x86 b/limine/common/protos/multiboot_32.asm_x86 new file mode 100644 index 0000000..89e4f17 --- /dev/null +++ b/limine/common/protos/multiboot_32.asm_x86 @@ -0,0 +1,23 @@ +bits 32 + +section .text +global multiboot_spinup_32: +multiboot_spinup_32: + sub esp, 6 + mov word [esp], 0x3ff + mov dword [esp+2], 0 + lidt [esp] + add esp, 6 + + add esp, 4 ; return address + + pop ebx ; reloc_stub + pop esi ; magic + pop edi ; protocol_info + pop ecx ; entry_point + pop eax ; elf_ranges + pop edx ; elf_ranges_count + + jmp ebx + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/protos/multiboot_reloc.asm_x86 b/limine/common/protos/multiboot_reloc.asm_x86 new file mode 100644 index 0000000..6ffc148 --- /dev/null +++ b/limine/common/protos/multiboot_reloc.asm_x86 @@ -0,0 +1,66 @@ +section .data + +bits 32 + +global multiboot_reloc_stub +multiboot_reloc_stub: + jmp .code + + times 4-($-multiboot_reloc_stub) db 0 + + ; EBX = self + ; ESI = magic value + ; EDI = protocol info + ; ECX = entry point + ; EAX = ranges + ; EDX = ranges count + + .code: + mov esp, ebx + add esp, .mini_stack_top - multiboot_reloc_stub + + push edi + push esi + push ecx + + .ranges_loop: + test edx, edx ; Loop until we're done + jz .ranges_loop_out + + mov esi, [eax] ; ESI = range.elsewhere + mov edi, [eax+8] ; EDI = range.target + mov ecx, [eax+16] ; ECX = range.length + rep movsb ; Copy range to target location + + add eax, 24 ; Move to the next range + + dec edx + jmp .ranges_loop + + .ranges_loop_out: + ; We're done relocating! + pop ecx + pop esi + pop edi + + push ecx + + mov eax, esi ; EAX = magic value + mov ebx, edi ; EBX = protocol info + xor ecx, ecx + xor edx, edx + xor esi, esi + xor edi, edi + xor ebp, ebp + + ret + + align 16 + .mini_stack: + times 3 dq 0 + .mini_stack_top: + +global multiboot_reloc_stub_end +multiboot_reloc_stub_end: + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/pxe/pxe.h b/limine/common/pxe/pxe.h new file mode 100644 index 0000000..49d094b --- /dev/null +++ b/limine/common/pxe/pxe.h @@ -0,0 +1,111 @@ +#ifndef PXE_H +#define PXE_H + +#include +#include + +#define DHCP_ACK_PACKET_LEN 296 + +extern uint8_t cached_dhcp_packet[DHCP_ACK_PACKET_LEN]; +extern bool cached_dhcp_ack_valid; + +#if defined (BIOS) + +struct volume *pxe_bind_volume(void); +void pxe_init(void); +int pxe_call(uint16_t opcode, uint16_t buf_seg, uint16_t buf_off); + +#define MAC_ADDR_LEN 16 +typedef uint8_t MAC_ADDR_t[MAC_ADDR_LEN]; + +struct bootph { + uint8_t opcode; + uint8_t Hardware; + uint8_t Hardlen; + uint8_t Gatehops; + uint32_t ident; + uint16_t seconds; + uint16_t Flags; + uint32_t cip; + uint32_t yip; + uint32_t sip; + uint32_t gip; + MAC_ADDR_t CAddr; + uint8_t Sname[64]; + uint8_t bootfile[128]; + union bootph_vendor { + uint8_t d[1024]; + struct bootph_vendor_v { + uint8_t magic[4]; + uint32_t flags; + uint8_t pad[52]; + } v; + } vendor; +}; + + struct PXENV_UNDI_GET_INFORMATION { + uint16_t Status; + uint16_t BaseIo; + uint16_t IntNumber; + uint16_t MaxTranUnit; + uint16_t HwType; + uint16_t HwAddrLen; + uint8_t CurrentNodeAddress[16]; + uint8_t PermNodeAddress[16]; + uint16_t ROMAddress; + uint16_t RxBufCt; + uint16_t TxBufCt; + }; + +#define PXE_SIGNATURE "PXENV+" +struct pxenv { + uint8_t signature[6]; + uint16_t version; + uint8_t length; + uint8_t checksum; + uint32_t rm_entry; + uint32_t pm_offset; + uint16_t pm_selector; + uint16_t stack_seg; + uint16_t stack_size; + uint16_t bc_code_seg; + uint16_t bc_code_size; + uint16_t bc_data_seg; + uint16_t bc_data_size; + uint16_t undi_data_seg; + uint16_t undi_data_size; + uint16_t undi_code_seg; + uint16_t undi_code_size; + uint32_t pxe_ptr; +} __attribute__((packed)); + +#define PXE_BANGPXE_SIGNATURE "!PXE" +struct bangpxe { + uint8_t signature[4]; + uint8_t length; + uint8_t chksum; + uint8_t rev; + uint8_t reserved; + uint32_t undiromid; + uint32_t baseromid; + uint32_t rm_entry; + uint32_t pm_entry; +} __attribute__((packed)); + +#define PXENV_GET_CACHED_INFO 0x0071 +#define PXENV_PACKET_TYPE_CACHED_REPLY 3 +struct pxenv_get_cached_info { + uint16_t status; + uint16_t packet_type; + uint16_t buffer_size; + uint32_t buffer; + uint16_t buffer_limit; +} __attribute__((packed)); + +#elif defined (UEFI) + +struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code); + +#endif + +#endif diff --git a/limine/common/pxe/pxe.s2.c b/limine/common/pxe/pxe.s2.c new file mode 100644 index 0000000..43762ad --- /dev/null +++ b/limine/common/pxe/pxe.s2.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#if defined (BIOS) +#include +#elif defined (UEFI) +#include +#endif + +#if defined (BIOS) + +void set_pxe_fp(uint32_t fp); + +struct volume *pxe_bind_volume(void) { + struct volume *volume = ext_mem_alloc(sizeof(struct volume)); + + volume->pxe = true; + + return volume; +} + +void pxe_init(void) { + //pxe installation check + struct rm_regs r = { 0 }; + r.ebx = 0; + r.ecx = 0; + r.eax = 0x5650; + r.es = 0; + + rm_int(0x1a, &r, &r); + if ((r.eax & 0xffff) != 0x564e) { + panic(false, "PXE installation check failed"); + } + + struct pxenv* pxenv = { 0 }; + + pxenv = (struct pxenv*)((r.es << 4) + (r.ebx & 0xffff)); + if (memcmp(pxenv->signature, PXE_SIGNATURE, sizeof(pxenv->signature)) != 0) { + panic(false, "PXENV structure signature corrupted"); + } + + if (pxenv->version < 0x201) { + //we won't support pxe < 2.1, grub does this too and it seems to work fine + panic(false, "pxe version too old"); + } + + struct bangpxe* bangpxe = (struct bangpxe*)((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4) + (pxenv->pxe_ptr & 0xffff)); + + if (memcmp(bangpxe->signature, PXE_BANGPXE_SIGNATURE, + sizeof(bangpxe->signature)) + != 0) { + panic(false, "!pxe signature corrupted"); + } + set_pxe_fp(bangpxe->rm_entry); + printv("pxe: Successfully initialized\n"); +} + +#elif defined (UEFI) + +struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code) { + struct volume *volume = ext_mem_alloc(sizeof(struct volume)); + + volume->efi_handle = efi_handle; + volume->pxe_base_code = pxe_base_code; + volume->pxe = true; + + return volume; +} + +#endif diff --git a/limine/common/pxe/pxe_asm.s2.asm_bios_ia32 b/limine/common/pxe/pxe_asm.s2.asm_bios_ia32 new file mode 100644 index 0000000..2935402 --- /dev/null +++ b/limine/common/pxe/pxe_asm.s2.asm_bios_ia32 @@ -0,0 +1,99 @@ +section .realmode + +global pxe_call +global set_pxe_fp + +set_pxe_fp: + mov eax, [esp + 4] + mov [pxe_call.pxe_fp], eax + ret + +pxe_call: + ; Save GDT in case BIOS overwrites it + sgdt [.gdt] + + ; Save IDT + sidt [.idt] + + ; Load BIOS IVT + lidt [.rm_idt] + + ; Save non-scratch GPRs + push ebx + push esi + push edi + push ebp + + lea ebp, [esp + 20] + + ; Jump to real mode + jmp 0x08:.bits16 + .bits16: + bits 16 + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov eax, cr0 + and al, 0xfe + mov cr0, eax + jmp 0x00:.cszero + .cszero: + xor ax, ax + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + sti + + push word [bp + 4] + push word [bp + 8] + push word [bp + 0] + call far [.pxe_fp] + add sp, 6 + + cli + + ; Restore GDT + o32 lgdt [cs:.gdt] + + ; Restore IDT + o32 lidt [cs:.idt] + + ; Jump back to pmode + mov ebx, cr0 + or bl, 1 + mov cr0, ebx + jmp 0x18:.bits32 + .bits32: + bits 32 + mov bx, 0x20 + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + mov ss, bx + + and eax, 0xffff + + ; Restore non-scratch GPRs + pop ebp + pop edi + pop esi + pop ebx + + ; Exit + ret + +align 16 + .pxe_fp: dd 0 + .gdt: dq 0 + .idt: dq 0 + .rm_idt: dw 0x3ff + dd 0 + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/pxe/tftp.h b/limine/common/pxe/tftp.h new file mode 100644 index 0000000..99c5861 --- /dev/null +++ b/limine/common/pxe/tftp.h @@ -0,0 +1,46 @@ +#ifndef TFTP_H +#define TFTP_H + +#include +#include +#include + +#if defined (BIOS) + +#define UNDI_GET_INFORMATION 0xC + +#define TFTP_OPEN 0x0020 +struct pxenv_open { + uint16_t status; + uint32_t sip; + uint32_t gip; + uint8_t name[128]; + uint16_t port; + uint16_t packet_size; + } __attribute__((packed)); + +#define TFTP_READ 0x22 +struct pxenv_read { + uint16_t status; + uint16_t pn; + uint16_t bsize; + uint16_t boff; + uint16_t bseg; +} __attribute__((packed)); + +#define TFTP_GET_FILE_SIZE 0x25 +struct pxenv_get_file_size { + uint16_t status; + uint32_t sip; + uint32_t gip; + uint8_t name[128]; + uint32_t file_size; +} __attribute__((packed)); + +#define TFTP_CLOSE 0x21 + +#endif + +struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name); + +#endif diff --git a/limine/common/pxe/tftp.s2.c b/limine/common/pxe/tftp.s2.c new file mode 100644 index 0000000..7847a73 --- /dev/null +++ b/limine/common/pxe/tftp.s2.c @@ -0,0 +1,267 @@ +#include +#include +#if defined (BIOS) +# include +#elif defined (UEFI) +# include +#endif +#include +#include +#include +#include + +// cache the dhcp packet +uint8_t cached_dhcp_packet[DHCP_ACK_PACKET_LEN] = { 0 }; +bool cached_dhcp_ack_valid = false; + +#if defined (BIOS) + +static uint32_t get_boot_server_info(void) { + struct pxenv_get_cached_info cachedinfo = { 0 }; + cachedinfo.packet_type = PXENV_PACKET_TYPE_CACHED_REPLY; + int ret = pxe_call(PXENV_GET_CACHED_INFO, ((uint16_t)rm_seg(&cachedinfo)), (uint16_t)rm_off(&cachedinfo)); + if (ret || cachedinfo.buffer == 0) { + panic(false, "tftp: Failed to get DHCP cached info"); + } + struct bootph *ph = (struct bootph*)(void *) (((((uint32_t)cachedinfo.buffer) >> 16) << 4) + (((uint32_t)cachedinfo.buffer) & 0xFFFF)); + if (!cached_dhcp_ack_valid) { + size_t copy_len = cachedinfo.buffer_size < DHCP_ACK_PACKET_LEN + ? cachedinfo.buffer_size : DHCP_ACK_PACKET_LEN; + memcpy(cached_dhcp_packet, ph, copy_len); + cached_dhcp_ack_valid = true; + } + return ph->sip; +} + +static uint32_t parse_ip_addr(const char *server_addr) { + uint32_t out; + + if (!server_addr || !strlen(server_addr)) { + return get_boot_server_info(); + } + + if (inet_pton(server_addr, &out)) { + panic(true, "tftp: Invalid IPv4 address: \"%s\"", server_addr); + } + + return out; +} + +struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name) { + uint32_t server_ip = parse_ip_addr(server_addr); + const uint16_t server_port = 69; // This couldn't be changed previously either + int ret = 0; + + (void)part; + + struct PXENV_UNDI_GET_INFORMATION undi_info = { 0 }; + ret = pxe_call(UNDI_GET_INFORMATION, ((uint16_t)rm_seg(&undi_info)), (uint16_t)rm_off(&undi_info)); + if (ret) { + return NULL; + } + + //TODO figure out a more proper way to do this. + if (undi_info.MaxTranUnit < 48) { + print("tftp: Invalid MTU (%u), too small for TFTP headers\n", undi_info.MaxTranUnit); + return NULL; + } + uint16_t mtu = undi_info.MaxTranUnit - 48; + + size_t name_len = strlen(name); + if (name_len >= 128) { + print("tftp: Filename too long (max 127 chars)\n"); + return NULL; + } + + struct pxenv_get_file_size fsize = { + .status = 0, + .sip = server_ip, + }; + memcpy(fsize.name, name, name_len + 1); + ret = pxe_call(TFTP_GET_FILE_SIZE, ((uint16_t)rm_seg(&fsize)), (uint16_t)rm_off(&fsize)); + if (ret) { + return NULL; + } + + struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle)); + + handle->size = fsize.file_size; + handle->is_memfile = true; + + handle->pxe = true; + handle->pxe_ip = server_ip; + handle->pxe_port = server_port; + + handle->path = ext_mem_alloc(1 + name_len + 1); + handle->path[0] = '/'; + memcpy(&handle->path[1], name, name_len); + handle->path_len = 1 + name_len + 1; + + struct pxenv_open open = { + .status = 0, + .sip = server_ip, + .port = (server_port >> 8) | (server_port << 8), + .packet_size = mtu + }; + memcpy(open.name, name, name_len + 1); + + ret = pxe_call(TFTP_OPEN, ((uint16_t)rm_seg(&open)), (uint16_t)rm_off(&open)); + if (ret) { + print("tftp: Failed to open file %x or bad packet size", open.status); + pmm_free(handle->path, handle->path_len); + pmm_free(handle, sizeof(struct file_handle)); + return NULL; + } + + // Validate server's negotiated packet size doesn't exceed our buffer + if (open.packet_size > mtu) { + print("tftp: Server requested packet size %u exceeds our MTU %u\n", open.packet_size, mtu); + uint16_t close = 0; + pxe_call(TFTP_CLOSE, ((uint16_t)rm_seg(&close)), (uint16_t)rm_off(&close)); + pmm_free(handle->path, handle->path_len); + pmm_free(handle, sizeof(struct file_handle)); + return NULL; + } + + uint16_t alloc_mtu = mtu; // Save original MTU for allocation/free + uint8_t *buf = conv_mem_alloc(alloc_mtu); + + mtu = open.packet_size; + handle->fd = ext_mem_alloc(handle->size); + + size_t progress = 0; + bool slow = false; + + while (progress < handle->size) { + struct pxenv_read read = { + .boff = ((uint16_t)rm_off(buf)), + .bseg = ((uint16_t)rm_seg(buf)), + }; + + ret = pxe_call(TFTP_READ, ((uint16_t)rm_seg(&read)), (uint16_t)rm_off(&read)); + if (ret) { + panic(false, "tftp: Read failure"); + } + + // Validate read size doesn't overflow the buffer (use alloc_mtu, not server's mtu) + if (read.bsize > alloc_mtu || progress + read.bsize > handle->size) { + panic(false, "tftp: Server sent more data than expected"); + } + + // Prevent infinite loop from zero-byte reads + if (read.bsize == 0 && progress < handle->size) { + panic(false, "tftp: Server returned zero bytes before transfer complete"); + } + + memcpy(handle->fd + progress, buf, read.bsize); + + progress += read.bsize; + + if (read.bsize < mtu && !slow && progress < handle->size) { + slow = true; + print("tftp: Server is sending the file in smaller packets (it sent %d bytes), download might take longer.\n", read.bsize); + } + } + + uint16_t close = 0; + ret = pxe_call(TFTP_CLOSE, ((uint16_t)rm_seg(&close)), (uint16_t)rm_off(&close)); + if (ret) { + panic(false, "tftp: Close failure"); + } + + pmm_free(buf, alloc_mtu); + + return handle; +} + +#elif defined (UEFI) + +static EFI_IP_ADDRESS *parse_ip_addr(struct volume *part, const char *server_addr) { + static EFI_IP_ADDRESS out; + + if (!server_addr || !strlen(server_addr)) { + EFI_PXE_BASE_CODE_PACKET* packet; + if (part->pxe_base_code->Mode->PxeReplyReceived) packet = &part->pxe_base_code->Mode->PxeReply; + else if (part->pxe_base_code->Mode->ProxyOfferReceived) packet = &part->pxe_base_code->Mode->ProxyOffer; + else packet = &part->pxe_base_code->Mode->DhcpAck; + memcpy(out.Addr, packet->Dhcpv4.BootpSiAddr, 4); + if (!cached_dhcp_ack_valid) { + memcpy(cached_dhcp_packet, packet, DHCP_ACK_PACKET_LEN); + cached_dhcp_ack_valid = true; + } + } else { + if (inet_pton(server_addr, &out.Addr)) { + panic(true, "tftp: Invalid IPv4 address: \"%s\"", server_addr); + } + } + + return &out; +} + +struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name) { + if (part == NULL || !part->pxe_base_code) { + return NULL; + } + + EFI_IP_ADDRESS *ip = parse_ip_addr(part, server_addr); + + uint64_t file_size; + EFI_STATUS status; + + status = part->pxe_base_code->Mtftp( + part->pxe_base_code, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + false, + &file_size, + NULL, + ip, + (uint8_t *)name, + NULL, + false); + + if (status) { + return NULL; + } + + struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle)); + + handle->efi_part_handle = part->efi_handle; + handle->size = file_size; + handle->is_memfile = true; + + handle->pxe = true; + handle->pxe_ip = *(uint32_t *)ip->Addr; + handle->pxe_port = 69; + + size_t name_len = strlen(name); + handle->path = ext_mem_alloc(1 + name_len + 1); + handle->path[0] = '/'; + memcpy(&handle->path[1], name, name_len); + handle->path_len = 1 + name_len + 1; + + handle->fd = ext_mem_alloc(handle->size); + + status = part->pxe_base_code->Mtftp( + part->pxe_base_code, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + handle->fd, + false, + &file_size, + NULL, + ip, + (uint8_t *)name, + NULL, + false); + + if (status) { + pmm_free(handle->fd, handle->size); + pmm_free(handle->path, handle->path_len); + pmm_free(handle, sizeof(struct file_handle)); + return NULL; + } + + return handle; +} + +#endif diff --git a/limine/common/stb_image.patch b/limine/common/stb_image.patch new file mode 100644 index 0000000..093eca2 --- /dev/null +++ b/limine/common/stb_image.patch @@ -0,0 +1,90 @@ +--- common/lib/stb_image.h 2023-03-05 18:57:03.930649341 +0100 ++++ common/lib/stb_image.h 2023-03-05 18:55:53.767318782 +0100 +@@ -127,6 +127,54 @@ + #ifndef STBI_INCLUDE_STB_IMAGE_H + #define STBI_INCLUDE_STB_IMAGE_H + ++#include ++#include ++#include ++#include ++ ++#define STBI_ASSERT(x) ++ ++#define STBI_MALLOC(x) ({ \ ++ size_t STBI_MALLOC_alloc_size = (x); \ ++ STBI_MALLOC_alloc_size += 16; \ ++ void *STBI_MALLOC_buf = ext_mem_alloc(STBI_MALLOC_alloc_size); \ ++ size_t *STBI_MALLOC_alloc_size_ptr = STBI_MALLOC_buf; \ ++ *STBI_MALLOC_alloc_size_ptr = STBI_MALLOC_alloc_size; \ ++ STBI_MALLOC_buf + 16; \ ++}) ++ ++#define STBI_FREE(x) do { \ ++ void *STBI_FREE_buf = (x); \ ++ if (STBI_FREE_buf == NULL) { \ ++ break; \ ++ } \ ++ STBI_FREE_buf -= 16; \ ++ size_t *STBI_FREE_alloc_size_ptr = STBI_FREE_buf; \ ++ size_t STBI_FREE_alloc_size = *STBI_FREE_alloc_size_ptr; \ ++ pmm_free(STBI_FREE_buf, STBI_FREE_alloc_size); \ ++} while (0) ++ ++#define STBI_REALLOC(x, y) ({ \ ++ void *STBI_REALLOC_buf = (x); \ ++ size_t STBI_REALLOC_alloc_size = (y); \ ++ void *STBI_REALLOC_new_buf = STBI_MALLOC(STBI_REALLOC_alloc_size); \ ++ if (STBI_REALLOC_buf != NULL) { \ ++ size_t STBI_REALLOC_old_size = (*(size_t *)((void *)STBI_REALLOC_buf - 16)) - 16; \ ++ memcpy(STBI_REALLOC_new_buf, STBI_REALLOC_buf, \ ++ MIN(STBI_REALLOC_alloc_size, STBI_REALLOC_old_size)); \ ++ STBI_FREE(STBI_REALLOC_buf); \ ++ } \ ++ STBI_REALLOC_new_buf; \ ++}) ++ ++#define STBI_NO_THREAD_LOCALS ++#define STBI_NO_STDIO ++#define STBI_NO_SIMD ++#define STBI_NO_LINEAR ++#define STBI_ONLY_JPEG ++#define STBI_ONLY_PNG ++#define STBI_ONLY_BMP ++ + // DOCUMENTATION + // + // Limitations: +@@ -381,7 +429,7 @@ + STBI_rgb_alpha = 4 + }; + +-#include ++// #include + typedef unsigned char stbi_uc; + typedef unsigned short stbi_us; + +@@ -584,8 +632,8 @@ + + #include + #include // ptrdiff_t on osx +-#include +-#include ++// #include ++// #include + #include + + #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +@@ -1574,10 +1622,12 @@ + STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } + #endif + ++/* + static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } + STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } ++*/ + + + ////////////////////////////////////////////////////////////////////////////// diff --git a/limine/common/sys/a20.h b/limine/common/sys/a20.h new file mode 100644 index 0000000..8e320b5 --- /dev/null +++ b/limine/common/sys/a20.h @@ -0,0 +1,9 @@ +#ifndef SYS__A20_H__ +#define SYS__A20_H__ + +#include + +bool a20_check(void); +bool a20_enable(void); + +#endif diff --git a/limine/common/sys/a20.s2.c b/limine/common/sys/a20.s2.c new file mode 100644 index 0000000..0059cbf --- /dev/null +++ b/limine/common/sys/a20.s2.c @@ -0,0 +1,92 @@ +#if defined (BIOS) + +#include +#include +#include +#include +#include +#include + +#define A20_KBC_TIMEOUT 50000 + +static bool a20_kbc_wait(uint8_t mask, uint8_t expected) { + for (int i = 0; i < A20_KBC_TIMEOUT; i++) { + if ((inb(0x64) & mask) == expected) + return true; + } + return false; +} + +bool a20_check(void) { + bool ret = false; + uint16_t orig = mminw(0x7dfe); + + mmoutw(0x7dfe, 0x1234); + + if (mminw(0x7dfe) != mminw(0x7dfe + 0x100000)) { + ret = true; + goto out; + } + + mmoutw(0x7dfe, ~mminw(0x7dfe)); + + if (mminw(0x7dfe) != mminw(0x7dfe + 0x100000)) { + ret = true; + goto out; + } + +out: + mmoutw(0x7dfe, orig); + return ret; +} + +// Keyboard controller method code below taken from: +// https://wiki.osdev.org/A20_Line + +bool a20_enable(void) { + if (a20_check()) + return true; + + // BIOS method + struct rm_regs r = {0}; + r.eax = 0x2401; + rm_int(0x15, &r, &r); + + if (a20_check()) + return true; + + // Keyboard controller method (with timeout for systems without KBC) + if (!a20_kbc_wait(2, 0)) goto kbc_failed; + outb(0x64, 0xad); + if (!a20_kbc_wait(2, 0)) goto kbc_failed; + outb(0x64, 0xd0); + if (!a20_kbc_wait(1, 1)) goto kbc_failed; + uint8_t b = inb(0x60); + if (!a20_kbc_wait(2, 0)) goto kbc_failed; + outb(0x64, 0xd1); + if (!a20_kbc_wait(2, 0)) goto kbc_failed; + outb(0x60, b | 2); + if (!a20_kbc_wait(2, 0)) goto kbc_failed; + outb(0x64, 0xae); + if (!a20_kbc_wait(2, 0)) goto kbc_failed; + + if (a20_check()) + return true; + +kbc_failed: + + // Fast A20 method + b = inb(0x92); + if ((b & 0x02) == 0) { + b &= ~0x01; + b |= 0x02; + outb(0x92, b); + } + + if (a20_check()) + return true; + + return false; +} + +#endif diff --git a/limine/common/sys/cpu.h b/limine/common/sys/cpu.h new file mode 100644 index 0000000..f8ee3b3 --- /dev/null +++ b/limine/common/sys/cpu.h @@ -0,0 +1,432 @@ +#ifndef SYS__CPU_H__ +#define SYS__CPU_H__ + +#include +#include +#include + +#if defined(__x86_64__) || defined(__i386__) + +static inline bool cpuid(uint32_t leaf, uint32_t subleaf, + uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { + uint32_t cpuid_max; + asm volatile ("cpuid" + : "=a" (cpuid_max) + : "a" (leaf & 0x80000000) + : "ebx", "ecx", "edx"); + if (leaf > cpuid_max) + return false; + asm volatile ("cpuid" + : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) + : "a" (leaf), "c" (subleaf)); + return true; +} + +static inline void outb(uint16_t port, uint8_t value) { + asm volatile ("outb %%al, %1" : : "a" (value), "Nd" (port) : "memory"); +} + +static inline void outw(uint16_t port, uint16_t value) { + asm volatile ("outw %%ax, %1" : : "a" (value), "Nd" (port) : "memory"); +} + +static inline void outd(uint16_t port, uint32_t value) { + asm volatile ("outl %%eax, %1" : : "a" (value), "Nd" (port) : "memory"); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t value; + asm volatile ("inb %1, %%al" : "=a" (value) : "Nd" (port) : "memory"); + return value; +} + +static inline uint16_t inw(uint16_t port) { + uint16_t value; + asm volatile ("inw %1, %%ax" : "=a" (value) : "Nd" (port) : "memory"); + return value; +} + +static inline uint32_t ind(uint16_t port) { + uint32_t value; + asm volatile ("inl %1, %%eax" : "=a" (value) : "Nd" (port) : "memory"); + return value; +} + +static inline void mmoutb(uintptr_t addr, uint8_t value) { + asm volatile ( + "movb %1, (%0)" + : + : "r" (addr), "ir" (value) + : "memory" + ); +} + +static inline void mmoutw(uintptr_t addr, uint16_t value) { + asm volatile ( + "movw %1, (%0)" + : + : "r" (addr), "ir" (value) + : "memory" + ); +} + +static inline void mmoutd(uintptr_t addr, uint32_t value) { + asm volatile ( + "movl %1, (%0)" + : + : "r" (addr), "ir" (value) + : "memory" + ); +} + +#if defined (__x86_64__) +static inline void mmoutq(uintptr_t addr, uint64_t value) { + asm volatile ( + "movq %1, (%0)" + : + : "r" (addr), "r" (value) + : "memory" + ); +} +#endif + +static inline uint8_t mminb(uintptr_t addr) { + uint8_t ret; + asm volatile ( + "movb (%1), %0" + : "=r" (ret) + : "r" (addr) + : "memory" + ); + return ret; +} + +static inline uint16_t mminw(uintptr_t addr) { + uint16_t ret; + asm volatile ( + "movw (%1), %0" + : "=r" (ret) + : "r" (addr) + : "memory" + ); + return ret; +} + +static inline uint32_t mmind(uintptr_t addr) { + uint32_t ret; + asm volatile ( + "movl (%1), %0" + : "=r" (ret) + : "r" (addr) + : "memory" + ); + return ret; +} + +#if defined (__x86_64__) +static inline uint64_t mminq(uintptr_t addr) { + uint64_t ret; + asm volatile ( + "movq (%1), %0" + : "=r" (ret) + : "r" (addr) + : "memory" + ); + return ret; +} +#endif + +static inline uint64_t rdmsr(uint32_t msr) { + uint32_t edx, eax; + asm volatile ("rdmsr" + : "=a" (eax), "=d" (edx) + : "c" (msr) + : "memory"); + return ((uint64_t)edx << 32) | eax; +} + +static inline void wrmsr(uint32_t msr, uint64_t value) { + uint32_t edx = value >> 32; + uint32_t eax = (uint32_t)value; + asm volatile ("wrmsr" + : + : "a" (eax), "d" (edx), "c" (msr) + : "memory"); +} + +static inline uint64_t rdtsc(void) { + uint32_t edx, eax; + asm volatile ("rdtsc" : "=a" (eax), "=d" (edx) :: "memory"); + return ((uint64_t)edx << 32) | eax; +} + +static inline uint64_t tsc_freq_arch(void) { + uint32_t eax, ebx, ecx, edx; + if (!cpuid(0x15, 0, &eax, &ebx, &ecx, &edx)) + return 0; + if (eax == 0 || ebx == 0 || ecx == 0) + return 0; + return (uint64_t)ecx * ebx / eax; +} + +#define rdrand(type) ({ \ + type rdrand__ret; \ + asm volatile ( \ + "1: " \ + "rdrand %0;" \ + "jnc 1b;" \ + : "=r" (rdrand__ret) \ + ); \ + rdrand__ret; \ +}) + +#define rdseed(type) ({ \ + type rdseed__ret; \ + asm volatile ( \ + "1: " \ + "rdseed %0;" \ + "jnc 1b;" \ + : "=r" (rdseed__ret) \ + ); \ + rdseed__ret; \ +}) + +#define write_cr(reg, val) do { \ + asm volatile ("mov %0, %%cr" reg :: "r" (val) : "memory"); \ +} while (0) + +#define read_cr(reg) ({ \ + size_t read_cr__cr; \ + asm volatile ("mov %%cr" reg ", %0" : "=r" (read_cr__cr) :: "memory"); \ + read_cr__cr; \ +}) + +#define locked_read(var) ({ \ + typeof(*var) locked_read__ret = 0; \ + asm volatile ( \ + "lock xadd %0, %1" \ + : "+r" (locked_read__ret) \ + : "m" (*(var)) \ + : "memory" \ + ); \ + locked_read__ret; \ +}) + +#define locked_write(var, val) do { \ + __auto_type locked_write__ret = val; \ + asm volatile ( \ + "lock xchg %0, %1" \ + : "+r" ((locked_write__ret)) \ + : "m" (*(var)) \ + : "memory" \ + ); \ +} while (0) + +#elif defined (__aarch64__) + +static inline uint64_t rdtsc(void) { + uint64_t v; + asm volatile ("mrs %0, cntpct_el0" : "=r" (v)); + return v; +} + +static inline uint64_t tsc_freq_arch(void) { + uint64_t v; + asm volatile ("mrs %0, cntfrq_el0" : "=r" (v)); + return v; +} + +#define locked_read(var) ({ \ + typeof(*var) locked_read__ret = 0; \ + asm volatile ( \ + "ldar %0, %1" \ + : "=r" (locked_read__ret) \ + : "m" (*(var)) \ + : "memory" \ + ); \ + locked_read__ret; \ +}) + +static inline size_t icache_line_size(void) { + uint64_t ctr; + asm volatile ("mrs %0, ctr_el0" : "=r"(ctr)); + + return 4 << (ctr & 0b1111); +} + +static inline size_t dcache_line_size(void) { + uint64_t ctr; + asm volatile ("mrs %0, ctr_el0" : "=r"(ctr)); + + return 4 << ((ctr >> 16) & 0b1111); +} + +static inline bool is_icache_pipt(void) { + uint64_t ctr; + asm volatile ("mrs %0, ctr_el0" : "=r"(ctr)); + + return ((ctr >> 14) & 0b11) == 0b11; +} + +// Clean D-Cache to Point of Coherency +static inline void clean_dcache_poc(uintptr_t start, uintptr_t end) { + size_t dsz = dcache_line_size(); + + uintptr_t addr = start & ~(dsz - 1); + while (addr < end) { + asm volatile ("dc cvac, %0" :: "r"(addr) : "memory"); + addr += dsz; + } + + asm volatile ("dsb sy\n\tisb"); +} + +// Invalidate I-Cache to Point of Unification +static inline void inval_icache_pou(uintptr_t start, uintptr_t end) { + if (!is_icache_pipt()) { + asm volatile ("ic ialluis" ::: "memory"); + asm volatile ("dsb sy\n\tisb"); + return; + } + + size_t isz = icache_line_size(); + + uintptr_t addr = start & ~(isz - 1); + while (addr < end) { + asm volatile ("ic ivau, %0" :: "r"(addr) : "memory"); + addr += isz; + } + + asm volatile ("dsb sy\n\tisb"); +} + +static inline int current_el(void) { + uint64_t v; + + asm volatile ("mrs %0, currentel" : "=r"(v)); + v = (v >> 2) & 0b11; + + return v; +} + +#elif defined (__riscv) + +static inline uint64_t rdtsc(void) { + uint64_t v; + asm volatile ("rdtime %0" : "=r"(v)); + return v; +} + +uint64_t riscv_time_base_frequency(void); + +static inline uint64_t tsc_freq_arch(void) { + return riscv_time_base_frequency(); +} + +#define csr_read(csr) ({\ + size_t v;\ + asm volatile ("csrr %0, " csr : "=r"(v));\ + v;\ +}) + +#define csr_write(csr, v) ({\ + size_t old;\ + asm volatile ("csrrw %0, " csr ", %1" : "=r"(old) : "r"(v));\ + old;\ +}) + +#define make_satp(mode, ppn) (((size_t)(mode) << 60) | ((size_t)(ppn) >> 12)) + +#define locked_read(var) ({ \ + typeof(*var) locked_read__ret; \ + asm volatile ( \ + "ld %0, (%1); fence r, rw" \ + : "=r"(locked_read__ret) \ + : "r"(var) \ + : "memory" \ + ); \ + locked_read__ret; \ +}) + +extern size_t bsp_hartid; + +struct riscv_hart { + struct riscv_hart *next; + const char *isa_string; + size_t hartid; + uint32_t acpi_uid; + uint8_t mmu_type; + uint8_t flags; +}; + +#define RISCV_HART_COPROC ((uint8_t)1 << 0) // is a coprocessor +#define RISCV_HART_HAS_MMU ((uint8_t)1 << 1) // `mmu_type` field is valid + +extern struct riscv_hart *hart_list; +extern struct riscv_hart *bsp_hart; + +bool riscv_check_isa_extension_for(size_t hartid, const char *ext, size_t *maj, size_t *min); + +static inline bool riscv_check_isa_extension(const char *ext, size_t *maj, size_t *min) { + return riscv_check_isa_extension_for(bsp_hartid, ext, maj, min); +} + +void init_riscv(const char *config); + +#elif defined (__loongarch64) + +static inline uint64_t rdtsc(void) { + uint64_t v; + asm volatile ("rdtime.d %0, $zero" : "=r" (v)); + return v; +} + +static inline uint32_t loongarch_cpucfg(uint32_t reg) { + uint32_t v; + asm volatile ("cpucfg %0, %1" : "=r" (v) : "r" (reg)); + return v; +} + +static inline uint64_t tsc_freq_arch(void) { + uint32_t cc_freq = loongarch_cpucfg(4); + uint32_t cc_cfg = loongarch_cpucfg(5); + uint32_t cc_mul = cc_cfg & 0xFFFF; + uint32_t cc_div = (cc_cfg >> 16) & 0xFFFF; + if (cc_freq == 0 || cc_mul == 0 || cc_div == 0) { + return 0; + } + return (uint64_t)cc_freq * cc_mul / cc_div; +} + +#else +#error Unknown architecture +#endif + +extern uint64_t tsc_freq; +void calibrate_tsc(void); + +static inline uint64_t rdtsc_usec(void) { + uint64_t exec_ticks = rdtsc(); + if (tsc_freq == 0) { + return 0; + } + return exec_ticks / tsc_freq * 1000000 + + exec_ticks % tsc_freq * 1000000 / tsc_freq; +} + +static inline void stall(uint64_t us) { +#if defined(BIOS) + if (tsc_freq == 0) { + // ~1 us per inb on ISA/LPC bus + for (uint64_t i = 0; i < us; i++) { + inb(0x80); + } + return; + } +#endif + uint64_t ticks = (tsc_freq * us + 999999) / 1000000; + uint64_t next_stop = rdtsc() + ticks; + while (rdtsc() < next_stop); +} + +#endif diff --git a/limine/common/sys/cpu.s2.c b/limine/common/sys/cpu.s2.c new file mode 100644 index 0000000..d4d6ec8 --- /dev/null +++ b/limine/common/sys/cpu.s2.c @@ -0,0 +1,26 @@ +#include +#include +#include +#if defined(UEFI) +#include +#include +#endif + +uint64_t tsc_freq = 0; + +void calibrate_tsc(void) { + tsc_freq = tsc_freq_arch(); + if (tsc_freq != 0) { + return; + } + +#if defined(UEFI) + uint64_t tsc_start = rdtsc(); + gBS->Stall(1000); + uint64_t tsc_end = rdtsc(); + + if (tsc_end > tsc_start) { + tsc_freq = (tsc_end - tsc_start) * 1000ULL; + } +#endif +} diff --git a/limine/common/sys/cpu_riscv.c b/limine/common/sys/cpu_riscv.c new file mode 100644 index 0000000..1491146 --- /dev/null +++ b/limine/common/sys/cpu_riscv.c @@ -0,0 +1,427 @@ + +#if defined(__riscv) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ACPI RISC-V Hart Capabilities Table +struct rhct { + struct sdt header; + uint32_t flags; + uint64_t time_base_frequency; + uint32_t nodes_len; + uint32_t nodes_offset; + uint8_t nodes[]; +} __attribute__((packed)); + +#define RHCT_ISA_STRING 0 +#define RHCT_CMO 1 +#define RHCT_MMU 2 +#define RHCT_HART_INFO 65535 + +struct rhct_header { + uint16_t type; // node type + uint16_t size; // node size (bytes) + uint16_t revision; // node revision +} __attribute__((packed)); + +// One `struct rhct_hart_info` structure exists per hart in the system. +// The `offsets` array points to other entries in the RHCT associated with the +// hart. +struct rhct_hart_info { + struct rhct_header header; + uint16_t offsets_len; + uint32_t acpi_processor_uid; + uint32_t offsets[]; +} __attribute__((packed)); + +struct rhct_isa_string { + struct rhct_header header; + uint16_t isa_string_len; + const char isa_string[]; +} __attribute__((packed)); + +#define RISCV_MMU_TYPE_SV39 0 +#define RISCV_MMU_TYPE_SV48 1 +#define RISCV_MMU_TYPE_SV57 2 + +struct rhct_mmu { + struct rhct_header header; + uint8_t reserved0; + uint8_t mmu_type; +} __attribute__((packed)); + +void *riscv_fdt = NULL; + +size_t bsp_hartid; +struct riscv_hart *hart_list = NULL; +struct riscv_hart *bsp_hart; +static const char *current_config = NULL; + +static uint64_t cached_time_base_freq = 0; + +uint64_t riscv_time_base_frequency(void) { + return cached_time_base_freq; +} + +static struct riscv_hart *riscv_get_hart(size_t hartid) { + for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) { + if (hart->hartid == hartid) { + return hart; + } + } + panic(false, "no `struct riscv_hart` for hartid %U", (uint64_t)hartid); +} + +static inline struct rhct_hart_info *rhct_get_hart_info(struct rhct *rhct, uint32_t acpi_uid) { + uint32_t offset = rhct->nodes_offset; + for (uint32_t i = 0; i < rhct->nodes_len; i++) { + if (offset + sizeof(struct rhct_header) > rhct->header.length) { + return NULL; + } + struct rhct_hart_info *node = (void *)((uintptr_t)rhct + offset); + if (node->header.type == RHCT_HART_INFO && node->acpi_processor_uid == acpi_uid) { + return node; + } + if (node->header.size == 0) { + return NULL; + } + offset += node->header.size; + } + return NULL; +} + +static void init_riscv_acpi(void) { + struct madt *madt = acpi_get_table("APIC", 0); + struct rhct *rhct = acpi_get_table("RHCT", 0); + if (madt == NULL || rhct == NULL) { + panic(false, "riscv: requires `APIC` and `RHCT` ACPI tables"); + } + + cached_time_base_freq = rhct->time_base_frequency; + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + if (*madt_ptr != 0x18) { + continue; + } + struct madt_riscv_intc *intc = (struct madt_riscv_intc *)madt_ptr; + + // Ignore harts we can't do anything with. + if (!(intc->flags & MADT_RISCV_INTC_ENABLED || + intc->flags & MADT_RISCV_INTC_ONLINE_CAPABLE)) { + continue; + } + + uint32_t acpi_uid = intc->acpi_processor_uid; + size_t hartid = intc->hartid; + + struct rhct_hart_info *hart_info = rhct_get_hart_info(rhct, acpi_uid); + if (hart_info == NULL) { + panic(false, "riscv: missing rhct node for hartid %U", (uint64_t)hartid); + } + + const char *isa_string = NULL; + uint8_t mmu_type = 0; + uint8_t flags = 0; + + for (uint32_t i = 0; i < hart_info->offsets_len; i++) { + uint32_t node_offset = hart_info->offsets[i]; + if (node_offset + sizeof(struct rhct_header) > rhct->header.length) { + continue; + } + const struct rhct_header *node = (void *)((uintptr_t)rhct + node_offset); + if (node->size < sizeof(struct rhct_header) || + node_offset + node->size > rhct->header.length) { + continue; + } + switch (node->type) { + case RHCT_ISA_STRING: { + if (node->size < sizeof(struct rhct_isa_string)) + break; + struct rhct_isa_string *isa_node = (struct rhct_isa_string *)node; + // Validate string is within node bounds and null-terminated + uint16_t max_str_len = node->size - sizeof(struct rhct_isa_string); + if (isa_node->isa_string_len > max_str_len) + break; + if (isa_node->isa_string_len == 0 || + isa_node->isa_string[isa_node->isa_string_len - 1] != '\0') + break; + isa_string = isa_node->isa_string; + break; + } + case RHCT_MMU: + if (node->size < sizeof(struct rhct_mmu)) + break; + mmu_type = ((struct rhct_mmu *)node)->mmu_type; + flags |= RISCV_HART_HAS_MMU; + break; + } + } + + if (isa_string == NULL) { + print("riscv: missing isa string for hartid %U, skipping.\n", (uint64_t)hartid); + continue; + } + + if (strncmp("rv64", isa_string, 4) && strncmp("rv32", isa_string, 4)) { + print("riscv: skipping hartid %U with invalid isa string: %s\n", (uint64_t)hartid, isa_string); + continue; + } + + struct riscv_hart *hart = ext_mem_alloc(sizeof(struct riscv_hart)); + if (hart == NULL) { + panic(false, "out of memory"); + } + + hart->hartid = hartid; + hart->acpi_uid = acpi_uid; + hart->isa_string = isa_string; + hart->mmu_type = mmu_type; + hart->flags = flags; + + hart->next = hart_list; + hart_list = hart; + + if (hart->hartid == bsp_hartid) { + bsp_hart = hart; + } + } +} + +static void init_riscv_fdt(const void *fdt) { + if (fdt_check_header(fdt)) { + panic(false, "riscv: invalid device tree"); + } + + int cpus = fdt_path_offset(fdt, "/cpus"); + if (cpus < 0) { + panic(false, "riscv: missing `/cpus` node"); + } + + int len; + const void *tbf = fdt_getprop(fdt, cpus, "timebase-frequency", &len); + if (tbf != NULL) { + if (len == 8) { + cached_time_base_freq = fdt64_ld(tbf); + } else if (len == 4) { + cached_time_base_freq = fdt32_ld(tbf); + } + } + + int node; + fdt_for_each_subnode(node, fdt, cpus) { + const void *prop; + + if (!(prop = fdt_getprop(fdt, node, "device_type", NULL)) || strcmp(prop, "cpu")) { + continue; + } + + if (!(prop = fdt_getprop(fdt, node, "reg", NULL))) { + continue; + } + size_t hartid = fdt32_ld(prop); + + uint8_t flags = 0; + uint8_t mmu_type = 0; + if ((prop = fdt_getprop(fdt, node, "mmu-type", NULL))) { + if (!strcmp(prop, "riscv,sv39")) { + mmu_type = RISCV_MMU_TYPE_SV39; + flags |= RISCV_HART_HAS_MMU; + } else if (!strcmp(prop, "riscv,sv48")) { + mmu_type = RISCV_MMU_TYPE_SV48; + flags |= RISCV_HART_HAS_MMU; + } else if (!strcmp(prop, "riscv,sv57")) { + mmu_type = RISCV_MMU_TYPE_SV57; + flags |= RISCV_HART_HAS_MMU; + } + } + + const char *isa_string = fdt_getprop(fdt, node, "riscv,isa", NULL); + if (isa_string == NULL) { + print("riscv: missing isa string for hartid %U, skipping.\n", (uint64_t)hartid); + continue; + } + + if (strncmp("rv64", isa_string, 4) && strncmp("rv32", isa_string, 4)) { + print("riscv: skipping hartid %U with invalid isa string: %s\n", (uint64_t)hartid, isa_string); + continue; + } + + struct riscv_hart *hart = ext_mem_alloc(sizeof(struct riscv_hart)); + if (hart == NULL) { + panic(false, "out of memory"); + } + + hart->hartid = hartid; + hart->acpi_uid = 0; + hart->isa_string = isa_string; + hart->mmu_type = mmu_type; + hart->flags = flags; + + hart->next = hart_list; + hart_list = hart; + + if (hart->hartid == bsp_hartid) { + bsp_hart = hart; + } + } +} + +void init_riscv(const char *config) { + if (current_config == config && hart_list != NULL) { + return; + } + + while (hart_list != NULL) { + void *cur_hart = hart_list; + hart_list = hart_list->next; + pmm_free(cur_hart, sizeof(struct riscv_hart)); + } + bsp_hart = NULL; + + if (riscv_fdt != NULL) { + pmm_free(riscv_fdt, fdt_totalsize(riscv_fdt)); + riscv_fdt = NULL; + } + + bool prioritise_dtb = false; + if (config != NULL) { + prioritise_dtb = config_get_value(config, 0, "dtb_path"); + } + if (!prioritise_dtb) { + prioritise_dtb = config_get_value(NULL, 0, "global_dtb"); + } + + if (!prioritise_dtb && acpi_get_rsdp()) { + init_riscv_acpi(); + } else { + riscv_fdt = get_device_tree_blob(config, 0); + if (riscv_fdt != NULL) { + init_riscv_fdt(riscv_fdt); + } else { + panic(false, "riscv: requires DTB or ACPI"); + } + } + + if (cached_time_base_freq != 0) { + tsc_freq = cached_time_base_freq; + } + + if (bsp_hart == NULL) { + panic(false, "riscv: missing `struct riscv_hart` for BSP"); + } + + if (strncasecmp(bsp_hart->isa_string, "rv64i", 5)) { + panic(false, "unsupported cpu: %s", bsp_hart->isa_string); + } + + for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) { + if (hart != bsp_hart && strcmp(bsp_hart->isa_string, hart->isa_string)) { + hart->flags |= RISCV_HART_COPROC; + } + } + + current_config = config; +} + +struct isa_extension { + const char *name; + size_t name_len; + uint32_t ver_maj; + uint32_t ver_min; +}; + +// Parse the next sequence of digit characters into an integer. +static bool parse_number(const char **s, size_t *_n) { + size_t n = 0; + bool parsed = false; + while (isdigit(**s)) { + n *= 10; + n += *(*s)++ - '0'; + parsed = true; + } + *_n = n; + return parsed; +} + +// Parse the next extension from an ISA string. +static bool parse_extension(const char **s, struct isa_extension *ext) { + if (**s == '\0') { + return false; + } + + const char *name = *s; + size_t name_len = 1; + if (**s == 's' || **s == 'S' || **s == 'x' || **s == 'X' || **s == 'z' || **s == 'Z') { + while (isalpha((*s)[name_len])) { + name_len++; + } + } + *s += name_len; + + size_t maj = 0, min = 0; + if (parse_number(s, &maj)) { + if (**s == 'p') { + *s += 1; + parse_number(s, &min); + } + } + + while (**s == '_') { + *s += 1; + } + + if (ext) { + ext->name = name; + ext->name_len = name_len; + ext->ver_maj = maj; + ext->ver_min = min; + } + return true; +} + +static bool extension_matches(const struct isa_extension *ext, const char *name) { + for (size_t i = 0; i < ext->name_len; i++) { + const char c1 = tolower(ext->name[i]); + const char c2 = tolower(*name++); + if (c2 == '\0' || c1 != c2) { + return false; + } + } + // Make sure `name` is not longer. + return *name == '\0'; +} + +bool riscv_check_isa_extension_for(size_t hartid, const char *name, size_t *maj, size_t *min) { + // Skip the `rv{32,64}` prefix so it's not parsed as extensions. + const char *isa_string = riscv_get_hart(hartid)->isa_string + 4; + + struct isa_extension ext; + while (parse_extension(&isa_string, &ext)) { + if (!extension_matches(&ext, name)) { + continue; + } + if (maj) { + *maj = ext.ver_maj; + } + if (min) { + *min = ext.ver_min; + } + return true; + } + + return false; +} + +#endif diff --git a/limine/common/sys/dummy_isr.asm_ia32 b/limine/common/sys/dummy_isr.asm_ia32 new file mode 100644 index 0000000..cb9123d --- /dev/null +++ b/limine/common/sys/dummy_isr.asm_ia32 @@ -0,0 +1,12 @@ +section .text + +extern lapic_eoi + +global dummy_isr +dummy_isr: + pusha + call lapic_eoi + popa + iretd + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/sys/dummy_isr.asm_x86_64 b/limine/common/sys/dummy_isr.asm_x86_64 new file mode 100644 index 0000000..2618ce9 --- /dev/null +++ b/limine/common/sys/dummy_isr.asm_x86_64 @@ -0,0 +1,28 @@ +section .text + +extern lapic_eoi + +global dummy_isr +dummy_isr: + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + call lapic_eoi + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + iretq + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/sys/e820.h b/limine/common/sys/e820.h new file mode 100644 index 0000000..b908625 --- /dev/null +++ b/limine/common/sys/e820.h @@ -0,0 +1,13 @@ +#ifndef SYS__E820_H__ +#define SYS__E820_H__ + +#include +#include +#include + +extern struct memmap_entry e820_map[]; +extern size_t e820_entries; + +void init_e820(void); + +#endif diff --git a/limine/common/sys/e820.s2.c b/limine/common/sys/e820.s2.c new file mode 100644 index 0000000..82a2222 --- /dev/null +++ b/limine/common/sys/e820.s2.c @@ -0,0 +1,44 @@ +#if defined (BIOS) + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_E820_ENTRIES 256 + +struct memmap_entry e820_map[MAX_E820_ENTRIES]; +size_t e820_entries = 0; + +void init_e820(void) { + struct rm_regs r = {0}; + + for (size_t i = 0; i < MAX_E820_ENTRIES; i++) { + struct memmap_entry entry; + + r.eax = 0xe820; + r.ecx = 24; + r.edx = 0x534d4150; + r.edi = (uint32_t)&entry; + rm_int(0x15, &r, &r); + + if (r.eflags & EFLAGS_CF) { + e820_entries = i; + return; + } + + e820_map[i] = entry; + + if (!r.ebx) { + e820_entries = ++i; + return; + } + } + + panic(false, "Too many E820 entries!"); +} + +#endif diff --git a/limine/common/sys/exceptions.s2.c b/limine/common/sys/exceptions.s2.c new file mode 100644 index 0000000..cbe68f8 --- /dev/null +++ b/limine/common/sys/exceptions.s2.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#if defined (BIOS) + +static const char *exception_names[] = { + "Division", + "Debug", + "NMI", + "Breakpoint", + "Overflow", + "Bound range exceeded", + "Invalid opcode", + "Device not available", + "Double fault", + "???", + "Invalid TSS", + "Segment not present", + "Stack-segment fault", + "General protection fault", + "Page fault", + "???", + "x87", + "Alignment check", + "Machine check", + "SIMD", + "Virtualisation", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "Security" +}; + +void except(uint32_t exception, uint32_t error_code, uint32_t ebp, uint32_t eip) { + (void)ebp; + const char *exception_name = exception < SIZEOF_ARRAY(exception_names) + ? exception_names[exception] + : "Unknown"; + panic(false, "%s exception at %x. Error code: %x", exception_name, eip, error_code); +} + +#endif diff --git a/limine/common/sys/gdt.h b/limine/common/sys/gdt.h new file mode 100644 index 0000000..d749baf --- /dev/null +++ b/limine/common/sys/gdt.h @@ -0,0 +1,29 @@ +#ifndef SYS__GDT_H__ +#define SYS__GDT_H__ + +#include + +struct gdtr { + uint16_t limit; +#if defined (__x86_64__) + uint64_t ptr; +#elif defined (__i386__) + uint32_t ptr; + uint32_t ptr_hi; +#endif +} __attribute__((packed)); + +struct gdt_desc { + uint16_t limit; + uint16_t base_low; + uint8_t base_mid; + uint8_t access; + uint8_t granularity; + uint8_t base_hi; +} __attribute__((packed)); + +extern struct gdtr gdt; + +void init_gdt(void); + +#endif diff --git a/limine/common/sys/gdt.s2.c b/limine/common/sys/gdt.s2.c new file mode 100644 index 0000000..a7d564a --- /dev/null +++ b/limine/common/sys/gdt.s2.c @@ -0,0 +1,95 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include + +static struct gdt_desc gdt_descs[] = { + {0}, + + { + .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0b10011011, + .granularity = 0b00000000, + .base_hi = 0x00 + }, + + { + .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0b10010011, + .granularity = 0b00000000, + .base_hi = 0x00 + }, + + { + .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0b10011011, + .granularity = 0b11001111, + .base_hi = 0x00 + }, + + { + .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0b10010011, + .granularity = 0b11001111, + .base_hi = 0x00 + }, + + { + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0b10011011, + .granularity = 0b00100000, + .base_hi = 0x00 + }, + + { + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0b10010011, + .granularity = 0b00000000, + .base_hi = 0x00 + }, + + { // 0x38: TSS descriptor low (base 0, limit 0) + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x89, + .granularity = 0x00, + .base_hi = 0x00 + }, + {0} // 0x40: TSS descriptor high (base upper = 0) +}; + +#if defined (BIOS) +__attribute__((section(".realmode"))) +#endif +struct gdtr gdt = { + sizeof(gdt_descs) - 1, + (uintptr_t)gdt_descs, +#if defined (__i386__) + 0 +#endif +}; + +#if defined (UEFI) +void init_gdt(void) { + struct gdt_desc *gdt_copy = ext_mem_alloc(sizeof(gdt_descs)); + memcpy(gdt_copy, gdt_descs, sizeof(gdt_descs)); + gdt.ptr = (uintptr_t)gdt_copy; +} +#endif + +#endif diff --git a/limine/common/sys/idt.c b/limine/common/sys/idt.c new file mode 100644 index 0000000..4669cf9 --- /dev/null +++ b/limine/common/sys/idt.c @@ -0,0 +1,71 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct idt_entry *dummy_idt = NULL; + +void dummy_isr(void); + +void init_flush_irqs(void) { + size_t dummy_idt_size = 256 * sizeof(struct idt_entry); + dummy_idt = ext_mem_alloc(dummy_idt_size); + + for (size_t i = 0; i < 256; i++) { + dummy_idt[i].offset_lo = (uint16_t)(uintptr_t)dummy_isr; + dummy_idt[i].type_attr = 0x8e; +#if defined (__i386__) + dummy_idt[i].selector = 0x18; + dummy_idt[i].offset_hi = (uint16_t)((uintptr_t)dummy_isr >> 16); +#elif defined (__x86_64__) + dummy_idt[i].selector = 0x28; + dummy_idt[i].offset_mid = (uint16_t)((uintptr_t)dummy_isr >> 16); + dummy_idt[i].offset_hi = (uint32_t)((uintptr_t)dummy_isr >> 32); +#endif + } +} + +int irq_flush_type = IRQ_NO_FLUSH; + +void flush_irqs(void) { + switch (irq_flush_type) { + case IRQ_PIC_ONLY_FLUSH: + pic_flush(); + // FALLTHRU + case IRQ_NO_FLUSH: + return; + case IRQ_PIC_APIC_FLUSH: + break; + default: + panic(false, "Invalid IRQ flush type"); + } + + struct idtr old_idt; + asm volatile ("sidt %0" : "=m"(old_idt) :: "memory"); + + struct idtr new_idt = { + 256 * sizeof(struct idt_entry) - 1, + (uintptr_t)dummy_idt + }; + asm volatile ("lidt %0" :: "m"(new_idt) : "memory"); + + // Flush the legacy PIC so we know the remaining ints come from the LAPIC + pic_flush(); + + asm volatile ("sti" ::: "memory"); + + // Delay a while to make sure we catch ALL pending IRQs + stall(10000); + + asm volatile ("cli" ::: "memory"); + + asm volatile ("lidt %0" :: "m"(old_idt) : "memory"); +} + +#endif diff --git a/limine/common/sys/idt.h b/limine/common/sys/idt.h new file mode 100644 index 0000000..a769131 --- /dev/null +++ b/limine/common/sys/idt.h @@ -0,0 +1,59 @@ +#ifndef SYS__IDT_H__ +#define SYS__IDT_H__ + +#include + +#if defined (__i386__) + +struct idtr { + uint16_t limit; + uint32_t ptr; +} __attribute__((packed)); + +struct idt_entry { + uint16_t offset_lo; + uint16_t selector; + uint8_t unused; + uint8_t type_attr; + uint16_t offset_hi; +} __attribute__((packed)); + +#elif defined (__x86_64__) + +struct idtr { + uint16_t limit; + uint64_t ptr; +} __attribute__((packed)); + +struct idt_entry { + uint16_t offset_lo; + uint16_t selector; + uint8_t ist; + uint8_t type_attr; + uint16_t offset_mid; + uint32_t offset_hi; + uint32_t reserved; +} __attribute__((packed)); + +#endif + +#if defined (BIOS) + +extern struct idtr idt; + +void init_idt(void); + +#endif + +enum { + IRQ_NO_FLUSH, + IRQ_PIC_ONLY_FLUSH, + IRQ_PIC_APIC_FLUSH +}; + +extern int irq_flush_type; + +void init_flush_irqs(void); +void flush_irqs(void); + +#endif diff --git a/limine/common/sys/idt.s2.c b/limine/common/sys/idt.s2.c new file mode 100644 index 0000000..33c2d73 --- /dev/null +++ b/limine/common/sys/idt.s2.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#if defined (BIOS) + +static struct idt_entry idt_entries[32]; + +__attribute__((section(".realmode"))) +struct idtr idt = { + sizeof(idt_entries) - 1, + (uintptr_t)idt_entries +}; + +static void register_interrupt_handler(size_t vec, void *handler, uint8_t type) { + uint32_t p = (uint32_t)handler; + + idt_entries[vec].offset_lo = (uint16_t)p; + idt_entries[vec].selector = 0x18; + idt_entries[vec].unused = 0; + idt_entries[vec].type_attr = type; + idt_entries[vec].offset_hi = (uint16_t)(p >> 16); +} + +extern void *exceptions[]; + +void init_idt(void) { + for (size_t i = 0; i < SIZEOF_ARRAY(idt_entries); i++) + register_interrupt_handler(i, exceptions[i], 0x8e); + + asm volatile ("lidt %0" :: "m"(idt) : "memory"); +} + +#endif diff --git a/limine/common/sys/int_thunks.s2.asm_bios_ia32 b/limine/common/sys/int_thunks.s2.asm_bios_ia32 new file mode 100644 index 0000000..715f721 --- /dev/null +++ b/limine/common/sys/int_thunks.s2.asm_bios_ia32 @@ -0,0 +1,41 @@ +section .text + +extern except + +%macro raise_exception 1 +align 16 +raise_exception_%1: + cld +%if %1 == 8 || %1 == 10 || %1 == 11 || %1 == 12 || %1 == 13 || %1 == 14 || %1 == 17 || %1 == 30 + pop eax +%else + xor eax, eax +%endif + push ebp + mov ebp, esp + push eax + push %1 + call except +%endmacro + +%assign i 0 +%rep 32 +raise_exception i +%assign i i+1 +%endrep + +section .rodata + +%macro raise_exception_getaddr 1 +dd raise_exception_%1 +%endmacro + +global exceptions +exceptions: +%assign i 0 +%rep 32 +raise_exception_getaddr i +%assign i i+1 +%endrep + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/common/sys/iommu.c b/limine/common/sys/iommu.c new file mode 100644 index 0000000..16cca3a --- /dev/null +++ b/limine/common/sys/iommu.c @@ -0,0 +1,144 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include + +// Intel VT-d registers +#define VTD_GCMD_REG 0x18 +#define VTD_GSTS_REG 0x1C + +// GSTS/GCMD bit positions +#define VTD_GSTS_TES (1u << 31) // Translation Enable Status +#define VTD_GSTS_QIES (1u << 26) // Queued Invalidation Enable Status +#define VTD_GSTS_IRES (1u << 25) // Interrupt Remapping Enable Status + +// Mask to clear one-shot command bits when reading GSTS for use as GCMD base. +// One-shot bits auto-clear after hardware processes them and must not be +// carried over from GSTS into GCMD writes: +// Bit 30: SRTP (Set Root Table Pointer) +// Bit 29: SFL (Set Fault Log) +// Bit 27: WBF (Write Buffer Flush) +// Bit 24: SIRTP (Set Interrupt Remap Table Pointer) +// All other bits (TE, EAFL, QIE, IRE, CFI) are persistent toggles. +#define VTD_GCMD_ONESHOT_MASK 0x96FFFFFF + +static void vtd_disable_unit(uintptr_t reg_base) { + uint32_t sts = mmind(reg_base + VTD_GSTS_REG); + + // Disable DMA translation first (most urgent: prevents stale lookups) + if (sts & VTD_GSTS_TES) { + uint32_t gcmd = (sts & VTD_GCMD_ONESHOT_MASK) & ~VTD_GSTS_TES; + mmoutd(reg_base + VTD_GCMD_REG, gcmd); + + while ((sts = mmind(reg_base + VTD_GSTS_REG)) & VTD_GSTS_TES) { + asm volatile ("pause"); + } + } + + // Disable interrupt remapping (depends on QIE, so disable before QIE) + if (sts & VTD_GSTS_IRES) { + uint32_t gcmd = (sts & VTD_GCMD_ONESHOT_MASK) & ~VTD_GSTS_IRES; + mmoutd(reg_base + VTD_GCMD_REG, gcmd); + + while ((sts = mmind(reg_base + VTD_GSTS_REG)) & VTD_GSTS_IRES) { + asm volatile ("pause"); + } + } + + // Disable queued invalidation last (was prerequisite for IRE) + if (sts & VTD_GSTS_QIES) { + uint32_t gcmd = (sts & VTD_GCMD_ONESHOT_MASK) & ~VTD_GSTS_QIES; + mmoutd(reg_base + VTD_GCMD_REG, gcmd); + + while ((sts = mmind(reg_base + VTD_GSTS_REG)) & VTD_GSTS_QIES) { + asm volatile ("pause"); + } + } +} + +static void vtd_disable_all(void) { + struct sdt *dmar = acpi_get_table("DMAR", 0); + if (dmar == NULL) { + return; + } + + // DMAR header is 48 bytes: 36 (SDT) + 1 (width) + 1 (flags) + 10 (reserved) + uint8_t *ptr = (uint8_t *)dmar + 48; + uint8_t *end = (uint8_t *)dmar + dmar->length; + + while (ptr + 4 <= end) { + uint16_t type = *(uint16_t *)(ptr + 0); + uint16_t length = *(uint16_t *)(ptr + 2); + + if (length < 4 || ptr + length > end) { + break; + } + + // Type 0 = DRHD (DMA Remapping Hardware Unit Definition) + if (type == 0 && length >= 16) { + uint64_t reg_base; + memcpy(®_base, ptr + 8, sizeof(reg_base)); + vtd_disable_unit((uintptr_t)reg_base); + } + + ptr += length; + } +} + +// AMD IOMMU control register (64-bit at offset 0x18) +#define AMDVI_CONTROL_REG 0x18 + +static void amdvi_disable_unit(uintptr_t mmio_base) { + // Read low 32 bits of the 64-bit control register + uint32_t ctrl_lo = mmind(mmio_base + AMDVI_CONTROL_REG); + + if (!(ctrl_lo & (1u << 0))) { + return; // IOMMU not enabled + } + + // Clear IommuEn (bit 0) — the master enable for all IOMMU functionality. + // Takes effect immediately, no polling needed (unlike Intel VT-d). + ctrl_lo &= ~(1u << 0); + mmoutd(mmio_base + AMDVI_CONTROL_REG, ctrl_lo); +} + +static void amdvi_disable_all(void) { + struct sdt *ivrs = acpi_get_table("IVRS", 0); + if (ivrs == NULL) { + return; + } + + // IVRS header is 48 bytes: 36 (SDT) + 4 (IVinfo) + 8 (reserved) + uint8_t *ptr = (uint8_t *)ivrs + 48; + uint8_t *end = (uint8_t *)ivrs + ivrs->length; + + while (ptr + 4 <= end) { + uint8_t type = *(uint8_t *)(ptr + 0); + uint16_t length = *(uint16_t *)(ptr + 2); + + if (length < 4 || ptr + length > end) { + break; + } + + // IVHD types: 0x10 (basic), 0x11 (extended), 0x40 (extended, newer) + if ((type == 0x10 || type == 0x11 || type == 0x40) && length >= 16) { + uint64_t mmio_base; + memcpy(&mmio_base, ptr + 8, sizeof(mmio_base)); + amdvi_disable_unit((uintptr_t)mmio_base); + } + + ptr += length; + } +} + +void iommu_disable_all(void) { + vtd_disable_all(); + amdvi_disable_all(); +} + +#endif diff --git a/limine/common/sys/iommu.h b/limine/common/sys/iommu.h new file mode 100644 index 0000000..6826eef --- /dev/null +++ b/limine/common/sys/iommu.h @@ -0,0 +1,6 @@ +#ifndef SYS__IOMMU_H__ +#define SYS__IOMMU_H__ + +void iommu_disable_all(void); + +#endif diff --git a/limine/common/sys/lapic.c b/limine/common/sys/lapic.c new file mode 100644 index 0000000..bc8c94d --- /dev/null +++ b/limine/common/sys/lapic.c @@ -0,0 +1,507 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LAPIC_REG_LVT_CMCI 0x2f0 +#define LAPIC_REG_LVT_TIMER 0x320 +#define LAPIC_REG_LVT_THERMAL 0x330 +#define LAPIC_REG_LVT_PMC 0x340 +#define LAPIC_REG_LVT_LINT0 0x350 +#define LAPIC_REG_LVT_LINT1 0x360 +#define LAPIC_REG_LVT_ERROR 0x370 +#define LAPIC_REG_SVR 0x0f0 +#define LAPIC_REG_TPR 0x080 +#define LAPIC_REG_VERSION 0x030 + +static uint32_t pending_lint0 = UINT32_MAX; // no override +static uint32_t pending_lint1 = UINT32_MAX; // no override + +static uint32_t lapic_madt_nmi_flags_to_lvt(uint16_t flags) { + uint32_t lvt = 0x10400; // masked + NMI delivery mode + + // Polarity: bits 1:0 of flags + uint8_t polarity = flags & 0x3; + if (polarity == 0x3) { + lvt |= (1 << 13); // active low + } + // 0b00 (conforms) and 0b01 (active high) leave bit 13 clear + + // Trigger mode: bits 3:2 of flags + uint8_t trigger = (flags >> 2) & 0x3; + if (trigger == 0x3) { + lvt |= (1 << 15); // level triggered + } + // 0b00 (conforms) and 0b01 (edge) leave bit 15 clear + + return lvt; +} + +void lapic_prep_lint(struct madt *madt, uint32_t acpi_uid, bool x2apic) { + pending_lint0 = UINT32_MAX; // no override + pending_lint1 = UINT32_MAX; // no override + + // Walk MADT entries looking for NMI entries + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 4: { + // Local APIC NMI + if (*(madt_ptr + 1) < sizeof(struct madt_lapic_nmi)) { + continue; + } + + struct madt_lapic_nmi *nmi = (void *)madt_ptr; + + // Match all processors (0xff) or specific UID + if (nmi->acpi_processor_uid != 0xff && nmi->acpi_processor_uid != (uint8_t)acpi_uid) { + continue; + } + + uint32_t lvt = lapic_madt_nmi_flags_to_lvt(nmi->flags); + if (nmi->lint == 0) { + pending_lint0 = lvt; + } else if (nmi->lint == 1) { + pending_lint1 = lvt; + } + continue; + } + case 0x0a: { + // Local x2APIC NMI + if (!x2apic) { + continue; + } + if (*(madt_ptr + 1) < sizeof(struct madt_x2apic_nmi)) { + continue; + } + + struct madt_x2apic_nmi *nmi = (void *)madt_ptr; + + // Match all processors (0xffffffff) or specific UID + if (nmi->acpi_processor_uid != 0xffffffff && nmi->acpi_processor_uid != acpi_uid) { + continue; + } + + uint32_t lvt = lapic_madt_nmi_flags_to_lvt(nmi->flags); + if (nmi->lint == 0) { + pending_lint0 = lvt; + } else if (nmi->lint == 1) { + pending_lint1 = lvt; + } + continue; + } + } + } +} + +static bool lvt_should_mask(uint32_t lvt) { + switch ((lvt >> 8) & 7) { + case 0b000: // Fixed + case 0b001: // Lowest Priority + case 0b100: // NMI + case 0b111: // ExtINT + return true; + default: // SMI, INIT, Reserved + return false; + } +} + +void lapic_configure_handoff_state(void) { + bool is_x2 = !!(rdmsr(0x1b) & (1 << 10)); + + uint32_t max_lvt; + if (is_x2) { + max_lvt = (x2apic_read(LAPIC_REG_VERSION) >> 16) & 0xff; + } else { + max_lvt = (lapic_read(LAPIC_REG_VERSION) >> 16) & 0xff; + } + + uint32_t lvt; + + if (is_x2) { + x2apic_write(LAPIC_REG_SVR, 0x1ff); + x2apic_write(LAPIC_REG_TPR, 0); + if (max_lvt >= 6) { + lvt = x2apic_read(LAPIC_REG_LVT_CMCI); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_CMCI, lvt | (1 << 16)); + } + } + lvt = x2apic_read(LAPIC_REG_LVT_TIMER); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_TIMER, lvt | (1 << 16)); + } + if (max_lvt >= 5) { + lvt = x2apic_read(LAPIC_REG_LVT_THERMAL); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_THERMAL, lvt | (1 << 16)); + } + } + if (max_lvt >= 4) { + lvt = x2apic_read(LAPIC_REG_LVT_PMC); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_PMC, lvt | (1 << 16)); + } + } + lvt = x2apic_read(LAPIC_REG_LVT_ERROR); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_ERROR, lvt | (1 << 16)); + } + lvt = x2apic_read(LAPIC_REG_LVT_LINT0); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_LINT0, pending_lint0 != UINT32_MAX ? pending_lint0 : lvt | (1 << 16)); + } + lvt = x2apic_read(LAPIC_REG_LVT_LINT1); + if (lvt_should_mask(lvt)) { + x2apic_write(LAPIC_REG_LVT_LINT1, pending_lint1 != UINT32_MAX ? pending_lint1 : lvt | (1 << 16)); + } + } else { + lapic_write(LAPIC_REG_SVR, 0x1ff); + lapic_write(LAPIC_REG_TPR, 0); + if (max_lvt >= 6) { + lvt = lapic_read(LAPIC_REG_LVT_CMCI); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_CMCI, lvt | (1 << 16)); + } + } + lvt = lapic_read(LAPIC_REG_LVT_TIMER); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_TIMER, lvt | (1 << 16)); + } + if (max_lvt >= 5) { + lvt = lapic_read(LAPIC_REG_LVT_THERMAL); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_THERMAL, lvt | (1 << 16)); + } + } + if (max_lvt >= 4) { + lvt = lapic_read(LAPIC_REG_LVT_PMC); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_PMC, lvt | (1 << 16)); + } + } + lvt = lapic_read(LAPIC_REG_LVT_ERROR); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_ERROR, lvt | (1 << 16)); + } + lvt = lapic_read(LAPIC_REG_LVT_LINT0); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_LINT0, pending_lint0 != UINT32_MAX ? pending_lint0 : lvt | (1 << 16)); + } + lvt = lapic_read(LAPIC_REG_LVT_LINT1); + if (lvt_should_mask(lvt)) { + lapic_write(LAPIC_REG_LVT_LINT1, pending_lint1 != UINT32_MAX ? pending_lint1 : lvt | (1 << 16)); + } + } +} + +void lapic_configure_bsp(void) { + struct madt *madt = acpi_get_table("APIC", 0); + if (madt == NULL) { + return; + } + + // Detect x2APIC from MSR + bool is_x2 = !!(rdmsr(0x1b) & (1 << 10)); + + // Find the BSP entry by matching LAPIC ID + uint32_t bsp_lapic_id; + if (is_x2) { + bsp_lapic_id = x2apic_read(LAPIC_REG_ID); + } else { + bsp_lapic_id = lapic_read(LAPIC_REG_ID) >> 24; + } + + uint32_t bsp_acpi_uid = 0; + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 0: { + if (*(madt_ptr + 1) < sizeof(struct madt_lapic)) { + continue; + } + struct madt_lapic *lapic = (void *)madt_ptr; + if (lapic->lapic_id == bsp_lapic_id) { + bsp_acpi_uid = lapic->acpi_processor_uid; + goto found; + } + continue; + } + case 9: { + if (!is_x2) { + continue; + } + if (*(madt_ptr + 1) < sizeof(struct madt_x2apic)) { + continue; + } + struct madt_x2apic *x2lapic = (void *)madt_ptr; + if (x2lapic->x2apic_id == bsp_lapic_id) { + bsp_acpi_uid = x2lapic->acpi_processor_uid; + goto found; + } + continue; + } + } + } + +found: + lapic_prep_lint(madt, bsp_acpi_uid, is_x2); + lapic_configure_handoff_state(); +} + +struct dmar { + struct sdt header; + uint8_t host_address_width; + uint8_t flags; + uint8_t reserved[10]; + symbol remapping_structures; +} __attribute__((packed)); + +bool lapic_check(void) { + uint32_t eax, ebx, ecx, edx; + if (!cpuid(1, 0, &eax, &ebx, &ecx, &edx)) + return false; + + if (!(edx & (1 << 9))) + return false; + + return true; +} + +uint32_t lapic_read(uint32_t reg) { + size_t lapic_mmio_base = (size_t)(rdmsr(0x1b) & 0xfffff000); + return mmind(lapic_mmio_base + reg); +} + +void lapic_write(uint32_t reg, uint32_t data) { + size_t lapic_mmio_base = (size_t)(rdmsr(0x1b) & 0xfffff000); + mmoutd(lapic_mmio_base + reg, data); +} + +void lapic_icr_wait(void) { + for (int i = 0; i < 1000000; i++) { + if (!(lapic_read(LAPIC_REG_ICR0) & (1 << 12))) { + return; + } + asm volatile ("pause"); + } +} + +bool x2apic_check(void) { + uint32_t eax, ebx, ecx, edx; + if (!cpuid(1, 0, &eax, &ebx, &ecx, &edx)) + return false; + + if (!(ecx & (1 << 21))) + return false; + + // According to the Intel VT-d spec, we're required + // to check if bit 0 and 1 of the flags field of the + // DMAR ACPI table are set, and if they are, we should + // not report x2APIC capabilities. + struct dmar *dmar = acpi_get_table("DMAR", 0); + if (!dmar) + return true; + + if ((dmar->flags & (1 << 0)) && (dmar->flags & (1 << 1))) + return false; + + return true; +} + +static bool x2apic_mode = false; + +bool x2apic_enable(void) { + if (!x2apic_check()) + return false; + + uint64_t ia32_apic_base = rdmsr(0x1b); + ia32_apic_base |= (1 << 10); + wrmsr(0x1b, ia32_apic_base); + + x2apic_mode = true; + + return true; +} + +bool x2apic_disable(void) { + uint64_t msr = rdmsr(0x1b); + if (!(msr & (1 << 10))) + return true; + + // Check for LEGACY_XAPIC_DISABLED (Intel Meteor Lake+). + // CPUID.07H.0:EDX[29] enumerates IA32_ARCH_CAPABILITIES MSR (0x10A). + // IA32_ARCH_CAPABILITIES bit 21 = XAPIC_DISABLE feature supported. + // IA32_XAPIC_DISABLE_STATUS MSR (0xBD) bit 0 = xAPIC permanently disabled. + uint32_t eax, ebx, ecx, edx; + if (cpuid(7, 0, &eax, &ebx, &ecx, &edx) && (edx & (1 << 29))) { + uint64_t arch_caps = rdmsr(0x10a); + if (arch_caps & (1 << 21)) { + if (rdmsr(0xbd) & 1) { + return false; + } + } + } + + // Save LAPIC state; clearing EN resets all registers except APIC ID. + uint32_t max_lvt = (x2apic_read(LAPIC_REG_VERSION) >> 16) & 0xff; + uint32_t saved_svr = x2apic_read(LAPIC_REG_SVR); + uint32_t saved_tpr = x2apic_read(LAPIC_REG_TPR); + uint32_t saved_timer = x2apic_read(LAPIC_REG_LVT_TIMER); + uint32_t saved_lint0 = x2apic_read(LAPIC_REG_LVT_LINT0); + uint32_t saved_lint1 = x2apic_read(LAPIC_REG_LVT_LINT1); + uint32_t saved_error = x2apic_read(LAPIC_REG_LVT_ERROR); + uint32_t saved_pmc = max_lvt >= 4 ? x2apic_read(LAPIC_REG_LVT_PMC) : 0; + uint32_t saved_thermal = max_lvt >= 5 ? x2apic_read(LAPIC_REG_LVT_THERMAL) : 0; + uint32_t saved_cmci = max_lvt >= 6 ? x2apic_read(LAPIC_REG_LVT_CMCI) : 0; + + // Transition x2APIC -> disabled -> xAPIC. + // Direct x2APIC -> xAPIC is an invalid transition (#GP). + msr &= ~((1ULL << 11) | (1ULL << 10)); + wrmsr(0x1b, msr); + + msr |= (1ULL << 11); + wrmsr(0x1b, msr); + + x2apic_mode = false; + + // Restore LAPIC state. SVR is restored last to re-enable the APIC. + lapic_write(LAPIC_REG_TPR, saved_tpr); + lapic_write(LAPIC_REG_LVT_TIMER, saved_timer); + lapic_write(LAPIC_REG_LVT_LINT0, saved_lint0); + lapic_write(LAPIC_REG_LVT_LINT1, saved_lint1); + lapic_write(LAPIC_REG_LVT_ERROR, saved_error); + if (max_lvt >= 4) lapic_write(LAPIC_REG_LVT_PMC, saved_pmc); + if (max_lvt >= 5) lapic_write(LAPIC_REG_LVT_THERMAL, saved_thermal); + if (max_lvt >= 6) lapic_write(LAPIC_REG_LVT_CMCI, saved_cmci); + lapic_write(LAPIC_REG_SVR, saved_svr); + + return true; +} + +void lapic_eoi(void) { + if (!x2apic_mode) { + lapic_write(0xb0, 0); + } else { + x2apic_write(0xb0, 0); + } +} + +uint64_t x2apic_read(uint32_t reg) { + return rdmsr(0x800 + (reg >> 4)); +} + +void x2apic_write(uint32_t reg, uint64_t data) { + wrmsr(0x800 + (reg >> 4), data); +} + +static struct madt_io_apic **io_apics = NULL; +static size_t max_io_apics = 0; + +void init_io_apics(void) { + static bool already_inited = false; + if (already_inited) { + return; + } + + struct madt *madt = acpi_get_table("APIC", 0); + + if (madt == NULL) { + goto out; + } + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 1: { + if (*(madt_ptr + 1) < sizeof(struct madt_io_apic)) + continue; + max_io_apics++; + continue; + } + } + } + + io_apics = ext_mem_alloc(max_io_apics * sizeof(struct madt_io_apic *)); + max_io_apics = 0; + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 1: { + if (*(madt_ptr + 1) < sizeof(struct madt_io_apic)) + continue; + io_apics[max_io_apics++] = (void *)madt_ptr; + continue; + } + } + } + +out: + already_inited = true; +} + +uint32_t io_apic_read(size_t io_apic, uint32_t reg) { + uintptr_t base = (uintptr_t)io_apics[io_apic]->address; + mmoutd(base, reg); + return mmind(base + 16); +} + +void io_apic_write(size_t io_apic, uint32_t reg, uint32_t value) { + uintptr_t base = (uintptr_t)io_apics[io_apic]->address; + mmoutd(base, reg); + mmoutd(base + 16, value); +} + +uint32_t io_apic_gsi_count(size_t io_apic) { + return ((io_apic_read(io_apic, 1) & 0xff0000) >> 16) + 1; +} + +void io_apic_mask_all(bool mask_nmi_and_extint) { + for (size_t i = 0; i < max_io_apics; i++) { + uint32_t gsi_count = io_apic_gsi_count(i); + for (uint32_t j = 0; j < gsi_count; j++) { + uintptr_t ioredtbl = j * 2 + 16; + switch ((io_apic_read(i, ioredtbl) >> 8) & 0b111) { + case 0b000: // Fixed + case 0b001: // Lowest Priority + break; + case 0b100: // NMI + case 0b111: // ExtINT + if (!mask_nmi_and_extint) { + continue; + } + break; + default: + continue; + } + + io_apic_write(i, ioredtbl, io_apic_read(i, ioredtbl) | (1 << 16)); + } + } +} + +#endif diff --git a/limine/common/sys/lapic.h b/limine/common/sys/lapic.h new file mode 100644 index 0000000..876cc05 --- /dev/null +++ b/limine/common/sys/lapic.h @@ -0,0 +1,38 @@ +#ifndef SYS__APIC_H__ +#define SYS__APIC_H__ + +#include +#include +#include + +struct madt; + +#define LAPIC_REG_ICR0 0x300 +#define LAPIC_REG_ICR1 0x310 +#define LAPIC_REG_SPURIOUS 0x0f0 +#define LAPIC_REG_EOI 0x0b0 +#define LAPIC_REG_ID 0x020 + +bool lapic_check(void); +void lapic_eoi(void); +uint32_t lapic_read(uint32_t reg); +void lapic_write(uint32_t reg, uint32_t data); +void lapic_icr_wait(void); + +bool x2apic_check(void); +bool x2apic_enable(void); +bool x2apic_disable(void); +uint64_t x2apic_read(uint32_t reg); +void x2apic_write(uint32_t reg, uint64_t data); + +void lapic_configure_bsp(void); +void lapic_prep_lint(struct madt *madt, uint32_t acpi_uid, bool x2apic); +void lapic_configure_handoff_state(void); + +void init_io_apics(void); +uint32_t io_apic_read(size_t io_apic, uint32_t reg); +void io_apic_write(size_t io_apic, uint32_t reg, uint32_t value); +uint32_t io_apic_gsi_count(size_t io_apic); +void io_apic_mask_all(bool mask_nmi_and_extint); + +#endif diff --git a/limine/common/sys/pic.c b/limine/common/sys/pic.c new file mode 100644 index 0000000..0c9dcee --- /dev/null +++ b/limine/common/sys/pic.c @@ -0,0 +1,48 @@ +#if defined (__x86_64__) || defined (__i386__) + +#include +#include +#include +#include +#include +#include + +void pic_eoi(int irq) { + if (irq >= 8) { + outb(0xa0, 0x20); + } + + outb(0x20, 0x20); +} + +// Flush all potentially pending IRQs +void pic_flush(void) { + for (int i = 0; i < 16; i++) + pic_eoi(i); +} + +void pic_set_mask(int line, bool status) { + uint16_t port; + uint8_t value; + + if (line < 8) { + port = 0x21; + } else { + port = 0xa1; + line -= 8; + } + + if (!status) + value = inb(port) & ~((uint8_t)1 << line); + else + value = inb(port) | ((uint8_t)1 << line); + + outb(port, value); +} + +void pic_mask_all(void) { + outb(0xa1, 0xff); + outb(0x21, 0xff); +} + +#endif diff --git a/limine/common/sys/pic.h b/limine/common/sys/pic.h new file mode 100644 index 0000000..0340724 --- /dev/null +++ b/limine/common/sys/pic.h @@ -0,0 +1,11 @@ +#ifndef SYS__PIC_H__ +#define SYS__PIC_H__ + +#include + +void pic_eoi(int irq); +void pic_flush(void); +void pic_set_mask(int line, bool status); +void pic_mask_all(void); + +#endif diff --git a/limine/common/sys/sbi.asm_riscv64 b/limine/common/sys/sbi.asm_riscv64 new file mode 100644 index 0000000..a601656 --- /dev/null +++ b/limine/common/sys/sbi.asm_riscv64 @@ -0,0 +1,19 @@ +.section .text + +.global sbicall +sbicall: +.option norelax + mv t0, a0 + mv t1, a1 + mv a0, a2 + mv a1, a3 + mv a2, a4 + mv a3, a5 + mv a4, a6 + mv a5, a7 + mv a7, t0 + mv a6, t1 + ecall + ret + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/sys/sbi.h b/limine/common/sys/sbi.h new file mode 100644 index 0000000..ef1fea2 --- /dev/null +++ b/limine/common/sys/sbi.h @@ -0,0 +1,32 @@ +#ifndef SYS__SBI_H__ +#define SYS__SBI_H__ + +#include +#include + +struct sbiret { + long error; + long value; +}; + +#define SBI_SUCCESS ((long)0) +#define SBI_ERR_FAILED ((long)-1) +#define SBI_ERR_NOT_SUPPORTED ((long)-2) +#define SBI_ERR_INVALID_PARAM ((long)-3) +#define SBI_ERR_DENIED ((long)-4) +#define SBI_ERR_INVALID_ADDRESS ((long)-5) +#define SBI_ERR_ALREADY_AVAILABLE ((long)-6) +#define SBI_ERR_ALREADY_STARTED ((long)-7) +#define SBI_ERR_ALREADY_STOPPED ((long)-8) + +extern struct sbiret sbicall(int eid, int fid, ...); + +#define SBI_EID_HSM 0x48534d + +static inline struct sbiret sbi_hart_start(unsigned long hartid, + unsigned long start_addr, + unsigned long opaque) { + return sbicall(SBI_EID_HSM, 0, hartid, start_addr, opaque); +} + +#endif diff --git a/limine/common/sys/smp.c b/limine/common/sys/smp.c new file mode 100644 index 0000000..b72a7e6 --- /dev/null +++ b/limine/common/sys/smp.c @@ -0,0 +1,881 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LIMINE_NO_POINTERS +#include +#if defined (__riscv) +#include +#endif +#if defined (__aarch64__) +#include +#endif + +extern symbol smp_trampoline_start; +extern size_t smp_trampoline_size; + +#if defined (__x86_64__) || defined (__i386__) + +struct trampoline_passed_info { + uint8_t smp_tpl_booted_flag; + uint8_t smp_tpl_target_mode; + uint32_t smp_tpl_pagemap; + uint32_t smp_tpl_info_struct; + struct gdtr smp_tpl_gdt; + uint64_t smp_tpl_hhdm; + uint64_t smp_tpl_bsp_apic_addr_msr; + uint64_t smp_tpl_mtrr_restore; + uint64_t smp_tpl_temp_stack; + uint64_t smp_tpl_lapic_setup; +} __attribute__((packed)); + +bool smp_configure_apic = false; + +static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr, + struct limine_mp_info *info_struct, + int paging_mode, uint32_t pagemap, + bool x2apic, bool nx, uint64_t hhdm, bool wp) { + // Prepare the trampoline + static void *trampoline = NULL; + if (trampoline == NULL) { + trampoline = conv_mem_alloc(smp_trampoline_size); + + memcpy(trampoline, smp_trampoline_start, smp_trampoline_size); + } + + static void *temp_stack = NULL; + if (temp_stack == NULL) { + temp_stack = ext_mem_alloc(8192); + } + + static struct trampoline_passed_info *passed_info = NULL; + if (passed_info == NULL) { + passed_info = (void *)(((uintptr_t)trampoline + smp_trampoline_size) + - sizeof(struct trampoline_passed_info)); + } + + passed_info->smp_tpl_info_struct = (uint32_t)(uintptr_t)info_struct; + passed_info->smp_tpl_booted_flag = 0; + passed_info->smp_tpl_pagemap = pagemap; + passed_info->smp_tpl_target_mode = ((uint32_t)(paging_mode == PAGING_MODE_X86_64_5LVL) << 1) + | ((uint32_t)nx << 3) + | ((uint32_t)wp << 4); + passed_info->smp_tpl_gdt = *gdtr; + passed_info->smp_tpl_hhdm = hhdm; + passed_info->smp_tpl_bsp_apic_addr_msr = rdmsr(0x1b); + passed_info->smp_tpl_mtrr_restore = (uint64_t)(uintptr_t)mtrr_restore; + passed_info->smp_tpl_temp_stack = (uint64_t)(uintptr_t)temp_stack + 8192; + passed_info->smp_tpl_lapic_setup = smp_configure_apic + ? (uint64_t)(uintptr_t)lapic_configure_handoff_state : 0; + + asm volatile ("" ::: "memory"); + + // Send the INIT IPI + if (x2apic) { + x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) | 0x4500); + } else { + lapic_icr_wait(); + lapic_write(LAPIC_REG_ICR1, lapic_id << 24); + lapic_write(LAPIC_REG_ICR0, 0x4500); + } + stall(10000); + + // Send two Startup IPIs per Intel SDM recommendation (Vol 3, 8.4.4.1) + for (int j = 0; j < 2; j++) { + if (x2apic) { + x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) | + ((size_t)trampoline / 4096) | 0x4600); + } else { + lapic_icr_wait(); + lapic_write(LAPIC_REG_ICR1, lapic_id << 24); + lapic_write(LAPIC_REG_ICR0, ((size_t)trampoline / 4096) | 0x4600); + } + if (j == 0) { + stall(200); + } + } + + if (!x2apic) { + lapic_icr_wait(); + } + + for (int i = 0; i < 100; i++) { + if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) { + return true; + } + stall(10000); + } + + return false; +} + +struct limine_mp_info *init_smp(size_t *cpu_count, + uint32_t *_bsp_lapic_id, + int paging_mode, + pagemap_t pagemap, + bool x2apic, + bool nx, + uint64_t hhdm, + bool wp) { + if (!lapic_check()) + return NULL; + + // Search for MADT table + struct madt *madt = acpi_get_table("APIC", 0); + + if (madt == NULL) + return NULL; + + struct gdtr gdtr = gdt; + + uint32_t bsp_lapic_id; + + // If x2APIC already enabled by firmware, try to revert to xAPIC + if (rdmsr(0x1b) & (1 << 10)) { + if (!x2apic) { + if (!x2apic_disable()) { + panic(false, "smp: Kernel does not support x2APIC and x2APIC cannot be disabled"); + } + printv("smp: Firmware had x2APIC enabled, reverted to xAPIC mode\n"); + } + } + + x2apic = x2apic && x2apic_enable(); + + if (x2apic) { + bsp_lapic_id = x2apic_read(LAPIC_REG_ID); + } else { + bsp_lapic_id = lapic_read(LAPIC_REG_ID) >> 24; + } + + *_bsp_lapic_id = bsp_lapic_id; + + *cpu_count = 0; + + // Count the MAX of startable APs and allocate accordingly + size_t max_cpus = 0; + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + // Prevent infinite loop on zero-length MADT entry + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 0: { + // Processor local xAPIC + if (*(madt_ptr + 1) < sizeof(struct madt_lapic)) + continue; + + struct madt_lapic *lapic = (void *)madt_ptr; + + // Check if we can actually try to start the AP + if ((lapic->flags & 1) ^ ((lapic->flags >> 1) & 1)) + max_cpus++; + + continue; + } + case 9: { + // Processor local x2APIC + if (!x2apic) + continue; + + if (*(madt_ptr + 1) < sizeof(struct madt_x2apic)) + continue; + + struct madt_x2apic *x2lapic = (void *)madt_ptr; + + // Check if we can actually try to start the AP + if ((x2lapic->flags & 1) ^ ((x2lapic->flags >> 1) & 1)) + max_cpus++; + + continue; + } + } + } + + if (max_cpus == 0) { + return NULL; + } + + struct limine_mp_info *ret = ext_mem_alloc(max_cpus * sizeof(struct limine_mp_info)); + *cpu_count = 0; + + // Try to start all APs + mtrr_save(); + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + // Prevent infinite loop on zero-length MADT entry + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 0: { + // Processor local xAPIC + if (*(madt_ptr + 1) < sizeof(struct madt_lapic)) + continue; + + struct madt_lapic *lapic = (void *)madt_ptr; + + // Check if we can actually try to start the AP + if (!((lapic->flags & 1) ^ ((lapic->flags >> 1) & 1))) + continue; + + struct limine_mp_info *info_struct = &ret[*cpu_count]; + + info_struct->processor_id = lapic->acpi_processor_uid; + info_struct->lapic_id = lapic->lapic_id; + + // Do not try to restart the BSP + if (lapic->lapic_id == bsp_lapic_id) { + (*cpu_count)++; + continue; + } + + printv("smp: [xAPIC] Found candidate AP for bring-up. LAPIC ID: %u\n", lapic->lapic_id); + + // Set up per-AP LINT values before starting + if (smp_configure_apic) { + lapic_prep_lint(madt, lapic->acpi_processor_uid, x2apic); + } + + // Try to start the AP + if (!smp_start_ap(lapic->lapic_id, &gdtr, info_struct, + paging_mode, (uintptr_t)pagemap.top_level, + x2apic, nx, hhdm, wp)) { + print("smp: FAILED to bring-up AP\n"); + continue; + } + + printv("smp: Successfully brought up AP\n"); + + (*cpu_count)++; + continue; + } + case 9: { + // Processor local x2APIC + if (!x2apic) + continue; + + if (*(madt_ptr + 1) < sizeof(struct madt_x2apic)) + continue; + + struct madt_x2apic *x2lapic = (void *)madt_ptr; + + // Check if we can actually try to start the AP + if (!((x2lapic->flags & 1) ^ ((x2lapic->flags >> 1) & 1))) + continue; + + struct limine_mp_info *info_struct = &ret[*cpu_count]; + + info_struct->processor_id = x2lapic->acpi_processor_uid; + info_struct->lapic_id = x2lapic->x2apic_id; + + // Do not try to restart the BSP + if (x2lapic->x2apic_id == bsp_lapic_id) { + (*cpu_count)++; + continue; + } + + printv("smp: [x2APIC] Found candidate AP for bring-up. LAPIC ID: %u\n", x2lapic->x2apic_id); + + // Set up per-AP LINT values before starting + if (smp_configure_apic) { + lapic_prep_lint(madt, x2lapic->acpi_processor_uid, true); + } + + // Try to start the AP + if (!smp_start_ap(x2lapic->x2apic_id, &gdtr, info_struct, + paging_mode, (uintptr_t)pagemap.top_level, + true, nx, hhdm, wp)) { + print("smp: FAILED to bring-up AP\n"); + continue; + } + + printv("smp: Successfully brought up AP\n"); + + (*cpu_count)++; + continue; + } + } + } + + if (*cpu_count == 0) { + pmm_free(ret, max_cpus * sizeof(struct limine_mp_info)); + return NULL; + } + + return ret; +} + +#elif defined (__aarch64__) + +struct trampoline_passed_info { + uint64_t smp_tpl_booted_flag; + + uint64_t smp_tpl_hhdm_offset; + + uint64_t smp_tpl_ttbr0; + uint64_t smp_tpl_ttbr1; + + uint64_t smp_tpl_mair; + uint64_t smp_tpl_tcr; + uint64_t smp_tpl_sctlr; + + uint64_t smp_tpl_info_struct; +}; + +enum { + BOOT_WITH_SPIN_TBL, + BOOT_WITH_PSCI_SMC, + BOOT_WITH_PSCI_HVC, + BOOT_WITH_ACPI_PARK +}; + +static uint32_t psci_cpu_on = 0xC4000003; + +static bool try_start_ap(int boot_method, uint64_t method_ptr, + struct limine_mp_info *info_struct, + uint64_t ttbr0, uint64_t ttbr1, uint64_t mair, + uint64_t tcr, uint64_t sctlr, + uint64_t hhdm_offset) { + // Prepare the trampoline + static void *trampoline = NULL; + if (trampoline == NULL) { + trampoline = ext_mem_alloc(smp_trampoline_size); + + memcpy(trampoline, smp_trampoline_start, smp_trampoline_size); + } + + static struct trampoline_passed_info *passed_info = NULL; + if (passed_info == NULL) { + passed_info = (void *)(((uintptr_t)trampoline + 0x1000) + - sizeof(struct trampoline_passed_info)); + } + + passed_info->smp_tpl_info_struct = (uint64_t)(uintptr_t)info_struct; + passed_info->smp_tpl_booted_flag = 0; + passed_info->smp_tpl_ttbr0 = ttbr0; + passed_info->smp_tpl_ttbr1 = ttbr1; + passed_info->smp_tpl_mair = mair; + passed_info->smp_tpl_tcr = tcr; + passed_info->smp_tpl_sctlr = sctlr; + passed_info->smp_tpl_hhdm_offset = hhdm_offset; + + // Cache coherency between the I-Cache and D-Cache is not guaranteed by the + // architecture and as such we must perform I-Cache invalidation. + // Additionally, the newly-booted AP may have caches disabled which implies + // it possibly does not see our cache contents either. + + clean_dcache_poc((uintptr_t)trampoline, (uintptr_t)trampoline + smp_trampoline_size); + inval_icache_pou((uintptr_t)trampoline, (uintptr_t)trampoline + smp_trampoline_size); + + asm volatile ("" ::: "memory"); + + switch (boot_method) { + case BOOT_WITH_SPIN_TBL: + *(volatile uint64_t *)method_ptr = (uint64_t)(uintptr_t)trampoline; + clean_dcache_poc(method_ptr, method_ptr + 8); + asm ("sev"); + break; + + case BOOT_WITH_PSCI_SMC: + case BOOT_WITH_PSCI_HVC: { + register int32_t result asm("w0"); + register uint32_t cmd asm("w0") = psci_cpu_on; + register uint64_t cpu asm("x1") = info_struct->mpidr; + register uint64_t addr asm("x2") = (uint64_t)(uintptr_t)trampoline; + register uint64_t ctx asm("x3") = 0; + + if (boot_method == BOOT_WITH_PSCI_SMC) + asm volatile ("smc #0" : "=r"(result) : "r"(cmd), "r"(cpu), "r"(addr), "r"(ctx)); + else + asm volatile ("hvc #0" : "=r"(result) : "r"(cmd), "r"(cpu), "r"(addr), "r"(ctx)); + + switch (result) { + case 0: // Success + break; + case -2: + printv("smp: PSCI says CPU_ON was given invalid arguments\n"); + return false; + case -4: + printv("smp: PSCI says AP is already on\n"); + return false; + case -5: + printv("smp: PSCI says CPU_ON is already pending for this AP\n"); + return false; + case -6: + printv("smp: PSCI reports internal failure\n"); + return false; + case -9: + printv("smp: PSCI says CPU_ON was given an invalid address\n"); + return false; + default: + printv("smp: PSCI reports an unexpected error (%d)\n", result); + return false; + } + + break; + } + + case BOOT_WITH_ACPI_PARK: + panic(false, "ACPI parking protocol is unsupported, please report this!"); + break; + + default: + panic(false, "Invalid boot method specified"); + } + + for (int i = 0; i < 1000000; i++) { + // We do not need cache invalidation here as by the time the AP gets to + // set this flag, it has enabled its caches + + if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) { + return true; + } + stall(100); + } + + return false; +} + +static struct limine_mp_info *try_acpi_smp(size_t *cpu_count, + uint64_t *_bsp_mpidr, + pagemap_t pagemap, + uint64_t mair, + uint64_t tcr, + uint64_t sctlr, + uint64_t hhdm_offset) { + int boot_method = BOOT_WITH_ACPI_PARK; + + // Search for FADT table + uint8_t *fadt = acpi_get_table("FACP", 0); + + if (fadt == NULL) + return NULL; + + // Check FADT length before accessing ARM boot flags at offset 129 + uint32_t fadt_length; + memcpy(&fadt_length, fadt + 4, 4); + if (fadt_length >= 131) { + // Read the single field from the FADT without defining a struct for the whole table + uint16_t arm_boot_args; + memcpy(&arm_boot_args, fadt + 129, 2); + + if (arm_boot_args & 1) // PSCI compliant? + boot_method = arm_boot_args & 2 ? BOOT_WITH_PSCI_HVC : BOOT_WITH_PSCI_SMC; + } + + // Search for MADT table + struct madt *madt = acpi_get_table("APIC", 0); + + if (madt == NULL) + return NULL; + + uint64_t bsp_mpidr; + asm volatile ("mrs %0, mpidr_el1" : "=r"(bsp_mpidr)); + + // This bit is Res1 in the system reg, but not included in the MPIDR from MADT + bsp_mpidr &= ~((uint64_t)1 << 31); + + *_bsp_mpidr = bsp_mpidr; + + printv("smp: BSP MPIDR is %X\n", bsp_mpidr); + + *cpu_count = 0; + + // Count the MAX of startable APs and allocate accordingly + size_t max_cpus = 0; + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 11: { + // GIC CPU Interface + if (*(madt_ptr + 1) < sizeof(struct madt_gicc)) + continue; + + struct madt_gicc *gicc = (void *)madt_ptr; + + // Check if we can actually try to start the AP + if (gicc->flags & 1) + max_cpus++; + + continue; + } + } + } + + struct limine_mp_info *ret = ext_mem_alloc(max_cpus * sizeof(struct limine_mp_info)); + *cpu_count = 0; + + // Try to start all APs + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; + madt_ptr += *(madt_ptr + 1)) { + if (*(madt_ptr + 1) == 0) { + break; + } + switch (*madt_ptr) { + case 11: { + // GIC CPU Interface + if (*(madt_ptr + 1) < sizeof(struct madt_gicc)) + continue; + + struct madt_gicc *gicc = (void *)madt_ptr; + + // Check if we can actually try to start the AP + if (!(gicc->flags & 1)) + continue; + + struct limine_mp_info *info_struct = &ret[*cpu_count]; + + info_struct->processor_id = gicc->acpi_uid; + info_struct->mpidr = gicc->mpidr; + + // Do not try to restart the BSP + if (gicc->mpidr == bsp_mpidr) { + (*cpu_count)++; + continue; + } + + printv("smp: Found candidate AP for bring-up. Interface no.: %x, MPIDR: %X\n", gicc->iface_no, gicc->mpidr); + + // Try to start the AP + if (!try_start_ap(boot_method, gicc->parking_addr, info_struct, + (uint64_t)(uintptr_t)pagemap.top_level[0], + (uint64_t)(uintptr_t)pagemap.top_level[1], + mair, tcr, sctlr, hhdm_offset)) { + print("smp: FAILED to bring-up AP\n"); + continue; + } + + printv("smp: Successfully brought up AP\n"); + + (*cpu_count)++; + continue; + } + } + } + + if (*cpu_count == 0) { + pmm_free(ret, max_cpus * sizeof(struct limine_mp_info)); + return NULL; + } + + return ret; +} + +static struct limine_mp_info *try_dtb_smp( void *dtb, + size_t *cpu_count, + uint64_t *_bsp_mpidr, + pagemap_t pagemap, + uint64_t mair, + uint64_t tcr, + uint64_t sctlr, + uint64_t hhdm_offset) { + uint64_t bsp_mpidr; + asm volatile ("mrs %0, mpidr_el1" : "=r"(bsp_mpidr)); + + // This bit is Res1 in the system reg, but not included in the MPIDR from DT + bsp_mpidr &= ~((uint64_t)1 << 31); + + *_bsp_mpidr = bsp_mpidr; + + printv("smp: BSP MPIDR is %X\n", bsp_mpidr); + + *cpu_count = 0; + + int cpus = fdt_path_offset(dtb, "/cpus"); + if (cpus < 0) { + printv("smp: failed to find /cpus node: %s\n", fdt_strerror(cpus)); + return NULL; + } + + int psci = fdt_path_offset(dtb, "/psci"); + + if (psci > 0 && !fdt_node_check_compatible(dtb, psci, "arm,psci")) { + int prop_len; + const void *prop; + if (!(prop = fdt_getprop(dtb, psci, "cpu_on", &prop_len)) || prop_len < 4) { + printv("smp: failed to find PSCI cpu_on prop\n"); + return NULL; + } + + const uint8_t *bytes = prop; + + psci_cpu_on = ((uint64_t)bytes[0] << 24) + | ((uint64_t)bytes[1] << 16) + | ((uint64_t)bytes[2] << 8) + | ((uint64_t)bytes[3]); + } + + int address_cells = fdt_address_cells(dtb, cpus); + if (address_cells < 1) { + printv("smp: fdt_address_cells failed: %s\n", fdt_strerror(address_cells)); + return NULL; + } + if (address_cells > 2) { + printv("smp: illegal #address-cells value: %d\n", address_cells); + return NULL; + } + + uint64_t max_cpus = 0; + int node; + fdt_for_each_subnode(node, dtb, cpus) { + const void *prop; + + if (!(prop = fdt_getprop(dtb, node, "device_type", NULL)) || strcmp(prop, "cpu")) { + continue; + } + + if (!(prop = fdt_getprop(dtb, node, "reg", NULL))) { + continue; + } + + max_cpus++; + } + + struct limine_mp_info *ret = ext_mem_alloc(max_cpus * sizeof(struct limine_mp_info)); + + fdt_for_each_subnode(node, dtb, cpus) { + const void *prop; + int prop_len; + + if (!(prop = fdt_getprop(dtb, node, "device_type", NULL)) || strcmp(prop, "cpu")) { + continue; + } + + if (!(prop = fdt_getprop(dtb, node, "reg", &prop_len)) || prop_len < address_cells * 4) { + continue; + } + + uint64_t mpidr = 0; + + if (address_cells == 1) { + const uint8_t *bytes = prop; + + mpidr = ((uint64_t)bytes[0] << 24) + | ((uint64_t)bytes[1] << 16) + | ((uint64_t)bytes[2] << 8) + | ((uint64_t)bytes[3]); + } else if (address_cells == 2) { + const uint8_t *bytes = prop; + + mpidr = ((uint64_t)bytes[3] << 32) + | ((uint64_t)bytes[4] << 24) + | ((uint64_t)bytes[5] << 16) + | ((uint64_t)bytes[6] << 8) + | ((uint64_t)bytes[7]); + } + + + struct limine_mp_info *info_struct = &ret[*cpu_count]; + + info_struct->processor_id = 0; + info_struct->mpidr = mpidr; + + // Do not try to restart the BSP + if (mpidr == bsp_mpidr) { + (*cpu_count)++; + continue; + } + + if (!(prop = fdt_getprop(dtb, node, "enable-method", NULL))) { + printv("smp: missing enable-method\n"); + continue; + } + + int boot_method = -1; + uint64_t method_ptr = 0; + + if (!strcmp(prop, "psci")) { + if (psci < 0) { + printv("smp: failed to find /psci: %s\n", fdt_strerror(psci)); + continue; + } + + const void *psci_method = fdt_getprop(dtb, psci, "method", NULL); + + if (psci_method == NULL) { + printv("smp: PSCI method property not found\n"); + continue; + } else if (!strcmp(psci_method, "smc")) { + boot_method = BOOT_WITH_PSCI_SMC; + } else if (!strcmp(psci_method, "hvc")) { + boot_method = BOOT_WITH_PSCI_HVC; + } else { + printv("smp: illegal PSCI method: '%s'\n", psci_method); + continue; + } + + } else if (!strcmp(prop, "spin-table")) { + boot_method = BOOT_WITH_SPIN_TBL; + + if (!(prop = fdt_getprop(dtb, node, "cpu-release-addr", &prop_len)) || prop_len < 8) { + printv("smp: missing cpu-release-addr\n"); + continue; + } + + const uint8_t *bytes = prop; + + method_ptr = ((uint64_t)bytes[0] << 56) + | ((uint64_t)bytes[1] << 48) + | ((uint64_t)bytes[2] << 40) + | ((uint64_t)bytes[3] << 32) + | ((uint64_t)bytes[4] << 24) + | ((uint64_t)bytes[5] << 16) + | ((uint64_t)bytes[6] << 8) + | ((uint64_t)bytes[7]); + } else { + printv("smp: illegal enable-method: '%s'\n", prop); + continue; + } + + printv("smp: Found candidate AP for bring-up. MPIDR: %X\n", mpidr); + + // Try to start the AP + if (!try_start_ap(boot_method, method_ptr, info_struct, + (uint64_t)(uintptr_t)pagemap.top_level[0], + (uint64_t)(uintptr_t)pagemap.top_level[1], + mair, tcr, sctlr, hhdm_offset)) { + print("smp: FAILED to bring-up AP\n"); + continue; + } + + printv("smp: Successfully brought up AP\n"); + + (*cpu_count)++; + } + + return ret; +} + + +struct limine_mp_info *init_smp(const char *config, + size_t *cpu_count, + uint64_t *bsp_mpidr, + pagemap_t pagemap, + uint64_t mair, + uint64_t tcr, + uint64_t sctlr, + uint64_t hhdm_offset) { + struct limine_mp_info *info = NULL; + + if (acpi_get_rsdp() && (info = try_acpi_smp( + cpu_count, bsp_mpidr, pagemap, + mair, tcr, sctlr, hhdm_offset))) + return info; + + // No RSDP means no ACPI, try device trees in that case. + void *dtb = get_device_tree_blob(config, 0); + if (dtb) { + info = try_dtb_smp(dtb, + cpu_count, bsp_mpidr, pagemap, + mair, tcr, sctlr, hhdm_offset); + pmm_free(dtb, fdt_totalsize(dtb)); + return info; + } + + printv("Failed to figure out how to start APs."); + + return NULL; +} + +#elif defined (__riscv) + +struct trampoline_passed_info { + uint64_t smp_tpl_booted_flag; + uint64_t smp_tpl_satp; + uint64_t smp_tpl_info_struct; + uint64_t smp_tpl_hhdm_offset; +}; + +static bool smp_start_ap(size_t hartid, size_t satp, struct limine_mp_info *info_struct, + uint64_t hhdm_offset) { + static struct trampoline_passed_info passed_info; + + passed_info.smp_tpl_booted_flag = 0; + passed_info.smp_tpl_satp = satp; + passed_info.smp_tpl_info_struct = (uint64_t)info_struct; + passed_info.smp_tpl_hhdm_offset = hhdm_offset; + + asm volatile ("" ::: "memory"); + + struct sbiret ret = sbi_hart_start(hartid, (size_t)smp_trampoline_start, (size_t)&passed_info); + if (ret.error != SBI_SUCCESS) + return false; + + for (int i = 0; i < 1000000; i++) { + if (locked_read(&passed_info.smp_tpl_booted_flag) == 1) + return true; + + stall(100); + } + + return false; +} + +struct limine_mp_info *init_smp(size_t *cpu_count, pagemap_t pagemap, uint64_t hhdm_offset) { + size_t num_cpus = 0; + for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) { + if (!(hart->flags & RISCV_HART_COPROC)) { + num_cpus += 1; + } + } + + struct limine_mp_info *ret = ext_mem_alloc(num_cpus * sizeof(struct limine_mp_info)); + + *cpu_count = 0; + for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) { + if (hart->flags & RISCV_HART_COPROC) { + continue; + } + struct limine_mp_info *info_struct = &ret[*cpu_count]; + + info_struct->hartid = hart->hartid; + info_struct->processor_id = hart->acpi_uid; + + // Don't try to start the BSP. + if (hart->hartid == bsp_hartid) { + *cpu_count += 1; + continue; + } + + printv("smp: Found candidate AP for bring-up. Hart ID: %U\n", (uint64_t)hart->hartid); + + // Try to start the AP. + size_t satp = make_satp(pagemap.paging_mode, pagemap.top_level); + if (!smp_start_ap(hart->hartid, satp, info_struct, hhdm_offset)) { + print("smp: FAILED to bring-up AP\n"); + continue; + } + + (*cpu_count)++; + continue; + } + + return ret; +} + +#elif defined (__loongarch64) +#else +#error Unknown architecture +#endif diff --git a/limine/common/sys/smp.h b/limine/common/sys/smp.h new file mode 100644 index 0000000..7cac37c --- /dev/null +++ b/limine/common/sys/smp.h @@ -0,0 +1,46 @@ +#ifndef SYS__SMP_H__ +#define SYS__SMP_H__ + +#include +#include +#include +#include +#define LIMINE_NO_POINTERS +#include + +#if defined (__x86_64__) || defined (__i386__) + +extern bool smp_configure_apic; + +struct limine_mp_info *init_smp(size_t *cpu_count, + uint32_t *_bsp_lapic_id, + int paging_mode, + pagemap_t pagemap, + bool x2apic, + bool nx, + uint64_t hhdm, + bool wp); + +#elif defined (__aarch64__) + +struct limine_mp_info *init_smp(const char *config, + size_t *cpu_count, + uint64_t *bsp_mpidr, + pagemap_t pagemap, + uint64_t mair, + uint64_t tcr, + uint64_t sctlr, + uint64_t hhdm_offset); + +#elif defined (__riscv) + +struct limine_mp_info *init_smp(size_t *cpu_count, + pagemap_t pagemap, + uint64_t hhdm_offset); + +#elif defined (__loongarch64) +#else +#error Unknown architecture +#endif + +#endif diff --git a/limine/common/sys/smp_trampoline.asm_aarch64 b/limine/common/sys/smp_trampoline.asm_aarch64 new file mode 100644 index 0000000..a6a4a24 --- /dev/null +++ b/limine/common/sys/smp_trampoline.asm_aarch64 @@ -0,0 +1,149 @@ +#include + +.set tpl_booted_flag, -64 +.set tpl_hhdm_offset, -56 +.set tpl_ttbr0, -48 +.set tpl_ttbr1, -40 +.set tpl_mair, -32 +.set tpl_tcr, -24 +.set tpl_sctlr, -16 +.set tpl_info_struct, -8 + +.section .text + +.global smp_trampoline_start +smp_trampoline_start: + bl .L_entry +.L_entry: + // Mask IRQs + msr daifset, #0b1111 + + // Address to next page (since our offsets into the boot data are negative) + add x1, x30, #0xFFC + + ldr x0, [x1, tpl_info_struct] + ldr x2, [x1, tpl_sctlr] + ldr x3, [x1, tpl_mair] + ldr x4, [x1, tpl_tcr] + ldr x5, [x1, tpl_ttbr0] + ldr x6, [x1, tpl_ttbr1] + ldr x7, [x1, tpl_hhdm_offset] + + PICK_EL x8, 1f, 0f +0: + // Configure EL2-specific state for EL1 + + // Configure EL1 page tables + msr mair_el1, x3 + msr tcr_el1, x4 + msr ttbr0_el1, x5 + msr ttbr1_el1, x6 + msr sctlr_el1, x2 + isb + dsb sy + isb + + // Don't trap counters to EL2 + mrs x8, cnthctl_el2 + orr x8, x8, #3 + msr cnthctl_el2, x8 + msr cntvoff_el2, xzr + + // Enable AArch64 in EL1 + mov x8, xzr + orr x8, x8, #(1 << 31) + orr x8, x8, #(1 << 1) + msr hcr_el2, x8 + + // Don't trap FP/SIMD to EL2 + mov x8, #0x33FF + msr cptr_el2, x8 + msr hstr_el2, xzr + + // Run rest of trampoline in EL1 + mov x8, #0x3c4 + msr spsr_el2, x8 + adrp x8, 3f + add x8, x8, :lo12:3f + add x8, x8, x7 // Add HHDM offset + msr elr_el2, x8 + + eret + +1: + msr spsel, #0 + + // Switch to the new page tables + + // Point the EL1t handler to the continuation, such that after we page fault, + // execution continues as expected. + adrp x8, 2f + add x8, x8, #:lo12:2f + add x8, x8, x7 + msr vbar_el1, x8 + isb + dsb sy + isb + + // Switch the page table registers + msr mair_el1, x3 + msr tcr_el1, x4 + msr ttbr0_el1, x5 + msr ttbr1_el1, x6 + msr sctlr_el1, x2 + isb + dsb sy + isb + + // Jump to the higher half mapping in case we didn't immediately crash + br x8 + +// Alignment required by VBAR register +.align 12 +2: + // Zero out VBAR to avoid confusion + msr vbar_el1, xzr + +3: + // Add HHDM offset to data pointer + add x1, x1, x7 + + // Notify BSP we are alive + mov x8, #1 + add x9, x1, tpl_booted_flag + stlr x8, [x9] + + // Wait for BSP to tell us where to go + // Add HHDM offset to our info struct pointer + add x0, x0, x7 + add x9, x0, #24 +4: + ldar x8, [x9] + cbnz x8, 5f + yield + b 4b + +5: + msr elr_el1, x8 + + msr spsel, #0 + ldr x8, [x0, #16] + mov sp, x8 + + // Enter kernel + mov x8, #0x3c4 + msr spsr_el1, x8 + + ZERO_REGS_EXCEPT_X0 + + eret + +smp_trampoline_end: + +.section .rodata + +.global smp_trampoline_size +smp_trampoline_size: + .quad smp_trampoline_end - smp_trampoline_start + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/sys/smp_trampoline.asm_riscv64 b/limine/common/sys/smp_trampoline.asm_riscv64 new file mode 100644 index 0000000..c2749c3 --- /dev/null +++ b/limine/common/sys/smp_trampoline.asm_riscv64 @@ -0,0 +1,84 @@ +.section .text + +.global smp_trampoline_start +smp_trampoline_start: +.option norelax + // The AP begins executing here with the following state: + // satp = 0 + // sstatus.SIE = 0 + // a0 = hartid + // a1 = struct trampoline_passed_info * + // + // All other registers are undefined. + +#define smp_tpl_booted_flag 0 +#define smp_tpl_satp 8 +#define smp_tpl_info_struct 16 +#define smp_tpl_hhdm_offset 24 + + ld a0, smp_tpl_info_struct(a1) + ld t1, smp_tpl_hhdm_offset(a1) + + // Set `stvec` so we page fault into the higher half after loading `satp`. + lla t0, 0f + add t0, t1, t0 + csrw stvec, t0 + ld t0, smp_tpl_satp(a1) + csrw satp, t0 + sfence.vma + unimp +0: + // Relocate the smp_info and passed_info pointers to the higher half. + add a0, t1, a0 + add a1, t1, a1 + + // Tell the BSP we've started. + li t0, 1 + fence rw, w + sd t0, (a1) + + // Zero all the things. + // Preserve a0 + mv a1, zero + mv a2, zero + mv a3, zero + mv a4, zero + mv a5, zero + mv a6, zero + mv a7, zero + mv s0, zero + mv s1, zero + mv s2, zero + mv s3, zero + mv s4, zero + mv s5, zero + mv s6, zero + mv s7, zero + mv s8, zero + mv s9, zero + mv s10, zero + mv s11, zero + mv t1, zero + mv t2, zero + mv t3, zero + mv t4, zero + mv t5, zero + mv t6, zero + mv tp, zero + mv ra, zero + + csrw sie, zero + csrw stvec, zero + + // Wait for kernel to tell us where to go. +0: .4byte 0x0100000f // pause + ld t0, 24(a0) + fence r, rw + beqz t0, 0b + + // Load sp from reserved field of info struct + ld sp, 16(a0) + + jr t0 + +.section .note.GNU-stack,"",%progbits diff --git a/limine/common/sys/smp_trampoline.asm_x86 b/limine/common/sys/smp_trampoline.asm_x86 new file mode 100644 index 0000000..9729634 --- /dev/null +++ b/limine/common/sys/smp_trampoline.asm_x86 @@ -0,0 +1,271 @@ +bits 16 + +section .rodata + +global smp_trampoline_start +smp_trampoline_start: + cli + cld + + mov ebx, cs + shl ebx, 4 + + o32 lidt [cs:(invalid_idt - smp_trampoline_start)] + o32 lgdt [cs:(passed_info.gdtr - smp_trampoline_start)] + + lea eax, [ebx + (.mode32 - smp_trampoline_start)] + mov [cs:(.farjmp_off - smp_trampoline_start)], eax + + mov eax, 0x00000011 + mov cr0, eax + o32 jmp far [cs:(.farjmp - smp_trampoline_start)] + + .farjmp: + .farjmp_off: dd 0 + .farjmp_seg: dd 0x18 + + bits 32 + .mode32: + mov ax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + xor eax, eax + lldt ax + + xor eax, eax + mov cr4, eax + + mov esi, ebx + mov eax, 1 + xor ecx, ecx + cpuid + test edx, 1 << 16 + jz .no_pat + mov ecx, 0x277 + mov eax, 0x00070406 + mov edx, 0x00000105 + wrmsr + .no_pat: + mov ebx, esi + + mov ecx, 0x1b + rdmsr + test eax, (1 << 10) + jz .write_apic_msr + + ; Check if target also has x2APIC + test dword [ebx + (passed_info.bsp_apic_addr_msr_lo - smp_trampoline_start)], (1 << 10) + jnz .write_apic_msr + + ; AP is x2APIC but target is xAPIC: go through disabled state + btr eax, 11 + btr eax, 10 + wrmsr + + .write_apic_msr: + mov eax, [ebx + (passed_info.bsp_apic_addr_msr_lo - smp_trampoline_start)] + mov edx, [ebx + (passed_info.bsp_apic_addr_msr_hi - smp_trampoline_start)] + bts eax, 11 + btr eax, 8 + wrmsr + mov esp, [ebx + (passed_info.temp_stack - smp_trampoline_start)] + + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + ; Set EFER (LME + NX if available), all other bits cleared + mov ecx, 0xc0000080 + xor edx, edx + mov eax, 1 << 8 + test dword [ebx + (passed_info.target_mode - smp_trampoline_start)], (1 << 3) + jz .no_nx + or eax, 1 << 11 + .no_nx: + wrmsr + + test dword [ebx + (passed_info.target_mode - smp_trampoline_start)], (1 << 1) + jz .no5lv + + mov eax, cr4 + bts eax, 12 + mov cr4, eax + + .no5lv: + mov eax, dword [ebx + (passed_info.pagemap - smp_trampoline_start)] + mov cr3, eax + + ; Enable CR0.PG (and WP if requested) + mov eax, cr0 + test dword [ebx + (passed_info.target_mode - smp_trampoline_start)], (1 << 4) + jz .no_wp + bts eax, 16 + .no_wp: + bts eax, 31 + mov cr0, eax + +%ifdef IA32_TARGET + ; Synchronise MTRRs with BSP + call [ebx + (passed_info.mtrr_restore - smp_trampoline_start)] + + ; Configure local APIC handoff state (if pointer is set) + mov eax, dword [ebx + (passed_info.lapic_setup - smp_trampoline_start)] + test eax, eax + jz .skip_lapic_setup32 + call eax + .skip_lapic_setup32: +%endif + + lea eax, [ebx + (.mode64 - smp_trampoline_start)] + push 0x28 + push eax + retf + + bits 64 + .mode64: + mov ax, 0x30 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + mov ebx, ebx + mov rax, qword [rbx + (passed_info.hhdm - smp_trampoline_start)] + add qword [rbx + (passed_info.gdtr - smp_trampoline_start) + 2], rax + lgdt [rbx + (passed_info.gdtr - smp_trampoline_start)] + + ; Clear TSS busy bit and load TR + mov rcx, [rbx + (passed_info.gdtr - smp_trampoline_start) + 2] + mov byte [rcx + 0x3d], 0x89 + mov ecx, 0x38 + ltr cx + + lea rax, [rax + rbx + (parking64 - smp_trampoline_start)] + + jmp rax + +bits 64 +parking64: + mov ebx, ebx + +%ifdef X86_64_TARGET + ; Synchronise MTRRs with BSP + call [rbx + (passed_info.mtrr_restore - smp_trampoline_start)] + + ; Configure local APIC handoff state (if pointer is set) + mov rax, [rbx + (passed_info.lapic_setup - smp_trampoline_start)] + test rax, rax + jz .skip_lapic_setup64 + call rax + .skip_lapic_setup64: +%endif + + mov edi, dword [rbx + (passed_info.smp_info_struct - smp_trampoline_start)] + add rdi, qword [rbx + (passed_info.hhdm - smp_trampoline_start)] + + mov eax, 1 + xchg dword [rbx + (passed_info.booted_flag - smp_trampoline_start)], eax + + ; Check for MONITOR/MWAIT support + mov eax, 1 + xor ecx, ecx + cpuid + test ecx, (1 << 3) + jnz .monitor_spin + + .loop: + mov rax, qword [rdi + 16] + test rax, rax + jnz .out + pause + jmp .loop + + .monitor_spin: + mov rax, qword [rdi + 16] + test rax, rax + jnz .out + lea rax, [rdi + 16] + xor ecx, ecx + xor edx, edx + monitor + mov rax, qword [rdi + 16] + test rax, rax + jnz .out + xor eax, eax + xor ecx, ecx + mwait + jmp .monitor_spin + + .out: + ; Clear TLB + mov rbx, cr3 + mov cr3, rbx + + ; Switch to new stack (HHDM address, safe after lower half unmap) + mov rsp, qword [rdi + 8] + + ; Push fake return address + push 0 + mov rsi, rsp + + ; Prepare iretq frame + push 0x30 + push rsi + push 0x2 + push 0x28 + push rax + + ; Zero out all GPRs (except rdi = mp_info pointer) + xor eax, eax + xor ebx, ebx + xor ecx, ecx + xor edx, edx + xor esi, esi + xor ebp, ebp + xor r8d, r8d + xor r9d, r9d + xor r10d, r10d + xor r11d, r11d + xor r12d, r12d + xor r13d, r13d + xor r14d, r14d + xor r15d, r15d + + iretq + +invalid_idt: + times 2 dq 0 + +align 16 +passed_info: + .booted_flag db 0 + .target_mode db 0 + .pagemap dd 0 + .smp_info_struct dd 0 + .gdtr: + dw 0 + dq 0 + .hhdm: + dq 0 + .bsp_apic_addr_msr_lo: + dd 0 + .bsp_apic_addr_msr_hi: + dd 0 + .mtrr_restore: + dq 0 + .temp_stack: + dq 0 + .lapic_setup: + dq 0 + +smp_trampoline_end: + +global smp_trampoline_size +smp_trampoline_size dq smp_trampoline_end - smp_trampoline_start + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/config.h.in b/limine/config.h.in new file mode 100644 index 0000000..c93e511 --- /dev/null +++ b/limine/config.h.in @@ -0,0 +1,7 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#define LIMINE_VERSION "@PACKAGE_VERSION@" +#define LIMINE_COPYRIGHT "@LIMINE_COPYRIGHT@" + +#endif diff --git a/limine/configure.ac b/limine/configure.ac new file mode 100644 index 0000000..312a0d1 --- /dev/null +++ b/limine/configure.ac @@ -0,0 +1,348 @@ +AC_INIT([Limine], [m4_esyscmd([./version.sh])], [https://codeberg.org/Limine/Limine/issues], [limine], [https://limine-bootloader.org/]) + +AC_PREREQ([2.69]) + +AC_CONFIG_AUX_DIR([build-aux]) + +SRCDIR="$(cd "$srcdir" && pwd -P)" +BUILDDIR="$(pwd -P)" + +AC_SUBST([SRCDIR]) +AC_SUBST([BUILDDIR]) + +. "$SRCDIR"/timestamps + +AC_SUBST([REGEN_DATE]) +AC_SUBST([SOURCE_DATE_EPOCH]) +AC_SUBST([SOURCE_DATE_EPOCH_TOUCH]) + +AC_CANONICAL_HOST + +# Portably convert relative paths into absolute paths. +rel2abs() { + rel2abs_first=true + for i in $1; do + if test $rel2abs_first = true; then + case "$i" in + /*) + printf "%s" "$i" + ;; + */*) + if test -d "$(dirname "$i")"; then + printf "%s" "$(cd "$(dirname "$i")" && pwd -P)/$(basename "$i")" + else + printf "false" + fi + ;; + *) + printf "%s" "$i" + ;; + esac + rel2abs_first=false + else + printf " %s" "$i" + fi + done + printf "\n" +} + +test "x${CFLAGS+set}" = "x" && CFLAGS='-g -O2 -pipe' + +AC_LANG([C]) +AC_PROG_CC +CC="$(rel2abs "$CC")" + +werror_state="no" +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--enable-werror], [treat warnings as errors])], + [werror_state="$enableval"]) +if test "$werror_state" = "yes"; then + AC_SUBST([WERROR_FLAG], [-Werror]) +else + AC_SUBST([WERROR_FLAG], [-Wno-error]) +fi + +AC_PROG_MKDIR_P +MKDIR_P="$(rel2abs "$MKDIR_P")" +AC_PROG_INSTALL +INSTALL="$(rel2abs "$INSTALL")" +AC_PROG_SED +SED="$(rel2abs "$SED")" +AC_PROG_GREP +GREP="$(rel2abs "$GREP")" +AC_PROG_AWK +AWK="$(rel2abs "$AWK")" + +AC_CHECK_PROG([FIND_FOUND], [find], [yes]) +if ! test "x$FIND_FOUND" = "xyes"; then + AC_MSG_ERROR([find not found, please install find before configuring]) +fi + +# $1 - UPPERCASEVAR, $2 - default command, $3 - 'tool' if toolchain, $4 - 'no-err' to ignore errors +AC_DEFUN([GET_PROG], [ + if test "x${$1+set}" = "xset"; then + first_elem="$(rel2abs $(echo "$$1" | cut -f 1 -d " "))" + case "$first_elem" in + /*) + AC_MSG_CHECKING([for $first_elem]) + if test -f "$first_elem" && test -x "$first_elem"; then + $1="$(rel2abs "$$1")" + $1_FOUND=yes + else + $1_FOUND=no + fi + AC_MSG_RESULT([$$1_FOUND]) + ;; + *) + AC_CHECK_PROG([$1_FOUND], [$first_elem], [yes]) + ;; + esac + else + if ! test -z "$2"; then + if test "x$3" = "xtool"; then + AC_CHECK_TOOL([$1_FOUND], [$2], [no]) + if ! test "x$$1_FOUND" = "xno"; then + $1="$(rel2abs "$$1_FOUND")" + $1_FOUND=yes + fi + else + first_elem="$(rel2abs $(echo "$2" | cut -f 1 -d " "))" + AC_CHECK_PROG([$1_FOUND], [$first_elem], [yes]) + if test "x$$1_FOUND" = "xyes"; then + $1="$(rel2abs "$2")" + fi + fi + else + $1_FOUND=no + fi + fi + + if ! test "x$$1_FOUND" = "xyes"; then + if test "x$4" = "xno-err"; then + AC_MSG_WARN([$first_elem invalid, set $1 to a valid program]) + else + AC_MSG_ERROR([$first_elem invalid, set $1 to a valid program]) + fi + fi +]) + +AC_ARG_VAR([STRIP], [strip command]) +GET_PROG([STRIP], [strip], [tool]) + +AC_CHECK_HEADERS([stdio.h stdlib.h stdint.h stddef.h stdbool.h stdarg.h string.h errno.h inttypes.h limits.h time.h], + [], [AC_MSG_ERROR([required header not found])]) + +AC_ARG_VAR([TOOLCHAIN_FOR_TARGET], [alternative toolchain prefix for Limine]) + +AC_ARG_VAR([CC_FOR_TARGET], [C compiler command for Limine]) +if test "x${CC_FOR_TARGET+set}" = "x"; then + if test "x${TOOLCHAIN_FOR_TARGET+set}" = "x"; then + CC_FOR_TARGET="clang" + else + CC_FOR_TARGET="${TOOLCHAIN_FOR_TARGET}gcc" + fi +fi +AC_ARG_VAR([LD_FOR_TARGET], [linker command for Limine]) +if test "x${LD_FOR_TARGET+set}" = "x"; then + if test "x${TOOLCHAIN_FOR_TARGET+set}" = "x"; then + LD_FOR_TARGET="ld.lld" + else + LD_FOR_TARGET="${TOOLCHAIN_FOR_TARGET}ld" + fi +fi + +test "x${TOOLCHAIN_FOR_TARGET+set}" = "x" && TOOLCHAIN_FOR_TARGET=llvm- + +AC_ARG_VAR([OBJCOPY_FOR_TARGET], [objcopy command for Limine]) +test "x${OBJCOPY_FOR_TARGET+set}" = "x" && OBJCOPY_FOR_TARGET="${TOOLCHAIN_FOR_TARGET}objcopy" +AC_ARG_VAR([OBJDUMP_FOR_TARGET], [objdump command for Limine]) +test "x${OBJDUMP_FOR_TARGET+set}" = "x" && OBJDUMP_FOR_TARGET="${TOOLCHAIN_FOR_TARGET}objdump" +AC_ARG_VAR([READELF_FOR_TARGET], [readelf command for Limine]) +test "x${READELF_FOR_TARGET+set}" = "x" && READELF_FOR_TARGET="${TOOLCHAIN_FOR_TARGET}readelf" + +GET_PROG([CC_FOR_TARGET]) +GET_PROG([LD_FOR_TARGET]) +GET_PROG([OBJCOPY_FOR_TARGET]) +GET_PROG([OBJDUMP_FOR_TARGET]) +GET_PROG([READELF_FOR_TARGET]) + +BUILD_ALL="no" + +AC_ARG_ENABLE([all], + [AS_HELP_STRING([--enable-all], [enable ALL ports and targets])], + [BUILD_ALL="$enableval"]) + +BUILD_BIOS_CD="$BUILD_ALL" + +AC_ARG_ENABLE([bios-cd], + [AS_HELP_STRING([--enable-bios-cd], [enable building the x86 BIOS CD image])], + [BUILD_BIOS_CD="$enableval"]) + +AC_SUBST([BUILD_BIOS_CD]) + +BUILD_BIOS_PXE="$BUILD_ALL" + +AC_ARG_ENABLE([bios-pxe], + [AS_HELP_STRING([--enable-bios-pxe], [enable building the x86 BIOS PXE image])], + [BUILD_BIOS_PXE="$enableval"]) + +AC_SUBST([BUILD_BIOS_PXE]) + +BUILD_BIOS="$BUILD_ALL" + +AC_ARG_ENABLE([bios], + [AS_HELP_STRING([--enable-bios], [enable building the x86 BIOS port])], + [BUILD_BIOS="$enableval"]) + +if test "x$BUILD_BIOS" = "xno"; then + if test "x$BUILD_BIOS_CD" = "xyes"; then + BUILD_BIOS="yes" + fi + if test "x$BUILD_BIOS_PXE" = "xyes"; then + BUILD_BIOS="yes" + fi +fi + +if test "x$BUILD_BIOS" = "xno"; then + BUILD_BIOS="" +else + BUILD_BIOS="limine-bios" + NEED_NASM=yes + NEED_GZIP=yes +fi + +AC_SUBST([BUILD_BIOS]) + +BUILD_UEFI_IA32="$BUILD_ALL" + +AC_ARG_ENABLE([uefi-ia32], + [AS_HELP_STRING([--enable-uefi-ia32], [enable building the IA-32 UEFI port])], + [BUILD_UEFI_IA32="$enableval"]) + +if test "x$BUILD_UEFI_IA32" = "xno"; then + BUILD_UEFI_IA32="" +else + BUILD_UEFI_IA32="limine-uefi-ia32" + NEED_NASM=yes +fi + +AC_SUBST([BUILD_UEFI_IA32]) + +BUILD_UEFI_X86_64="$BUILD_ALL" + +AC_ARG_ENABLE([uefi-x86-64], + [AS_HELP_STRING([--enable-uefi-x86-64], [enable building the x86-64 UEFI port])], + [BUILD_UEFI_X86_64="$enableval"]) + +if test "x$BUILD_UEFI_X86_64" = "xno"; then + BUILD_UEFI_X86_64="" +else + BUILD_UEFI_X86_64="limine-uefi-x86-64" + NEED_NASM=yes +fi + +AC_SUBST([BUILD_UEFI_X86_64]) + +BUILD_UEFI_AARCH64="$BUILD_ALL" + +AC_ARG_ENABLE([uefi-aarch64], + [AS_HELP_STRING([--enable-uefi-aarch64], [enable building the aarch64 UEFI port])], + [BUILD_UEFI_AARCH64="$enableval"]) + +if test "x$BUILD_UEFI_AARCH64" = "xno"; then + BUILD_UEFI_AARCH64="" +else + BUILD_UEFI_AARCH64="limine-uefi-aarch64" +fi + +AC_SUBST([BUILD_UEFI_AARCH64]) + +BUILD_UEFI_RISCV64="$BUILD_ALL" + +AC_ARG_ENABLE([uefi-riscv64], + [AS_HELP_STRING([--enable-uefi-riscv64], [enable building the riscv64 UEFI port])], + [BUILD_UEFI_RISCV64="$enableval"]) + +if test "x$BUILD_UEFI_RISCV64" = "xno"; then + BUILD_UEFI_RISCV64="" +else + BUILD_UEFI_RISCV64="limine-uefi-riscv64" +fi + +AC_SUBST([BUILD_UEFI_RISCV64]) + +BUILD_UEFI_LOONGARCH64="$BUILD_ALL" + +AC_ARG_ENABLE([uefi-loongarch64], + [AS_HELP_STRING([--enable-uefi-loongarch64], [enable building the loongarch64 UEFI port])], + [BUILD_UEFI_LOONGARCH64="$enableval"]) + +if test "x$BUILD_UEFI_LOONGARCH64" = "xno"; then + BUILD_UEFI_LOONGARCH64="" +else + BUILD_UEFI_LOONGARCH64="limine-uefi-loongarch64" +fi + +AC_SUBST([BUILD_UEFI_LOONGARCH64]) + +BUILD_UEFI_CD="$BUILD_ALL" + +AC_ARG_ENABLE([uefi-cd], + [AS_HELP_STRING([--enable-uefi-cd], [enable building limine-uefi-cd.bin])], + [BUILD_UEFI_CD="$enableval"]) + +if ! test "x$BUILD_UEFI_CD" = "xno"; then + AC_CHECK_PROG([MTOOLS_FOUND], [mcopy], [yes]) + if ! test "x$MTOOLS_FOUND" = "xyes"; then + if test "x$BUILD_UEFI_CD" = "xyes"; then + AC_MSG_ERROR([mtools not found, install mtools to build limine-uefi-cd.bin]) + fi + AC_MSG_WARN([mtools not found, install mtools to build limine-uefi-cd.bin]) + BUILD_UEFI_CD="no" + fi +fi + +AC_SUBST([BUILD_UEFI_CD]) + +if test "x$NEED_NASM" = "xyes"; then + AC_CHECK_PROG([NASM_FOUND], [nasm], [yes]) + if ! test "x$NASM_FOUND" = "xyes"; then + AC_MSG_ERROR([nasm not found, please install nasm before configuring]) + fi +fi + +if test "x$NEED_GZIP" = "xyes"; then + AC_CHECK_PROG([GZIP_FOUND], [gzip], [yes]) + if ! test "x$GZIP_FOUND" = "xyes"; then + AC_MSG_ERROR([gzip not found, please install gzip before configuring]) + fi +fi + +BORROWED_CFLAGS="" +for cflag in $CFLAGS; do + case $cflag in + -O*|-pipe|-g|-f*-prefix-map*) + BORROWED_CFLAGS="$BORROWED_CFLAGS $cflag" + ;; + esac +done + +AC_ARG_VAR([CFLAGS_FOR_TARGET], [C flags for Limine]) +test "x${CFLAGS_FOR_TARGET+set}" = "x" && CFLAGS_FOR_TARGET="$BORROWED_CFLAGS" + +AC_ARG_VAR([CPPFLAGS_FOR_TARGET], [C preprocessor flags for Limine]) +test "x${CPPFLAGS_FOR_TARGET+set}" = "x" && CPPFLAGS_FOR_TARGET="" + +AC_ARG_VAR([LDFLAGS_FOR_TARGET], [linker flags for Limine]) +test "x${LDFLAGS_FOR_TARGET+set}" = "x" && LDFLAGS_FOR_TARGET="" + +AC_ARG_VAR([NASMFLAGS_FOR_TARGET], [nasm flags for Limine]) +test "x${NASMFLAGS_FOR_TARGET+set}" = "x" && NASMFLAGS_FOR_TARGET="-g" + +LIMINE_COPYRIGHT=$($GREP Copyright "$SRCDIR/COPYING") +AC_SUBST([LIMINE_COPYRIGHT]) + +AC_PREFIX_DEFAULT([/usr/local]) + +AC_CONFIG_FILES([man/man1/limine.1 GNUmakefile config.h]) +AC_OUTPUT diff --git a/limine/decompressor/decompressor.mk b/limine/decompressor/decompressor.mk new file mode 100644 index 0000000..718d07f --- /dev/null +++ b/limine/decompressor/decompressor.mk @@ -0,0 +1,84 @@ +.SUFFIXES: + +override SPACE := $(subst ,, ) + +override MKESCAPE = $(subst $(SPACE),\ ,$(1)) +override SHESCAPE = $(subst ','\'',$(1)) +override OBJESCAPE = $(subst .a ,.a' ',$(subst .o ,.o' ',$(call SHESCAPE,$(1)))) + +override CC_FOR_TARGET_IS_CLANG := $(shell ! $(CC_FOR_TARGET) --version 2>/dev/null | $(GREP) -q '^Target: '; echo $$?) + +ifeq ($(CC_FOR_TARGET_IS_CLANG),1) + override CC_FOR_TARGET += \ + -target i686-unknown-none-elf +endif + +override CFLAGS_FOR_TARGET += \ + -Os \ + -Wall \ + -Wextra \ + -Wshadow \ + -Wvla \ + $(WERROR_FLAG) \ + -std=gnu11 \ + -nostdinc \ + -ffreestanding \ + -ffunction-sections \ + -fdata-sections \ + -fno-stack-protector \ + -fno-stack-check \ + -fomit-frame-pointer \ + -fno-strict-aliasing \ + -fno-lto \ + -fno-PIC \ + -m32 \ + -march=i686 \ + -mabi=sysv \ + -mno-80387 \ + -mno-mmx + +override CPPFLAGS_FOR_TARGET := \ + -I . \ + -I tinf \ + -isystem ../freestnd-c-hdrs/include \ + $(CPPFLAGS_FOR_TARGET) \ + -MMD \ + -MP + +override LDFLAGS_FOR_TARGET += \ + -m elf_i386 \ + -nostdlib \ + -z max-page-size=0x1000 \ + --gc-sections \ + -static \ + -T linker.ld + +override NASMFLAGS_FOR_TARGET := \ + -f elf32 \ + $(patsubst -g,-g -F dwarf,$(NASMFLAGS_FOR_TARGET)) \ + -Wall \ + -w-unknown-warning \ + -w-reloc \ + $(WERROR_FLAG) + +override C_FILES := $(shell find . -type f -name '*.c' | LC_ALL=C sort) +override ASM_FILES := $(shell find . -type f -name '*.asm' | LC_ALL=C sort) +override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(ASM_FILES:.asm=.o) $(C_FILES:.c=.o)) +override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d)) + +.PHONY: all +all: $(call MKESCAPE,$(BUILDDIR))/decompressor.bin + +$(call MKESCAPE,$(BUILDDIR))/decompressor.bin: $(OBJ) + $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$(BUILDDIR))/decompressor.elf' + $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$(BUILDDIR))/decompressor.elf' '$(call SHESCAPE,$@)' + +-include $(HEADER_DEPS) + +$(call MKESCAPE,$(BUILDDIR))/%.o: %.c + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' + +$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm + $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" + nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' diff --git a/limine/decompressor/entry.asm b/limine/decompressor/entry.asm new file mode 100644 index 0000000..70aa0cf --- /dev/null +++ b/limine/decompressor/entry.asm @@ -0,0 +1,20 @@ +extern bss_begin +extern bss_end +extern entry + +section .entry progbits alloc exec nowrite align=16 + +global _start +_start: + cld + + ; Zero out .bss + xor al, al + mov edi, bss_begin + mov ecx, bss_end + sub ecx, bss_begin + rep stosb + + jmp entry + +section .note.GNU-stack noalloc noexec nowrite progbits diff --git a/limine/decompressor/linker.ld b/limine/decompressor/linker.ld new file mode 100644 index 0000000..c6adcfa --- /dev/null +++ b/limine/decompressor/linker.ld @@ -0,0 +1,39 @@ +OUTPUT_FORMAT(elf32-i386) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(0x05); + rodata PT_LOAD FLAGS(0x04); + data PT_LOAD FLAGS(0x06); +} + +SECTIONS +{ + . = 0x70000; + + .text : { + *(.entry) + *(.text .text.*) + } :text + + .rodata : { + *(.rodata .rodata.*) + } :rodata + + .data : { + *(.data .data.*) + } :data + + .bss : { + bss_begin = .; + *(.bss .bss.*) + *(COMMON) + bss_end = .; + } :data + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + } +} diff --git a/limine/decompressor/main.c b/limine/decompressor/main.c new file mode 100644 index 0000000..94e8214 --- /dev/null +++ b/limine/decompressor/main.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +noreturn void entry(uint8_t *compressed_stage2, size_t stage2_size, uint8_t boot_drive, int pxe) { + // The decompressor should decompress compressed_stage2 to address 0xf000. + uint8_t *dest = (uint8_t *)0xf000; + + tinf_gzip_uncompress(dest, compressed_stage2, stage2_size); + + asm volatile ( + "movl $0xf000, %%esp\n\t" + "xorl %%ebp, %%ebp\n\t" + "pushl %1\n\t" + "pushl %0\n\t" + "pushl $0\n\t" + "pushl $0xf000\n\t" + "ret\n\t" + : + : "r" ((uint32_t)boot_drive), "r" (pxe) + : "memory" + ); + + __builtin_unreachable(); +} diff --git a/limine/decompressor/memory.c b/limine/decompressor/memory.c new file mode 100644 index 0000000..a5353b2 --- /dev/null +++ b/limine/decompressor/memory.c @@ -0,0 +1,53 @@ +#include +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) { + uint8_t *restrict pdest = (uint8_t *restrict)dest; + const uint8_t *restrict psrc = (const uint8_t *restrict)src; + + for (size_t i = 0; i < n; i++) { + pdest[i] = psrc[i]; + } + + return dest; +} + +void *memset(void *s, int c, size_t n) { + uint8_t *p = (uint8_t *)s; + + for (size_t i = 0; i < n; i++) { + p[i] = (uint8_t)c; + } + + return s; +} + +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *pdest = (uint8_t *)dest; + const uint8_t *psrc = (const uint8_t *)src; + + if ((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; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t *p1 = (const uint8_t *)s1; + const uint8_t *p2 = (const uint8_t *)s2; + + for (size_t i = 0; i < n; i++) { + if (p1[i] != p2[i]) { + return p1[i] < p2[i] ? -1 : 1; + } + } + + return 0; +} diff --git a/limine/decompressor/tinf.patch b/limine/decompressor/tinf.patch new file mode 100644 index 0000000..3f87e8e --- /dev/null +++ b/limine/decompressor/tinf.patch @@ -0,0 +1,264 @@ +diff '--color=auto' -urN tinf-clean/tinf.h decompressor/tinf/tinf.h +--- tinf-clean/tinf.h 2023-06-06 00:57:04.683481827 +0200 ++++ decompressor/tinf/tinf.h 2023-06-06 00:56:14.485629430 +0200 +@@ -59,7 +59,9 @@ + * + * @deprecated No longer required, may be removed in a future version. + */ ++/* + void TINFCC tinf_init(void); ++*/ + + /** + * Decompress `sourceLen` bytes of deflate data from `source` to `dest`. +@@ -76,7 +78,7 @@ + * @param sourceLen size of compressed data + * @return `TINF_OK` on success, error code on error + */ +-int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, ++int TINFCC tinf_uncompress(void *dest, + const void *source, unsigned int sourceLen); + + /** +@@ -94,7 +96,7 @@ + * @param sourceLen size of compressed data + * @return `TINF_OK` on success, error code on error + */ +-int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen, ++int TINFCC tinf_gzip_uncompress(void *dest, + const void *source, unsigned int sourceLen); + + /** +@@ -112,8 +114,10 @@ + * @param sourceLen size of compressed data + * @return `TINF_OK` on success, error code on error + */ ++/* + int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); ++*/ + + /** + * Compute Adler-32 checksum of `length` bytes starting at `data`. +@@ -122,7 +126,9 @@ + * @param length size of data + * @return Adler-32 checksum + */ ++/* + unsigned int TINFCC tinf_adler32(const void *data, unsigned int length); ++*/ + + /** + * Compute CRC32 checksum of `length` bytes starting at `data`. +@@ -131,7 +137,9 @@ + * @param length size of data + * @return CRC32 checksum + */ ++/* + unsigned int TINFCC tinf_crc32(const void *data, unsigned int length); ++*/ + + #ifdef __cplusplus + } /* extern "C" */ +diff '--color=auto' -urN tinf-clean/tinfgzip.c decompressor/tinf/tinfgzip.c +--- tinf-clean/tinfgzip.c 2023-06-06 00:57:16.983772372 +0200 ++++ decompressor/tinf/tinfgzip.c 2023-06-06 01:09:39.119942087 +0200 +@@ -39,6 +39,8 @@ + | ((unsigned int) p[1] << 8); + } + ++/* ++ + static unsigned int read_le32(const unsigned char *p) + { + return ((unsigned int) p[0]) +@@ -47,13 +49,17 @@ + | ((unsigned int) p[3] << 24); + } + +-int tinf_gzip_uncompress(void *dest, unsigned int *destLen, ++*/ ++ ++int tinf_gzip_uncompress(void *dest, + const void *source, unsigned int sourceLen) + { + const unsigned char *src = (const unsigned char *) source; + unsigned char *dst = (unsigned char *) dest; + const unsigned char *start; ++/* + unsigned int dlen, crc32; ++*/ + int res; + unsigned char flg; + +@@ -101,7 +107,7 @@ + /* Skip file name if present */ + if (flg & FNAME) { + do { +- if (start - src >= sourceLen) { ++ if (((unsigned int)(start - src)) >= sourceLen) { + return TINF_DATA_ERROR; + } + } while (*start++); +@@ -110,7 +116,7 @@ + /* Skip file comment if present */ + if (flg & FCOMMENT) { + do { +- if (start - src >= sourceLen) { ++ if (((unsigned int)(start - src)) >= sourceLen) { + return TINF_DATA_ERROR; + } + } while (*start++); +@@ -118,6 +124,7 @@ + + /* Check header crc if present */ + if (flg & FHCRC) { ++/* + unsigned int hcrc; + + if (start - src > sourceLen - 2) { +@@ -129,10 +136,12 @@ + if (hcrc != (tinf_crc32(src, start - src) & 0x0000FFFF)) { + return TINF_DATA_ERROR; + } ++*/ + + start += 2; + } + ++#if 0 + /* -- Get decompressed length -- */ + + dlen = read_le32(&src[sourceLen - 4]); +@@ -144,6 +153,7 @@ + /* -- Get CRC32 checksum of original data -- */ + + crc32 = read_le32(&src[sourceLen - 8]); ++#endif + + /* -- Decompress data -- */ + +@@ -151,13 +161,14 @@ + return TINF_DATA_ERROR; + } + +- res = tinf_uncompress(dst, destLen, start, ++ res = tinf_uncompress(dst, start, + (src + sourceLen) - start - 8); + + if (res != TINF_OK) { + return TINF_DATA_ERROR; + } + ++#if 0 + if (*destLen != dlen) { + return TINF_DATA_ERROR; + } +@@ -167,6 +178,7 @@ + if (crc32 != tinf_crc32(dst, dlen)) { + return TINF_DATA_ERROR; + } ++#endif + + return TINF_OK; + } +diff '--color=auto' -urN tinf-clean/tinflate.c decompressor/tinf/tinflate.c +--- tinf-clean/tinflate.c 2023-06-06 00:57:10.746958386 +0200 ++++ decompressor/tinf/tinflate.c 2023-06-06 01:12:19.629674294 +0200 +@@ -25,7 +25,7 @@ + + #include "tinf.h" + +-#include ++#define assert(...) + #include + + #if defined(UINT_MAX) && (UINT_MAX) < 0xFFFFFFFFUL +@@ -49,7 +49,9 @@ + + unsigned char *dest_start; + unsigned char *dest; ++/* + unsigned char *dest_end; ++*/ + + struct tinf_tree ltree; /* Literal/length tree */ + struct tinf_tree dtree; /* Distance tree */ +@@ -425,9 +427,12 @@ + } + + if (sym < 256) { ++/* + if (d->dest == d->dest_end) { + return TINF_BUF_ERROR; + } ++*/ ++ + *d->dest++ = sym; + } + else { +@@ -465,9 +470,11 @@ + return TINF_DATA_ERROR; + } + ++/* + if (d->dest_end - d->dest < length) { + return TINF_BUF_ERROR; + } ++*/ + + /* Copy match */ + for (i = 0; i < length; ++i) { +@@ -501,6 +508,7 @@ + + d->source += 4; + ++/* + if (d->source_end - d->source < length) { + return TINF_DATA_ERROR; + } +@@ -508,6 +516,7 @@ + if (d->dest_end - d->dest < length) { + return TINF_BUF_ERROR; + } ++*/ + + /* Copy block */ + while (length--) { +@@ -548,13 +557,15 @@ + /* -- Public functions -- */ + + /* Initialize global (static) data */ ++/* + void tinf_init(void) + { + return; + } ++*/ + + /* Inflate stream from source to dest */ +-int tinf_uncompress(void *dest, unsigned int *destLen, ++int tinf_uncompress(void *dest, + const void *source, unsigned int sourceLen) + { + struct tinf_data d; +@@ -569,7 +580,9 @@ + + d.dest = (unsigned char *) dest; + d.dest_start = d.dest; ++/* + d.dest_end = d.dest + *destLen; ++*/ + + do { + unsigned int btype; +@@ -610,7 +623,9 @@ + return TINF_DATA_ERROR; + } + ++/* + *destLen = d.dest - d.dest_start; ++*/ + + return TINF_OK; + } diff --git a/limine/host/.gitignore b/limine/host/.gitignore new file mode 100644 index 0000000..a1492fd --- /dev/null +++ b/limine/host/.gitignore @@ -0,0 +1,2 @@ +limine +limine.exe diff --git a/limine/host/hgen.sh b/limine/host/hgen.sh new file mode 100755 index 0000000..934a5a5 --- /dev/null +++ b/limine/host/hgen.sh @@ -0,0 +1,16 @@ +#! /bin/sh + +set -e + +LC_ALL=C +export LC_ALL + +cat < +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LIMINE_NO_BIOS +#include "limine-bios-hdd.h" +#endif + +static char *program_name = NULL; + +static void perror_wrap(const char *fmt, ...) { + int old_errno = errno; + + fprintf(stderr, "%s: ", program_name); + + va_list args; + va_start(args, fmt); + + vfprintf(stderr, fmt, args); + + va_end(args); + + fprintf(stderr, ": %s\n", strerror(old_errno)); +} + +static void remove_arg(int *argc, char *argv[], int index) { + for (int i = index; i < *argc - 1; i++) { + argv[i] = argv[i + 1]; + } + + (*argc)--; + + argv[*argc] = NULL; +} + +static inline bool mul_u64_overflow(uint64_t a, uint64_t b, uint64_t *res) { + *res = a * b; + return a != 0 && b > UINT64_MAX / a; +} + +static inline bool add_u64_overflow(uint64_t a, uint64_t b, uint64_t *res) { + *res = a + b; + return a > UINT64_MAX - b; +} + +#ifndef LIMINE_NO_BIOS + +static bool quiet = false; + +static int set_pos(FILE *stream, uint64_t pos) { + if (sizeof(long) >= 8) { + return fseek(stream, (long)pos, SEEK_SET); + } + + long jump_size = (LONG_MAX / 2) + 1; + long last_jump = pos % jump_size; + uint64_t jumps = pos / jump_size; + + rewind(stream); + + for (uint64_t i = 0; i < jumps; i++) { + if (fseek(stream, jump_size, SEEK_CUR) != 0) { + return -1; + } + } + if (fseek(stream, last_jump, SEEK_CUR) != 0) { + return -1; + } + + return 0; +} + +#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0])) +#define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b)) + +struct gpt_table_header { + // the head + char signature[8]; + uint32_t revision; + uint32_t header_size; + uint32_t crc32; + uint32_t _reserved0; + + // the partitioning info + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + + // the guid + uint64_t disk_guid[2]; + + // entries related + uint64_t partition_entry_lba; + uint32_t number_of_partition_entries; + uint32_t size_of_partition_entry; + uint32_t partition_entry_array_crc32; +}; + +struct gpt_entry { + uint64_t partition_type_guid[2]; + + uint64_t unique_partition_guid[2]; + + uint64_t starting_lba; + uint64_t ending_lba; + + uint64_t attributes; + + uint16_t partition_name[36]; +}; + +struct gpt2mbr_type_conv { + uint64_t gpt_type1; + uint64_t gpt_type2; + uint8_t mbr_type; +}; + +// This table is very incomplete, but it should be enough for covering +// all that matters for ISOHYBRIDs. +// Of course, though, expansion is welcome. +static struct gpt2mbr_type_conv gpt2mbr_type_conv_table[] = { + { 0x11d2f81fc12a7328, 0x3bc93ec9a0004bba, 0xef }, // EFI system partition + { 0x4433b9e5ebd0a0a2, 0xc79926b7b668c087, 0x07 }, // Microsoft basic data + { 0x11aa000048465300, 0xacec4365300011aa, 0xaf }, // HFS/HFS+ +}; + +static int gpt2mbr_type(uint64_t gpt_type1, uint64_t gpt_type2) { + for (size_t i = 0; i < SIZEOF_ARRAY(gpt2mbr_type_conv_table); i++) { + if (gpt2mbr_type_conv_table[i].gpt_type1 == gpt_type1 + && gpt2mbr_type_conv_table[i].gpt_type2 == gpt_type2) { + return gpt2mbr_type_conv_table[i].mbr_type; + } + } + return -1; +} + +static void lba2chs(uint8_t *chs, uint64_t lba) { + // If LBA is too big to express, use a standard value for CHS. + if (lba > 63 * 255 * 1024) { + goto lba_too_big; + } + + uint64_t cylinder = lba / (255 * 63); + if (cylinder >= 1024) { +lba_too_big: + chs[0] = 0xfe; + chs[1] = 0xff; + chs[2] = 0xff; + return; + } + uint64_t head = (lba / 63) % 255; + uint64_t sector = (lba % 63) + 1; + + chs[0] = head; + chs[1] = (cylinder >> 2) & 0xc0; // high 2 bits + chs[1] |= sector & 0x3f; + chs[2] = cylinder; // low 8 bits +} + +static uint16_t endswap16(uint16_t value) { + uint16_t ret = 0; + ret |= (value >> 8) & 0x00ff; + ret |= (value << 8) & 0xff00; + return ret; +} + +static uint32_t endswap32(uint32_t value) { + uint32_t ret = 0; + ret |= (value >> 24) & 0x000000ff; + ret |= (value >> 8) & 0x0000ff00; + ret |= (value << 8) & 0x00ff0000; + ret |= (value << 24) & 0xff000000; + return ret; +} + +static uint64_t endswap64(uint64_t value) { + uint64_t ret = 0; + ret |= (value >> 56) & 0x00000000000000ff; + ret |= (value >> 40) & 0x000000000000ff00; + ret |= (value >> 24) & 0x0000000000ff0000; + ret |= (value >> 8) & 0x00000000ff000000; + ret |= (value << 8) & 0x000000ff00000000; + ret |= (value << 24) & 0x0000ff0000000000; + ret |= (value << 40) & 0x00ff000000000000; + ret |= (value << 56) & 0xff00000000000000; + return ret; +} + +#ifdef __BYTE_ORDER__ + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define bigendian true +#else +#define bigendian false +#endif + +#else /* !__BYTE_ORDER__ */ + +static bool bigendian = false; + +#endif /* !__BYTE_ORDER__ */ + +#define ENDSWAP(VALUE) (bigendian ? ( \ + sizeof(VALUE) == 1 ? (VALUE) : \ + sizeof(VALUE) == 2 ? endswap16(VALUE) : \ + sizeof(VALUE) == 4 ? endswap32(VALUE) : \ + sizeof(VALUE) == 8 ? endswap64(VALUE) : (abort(), 1) \ +) : (VALUE)) + +static enum { + CACHE_CLEAN, + CACHE_DIRTY +} cache_state; +static uint64_t cached_block; +static uint8_t *cache = NULL; +static FILE *device = NULL; +static size_t block_size; + +static bool device_init(void) { + size_t guesses[] = { 512, 2048, 4096 }; + + for (size_t i = 0; i < SIZEOF_ARRAY(guesses); i++) { + void *tmp = realloc(cache, guesses[i]); + if (tmp == NULL) { + perror_wrap("error: device_init(): realloc()"); + return false; + } + cache = tmp; + + rewind(device); + + size_t ret = fread(cache, guesses[i], 1, device); + if (ret != 1) { + continue; + } + + block_size = guesses[i]; + + if (!quiet) { + fprintf(stderr, "Physical block size of %zu bytes.\n", block_size); + } + + cache_state = CACHE_CLEAN; + cached_block = 0; + return true; + } + + fprintf(stderr, "error: device_init(): Couldn't determine block size of device.\n"); + return false; +} + +static bool device_flush_cache(void) { + if (cache_state == CACHE_CLEAN) + return true; + + if (set_pos(device, cached_block * block_size) != 0) { + perror_wrap("error: device_flush_cache(): set_pos()"); + return false; + } + + size_t ret = fwrite(cache, block_size, 1, device); + if (ret != 1) { + if (ferror(device)) { + perror_wrap("error: device_flush_cache(): fwrite()"); + } + return false; + } + + cache_state = CACHE_CLEAN; + return true; +} + +static bool device_cache_block(uint64_t block) { + if (cached_block == block) + return true; + + if (cache_state == CACHE_DIRTY) { + if (!device_flush_cache()) + return false; + } + + if (set_pos(device, block * block_size) != 0) { + perror_wrap("error: device_cache_block(): set_pos()"); + return false; + } + + size_t ret = fread(cache, block_size, 1, device); + if (ret != 1) { + if (ferror(device)) { + perror_wrap("error: device_cache_block(): fread()"); + } + return false; + } + + cached_block = block; + + return true; +} + +struct uninstall_data { + void *data; + uint64_t loc; + uint64_t count; +}; + +#define UNINSTALL_DATA_MAX 256 + +static bool uninstalling = false; +static struct uninstall_data uninstall_data[UNINSTALL_DATA_MAX]; +static struct uninstall_data uninstall_data_rev[UNINSTALL_DATA_MAX]; +static uint64_t uninstall_data_i = 0; +static const char *uninstall_file = NULL; + +static void reverse_uninstall_data(void) { + for (size_t i = 0, j = uninstall_data_i - 1; i < uninstall_data_i; i++, j--) { + uninstall_data_rev[j] = uninstall_data[i]; + } + + memcpy(uninstall_data, uninstall_data_rev, uninstall_data_i * sizeof(struct uninstall_data)); +} + +static void free_uninstall_data(void) { + for (size_t i = 0; i < uninstall_data_i; i++) { + free(uninstall_data[i].data); + } +} + +static bool store_uninstall_data(const char *filename) { + if (!quiet) { + fprintf(stderr, "Storing uninstall data to file: `%s`...\n", filename); + } + + FILE *udfile = fopen(filename, "wb"); + if (udfile == NULL) { + perror_wrap("error: `%s`", filename); + goto error; + } + + if (fwrite(&uninstall_data_i, sizeof(uint64_t), 1, udfile) != 1) { + goto fwrite_error; + } + + for (size_t i = 0; i < uninstall_data_i; i++) { + if (fwrite(&uninstall_data[i].loc, sizeof(uint64_t), 1, udfile) != 1) { + goto fwrite_error; + } + if (fwrite(&uninstall_data[i].count, sizeof(uint64_t), 1, udfile) != 1) { + goto fwrite_error; + } + if (fwrite(uninstall_data[i].data, uninstall_data[i].count, 1, udfile) != 1) { + goto fwrite_error; + } + } + + fclose(udfile); + return true; + +fwrite_error: + perror_wrap("error: store_uninstall_data(): fwrite()"); + +error: + if (udfile != NULL) { + fclose(udfile); + } + return false; +} + +static bool load_uninstall_data(const char *filename) { + size_t loaded_count = 0; + + if (!quiet) { + fprintf(stderr, "Loading uninstall data from file: `%s`...\n", filename); + } + + FILE *udfile = fopen(filename, "rb"); + if (udfile == NULL) { + perror_wrap("error: `%s`", filename); + goto error; + } + + if (fread(&uninstall_data_i, sizeof(uint64_t), 1, udfile) != 1) { + goto fread_error; + } + + if (uninstall_data_i > UNINSTALL_DATA_MAX) { + fprintf(stderr, "error: load_uninstall_data(): too many entries (%zu > %d)\n", + (size_t)uninstall_data_i, UNINSTALL_DATA_MAX); + goto error; + } + + for (size_t i = 0; i < uninstall_data_i; i++) { + if (fread(&uninstall_data[i].loc, sizeof(uint64_t), 1, udfile) != 1) { + goto fread_error; + } + if (fread(&uninstall_data[i].count, sizeof(uint64_t), 1, udfile) != 1) { + goto fread_error; + } + uninstall_data[i].data = malloc(uninstall_data[i].count); + if (uninstall_data[i].data == NULL) { + perror_wrap("error: load_uninstall_data(): malloc()"); + goto error; + } + if (fread(uninstall_data[i].data, uninstall_data[i].count, 1, udfile) != 1) { + free(uninstall_data[i].data); + goto fread_error; + } + loaded_count++; + } + + fclose(udfile); + return true; + +fread_error: + perror_wrap("error: load_uninstall_data(): fread()"); + +error: + // Free any previously allocated uninstall data + for (size_t j = 0; j < loaded_count; j++) { + free(uninstall_data[j].data); + } + if (udfile != NULL) { + fclose(udfile); + } + return false; +} + +static bool _device_read(void *_buffer, uint64_t loc, size_t count) { + uint8_t *buffer = _buffer; + uint64_t progress = 0; + while (progress < count) { + uint64_t block = (loc + progress) / block_size; + + if (!device_cache_block(block)) { + return false; + } + + uint64_t chunk = count - progress; + uint64_t offset = (loc + progress) % block_size; + if (chunk > block_size - offset) + chunk = block_size - offset; + + memcpy(buffer + progress, &cache[offset], chunk); + progress += chunk; + } + + return true; +} + +static bool _device_write(const void *_buffer, uint64_t loc, size_t count) { + struct uninstall_data *ud = NULL; + + if (uninstalling) { + goto skip_save; + } + + if (uninstall_data_i >= UNINSTALL_DATA_MAX) { + fprintf(stderr, "error: Too many uninstall data entries! Please report this bug upstream.\n"); + return false; + } + + ud = &uninstall_data[uninstall_data_i]; + + ud->data = malloc(count); + if (ud->data == NULL) { + perror_wrap("error: _device_write(): malloc()"); + return false; + } + + if (!_device_read(ud->data, loc, count)) { + free(ud->data); + return false; + } + + ud->loc = loc; + ud->count = count; + +skip_save:; + const uint8_t *buffer = _buffer; + uint64_t progress = 0; + while (progress < count) { + uint64_t block = (loc + progress) / block_size; + + if (!device_cache_block(block)) { + if (!uninstalling) { + free(ud->data); + } + return false; + } + + uint64_t chunk = count - progress; + uint64_t offset = (loc + progress) % block_size; + if (chunk > block_size - offset) + chunk = block_size - offset; + + memcpy(&cache[offset], buffer + progress, chunk); + cache_state = CACHE_DIRTY; + progress += chunk; + } + + if (!uninstalling) { + uninstall_data_i++; + } + return true; +} + +static bool uninstall(bool quiet_arg) { + bool print_cache_flush_fail = false; + bool print_write_fail = false; + bool ret = true; + + uninstalling = true; + + cache_state = CACHE_CLEAN; + cached_block = (uint64_t)-1; + + for (size_t i = 0; i < uninstall_data_i; i++) { + struct uninstall_data *ud = &uninstall_data[i]; + bool retry = false; + while (!_device_write(ud->data, ud->loc, ud->count)) { + if (retry) { + fprintf(stderr, "warning: Retry failed.\n"); + print_write_fail = true; + break; + } + if (!quiet) { + fprintf(stderr, "warning: Uninstall data index %zu failed to write, retrying...\n", i); + } + if (!device_flush_cache()) { + print_cache_flush_fail = true; + } + cache_state = CACHE_CLEAN; + cached_block = (uint64_t)-1; + retry = true; + } + } + + if (!device_flush_cache()) { + print_cache_flush_fail = true; + } + + if (print_write_fail) { + fprintf(stderr, "error: Some data failed to be uninstalled correctly.\n"); + ret = false; + } + + if (print_cache_flush_fail) { + fprintf(stderr, "error: Device cache flush failure. Uninstall may be incomplete.\n"); + ret = false; + } + + if (ret == true && !quiet && !quiet_arg) { + fprintf(stderr, "Uninstall data restored successfully.\n"); + } + + return ret; +} + +#define device_read(BUFFER, LOC, COUNT) \ + do { \ + if (!_device_read(BUFFER, LOC, COUNT)) \ + goto cleanup; \ + } while (0) + +#define device_write(BUFFER, LOC, COUNT) \ + do { \ + if (!_device_write(BUFFER, LOC, COUNT)) \ + goto cleanup; \ + } while (0) + +static void bios_install_usage(void) { + printf("usage: %s bios-install [GPT partition index]\n", program_name); + printf("\n"); + printf(" --force Force installation even if the safety checks fail\n"); + printf(" (DANGEROUS!)\n"); + printf("\n"); + printf(" --uninstall Reverse the entire install procedure\n"); + printf("\n"); + printf(" --uninstall-data-file=\n"); + printf(" Set the input (for --uninstall) or output file\n"); + printf(" name of the file which contains uninstall data\n"); + printf("\n"); + printf(" --no-gpt-to-mbr-isohybrid-conversion\n"); + printf(" Do not automatically convert a GUID partition table (GPT)\n"); + printf(" found on an ISOHYBRID image into an MBR partition table\n"); + printf(" (which is done for better hardware compatibility)\n"); + printf("\n"); + printf(" --quiet Do not print verbose diagnostic messages\n"); + printf("\n"); + printf(" --help | -h Display this help message\n"); + printf("\n"); +} + +static bool validate_or_force(uint64_t offset, bool force, bool *err) { + *err = false; + + char hintc[64]; + device_read(hintc, offset + 3, 4); + if (memcmp(hintc, "NTFS", 4) == 0) { + if (!force) { + return false; + } else { + memset(hintc, 0, 4); + device_write(hintc, offset + 3, 4); + } + } + device_read(hintc, offset + 54, 3); + if (memcmp(hintc, "FAT", 3) == 0) { + if (!force) { + return false; + } else { + memset(hintc, 0, 5); + device_write(hintc, offset + 54, 5); + } + } + device_read(hintc, offset + 82, 3); + if (memcmp(hintc, "FAT", 3) == 0) { + if (!force) { + return false; + } else { + memset(hintc, 0, 5); + device_write(hintc, offset + 82, 5); + } + } + device_read(hintc, offset + 3, 5); + if (memcmp(hintc, "FAT32", 5) == 0) { + if (!force) { + return false; + } else { + memset(hintc, 0, 5); + device_write(hintc, offset + 3, 5); + } + } + uint16_t hint16 = 0; + device_read(&hint16, offset + 1080, sizeof(uint16_t)); + hint16 = ENDSWAP(hint16); + if (hint16 == 0xef53) { + if (!force) { + return false; + } else { + hint16 = 0; + hint16 = ENDSWAP(hint16); + device_write(&hint16, offset + 1080, sizeof(uint16_t)); + } + } + + return true; + +cleanup: + *err = true; + return false; +} + +static int bios_install(int argc, char *argv[]) { + int ok = EXIT_FAILURE; + bool force = false; + bool gpt2mbr_allowed = true; + bool uninstall_mode = false; + const uint8_t *bootloader_img = binary_limine_hdd_bin_data; + size_t bootloader_file_size = sizeof(binary_limine_hdd_bin_data); + uint8_t orig_mbr[70], timestamp[6]; + const char *part_ndx = NULL; + +#ifndef __BYTE_ORDER__ + uint32_t endcheck = 0x12345678; + uint8_t endbyte = *((uint8_t *)&endcheck); + bigendian = endbyte == 0x12; +#endif + + if (argc < 2) { + bios_install_usage(); +#ifdef IS_WINDOWS + system("pause"); +#endif + return EXIT_FAILURE; + } + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + bios_install_usage(); + return EXIT_SUCCESS; + } else if (strcmp(argv[i], "--quiet") == 0) { + quiet = true; + } else if (strcmp(argv[i], "--force") == 0) { + if (force && !quiet) { + fprintf(stderr, "warning: --force already set.\n"); + } + force = true; + } else if (strcmp(argv[i], "--no-gpt-to-mbr-isohybrid-conversion") == 0) { + gpt2mbr_allowed = false; + } else if (strcmp(argv[i], "--uninstall") == 0) { + if (uninstall_mode && !quiet) { + fprintf(stderr, "warning: --uninstall already set.\n"); + } + uninstall_mode = true; + } else if (memcmp(argv[i], "--uninstall-data-file=", 22) == 0) { + if (uninstall_file != NULL && !quiet) { + fprintf(stderr, "warning: --uninstall-data-file already set. Overriding...\n"); + } + uninstall_file = argv[i] + 22; + if (strlen(uninstall_file) == 0) { + fprintf(stderr, "error: Uninstall data file has a zero-length name!\n"); + return EXIT_FAILURE; + } + } else { + if (device != NULL) { // [GPT partition index] + part_ndx = argv[i]; // TODO: Make this non-positional? + } else if ((device = fopen(argv[i], "r+b")) == NULL) { // + perror_wrap("error: `%s`", argv[i]); + return EXIT_FAILURE; + } + } + } + + if (device == NULL) { + fprintf(stderr, "error: No device specified\n"); + bios_install_usage(); + return EXIT_FAILURE; + } + + if (!device_init()) { + goto uninstall_mode_cleanup; + } + + if (uninstall_mode) { + if (uninstall_file == NULL) { + fprintf(stderr, "error: Uninstall mode set but no --uninstall-data-file=... passed.\n"); + goto uninstall_mode_cleanup; + } + + if (!load_uninstall_data(uninstall_file)) { + goto uninstall_mode_cleanup; + } + + if (uninstall(false) == false) { + ok = EXIT_FAILURE; + } else { + ok = EXIT_SUCCESS; + } + goto uninstall_mode_cleanup; + } + + // Probe for GPT and logical block size + int gpt = 0; + struct gpt_table_header gpt_header; + uint64_t lb_guesses[] = { 512, 4096 }; + uint64_t lb_size = 0; + for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) { + device_read(&gpt_header, lb_guesses[i], sizeof(struct gpt_table_header)); + if (!strncmp(gpt_header.signature, "EFI PART", 8)) { + lb_size = lb_guesses[i]; + gpt = 1; + if (!quiet) { + fprintf(stderr, "Installing to GPT. Logical block size of %" PRIu64 " bytes.\n", + lb_guesses[i]); + } + break; + } + } + + struct gpt_table_header secondary_gpt_header; + if (gpt) { + if (!quiet) { + fprintf(stderr, "Secondary header at LBA 0x%" PRIx64 ".\n", + ENDSWAP(gpt_header.alternate_lba)); + } + device_read(&secondary_gpt_header, lb_size * ENDSWAP(gpt_header.alternate_lba), + sizeof(struct gpt_table_header)); + if (!strncmp(secondary_gpt_header.signature, "EFI PART", 8)) { + if (!quiet) { + fprintf(stderr, "Secondary header valid.\n"); + } + } else { + fprintf(stderr, "error: Secondary header not valid, aborting.\n"); + goto cleanup; + } + } + + // Check if this is an ISO w/ a GPT, in which case try converting it + // to MBR for improved compatibility with a whole range of hardware that + // does not like booting off of GPT in BIOS or CSM mode, and other + // broken hardware. + if (gpt && gpt2mbr_allowed == true) { + char iso_signature[5]; + device_read(iso_signature, 32769, 5); + + if (strncmp(iso_signature, "CD001", 5) != 0) { + goto no_mbr_conv; + } + + if (!quiet) { + fprintf(stderr, "Detected ISOHYBRID with a GUID partition table (GPT).\n"); + fprintf(stderr, "Converting to MBR for improved compatibility...\n"); + } + + // Gather the (up to 4) GPT partition to convert. + struct { + uint64_t lba_start; + uint64_t lba_end; + uint8_t chs_start[3]; + uint8_t chs_end[3]; + uint8_t type; + } part_to_conv[4]; + size_t part_to_conv_i = 0; + + uint64_t part_entry_base; + if (mul_u64_overflow(ENDSWAP(gpt_header.partition_entry_lba), lb_size, &part_entry_base)) { + goto no_mbr_conv; + } + + for (int64_t i = 0; i < (int64_t)ENDSWAP(gpt_header.number_of_partition_entries); i++) { + struct gpt_entry gpt_entry; + uint64_t entry_offset = (uint64_t)i * ENDSWAP(gpt_header.size_of_partition_entry); + if (add_u64_overflow(part_entry_base, entry_offset, &entry_offset)) { + goto no_mbr_conv; + } + device_read(&gpt_entry, entry_offset, sizeof(struct gpt_entry)); + + if (gpt_entry.unique_partition_guid[0] == 0 && + gpt_entry.unique_partition_guid[1] == 0) { + continue; + } + + if (part_to_conv_i == 4) { + if (!quiet) { + fprintf(stderr, "GPT contains more than 4 partitions, will not convert.\n"); + } + goto no_mbr_conv; + } + + if (ENDSWAP(gpt_entry.starting_lba) > UINT32_MAX) { + if (!quiet) { + fprintf(stderr, "Starting LBA of partition %" PRIi64 " is greater than UINT32_MAX, will not convert GPT.\n", i + 1); + } + goto no_mbr_conv; + } + part_to_conv[part_to_conv_i].lba_start = ENDSWAP(gpt_entry.starting_lba); + lba2chs(part_to_conv[part_to_conv_i].chs_start, part_to_conv[part_to_conv_i].lba_start); + + if (ENDSWAP(gpt_entry.ending_lba) > UINT32_MAX) { + if (!quiet) { + fprintf(stderr, "Ending LBA of partition %" PRIi64 " is greater than UINT32_MAX, will not convert GPT.\n", i + 1); + } + goto no_mbr_conv; + } + part_to_conv[part_to_conv_i].lba_end = ENDSWAP(gpt_entry.ending_lba); + lba2chs(part_to_conv[part_to_conv_i].chs_end, part_to_conv[part_to_conv_i].lba_end); + + if (part_to_conv[part_to_conv_i].lba_end - part_to_conv[part_to_conv_i].lba_start + 1 > UINT32_MAX) { + if (!quiet) { + fprintf(stderr, "Sector count of partition %" PRIi64 " is greater than UINT32_MAX, will not convert GPT.\n", i + 1); + } + goto no_mbr_conv; + } + + int type = gpt2mbr_type(ENDSWAP(gpt_entry.partition_type_guid[0]), + ENDSWAP(gpt_entry.partition_type_guid[1])); + if (type == -1) { + if (!quiet) { + fprintf(stderr, "Cannot convert partition type for partition %" PRIi64 ", will not convert GPT.\n", i + 1); + } + goto no_mbr_conv; + } + + part_to_conv[part_to_conv_i].type = type; + + part_to_conv_i++; + } + + // Nuke the GPTs. + void *empty_lba = calloc(1, lb_size); + if (empty_lba == NULL) { + perror_wrap("error: bios_install(): malloc()"); + goto cleanup; + } + + // ... nuke primary GPT + protective MBR. + for (size_t i = 0; i < 34; i++) { + device_write(empty_lba, i * lb_size, lb_size); + } + + // ... nuke secondary GPT. + uint64_t alt_lba = ENDSWAP(gpt_header.alternate_lba); + if (alt_lba >= 32) { + for (size_t i = 0; i < 33; i++) { + device_write(empty_lba, (alt_lba - 32 + i) * lb_size, lb_size); + } + } + + free(empty_lba); + + // We're no longer GPT. + gpt = 0; + + // Generate pseudorandom MBR disk ID. + srand(time(NULL)); + for (size_t i = 0; i < 4; i++) { + uint8_t r = rand(); + device_write(&r, 0x1b8 + i, 1); + } + + // Write out the partition entries. + for (size_t i = 0; i < part_to_conv_i; i++) { + device_write(&part_to_conv[i].type, 0x1be + i * 16 + 0x04, 1); + uint32_t lba_start = ENDSWAP((uint32_t)part_to_conv[i].lba_start); + device_write(&lba_start, 0x1be + i * 16 + 0x08, 4); + uint32_t sect_count = ENDSWAP((uint32_t)((part_to_conv[i].lba_end - part_to_conv[i].lba_start) + 1)); + device_write(§_count, 0x1be + i * 16 + 0x0c, 4); + + device_write(part_to_conv[i].chs_start, 0x1be + i * 16 + 1, 3); + device_write(part_to_conv[i].chs_end, 0x1be + i * 16 + 5, 3); + } + + if (!quiet) { + fprintf(stderr, "Conversion successful.\n"); + } + } + +no_mbr_conv:; + + int mbr = 0; + if (gpt == 0) { + // Do all sanity checks on MBR + mbr = 1; + + uint8_t hint8 = 0; + uint32_t hint32 = 0; + + bool any_active = false; + + device_read(&hint8, 446, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force) { + mbr = 0; + } else { + hint8 &= 0x80; + device_write(&hint8, 446, sizeof(uint8_t)); + } + } + any_active = any_active || (hint8 & 0x80) != 0; + device_read(&hint8, 446 + 4, sizeof(uint8_t)); + if (hint8 != 0x00) { + device_read(&hint32, 446 + 8, sizeof(uint32_t)); + hint32 = ENDSWAP(hint32); + if (hint32 < 63) { + goto part_too_low; + } + } + device_read(&hint8, 462, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force) { + mbr = 0; + } else { + hint8 &= 0x80; + device_write(&hint8, 462, sizeof(uint8_t)); + } + } + any_active = any_active || (hint8 & 0x80) != 0; + device_read(&hint8, 462 + 4, sizeof(uint8_t)); + if (hint8 != 0x00) { + device_read(&hint32, 462 + 8, sizeof(uint32_t)); + hint32 = ENDSWAP(hint32); + if (hint32 < 63) { + goto part_too_low; + } + } + device_read(&hint8, 478, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force) { + mbr = 0; + } else { + hint8 &= 0x80; + device_write(&hint8, 478, sizeof(uint8_t)); + } + } + any_active = any_active || (hint8 & 0x80) != 0; + device_read(&hint8, 478 + 4, sizeof(uint8_t)); + if (hint8 != 0x00) { + device_read(&hint32, 478 + 8, sizeof(uint32_t)); + hint32 = ENDSWAP(hint32); + if (hint32 < 63) { + goto part_too_low; + } + } + device_read(&hint8, 494, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force) { + mbr = 0; + } else { + hint8 &= 0x80; + device_write(&hint8, 494, sizeof(uint8_t)); + } + } + any_active = any_active || (hint8 & 0x80) != 0; + device_read(&hint8, 494 + 4, sizeof(uint8_t)); + if (hint8 != 0x00) { + device_read(&hint32, 494 + 8, sizeof(uint32_t)); + hint32 = ENDSWAP(hint32); + if (hint32 < 63) { + goto part_too_low; + } + } + + if (0) { +part_too_low: + fprintf(stderr, "error: A partition's start sector is less than 63, aborting.\n"); + goto cleanup; + } + + if (mbr) { + bool err; + mbr = validate_or_force(0, force, &err); + if (err) { + goto cleanup; + } + } + + if (mbr && !any_active) { + if (!quiet) { + fprintf(stderr, "No active partition found, some systems may not boot.\n"); + fprintf(stderr, "Setting partition 1 as active to work around the issue...\n"); + } + hint8 = 0x80; + device_write(&hint8, 446, sizeof(uint8_t)); + } + } + + if (gpt == 0 && mbr == 0) { + fprintf(stderr, "error: Could not determine if the device has a valid partition table.\n"); + fprintf(stderr, " Please ensure the device has a valid MBR or GPT.\n"); + fprintf(stderr, " Alternatively, pass `--force` to override these checks.\n"); + fprintf(stderr, " **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n"); + goto cleanup; + } + + // Default location of stage2 for MBR (in post MBR gap) + uint64_t stage2_loc = 512; + + if (gpt) { + struct gpt_entry gpt_entry; + uint32_t partition_num; + + uint64_t gpt_part_entry_base; + if (mul_u64_overflow(ENDSWAP(gpt_header.partition_entry_lba), lb_size, &gpt_part_entry_base)) { + fprintf(stderr, "error: GPT partition entry LBA overflows.\n"); + goto cleanup; + } + + if (part_ndx != NULL) { + if (sscanf(part_ndx, "%" SCNu32, &partition_num) != 1) { + fprintf(stderr, "error: Invalid partition number format.\n"); + goto cleanup; + } + partition_num--; + if (partition_num >= ENDSWAP(gpt_header.number_of_partition_entries)) { + fprintf(stderr, "error: Partition number is too large.\n"); + goto cleanup; + } + + uint64_t entry_off = (uint64_t)partition_num * ENDSWAP(gpt_header.size_of_partition_entry); + if (add_u64_overflow(gpt_part_entry_base, entry_off, &entry_off)) { + fprintf(stderr, "error: GPT partition entry offset overflows.\n"); + goto cleanup; + } + device_read(&gpt_entry, entry_off, sizeof(struct gpt_entry)); + + if (gpt_entry.unique_partition_guid[0] == 0 && + gpt_entry.unique_partition_guid[1] == 0) { + fprintf(stderr, "error: No such partition: %" PRIu32 ".\n", partition_num + 1); + goto cleanup; + } + + if (!force && memcmp("Hah!IdontNeedEFI", &gpt_entry.partition_type_guid, 16) != 0) { + fprintf(stderr, "error: Chosen partition for BIOS boot code is not of BIOS boot partition type.\n"); + fprintf(stderr, " Pass `--force` to override this check.\n"); + fprintf(stderr, " **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n"); + goto cleanup; + } + } else { + // Try to autodetect the BIOS boot partition + for (partition_num = 0; partition_num < ENDSWAP(gpt_header.number_of_partition_entries); partition_num++) { + uint64_t entry_off = (uint64_t)partition_num * ENDSWAP(gpt_header.size_of_partition_entry); + if (add_u64_overflow(gpt_part_entry_base, entry_off, &entry_off)) { + fprintf(stderr, "error: GPT partition entry offset overflows.\n"); + goto cleanup; + } + device_read(&gpt_entry, entry_off, sizeof(struct gpt_entry)); + + if (memcmp("Hah!IdontNeedEFI", &gpt_entry.partition_type_guid, 16) == 0) { + if (!quiet) { + fprintf(stderr, "Autodetected partition %" PRIu32 " as BIOS boot partition.\n", partition_num + 1); + } + goto bios_boot_autodetected; + } + } + + fprintf(stderr, "error: Installing to a GPT device, but no BIOS boot partition specified or\n"); + fprintf(stderr, " detected.\n"); + goto cleanup; + } + +bios_boot_autodetected:; + uint64_t starting_lba = ENDSWAP(gpt_entry.starting_lba); + uint64_t ending_lba = ENDSWAP(gpt_entry.ending_lba); + + if (ending_lba < starting_lba) { + fprintf(stderr, "error: Partition %" PRIu32 " has ending LBA less than starting LBA.\n", partition_num + 1); + goto cleanup; + } + + uint64_t part_size; + if (mul_u64_overflow(ending_lba - starting_lba + 1, lb_size, &part_size)) { + fprintf(stderr, "error: Partition %" PRIu32 " size overflows.\n", partition_num + 1); + goto cleanup; + } + + if (part_size < 32768) { + fprintf(stderr, "error: Partition %" PRIu32 " is smaller than 32KiB.\n", partition_num + 1); + goto cleanup; + } + + if (mul_u64_overflow(starting_lba, lb_size, &stage2_loc)) { + fprintf(stderr, "error: Partition %" PRIu32 " starting LBA overflows.\n", partition_num + 1); + goto cleanup; + } + + bool err; + bool valid = validate_or_force(stage2_loc, force, &err); + if (err) { + goto cleanup; + } + + if (!valid) { + fprintf(stderr, "error: The partition selected to install the BIOS boot code to contains\n"); + fprintf(stderr, " a recognised filesystem.\n"); + fprintf(stderr, " Pass `--force` to override these checks.\n"); + fprintf(stderr, " **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n"); + goto cleanup; + } + + if (!quiet) { + fprintf(stderr, "Installing BIOS boot code to partition %" PRIu32 ".\n", partition_num + 1); + } + } else { + if (!quiet) { + fprintf(stderr, "Installing to MBR.\n"); + } + } + + if (!quiet) { + fprintf(stderr, "Stage 2 to be located at byte offset 0x%" PRIx64 ".\n", stage2_loc); + } + + // Save original timestamp + device_read(timestamp, 218, 6); + + // Save the original partition table of the device + device_read(orig_mbr, 440, 70); + + // Write the bootsector from the bootloader to the device + device_write(&bootloader_img[0], 0, 512); + + // Write the rest of stage 2 to the device + device_write(&bootloader_img[512], stage2_loc, bootloader_file_size - 512); + + // Hardcode in the bootsector the location of stage 2 + stage2_loc = ENDSWAP(stage2_loc); + device_write(&stage2_loc, 0x1a4, sizeof(uint64_t)); + + // Write back timestamp + device_write(timestamp, 218, 6); + + // Write back the saved partition table to the device + device_write(orig_mbr, 440, 70); + + if (!device_flush_cache()) + goto cleanup; + + if (!quiet) { + fprintf(stderr, "Reminder: Remember to copy the limine-bios.sys file in either\n" + " the root, /boot, /limine, or /boot/limine directories of\n" + " one of the partitions on the device, or boot will fail!\n"); + + fprintf(stderr, "Limine BIOS stages installed successfully.\n"); + } + + ok = EXIT_SUCCESS; + +cleanup: + reverse_uninstall_data(); + if (ok != EXIT_SUCCESS) { + // If we failed, attempt to reverse install process + fprintf(stderr, "Install failed, undoing work...\n"); + uninstall(true); + } else if (uninstall_file != NULL) { + store_uninstall_data(uninstall_file); + } +uninstall_mode_cleanup: + free_uninstall_data(); + if (cache) + free(cache); + if (device != NULL) + fclose(device); + + return ok; +} +#endif + +#define CONFIG_B2SUM_SIGNATURE "++CONFIG_B2SUM_SIGNATURE++" + +static void enroll_config_usage(void) { + printf("usage: %s enroll-config \n", program_name); + printf("\n"); + printf(" --reset Remove enrolled BLAKE2B, will not check config integrity\n"); + printf("\n"); + printf(" --quiet Do not print verbose diagnostic messages\n"); + printf("\n"); + printf(" --help | -h Display this help message\n"); + printf("\n"); +} + +static int enroll_config(int argc, char *argv[]) { + int ret = EXIT_FAILURE; + + char *bootloader = NULL; + FILE *bootloader_file = NULL; + bool quiet = false; + bool reset = false; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + enroll_config_usage(); + return EXIT_SUCCESS; + } else if (strcmp(argv[i], "--quiet") == 0) { + remove_arg(&argc, argv, i--); + quiet = true; + } else if (strcmp(argv[i], "--reset") == 0) { + remove_arg(&argc, argv, i--); + reset = true; + } + } + + if (argc <= (reset ? 1 : 2)) { + enroll_config_usage(); +#ifdef IS_WINDOWS + system("pause"); +#endif + return EXIT_FAILURE; + } + + if (!reset && strlen(argv[2]) != 128) { + fprintf(stderr, "error: BLAKE2B specified is not 128 characters long.\n"); + goto cleanup; + } + + bootloader_file = fopen(argv[1], "r+b"); + if (bootloader_file == NULL) { + perror_wrap("error: `%s`", argv[1]); + goto cleanup; + } + + if (fseek(bootloader_file, 0, SEEK_END) != 0) { + perror_wrap("error: enroll_config(): fseek()"); + goto cleanup; + } + long ftell_result = ftell(bootloader_file); + if (ftell_result < 0) { + perror_wrap("error: enroll_config(): ftell()"); + goto cleanup; + } + size_t bootloader_size = (size_t)ftell_result; + rewind(bootloader_file); + + size_t min_size = (sizeof(CONFIG_B2SUM_SIGNATURE) - 1) + 128; + if (bootloader_size < min_size) { + fprintf(stderr, "error: Bootloader file too small (need at least %zu bytes)\n", min_size); + goto cleanup; + } + + bootloader = malloc(bootloader_size); + if (bootloader == NULL) { + perror_wrap("error: enroll_config(): malloc()"); + goto cleanup; + } + + if (fread(bootloader, bootloader_size, 1, bootloader_file) != 1) { + perror_wrap("error: enroll_config(): fread()"); + goto cleanup; + } + + char *checksum_loc = NULL; + size_t checked_count = 0; + const char *config_b2sum_sign = CONFIG_B2SUM_SIGNATURE; + for (size_t i = 0; i < bootloader_size - min_size + 1; i++) { + if (bootloader[i] != config_b2sum_sign[checked_count]) { + if (checked_count > 0) { + i -= checked_count; // restart after first byte of failed match + checked_count = 0; + } + continue; + } + + checked_count++; + + if (checked_count == sizeof(CONFIG_B2SUM_SIGNATURE) - 1) { + checksum_loc = &bootloader[i + 1]; + break; + } + } + + if (checksum_loc == NULL) { + fprintf(stderr, "error: Checksum location not found in provided executable.\n"); + goto cleanup; + } + + if (!reset) { + memcpy(checksum_loc, argv[2], 128); + } else { + memset(checksum_loc, '0', 128); + } + + if (fseek(bootloader_file, 0, SEEK_SET) != 0) { + perror_wrap("error: enroll_config(): fseek()"); + goto cleanup; + } + if (fwrite(bootloader, bootloader_size, 1, bootloader_file) != 1) { + perror_wrap("error: enroll_config(): fwrite()"); + goto cleanup; + } + + if (!quiet) { + fprintf(stderr, "Config file BLAKE2B successfully %s.\n", reset ? "reset" : "enrolled"); + } + ret = EXIT_SUCCESS; + +cleanup: + if (bootloader != NULL) { + free(bootloader); + } + if (bootloader_file != NULL) { + fclose(bootloader_file); + } + return ret; +} + +#define LIMINE_VERSION "%VERSION%" +#define LIMINE_COPYRIGHT "%COPYRIGHT%" + +static void version_usage(void) { + printf("usage: %s version [options...]\n", program_name); + printf("\n"); + printf(" --version-only Only print the version number without licensing info\n"); + printf(" and other distractions\n"); + printf("\n"); + printf(" --help | -h Display this help message\n"); + printf("\n"); +} + +static int version(int argc, char *argv[]) { + if (argc >= 2) { + if (strcmp(argv[1], "--help") == 0) { + version_usage(); + return EXIT_SUCCESS; + } else if (strcmp(argv[1], "--version-only") == 0) { + puts(LIMINE_VERSION); + return EXIT_SUCCESS; + } + } + + puts("Limine " LIMINE_VERSION); + puts(LIMINE_COPYRIGHT); + puts("Limine is distributed under the terms of the BSD-2-Clause license."); + puts("There is ABSOLUTELY NO WARRANTY, to the extent permitted by law."); + return EXIT_SUCCESS; +} + +static void general_usage(void) { + printf("usage: %s \n", program_name); + printf("\n"); + printf(" --print-datadir Print the directory containing the bootloader files\n"); + printf("\n"); + printf(" --version Print the Limine version (like the `version` command)\n"); + printf("\n"); + printf(" --help | -h Display this help message\n"); + printf("\n"); + printf("Commands: `help`, `version`, `bios-install`, `enroll-config`\n"); + printf("Use `--help` after specifying the command for command-specific help.\n"); +} + +static int print_datadir(void) { +#ifdef LIMINE_DATADIR + puts(LIMINE_DATADIR); + return EXIT_SUCCESS; +#else + fprintf(stderr, "error: Cannot print datadir for `limine` built standalone.\n"); + return EXIT_FAILURE; +#endif +} + +int main(int argc, char *argv[]) { + program_name = argv[0]; + + if (argc <= 1) { + general_usage(); + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "help") == 0 + || strcmp(argv[1], "--help") == 0 + || strcmp(argv[1], "-h") == 0) { + general_usage(); + return EXIT_SUCCESS; + } else if (strcmp(argv[1], "bios-install") == 0) { +#ifndef LIMINE_NO_BIOS + return bios_install(argc - 1, &argv[1]); +#else + fprintf(stderr, "error: Limine has been compiled without BIOS support.\n"); + return EXIT_FAILURE; +#endif + } else if (strcmp(argv[1], "enroll-config") == 0) { + return enroll_config(argc - 1, &argv[1]); + } else if (strcmp(argv[1], "--print-datadir") == 0) { + return print_datadir(); + } else if (strcmp(argv[1], "version") == 0 + || strcmp(argv[1], "--version") == 0) { + return version(argc - 1, &argv[1]); + } + + general_usage(); + return EXIT_FAILURE; +} diff --git a/limine/logo.png b/limine/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9083c1b518b617c487413a364afb72a3028911de GIT binary patch literal 755 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST~P>foYegi(^Q|oHut5`rdI6X-E_f zJSq0n$|8_O{nd;`8Le}2HS0HBteRW6?>!sSTaK64Z?`YKDbM}Eo2gMjfP)<3L)AA% zz7MsZUhkjt=aB&K^0Eo)nwx&pG4H*&UdO@OSgu@=2>JfA4r(zQX&hiW&z?qk{kki!}$o>9HGcR9^4$+dFv% zC}e@b`$j<~>)YHTWCxPLRaw{ijBn@v%swUYc-{PReX*DhNTj@fc6ZP5yZ3pyzZ*CL zV+j~lK40x$x^I|lb3Nz0?Kh}v8UFP_qZu666cZ2A85kJ;|3AA+(?@%XWDAhP;OXk; Jvd$@?2>@Ui(D?uW literal 0 HcmV?d00001 diff --git a/limine/man/man1/limine.1.in b/limine/man/man1/limine.1.in new file mode 100644 index 0000000..ba308af --- /dev/null +++ b/limine/man/man1/limine.1.in @@ -0,0 +1,13 @@ +.TH LIMINE 1 "version @PACKAGE_VERSION@" "@REGEN_DATE@" +.SH NAME +limine \- Multiplexer to several Limine-related utilities. +.SH SYNOPSIS +.B limine +.RI " " +.SH DESCRIPTION +\fBlimine\fR provides a series of Limine-related utilities condensed in a single executable. +.SH BUGS +Please report bugs via +.IR @PACKAGE_BUGREPORT@ . +.SH HOMEPAGE +.I @PACKAGE_URL@ diff --git a/limine/screenshot.png b/limine/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7644e775eeb4ee2e344f6b920e18aad25e6b4a GIT binary patch literal 860008 zcmV(|K+(U6P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x zfB;EEK~#9!?ETx4Bgv8^2r`SP!96moy1RykW`=g3cHj2@ziWxQ=bVgiz*L0iK~xP2 zIN;$C8F@|>RT(8k!f~;SUPV|~Sor__zy41w2g&1gL>#Zp3{Q^rCs028^!T@4NV=X` z*H72?0XXgWer6u8*DD^cB|ZD-{w8ma^}#;*6TW-Qd9J5FNe}9Jxq3~(Ad>Z`g8;VH zD1=hX%mmQPn>m1HW@ZK%&*OS^9ykC3DTxTEM|Low5UXWec633Vc0AzHBksQ5bEoc} zqQ2HdBT1TBy;Ju^N_nY%WU9H>Lme$?^R*&<4u-VRzjdExqto3m&Xn0RK+hE1Xzgvs zzV9$fX=X`CAt`cPD8PgWPbd>1so60|>Zm_W03ahsadwS_Nb))~>aLCl3FH{Mf~0zu zdR-?;ATveC#;=%K&sJTKnIFuod@`Dv#!R8xI%P<{(6P)f&I6`o+Wam7r&YtIU!Mii zW^Q0aq*>y6o@2}y_%GY3+AuTseK)fjIm)btIy!V*olLX4mal(zbzq!Xu$os11JIU+ zsjJUXXzQ$9Ti{p$YPnb!pB>%ZU8zVnHo|Gm?2D67jZzA0lkb^H)E&zxsTzdyDM*e7 zp9g8jN5{R*)-wQt0Z1qV0i<-!{kU6qKThSk&KHuW)2Y5=0u)CBW~RH>Oic5{wUPII zKRN>K?mow9w>iky;k`EI(e=k^0d(B-<1vOeIDv?$lP%vzZZGKjmizzl-+%v|__vpn zGG3_PU-jzT2oGKMcry#Ozi0l>Ki<;(^zz#!{C|D@WA7P|6jB=c$@sdRes3rAG&4$< z-r?c77`U0cp+N>3(!dvYw=^?DI*k;ent(>RyD3lGme~oJnS(^u9U8KkkLM;E z%`F`?hcR2T%}MF>(l+MS*qSxNsku4L%n3t6aWv-5LyhS+h8%$C+V?#o+*?-PBx~!G zKyr6agpqB`%s_@Y9Nza#T80u#F%vxju(O@_o;^rz-bnhqnO)T&Y-W-`D}0*u5*UD*K)pYd^O8#x&CT$H2hWLdG-JxK!YZ-8V<+XS-=( z2)7M9{!YPfRtJ4^&kN)Fb-`!}0#2 zx@*|!;yq~a&(ADL}f`kP-Oy?OTUOMCMJqmv0=>>&uA(GSR!F)9K{r^Dgz? z3lgch8nI_4+&RXhG;a@|YpYw+d})v;-}&=%`6rH}BR)R>swuwP#VdFxjGugbm32t` zl1Home|*Jt@wtTn^5OSy{uF2=>OVaE$@@pEeQJ4Mc+&gpdUAcJ?+ue@JyJ(moMr<% zxcmN17x22a^Y;AX-`iba{n9W!mo;aYBAJ;vkWHa2n9Xt8%tFHmAOOoWGk0^riLP!m zO{~idA(7_ppqatUk`zEwIUz}!*>C`m>*7=E`pr!a7~;N_5BvVvqWy8R|A#oP4gj8x zvi1*q$e(D||L7fXw>{Sc)BRo|k|GGm*vuuBIb8dEO{s*CW2GULUY>VKLJ*!9+g(sj zsJdJ!0|T&SO~kFy_TG%U-rM`tYHK#;~c|3M>_WO+x+itx}iFPH?W%;aLK9$%|y*!9iPTp#U6B*C$0 z;&ukEkNgu3z|my}%9f5xb;NNVGXqB_M(mh|3&(W_+Fp3rapuTS*`{+beScSn!zO89 z&zU4o&9^H83K?sM2q|OSQUwIAtAW8_fnPt)kF$=D z$Rk|phmKt^Mb>ymdYQ-1if=o5J6{z3LkzpnSZ9EAa zXjc|N> zd$X_aejO@MFDomI+ zm$*knL_#8z0+7Q z2jH*9Fn%He@Y8_Sufb)^H~R!Uh;qu8!8n1T#2^ESW>#hunUv`!z>zb=yv~gqzg+Nn{^K$> zY;Gl0cuGP!AHg3DlbQ`hK;hifuK!Xf2dU0KjCSa@O;&6h_zTR{M~6MT3~P}x%rR4N zzNjczz1Bx#&hR=@MMA2To?`71umSvx#~ F*Y8rSr5abB%8q?mYGX{OfW$^0PM1r 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

pr|Gz$%O0G~q08 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%0A8K; zi3i}a0Ad$i0yg{#T{zjB)8uDu&KoTgb0>xEQT?kjl}gX+hft82-+todSeYwoe6oOti%Z&BcExuH_?QI^`yjS)i z7iW55=6S`G=MgOkq|S}xD@>ftvF1prSCdM*DM!YQ1z z6A$v$BZx2-v6Z5&65&MudMrUHQ;7~jBXX1gBFm6l5GrES28dt*>?d@oOJ7WKNaD;h zBLuznE>!*Rs<25b2UyCH1kvZ9t)ifW5_bhfBTB(Xvq>oTaHL;F-4)>DrA`FW z!>3EAt6Bz$B(;BvIM=wV)G|sl&5+!JmfZ+udgLaskfn6~P!b2m0q6fEVpw{Y4_f43m!Wd~etAGHUHkIUlBRC|=Mh&;J=PH#@kP zAhOJQ;G+LbG+v(FNm5`{S1yxfPMimu8v7Y#GcOw6a>BH`vIJ*7Q_5GBJB~((^&{f$ zOABdUWKV_kE>HaQ1a2>)s!bzfMUif5iT(O?_;>OEpl@54Ob@^>HS5)r{+*7`>Iy$Q z{rcCczJD6x!IyXWYmcug>t8u-+WCLU?f#nMFS4Nj>f>h_O22lK6cJA?J$VupdWA;R z$+?y>d4Pe+m$su6I(4ZPuq55v{k0c~9C^n5e~`>ME-V->f)OXvG-P(rP^32Lw4>sx zjeWr=z*fLJ6G=yK7*r~ONo-5$n%7__≦QLSAr`!g)P2AGHx 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 R@gO zzYlpEMYelPJh+_?i`nPWJU^|R&nxW*bboCJ@mu<$&2e#WK0t2H8z zrQWs}-B@K-giI|a(cbffm@>Et7c`VxBE=X?Lxf9+A5In2$EBkJE$V>?BOojwxTh7y z6;1>Zt_3!RE~|e^1bV%Z2+uR!<20+neQd*~iwKGS{XhKWeoQ9aZZFlkc+7B5LXsfK zJi<}n%8ZB%X9-YicR&SB7mmb|V{Bu)CBP!T{Q6c#qBEL+oV3h00MFpdkO64r+NuR^ zgl&_rUtgnc9G^&=^PWNTrX z1}JkI$|4bQ&QnCjwt0j_%z(Ssq?1Vq$Os`GeBL^e*?;yzwNjE#XY`# zd;9p9`#x?j`|Y;BeSd#{KLB#e0KI+O=jjA3#zjVsF+icp%TcFpPt;uLWl!PE;>ceTA3n14_4t^k~-(Y3I#sB74o~8KSKKz9{W@ z-Vi%U=@JmvL9oa?gi6Qj4=*l%7V1eDiW$fr)+0t@(Jf25TD579fRliEPFiIsaqGp4 zj+~41lcnWYdw)CQMEH|uNg~7Ci(1}R4av0u_pG5YC|M{i5ydB(=qjeJJCiygA~|ZF zMHiE)*;OV1Nu^wKUE?9 z7YPosK8M=DyV+ar;&o%UH!&KkNONa;pk|j?znhuTqA(G1-?xfXuHkcy5ZlTu$=o6- z0b%IieUq@vhr#A6o^a6q2&-8xx4=usKg_MJSkA zr^Wq#g87_hHj}C^SY`?}F0&|Dd>rRI9UvKsCkkaLwtxyRfQ}J?<23iMSTmEg-ea{2 zv2^Size6Wp=N?_AM%VDR=32Ga$D-t{A;8rP_g~^70g?QE4Mb}ttpcYL1f#Z~bT@=rJY^Dck;y8VYQLsrqxiDezZ%xrmPre`<^$HqkC{KMNFfb_)FUw5k5_fqf_Lm)EeXSC&N&aO>-Aua%_eGj78wy|w0M5;1` zhf=tiN90DtnQ@*6(Uq5;obB#ZUDG+uJTl{$=D{9bvsI5I=H5ArFxn?5dEzSfJ<=mB zVp&GldMF3!Pn@OSR`$R9Prv=Ks#7nT5b$FIa9x?lc1TU7oh0)UFOeFfV>>CNux1|M;KW{4am~%Y>93ot}tYxrXww(hVDV1vrV|^Kb7*^WQQl;cmAf`xwHsZDUiN z^F(-!ME7mHyuK1J=RA%h(m`~dr%}xmvM8>EqD-)|?VBJw@iHh_Hi7?ai1&$6w{9K{)s4TZwutMZ6e!oKWq3yL}RGgRH3nLolCqdahQ46 zRHc32b@a`nDkA!FySdFdXMulAm6mmivThioV~n$1{OUeQA+6FUB97yzV`GdVqUUke zo*WVTzK=0hLD%r=DqLCvQ&>%LX+&h2A#>mNh!7QI*5hCMaArqA70OsWMjbI3fp9__ zPh53;QCyNLUG9PCHszZ@PpPq0W)&rX%fhlYlR2w*5*84D|Nia$?fv!3m)Fa%mt%m(#7JWxD0a2Z1pNDfzAgP zuRy8jqk&rJ393;i0T~^^wr1|(!Sz906$#VT9!F#u>btw2lb?L3E{ZJLE21EbxLExa zkN3nzz4ol^G_8frKa3XuUXRT0!gg1I`(0+h@CoP=3aMZ|no{n~HA zBiu7F{dz5d3{|c61zFBKD2;dlNIhF$8tjX(-pr{8W{K6#twCd(dMuA{M6_=Y)?S#I z+YlhlY~R|SK!uNaGSMiO9mu@R03yZBGGp76g}%J(qC8ZZx&tt?nZLiinOTQ+g8{f3 zGY=JJ-uDg2p;E8wIIJ+!bNX@2D%+XGeMM+d$5T+{+KWUu3%SL4P9n*q(GG2}eY`^TKqGUPlh6K?70 z?;ma!$P_VEb?<0Er`tE2>+U{vJr zRun`XXhgtr4*BuxL6cYr%jb9g^mETe{KMMzd3~-$S4p_5G{-Uho(W)XS)c>e(Cbss-}eGw!;LVR!@(@TsO1dx23em{L1x_NHH5iU%UBuO5) z-wzSlwvCvN;|_ujw%1A;8V=-*K_LY6#_uGa{_hc4g_GunfD>#Mh+eQ$Z ze81l_a_AsdP^BgS!bBYDZsEw|IOm*5xXt_hBvz8RAJZIdfS&UuNt8W&#^Dz4A4f#! z_HsYw{rK26`RV6ByT{wx-DbE$hrGXkM1C-dM-am;PP+pcQBhVA=?f5os+2+D*tVg= z%pzmoZriqH#@NQ#M!Wtpkh{+r;h?y9&X)wUIM!v$Gln9u?^{t8s;m}fW(q=3MTUhJ zmv$vL)nZ??^D^%!+xQ0tYsJ1=1?*LwM+=u3T>J1`bg8uhS(=5LwU(*LDL~owE@Pj3 zi~zZWiBVpS4YC1!P;=uS;Wp<45G)}6c>6%+ZQrH66f-=gyHDS4`|Fq2aobnku$0=X z&VCX3^7XX^R2d**ZP{pM+B70cN+$L&Za*XjP{Cx`#x;dNHdk}?Ez4lA=eP^%UKPY5 zJAO`BZ;MZq>j9EL0&0Fs&#jotH}ye*xpq>tsodhP4^Wn{ZJr*W1f!DJ%HAL&DZ;H3 z;AP-a7s}?WgQ9?H0;qCjDo|S-3GDb%1(*?Eh^-(xF};AR4SeaR7WiO|7Go8mStO(EJ0}K!)&^*wfA}4 z8xdKVky4aMQBVkc1HwZPp+|!TG~fql@C%ecgAbrbAHINoeE}`Lprop@GBWPH=bXJB z?sJ-{Dh*Ye?)$_|$rOqe5jXZ-doN$^b51i;9XcKan&xBrIA--)euf)zq0LMxJ;}T( zc|K&Ti3D-IRIDBzRo~TZWCG_n$P7$m;bullF3bc{0?0=dZA|30uLXI)a{IaE40x|QLb51yOm`}6La*Wxb*Xzr)`#5G^zx_DUfA)Iah?9#{iYAks1Jk0HoRpavXDv zVYov`So2@rZ^!57&CDp~4?pP3%O$g5ZugJe3y$O1Z#*W3C4}CM(zOcaF)sc_&KKv>6Zq}`QrLT4lhW$L0Lqyz!F zP%?s~xw2$OE=q*ZBn!!0md{k~q>}eLzw|ww@YsdxN!8&ThE(0 zrzIQSREWtV?91!5H<@PU^8Wt*_V)JUk3U6(YCHDf;rBc3``GV$Yi;Y&yY`KH*S^Wt zHNwq&9CM7>w+&gWfox2cHY~oq-S_E-!Gi(n5>05#efkt>=5g$4jzgEr_2u;f%xUA} z{gbIPt8hQ;y){v7V^~@crVfEX8Zze|;i|G-TFP--Q>AtsM+OF^f+EpVx6vDjA;Oof z!(E%C_CvVm@tmE2sEg+qbkp{aGR8A;;nRcSGc)&Lt- zR$I!x(+?a)^|anM5lMR=e@ZYlcM;8sHj*-%Mj2=Rg?0Fm?Adamqor5Ye3G?m}p-iJYJS%)$)^VwlY_JtDx1Rz#&)tSKTqd!$OI zTwI1EFfTib{K>zvUWh1p%}*GDm3a(hb5Z$UbdHRQM&OUO;6VZUcT52CZ=zU;R1~E8 z=keq~nj47J)`cV~H7CSyzkdC?b=@@P zoGP7&k0bLwbSAJM*uF=_x)hf zAa)jQmxy@(ydTH0@4IM!`}`PVTwlHb2&RbGKjR-i?H@jFf4a@T?2}M2QxxQ0+>%6; z&kB_(2LcXCKhzvpr|N$tj1VPvybdQ)^V1cfW`IN><_UpM0FCkGdTGomxb_aVwlx5^ z+wC}x$_-`Nk2z+W$qgWCyOsI>jfWHv&k%1!@|v zFITgh&HMGbNB0E)0u1d*6cX3f_pFtMLdMUDlCD0vz|6+uJkbG{lHib%3Hi`23@ zw;^VDu}biKvMvTWL_y@ouoR_mLi=?JWbIjSqFxbEr2IK>`h}mHXu>pRXRhb8bhKd> zB~lRQ`^;L~D>Xq_xBEWaBEU*Y084-zW1k*0LHGLsA{A*ObM92elaq81(0h;K zOnPRMv9$d@#vCe|=8fquW^ROeUfZWa2egD`L~H*kYK2FI6w=y}3U9S)8i^|E3u~;z z9+66(0Bq*bnKjxvL6^m3*=|rsXWmu>j(r@*$oYh&cFV1)5To}-M53at zB`?d&&7!xqALH|OS8c7gIp^(uLqJuFAxTsTrK}!(jAM>*ec4`KGNOATd>_u#rpYg^HOlIDC69z?$xvOe8jWLfS-rw)7^&ftE6&7>)yzR$+?8oTa20&XH z*UBRCfe82e9!TrqpygYuqFO!{xbJD0ma0Wmj!Hz%d8s`5Dmyh95>YN33BB?;xjq9g zv(;>>5qUc_328Y!GdDY%ZmJ-vRr=HE(VmVg|5O9;`v%~KRk6+1e#F4&T5wUHX5cd0hH$9Fy=Tv3z z!%{yY(1-%6U5P~+oYJeB*qM=;mdxdwOdK2#h7c&cH&V=(i@sc1GH)HHBV4gOj^j9v zqi@&z{*OQWz|3=&Nso>RGjbyb%*~G5KFlajeDaAof#5lYR&anyW5f^r5hT(`)MtXHsrW+jiTByT4upPQAaX zDvKY7SeS^E{ogSpT!F1|8@x!H9sx#5)0Qr%Q0E*YMU`7X+HVlb#Xl#4NiHdi&hQDE zk-0swT88uk4uf&#owE!>+GG*T;!v9en$tYzuAI&F_u>X z)=@}j>AHT&ISW7`U`gqx`q7e3Q3MgE>)p&EzTPvPAL}HXCJu1qC{k6Fc)G;O7Dp^V z&6TrCsAH+*>vfTkj*7`Zbw|3u=HKUekU{st=>ULZ%+$mNu}>S3yv<-jYqVV&t6pE) z{g^s~SmrbiBeRT=XO^tl&l$^i3MZoW z%5WnY(;-mC4_|)}#{2DanqPZ+y>6{F3+%(TCV&3+xwVUjl@SxglHQT@o>Qr!l{tiQ zzwbU{`zc;7*RG71KEcX1N8387VjLjkF=jR?C?jLG>t^nA4pF^aUT(J`vN3NYrmT!$ z!pqi`Y0i1S-;ZOkaN~_w?}vSR`#h#KwS7!xVMN3MA`ecBwT$qS$}>hPvy=aqPoa=t zk+pmpRG6_f4UpSR0iX@f1hI&4Hwvg|Q;xvr=gq@f)30B?lF^(;dJeidF__g1=FYrH zW1WM~AV8eZSpjZK8%-<5GCM0H@nl@Hngy7S=T|_|yAE&vBsH}21lCw}yax8OscQb=o*@WQ~*{+=~ z1(0jdfz$u@#O%x2_H0+Z<_91oH7JBc5SU|FgqtBkh(vh-g;@0VG;&yzg$Plp0IzgU zSw3C_I*t)RmX~d&2~z}krUe{%@4Rg~=J1pU5{)^JF+_yIH=!m=q72v0Lh09aa+eTr zv#BlZ=#zv*$=D~DFXp{_+MX7%o$2A@@GQs#SPLlPiQ*{BmSaVR(>GC@55k%)>inVT)P2u}O|D8&Y)Uoavtr$tO6 z7TH83!1sL+K`hj~cg>cOME2uw_v09D0Z^I~@X9WT2q6iNwly;wzGtBIblXK*-9Li&ixA&J$YPXX7*vK0mHrM+%x3lU!m-% z>TTJAvrwtt>HWP(V(Umt+Hmm)^M8q=;u{EHt<;e?rSnS{{A}+-wb1@OSw8=+uk{+= z<*7a(3=q!q_eoHBPB6F_N;sL%=Qe0&W${jMgn-5v!_CZMN;2GPR_vZeFb=b1hhh>n zTH0q$RwEwT=`>}kc=a?RCgQ;L<>k`pa_O521WB&R6qJ;lwU^76m)H9-NVT>0_3PJM zj&E;o5kdd?|NcKpBkCEr-R~A)QcEH}^KskfoR`bxdcC&RP@pox4`x1&`S$Gtz~|@f z>({TZuU}uk^pB4>)y^V^xmz65=A8HAU}i;ZBAux>VkHl|A4g1A?XRz|*VnIe&iBuo zIU?fi+<>eImZ6;k3Y56F1;l+^jG6Q-{-&j?e-7vJFr;l5fPGPLsZtxQhJ@K z*UPytshM+HI@ZN0a!tfAY&b8WNrC9qXsl^7<&*7MeFYJuNu@;<$C89aRioG;9?1GK zyJsfC=W!t2N*zHw9s4x*um}+uHX})CDI;U{9 z&W}gG7WsFh?en3}^Pv1r6muk=YGs# zZsshFh+FRjQEeb%*4{faef#Z~AiCY|W+APc$26a2PGs&g<8V{uQl}?jCEEP%%flW@ zi3;1U@qC3~EePcNk(8dCL!QS;97GX9A#jP=gns&RX#^3vZcUZHetrFXdmnRLt}j3Q z^z&iRvH2n4@v&;gJn1kWj4>snPmrIx($6<3^FU@?GcZa)h zRj_&Qk`+Jak(#arfDj?HtHl)7B$Ir6-fhM-Pd7a`dwqTV^7_g|V;*kS`^F;2oNoT{ zc^}gvaJzkKlitfzKt%!Aocn#>Hf3R1#wiiedQ)j*OljTcsHwY$^bX>E%xP%|leLtG zn-KM`tw~D+J(~ct#q-YNCd)HA_l9cadEveQqZ}%@*fmeVJf{>Gm|;sCO9kw$2iUE= zKD4Q*;@nr00u>L&&glx8*`!P4948N02%1t(tgx;0-CKLrdC;Ggy<_Gsm5+LBt-c?@ z0%pm}mN^>7F>bdzSW|PSBG=1iW{doiteq8Wl1Is+J%AWN#6pnO97Vuwrac&$!GbcO z(mak7#ALbY-kQRk0TDJIwR47tdsB)S1SjUUT~zcqj@~*mR7kkqNO*)7iG&D~1$u_R2Ve!HNYC)&b)iwQvO4YbPrWn-8Q zGbfBmoT3sE1_2CD$uH9i=&q6pKVRD)uI+EXw157?Mb)M355UZ@nKQuQ%^l3>y`Hx$ z4FJE}K|}&32ty($rj0Q~!)cKhvkc=PA^|m~tuaw-!Zz>9u_^Vf^{#n;GoOCWLK*

-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

*qz}#4;P20Y{)pT9&;p1MjTUAu$;$nE? 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

xS-`;d4-`44=2>PDMv1+aP{U5xd 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

GX={h3=p64Li;hk)p7<}kaP#2cC}2FyqJ-i2 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 UIT`>x>KTBxhQ4|=!anYQ0S7(l loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 12 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + $(BINDIR)/limine bios-install test.hdd + qemu-system-x86_64 -net none -smp 4 -hda test.hdd -debugcon stdio + +.PHONY: fat16-test +fat16-test: + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-bios + $(MAKE) limine + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 16 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + $(BINDIR)/limine bios-install test.hdd + qemu-system-x86_64 -net none -smp 4 -hda test.hdd -debugcon stdio + +.PHONY: legacy-fat16-test +legacy-fat16-test: + $(MAKE) test-clean + $(MAKE) mbrtest.hdd + fdisk -l mbrtest.hdd + $(MAKE) limine-bios + $(MAKE) limine + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show mbrtest.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 16 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + $(BINDIR)/limine bios-install mbrtest.hdd + qemu-system-i386 -cpu pentium2 -m 16M -M isapc -net none -hda mbrtest.hdd -debugcon stdio + +.PHONY: fat32-test +fat32-test: + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-bios + $(MAKE) limine + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 32 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + $(BINDIR)/limine bios-install test.hdd + qemu-system-x86_64 -net none -smp 4 -hda test.hdd -debugcon stdio + +.PHONY: iso9660-test +iso9660-test: + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-bios + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + $(MKDIR_P) test_image/boot + cp -rv $(BINDIR)/* test_image/boot/ + cp -rv test/* test_image/boot/ + xorriso -as mkisofs -b boot/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table test_image/ -o test.iso + qemu-system-x86_64 -net none -smp 4 -cdrom test.iso -debugcon stdio + +.PHONY: full-hybrid-test +full-hybrid-test: + $(MAKE) edk2-ovmf + $(MAKE) test-clean + $(MAKE) all + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + $(MKDIR_P) test_image/boot + cp -rv $(BINDIR)/* test_image/boot/ + cp -rv test/* test_image/boot/ + $(MKDIR_P) test_image/EFI/BOOT + cp -v $(BINDIR)/BOOT*.EFI test_image/EFI/BOOT/ + xorriso -as mkisofs -R -r -J -b boot/limine-bios-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 test_image/ -o test.iso + $(BINDIR)/limine bios-install test.iso + qemu-system-x86_64 -m 512M -M q35 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-x86_64.fd,readonly=on -net none -smp 4 -cdrom test.iso -debugcon stdio + qemu-system-x86_64 -m 512M -M q35 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-x86_64.fd,readonly=on -net none -smp 4 -hda test.iso -debugcon stdio + qemu-system-x86_64 -m 512M -M q35 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-ia32.fd,readonly=on -net none -smp 4 -cdrom test.iso -debugcon stdio + qemu-system-x86_64 -m 512M -M q35 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-ia32.fd,readonly=on -net none -smp 4 -hda test.iso -debugcon stdio + qemu-system-x86_64 -m 512M -M q35 -net none -smp 4 -cdrom test.iso -debugcon stdio + qemu-system-x86_64 -m 512M -M q35 -net none -smp 4 -hda test.iso -debugcon stdio + +.PHONY: pxe-test +pxe-test: + $(MAKE) test-clean + $(MAKE) limine-bios + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + $(MKDIR_P) test_image/boot + cp -rv $(BINDIR)/* test_image/boot/ + cp -rv test/* test_image/boot/ + qemu-system-x86_64 -smp 4 -netdev user,id=n0,tftp=./test_image,bootfile=boot/limine-bios-pxe.bin -device rtl8139,netdev=n0,mac=00:00:00:11:11:11 -debugcon stdio + +.PHONY: uefi-x86-64-test +uefi-x86-64-test: + $(MAKE) edk2-ovmf + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-uefi-x86-64 + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 32 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sudo $(MKDIR_P) test_image/EFI/BOOT + sudo cp $(BINDIR)/BOOTX64.EFI test_image/EFI/BOOT/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + qemu-system-x86_64 -m 512M -M q35 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-x86_64.fd,readonly=on -net none -smp 4 -hda test.hdd -debugcon stdio + +.PHONY: uefi-aa64-test +uefi-aa64-test: + $(MAKE) edk2-ovmf + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-uefi-aarch64 + $(MAKE) -C test -f test.mk ARCH=aarch64 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 32 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sudo $(MKDIR_P) test_image/EFI/BOOT + sudo cp $(BINDIR)/BOOTAA64.EFI test_image/EFI/BOOT/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + qemu-system-aarch64 -m 512M -M virt -cpu cortex-a72 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-aarch64.fd,readonly=on -net none -smp 4 -device ramfb -device qemu-xhci -device usb-kbd -hda test.hdd -serial stdio + +.PHONY: uefi-rv64-test +uefi-rv64-test: + $(MAKE) edk2-ovmf + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-uefi-riscv64 + $(MAKE) -C test -f test.mk ARCH=riscv64 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 32 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sudo $(MKDIR_P) test_image/EFI/BOOT + sudo cp $(BINDIR)/BOOTRISCV64.EFI test_image/EFI/BOOT/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + qemu-system-riscv64 -m 512M -M virt -cpu rv64 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-riscv64.fd,readonly=on -net none -smp 4 -device ramfb -device qemu-xhci -device usb-kbd -hda test.hdd -serial stdio + +.PHONY: uefi-loongarch64-test +uefi-loongarch64-test: + $(MAKE) edk2-ovmf + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-uefi-loongarch64 + $(MAKE) -C test -f test.mk ARCH=loongarch64 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 32 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sudo $(MKDIR_P) test_image/EFI/BOOT + sudo cp $(BINDIR)/BOOTLOONGARCH64.EFI test_image/EFI/BOOT/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + qemu-system-loongarch64 -m 1G -net none -M virt -cpu la464 -device ramfb -device qemu-xhci -device usb-kbd -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-loongarch64.fd,readonly=on -hda test.hdd -serial stdio + +.PHONY: uefi-ia32-test +uefi-ia32-test: + $(MAKE) edk2-ovmf + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-uefi-ia32 + $(MAKE) -C test -f test.mk ARCH=x86 + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 32 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv $(BINDIR)/* test_image/boot/ + sudo cp -rv test/* test_image/boot/ + sudo $(MKDIR_P) test_image/EFI/BOOT + sudo cp $(BINDIR)/BOOTIA32.EFI test_image/EFI/BOOT/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + qemu-system-x86_64 -m 512M -M q35 -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-ia32.fd,readonly=on -net none -smp 4 -hda test.hdd -debugcon stdio diff --git a/limine/test/.gitignore b/limine/test/.gitignore new file mode 100644 index 0000000..4ce83ce --- /dev/null +++ b/limine/test/.gitignore @@ -0,0 +1,2 @@ +test.o +test.elf diff --git a/limine/test/bg.jpg b/limine/test/bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d7ae60f2337d8055c84f0e9990f7b393bcece484 GIT binary patch literal 271346 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 !kBa9dJ_YzVrdHzR;CfuPM3i@KzM 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�h&f;CG%TR|+jRFIH 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 literal 0 HcmV?d00001 diff --git a/limine/test/device_tree.dts b/limine/test/device_tree.dts new file mode 100644 index 0000000..8e981e9 --- /dev/null +++ b/limine/test/device_tree.dts @@ -0,0 +1,16 @@ +// Example device tree. + +/dts-v1/; + +/ { + soc { + limine_node: limine@deadbeef { + reg = <0xdeadbeef 0x1000>; + label = "KANKER"; + }; + fake_memory: memory@ffff0000 { + reg = <0xffff0000 0xffff>; + label = "This node will be removed by Limine."; + }; + }; +}; diff --git a/limine/test/e9print.c b/limine/test/e9print.c new file mode 100644 index 0000000..88d3d6a --- /dev/null +++ b/limine/test/e9print.c @@ -0,0 +1,98 @@ +#include +#include +#include + +#if defined (_LIMINE_PROTO) +#include +extern struct flanterm_context *ft_ctx; +#endif + +static const char CONVERSION_TABLE[] = "0123456789abcdef"; + +void e9_putc(char c) { +#if defined (__x86_64__) || defined (__i386__) + __asm__ __volatile__ ("outb %0, %1" :: "a" (c), "Nd" (0xe9) : "memory"); +#endif +#if defined (_LIMINE_PROTO) + if (ft_ctx != NULL) { + if (c == '\n') { + flanterm_write(ft_ctx, "\r", 1); + } + flanterm_write(ft_ctx, &c, 1); + } +#endif +} + +void e9_print(const char *msg) { + for (size_t i = 0; msg[i]; i++) { + e9_putc(msg[i]); + } +} + +void e9_puts(const char *msg) { + e9_print(msg); + e9_putc('\n'); +} + +static void e9_printhex(size_t num) { + int i; + char buf[17]; + + if (!num) { + e9_print("0x0"); + return; + } + + buf[16] = 0; + + for (i = 15; num; i--) { + buf[i] = CONVERSION_TABLE[num % 16]; + num /= 16; + } + + i++; + e9_print("0x"); + e9_print(&buf[i]); +} + +static void e9_printdec(size_t num) { + int i; + char buf[21] = {0}; + + if (!num) { + e9_putc('0'); + return; + } + + for (i = 19; num; i--) { + buf[i] = (num % 10) + 0x30; + num = num / 10; + } + + i++; + e9_print(buf + i); +} + +void e9_printf(const char *format, ...) { + va_list argp; + va_start(argp, format); + + while (*format != '\0') { + if (*format == '%') { + format++; + if (*format == 'x') { + e9_printhex(va_arg(argp, size_t)); + } else if (*format == 'd') { + e9_printdec(va_arg(argp, size_t)); + } else if (*format == 's') { + e9_print(va_arg(argp, char*)); + } + } else { + e9_putc(*format); + } + format++; + } + + e9_putc('\n'); + va_end(argp); +} diff --git a/limine/test/e9print.h b/limine/test/e9print.h new file mode 100644 index 0000000..15aedf0 --- /dev/null +++ b/limine/test/e9print.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +void e9_putc(char c); +void e9_print(const char *msg); +void e9_puts(const char *msg); +void e9_printf(const char *format, ...); diff --git a/limine/test/limine.c b/limine/test/limine.c new file mode 100644 index 0000000..b84e7b7 --- /dev/null +++ b/limine/test/limine.c @@ -0,0 +1,612 @@ +#include +#include +#include +#include +#include +#include +#include + +__attribute__((section(".limine_requests"))) +static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(5); + +static void limine_main(void); + +__attribute__((used, section(".limine_requests_start_marker"))) +static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER; + +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_entry_point_request entry_point_request = { + .id = LIMINE_ENTRY_POINT_REQUEST_ID, + .revision = 0, .response = NULL, + + .entry = limine_main +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_framebuffer_request framebuffer_request = { + .id = LIMINE_FRAMEBUFFER_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_bootloader_info_request bootloader_info_request = { + .id = LIMINE_BOOTLOADER_INFO_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_executable_cmdline_request executable_cmdline_request = { + .id = LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_firmware_type_request firmware_type_request = { + .id = LIMINE_FIRMWARE_TYPE_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_hhdm_request hhdm_request = { + .id = LIMINE_HHDM_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_memmap_request memmap_request = { + .id = LIMINE_MEMMAP_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_executable_file_request exec_file_request = { + .id = LIMINE_EXECUTABLE_FILE_REQUEST_ID, + .revision = 0, .response = NULL +}; + +struct limine_internal_module internal_module1 = { + .path = "/boot/test.elf", + .string = "First internal module" +}; + +struct limine_internal_module internal_module2 = { + .path = "test.elf", + .string = "Second internal module" +}; + +struct limine_internal_module internal_module3 = { + .path = "./limine.conf", + .string = "Third internal module" +}; + +struct limine_internal_module *internal_modules[] = { + &internal_module1, + &internal_module2, + &internal_module3 +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_module_request module_request = { + .id = LIMINE_MODULE_REQUEST_ID, + .revision = 1, .response = NULL, + + .internal_module_count = 3, + .internal_modules = internal_modules +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_rsdp_request rsdp_request = { + .id = LIMINE_RSDP_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_smbios_request smbios_request = { + .id = LIMINE_SMBIOS_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_efi_system_table_request est_request = { + .id = LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_efi_memmap_request efi_memmap_request = { + .id = LIMINE_EFI_MEMMAP_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_date_at_boot_request date_at_boot_request = { + .id = LIMINE_DATE_AT_BOOT_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_executable_address_request executable_address_request = { + .id = LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, + .revision = 0, .response = NULL +}; + +#ifndef __loongarch__ +__attribute__((section(".limine_requests"))) +static volatile struct limine_mp_request _mp_request = { + .id = LIMINE_MP_REQUEST_ID, + .revision = 0, .response = NULL +}; +#endif + +__attribute__((section(".limine_requests"))) +static volatile struct limine_dtb_request _dtb_request = { + .id = LIMINE_DTB_REQUEST_ID, + .revision = 0, .response = NULL +}; + +__attribute__((section(".limine_requests"))) +static volatile struct limine_paging_mode_request _pm_request = { + .id = LIMINE_PAGING_MODE_REQUEST_ID, + .revision = 1, .response = NULL, +#if defined (__x86_64__) + .mode = LIMINE_PAGING_MODE_X86_64_5LVL, + .max_mode = LIMINE_PAGING_MODE_X86_64_5LVL, + .min_mode = LIMINE_PAGING_MODE_X86_64_MIN +#elif defined (__aarch64__) + .mode = LIMINE_PAGING_MODE_AARCH64_5LVL, + .max_mode = LIMINE_PAGING_MODE_AARCH64_5LVL, + .min_mode = LIMINE_PAGING_MODE_AARCH64_MIN +#elif defined (__riscv) + .mode = LIMINE_PAGING_MODE_RISCV_SV57, + .max_mode = LIMINE_PAGING_MODE_RISCV_SV57, + .min_mode = LIMINE_PAGING_MODE_RISCV_MIN, +#elif defined (__loongarch__) + .mode = LIMINE_PAGING_MODE_LOONGARCH_DEFAULT, + .max_mode = LIMINE_PAGING_MODE_LOONGARCH_DEFAULT, + .min_mode = LIMINE_PAGING_MODE_LOONGARCH_MIN +#endif +}; + +#ifdef __riscv +__attribute__((section(".limine_requests"))) +static volatile struct limine_riscv_bsp_hartid_request _bsp_request = { + .id = LIMINE_RISCV_BSP_HARTID_REQUEST_ID, + .revision = 0, .response = NULL, +}; +#endif + +__attribute__((section(".limine_requests"))) +static volatile struct limine_bootloader_performance_request _perf_request = { + .id = LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID, + .revision = 0, .response = NULL, +}; + +__attribute__((used, section(".limine_requests_end_marker"))) +static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; + +static char *get_memmap_type(uint64_t type) { + switch (type) { + case LIMINE_MEMMAP_USABLE: + return "Usable"; + case LIMINE_MEMMAP_RESERVED: + return "Reserved"; + case LIMINE_MEMMAP_RESERVED_MAPPED: + return "Reserved (Mapped)"; + case LIMINE_MEMMAP_ACPI_RECLAIMABLE: + return "ACPI reclaimable"; + case LIMINE_MEMMAP_ACPI_NVS: + return "ACPI NVS"; + case LIMINE_MEMMAP_BAD_MEMORY: + return "Bad memory"; + case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: + return "Bootloader reclaimable"; + case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES: + return "Executable and modules"; + case LIMINE_MEMMAP_FRAMEBUFFER: + return "Framebuffer"; + default: + return "???"; + } +} + +static char *firmware_type_str(uint64_t t) { + switch (t) { + case LIMINE_FIRMWARE_TYPE_X86BIOS: + return "x86 BIOS"; + case LIMINE_FIRMWARE_TYPE_EFI32: + return "32-bit EFI"; + case LIMINE_FIRMWARE_TYPE_EFI64: + return "64-bit EFI"; + default: + return "???"; + } +} + +static void print_file(struct limine_file *file) { + e9_printf("File->Revision: %d", file->revision); + e9_printf("File->Address: %x", file->address); + e9_printf("File->Size: %x", file->size); + e9_printf("File->Path: %s", file->path); + e9_printf("File->String: %s", file->string); + e9_printf("File->MediaType: %d", file->media_type); + e9_printf("File->PartIndex: %d", file->partition_index); + e9_printf("File->TFTPIP: %d.%d.%d.%d", + (file->tftp_ip & (0xff << 0)) >> 0, + (file->tftp_ip & (0xff << 8)) >> 8, + (file->tftp_ip & (0xff << 16)) >> 16, + (file->tftp_ip & (0xff << 24)) >> 24); + e9_printf("File->TFTPPort: %d", file->tftp_port); + e9_printf("File->MBRDiskId: %x", file->mbr_disk_id); + e9_printf("File->GPTDiskUUID: %x-%x-%x-%x", + file->gpt_disk_uuid.a, + file->gpt_disk_uuid.b, + file->gpt_disk_uuid.c, + *(uint64_t *)file->gpt_disk_uuid.d); + e9_printf("File->GPTPartUUID: %x-%x-%x-%x", + file->gpt_part_uuid.a, + file->gpt_part_uuid.b, + file->gpt_part_uuid.c, + *(uint64_t *)file->gpt_part_uuid.d); + e9_printf("File->PartUUID: %x-%x-%x-%x", + file->part_uuid.a, + file->part_uuid.b, + file->part_uuid.c, + *(uint64_t *)file->part_uuid.d); +} + +uint32_t ctr = 0; + +void ap_entry(struct limine_mp_info *info) { + e9_printf("Hello from AP!"); + +#if defined (__x86_64__) + e9_printf("My LAPIC ID: %x", info->lapic_id); +#elif defined (__aarch64__) + e9_printf("My MPIDR: %x", info->mpidr); +#elif defined (__riscv) + e9_printf("My Hart ID: %x", info->hartid); +#elif defined (__loongarch__) + (void)info; +#endif + + __atomic_fetch_add(&ctr, 1, __ATOMIC_SEQ_CST); + + while (1); +} + +#define FEAT_START do { +#define FEAT_END } while (0); + +extern char executable_start[]; + +struct flanterm_context *ft_ctx = NULL; + +static void limine_main(void) { + e9_printf("\nWe're alive"); + + if (LIMINE_LOADED_BASE_REVISION_VALID(limine_base_revision) == true) { + e9_printf("Bootloader has loaded us using base revision %d", + LIMINE_LOADED_BASE_REVISION(limine_base_revision)); + } + + if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) { + e9_printf("Limine base revision not supported"); + for (;;); + } + + e9_printf(""); + + struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0]; + + ft_ctx = flanterm_fb_init( + NULL, + NULL, + fb->address, fb->width, fb->height, fb->pitch, + fb->red_mask_size, fb->red_mask_shift, + fb->green_mask_size, fb->green_mask_shift, + fb->blue_mask_size, fb->blue_mask_shift, + NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, 0, 0, 1, + 0, 0, + 0, + FLANTERM_FB_ROTATE_0 + ); + + uint64_t executable_slide = (uint64_t)executable_start - 0xffffffff80000000; + + e9_printf("Executable start: %x", executable_start); + e9_printf("Executable slide: %x", executable_slide); + +FEAT_START + e9_printf(""); + if (bootloader_info_request.response == NULL) { + e9_printf("Bootloader info not passed"); + break; + } + struct limine_bootloader_info_response *bootloader_info_response = bootloader_info_request.response; + e9_printf("Bootloader info feature, revision %d", bootloader_info_response->revision); + e9_printf("Bootloader name: %s", bootloader_info_response->name); + e9_printf("Bootloader version: %s", bootloader_info_response->version); +FEAT_END + +FEAT_START + e9_printf(""); + if (executable_cmdline_request.response == NULL) { + e9_printf("Executable command line not passed"); + break; + } + struct limine_executable_cmdline_response *executable_cmdline_response = executable_cmdline_request.response; + e9_printf("Executable command line feature, revision %d", executable_cmdline_response->revision); + e9_printf("Command line: %s", executable_cmdline_response->cmdline); +FEAT_END + +FEAT_START + e9_printf(""); + if (firmware_type_request.response == NULL) { + e9_printf("Firmware type not passed"); + break; + } + struct limine_firmware_type_response *firmware_type_response = firmware_type_request.response; + e9_printf("Firmware type feature, revision %d", firmware_type_response->revision); + e9_printf("Firmware type: %s", firmware_type_str(firmware_type_response->firmware_type)); +FEAT_END + +FEAT_START + e9_printf(""); + if (executable_address_request.response == NULL) { + e9_printf("Executable address not passed"); + break; + } + struct limine_executable_address_response *exec_addr_response = executable_address_request.response; + e9_printf("Executable address feature, revision %d", exec_addr_response->revision); + e9_printf("Physical base: %x", exec_addr_response->physical_base); + e9_printf("Virtual base: %x", exec_addr_response->virtual_base); +FEAT_END + +FEAT_START + e9_printf(""); + if (hhdm_request.response == NULL) { + e9_printf("HHDM not passed"); + break; + } + struct limine_hhdm_response *hhdm_response = hhdm_request.response; + e9_printf("HHDM feature, revision %d", hhdm_response->revision); + e9_printf("Higher half direct map at: %x", hhdm_response->offset); +FEAT_END + +FEAT_START + e9_printf(""); + if (memmap_request.response == NULL) { + e9_printf("Memory map not passed"); + break; + } + struct limine_memmap_response *memmap_response = memmap_request.response; + e9_printf("Memory map feature, revision %d", memmap_response->revision); + e9_printf("%d memory map entries", memmap_response->entry_count); + for (size_t i = 0; i < memmap_response->entry_count; i++) { + struct limine_memmap_entry *e = memmap_response->entries[i]; + e9_printf("%x->%x %s", e->base, e->base + e->length, get_memmap_type(e->type)); + } +FEAT_END + +FEAT_START + e9_printf(""); + if (framebuffer_request.response == NULL) { + e9_printf("Framebuffer not passed"); + break; + } + struct limine_framebuffer_response *fb_response = framebuffer_request.response; + e9_printf("Framebuffers feature, revision %d", fb_response->revision); + e9_printf("%d framebuffer(s)", fb_response->framebuffer_count); + for (size_t i = 0; i < fb_response->framebuffer_count; i++) { + struct limine_framebuffer *fb = fb_response->framebuffers[i]; + e9_printf("Address: %x", fb->address); + e9_printf("Width: %d", fb->width); + e9_printf("Height: %d", fb->height); + e9_printf("Pitch: %d", fb->pitch); + e9_printf("BPP: %d", fb->bpp); + e9_printf("Memory model: %d", fb->memory_model); + e9_printf("Red mask size: %d", fb->red_mask_size); + e9_printf("Red mask shift: %d", fb->red_mask_shift); + e9_printf("Green mask size: %d", fb->green_mask_size); + e9_printf("Green mask shift: %d", fb->green_mask_shift); + e9_printf("Blue mask size: %d", fb->blue_mask_size); + e9_printf("Blue mask shift: %d", fb->blue_mask_shift); + e9_printf("EDID size: %d", fb->edid_size); + e9_printf("EDID at: %x", fb->edid); + e9_printf("Video modes:"); + for (size_t j = 0; j < fb->mode_count; j++) { + e9_printf(" %dx%dx%d", fb->modes[j]->width, fb->modes[j]->height, fb->modes[j]->bpp); + } + } +FEAT_END + +FEAT_START + e9_printf(""); + if (exec_file_request.response == NULL) { + e9_printf("Executable file not passed"); + break; + } + struct limine_executable_file_response *exec_file_response = exec_file_request.response; + e9_printf("Executable file feature, revision %d", exec_file_response->revision); + print_file(exec_file_response->executable_file); +FEAT_END + +FEAT_START + e9_printf(""); + if (module_request.response == NULL) { + e9_printf("Modules not passed"); + break; + } + struct limine_module_response *module_response = module_request.response; + e9_printf("Modules feature, revision %d", module_response->revision); + e9_printf("%d module(s)", module_response->module_count); + for (size_t i = 0; i < module_response->module_count; i++) { + struct limine_file *f = module_response->modules[i]; + e9_printf("---"); + print_file(f); + } +FEAT_END + +FEAT_START + e9_printf(""); + if (rsdp_request.response == NULL) { + e9_printf("RSDP not passed"); + break; + } + struct limine_rsdp_response *rsdp_response = rsdp_request.response; + e9_printf("RSDP feature, revision %d", rsdp_response->revision); + e9_printf("RSDP at: %x", rsdp_response->address); +FEAT_END + +FEAT_START + e9_printf(""); + if (smbios_request.response == NULL) { + e9_printf("SMBIOS not passed"); + break; + } + struct limine_smbios_response *smbios_response = smbios_request.response; + e9_printf("SMBIOS feature, revision %d", smbios_response->revision); + e9_printf("SMBIOS 32-bit entry at: %x", smbios_response->entry_32); + e9_printf("SMBIOS 64-bit entry at: %x", smbios_response->entry_64); +FEAT_END + +FEAT_START + e9_printf(""); + if (est_request.response == NULL) { + e9_printf("EFI system table not passed"); + break; + } + struct limine_efi_system_table_response *est_response = est_request.response; + e9_printf("EFI system table feature, revision %d", est_response->revision); + e9_printf("EFI system table at: %x", est_response->address); +FEAT_END + +FEAT_START + e9_printf(""); + if (efi_memmap_request.response == NULL) { + e9_printf("EFI memory map not passed"); + break; + } + struct limine_efi_memmap_response *efi_memmap_response = efi_memmap_request.response; + e9_printf("EFI memory map feature, revision %d", efi_memmap_response->revision); + e9_printf("EFI memory map at: %x", efi_memmap_response->memmap); + e9_printf("EFI memory map size: %x", efi_memmap_response->memmap_size); + e9_printf("EFI memory descriptor size: %x", efi_memmap_response->desc_size); + e9_printf("EFI memory descriptor version: %d", efi_memmap_response->desc_version); +FEAT_END + +FEAT_START + e9_printf(""); + if (date_at_boot_request.response == NULL) { + e9_printf("Boot time not passed"); + break; + } + struct limine_date_at_boot_response *date_at_boot_response = date_at_boot_request.response; + e9_printf("Date at boot feature, revision %d", date_at_boot_response->revision); + e9_printf("Timestamp: %d", date_at_boot_response->timestamp); +FEAT_END + +// TODO: LoongArch MP +#ifndef __loongarch__ +FEAT_START + e9_printf(""); + if (_mp_request.response == NULL) { + e9_printf("MP info not passed"); + break; + } + struct limine_mp_response *mp_response = _mp_request.response; + e9_printf("MP feature, revision %d", mp_response->revision); + e9_printf("Flags: %x", mp_response->flags); +#if defined (__x86_64__) + e9_printf("BSP LAPIC ID: %x", mp_response->bsp_lapic_id); +#elif defined (__aarch64__) + e9_printf("BSP MPIDR: %x", mp_response->bsp_mpidr); +#elif defined (__riscv) + e9_printf("BSP Hart ID: %x", mp_response->bsp_hartid); +#endif + e9_printf("CPU count: %d", mp_response->cpu_count); + for (size_t i = 0; i < mp_response->cpu_count; i++) { + struct limine_mp_info *cpu = mp_response->cpus[i]; + e9_printf("Processor ID: %x", cpu->processor_id); +#if defined (__x86_64__) + e9_printf("LAPIC ID: %x", cpu->lapic_id); +#elif defined (__aarch64__) + e9_printf("MPIDR: %x", cpu->mpidr); +#elif defined (__riscv) + e9_printf("Hart ID: %x", cpu->hartid); +#endif + + +#if defined (__x86_64__) + if (cpu->lapic_id != mp_response->bsp_lapic_id) { +#elif defined (__aarch64__) + if (cpu->mpidr != mp_response->bsp_mpidr) { +#elif defined (__riscv) + if (cpu->hartid != mp_response->bsp_hartid) { +#endif + uint32_t old_ctr = __atomic_load_n(&ctr, __ATOMIC_SEQ_CST); + + __atomic_store_n(&cpu->goto_address, ap_entry, __ATOMIC_SEQ_CST); + + while (__atomic_load_n(&ctr, __ATOMIC_SEQ_CST) == old_ctr) + ; + } + } +FEAT_END +#endif + +FEAT_START + e9_printf(""); + if (_dtb_request.response == NULL) { + e9_printf("Device tree blob not passed"); + break; + } + struct limine_dtb_response *dtb_response = _dtb_request.response; + e9_printf("Device tree blob feature, revision %d", dtb_response->revision); + e9_printf("Device tree blob pointer: %x", dtb_response->dtb_ptr); + uint32_t dtb_magic = *(uint32_t*)dtb_response->dtb_ptr; + e9_printf("Device tree header magic: %x", dtb_magic); +FEAT_END + +FEAT_START + e9_printf(""); + if (_pm_request.response == NULL) { + e9_printf("Paging mode not passed"); + break; + } + struct limine_paging_mode_response *pm_response = _pm_request.response; + e9_printf("Paging mode feature, revision %d", pm_response->revision); + e9_printf(" mode: %d", pm_response->mode); +FEAT_END + +#if defined (__riscv) +FEAT_START + e9_printf(""); + struct limine_riscv_bsp_hartid_response *bsp_response = _bsp_request.response; + if (bsp_response == NULL) { + e9_printf("RISC-V BSP Hart ID was not passed"); + break; + } + e9_printf("RISC-V BSP Hart ID: %x", bsp_response->bsp_hartid); +FEAT_END +#endif + +FEAT_START + e9_printf(""); + struct limine_bootloader_performance_response *perf_response = _perf_request.response; + if (perf_response == NULL) { + e9_printf("Bootloader performance not passed"); + break; + } + e9_printf("Bootloader performance feature, revision %d", perf_response->revision); + e9_printf("Reset time: %d usec", perf_response->reset_usec); + e9_printf("Init time: %d usec", perf_response->init_usec); + e9_printf("Exec time: %d usec", perf_response->exec_usec); +FEAT_END + + for (;;); +} diff --git a/limine/test/limine.conf b/limine/test/limine.conf new file mode 100644 index 0000000..bae7f13 --- /dev/null +++ b/limine/test/limine.conf @@ -0,0 +1,59 @@ +# Some example macros +${TEST_KERNEL}=boot():/boot/test.elf +${WALLPAPER_PATH}=boot():/boot/bg.jpg + +default_entry: 1 +timeout: 3 +verbose: yes + +wallpaper: ${WALLPAPER_PATH} +backdrop: 008080 + +interface_help_colour: 3 + +/Limine Test + comment: Test of the Limine boot protocol. ${ARCH} ${FW_TYPE} + + protocol: limine + path: ${TEST_KERNEL} + cmdline: This is an example command line. + + module_path: ${WALLPAPER_PATH} + module_string: This is the first module. + + module_path: boot():/boot/bg.jpg + +/Multiboot2 Test + comment: Test of the multiboot2 boot protocol. + + protocol: multiboot2 + kernel_path: boot():/boot/multiboot2.elf + kernel_cmdline: This is an example kernel command line. + + module_path: boot():/boot/bg.jpg + module_string: This is the first module. + +/EFI Chainloading + comment: Test EFI image chainloading. + + protocol: efi_chainload + image_path: boot():/EFI/BOOT/BOOTX64.EFI + +/BIOS Chainloading + comment: Test BIOS chainloading. + + protocol: bios_chainload + drive: 1 + +/+Legacy + comment: Directory containing legacy entries. + + //Multiboot1 Test + comment: Test of the multiboot1 boot protocol. + + protocol: multiboot1 + kernel_path: boot():/boot/multiboot.elf + kernel_cmdline: This is an example kernel command line. + + module_path: boot():/boot/bg.jpg + module_string: This is the first module. diff --git a/limine/test/linker.ld b/limine/test/linker.ld new file mode 100644 index 0000000..2afa054 --- /dev/null +++ b/limine/test/linker.ld @@ -0,0 +1,53 @@ +PHDRS +{ + headers PT_PHDR PHDRS; + text PT_LOAD FILEHDR PHDRS; + rodata PT_LOAD; + data PT_LOAD; + dynamic PT_DYNAMIC; +} + +SECTIONS +{ + . = SIZEOF_HEADERS; + executable_start = . - SIZEOF_HEADERS; + + .text : { + *(.text .text.*) + } :text + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + .rodata : { + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.sdata2 .sdata2.*) + } :rodata + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + .data : { + *(.data .data.*) + *(.sdata .sdata.*) + + KEEP(*(.limine_requests_start_marker)) + KEEP(*(.limine_requests)) + KEEP(*(.limine_requests_end_marker)) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + .bss : { + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + } :data + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + *(.interp) + } +} diff --git a/limine/test/memory.c b/limine/test/memory.c new file mode 100644 index 0000000..d597807 --- /dev/null +++ b/limine/test/memory.c @@ -0,0 +1,52 @@ +#include +#include + +void *memcpy(void *dest, const void *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; +} + +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; +} + +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; +} + +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/limine/test/multiboot.c b/limine/test/multiboot.c new file mode 100644 index 0000000..b1fd8e7 --- /dev/null +++ b/limine/test/multiboot.c @@ -0,0 +1,92 @@ +#include +#include +#include + +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2badb002 + +void multiboot_main(uint32_t magic, struct multiboot1_info *info) { + if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { + e9_printf("multiboot: Invalid magic: %x\n", magic); + goto out; + } + + e9_printf("Welcome to the multiboot1 test kernel: "); + + e9_printf("\t flags: %x", info->flags); + + e9_printf("\t mem_lower: %x", info->mem_lower); + e9_printf("\t mem_upper: %x", info->mem_upper); + + e9_printf("\t boot_device: %x", info->boot_device); + e9_printf("\t cmdline: %s", info->cmdline); + + { + struct multiboot1_module *start = (struct multiboot1_module *)info->mods_addr; + struct multiboot1_module *end = (struct multiboot1_module *)(info->mods_addr + info->mods_count); + + e9_printf("\t modules:"); + for (struct multiboot1_module* entry = start; entry < end; entry++) { + e9_printf("\t\t begin=%x", entry->begin); + e9_printf("\t\t end=%x", entry->end); + e9_printf("\t\t cmdline=%s", entry->cmdline); + } + } + + { + struct multiboot1_mmap_entry *start = (struct multiboot1_mmap_entry *)info->mmap_addr; + struct multiboot1_mmap_entry *end = (struct multiboot1_mmap_entry *)(info->mmap_addr + info->mmap_length); + + e9_printf("\t usable_entries_mmap:"); + + size_t total_mem = 0; + + // For now we only print the usable memory map entries since + // printing the whole memory map blows my terminal up. We also + // iterate through the available memory map entries and add up + // to find the total amount of usable memory. + for (struct multiboot1_mmap_entry* entry = start; entry < end; entry++) { + // Check if the memory map entry is marked as usable! + if (entry->type != 1) { + continue; + } + + e9_printf("\t\t addr=%x", entry->addr); + e9_printf("\t\t length=%x", entry->len); + e9_printf("\t\t type=Usable"); + + // Now this might be a bit confusing since but `entry->size` represents the + // is the size of the associated structure in bytes and `entry->len` represents the + // size of the memory region. + total_mem += entry->len; + } + + e9_printf("Total usable memory: %x", total_mem); + } + + // TODO(Andy-Python-Programmer): Drives are unimplemented + // TODO(Andy-Python-Programmer): ROM config is unimplemented + + e9_printf("\t bootloader_name: %s", info->bootloader_name); + + // TODO(Andy-Python-Programmer): APM table is unimplemented + // TODO(Andy-Python-Programmer): VBE tag is unimplemented + + e9_printf("\t fb_addr: %x", info->fb_addr); + e9_printf("\t fb_pitch: %x", info->fb_pitch); + e9_printf("\t fb_width: %x", info->fb_width); + e9_printf("\t fb_height: %x", info->fb_height); + e9_printf("\t fb_bpp: %x", info->fb_bpp); + e9_printf("\t fb_type: %x", info->fb_type); + + e9_printf("\t fb_red_mask_shift: %x", info->fb_red_mask_shift); + e9_printf("\t fb_red_mask_size: %x", info->fb_red_mask_size); + + e9_printf("\t fb_green_mask_shift: %x", info->fb_green_mask_shift); + e9_printf("\t fb_green_mask_size: %x", info->fb_green_mask_size); + + e9_printf("\t fb_blue_mask_shift: %x", info->fb_blue_mask_shift); + e9_printf("\t fb_blue_mask_size: %x", info->fb_blue_mask_size); + +out: + for (;;); +} diff --git a/limine/test/multiboot.ld b/limine/test/multiboot.ld new file mode 100644 index 0000000..c602227 --- /dev/null +++ b/limine/test/multiboot.ld @@ -0,0 +1,35 @@ +ENTRY(_start) + +SECTIONS { + . = 1M; + + .boot : + { + /* Ensure that the multiboot header is at the beginning! */ + *(.multiboot_header) + } + + . = ALIGN(4K); + .text : + { + *(.text .text.*) + } + + . = ALIGN(4K); + .rodata : + { + *(.rodata .rodata.*) + } + + . = ALIGN(4K); + .data : + { + *(.data .data.*) + } + + . = ALIGN(4K); + .bss : + { + *(.bss .bss.*) + } +} diff --git a/limine/test/multiboot2.c b/limine/test/multiboot2.c new file mode 100644 index 0000000..e1e9fd2 --- /dev/null +++ b/limine/test/multiboot2.c @@ -0,0 +1,181 @@ +#include +#include +#include + +struct multiboot_info { + uint32_t size; + uint32_t reserved; + struct multiboot_tag *first; +}; + +void multiboot2_main(uint32_t magic, struct multiboot_info* mb_info_addr) { + if (magic != MULTIBOOT2_BOOTLOADER_MAGIC) { + e9_printf("multiboot2: Invalid magic: %x\n", magic); + goto out; + } + + e9_printf("Welcome to the multiboot2 test kernel: "); + e9_printf("\t size=%d", mb_info_addr->size); + e9_printf("\t reserved=%d", mb_info_addr->reserved); + + e9_print("\nTags:\n"); + + size_t add_size = 0; + + // NOTE: We set i to 8 to skip size and reserved fields: + for (size_t i = 8; i < mb_info_addr->size; i += add_size) { + struct multiboot_tag *tag = (struct multiboot_tag *)((uint8_t *)mb_info_addr + i); + + if (tag->type == MULTIBOOT_TAG_TYPE_END) { + break; + } + + switch (tag->type) { + case MULTIBOOT_TAG_TYPE_CMDLINE: { + struct multiboot_tag_string *cmdline = (struct multiboot_tag_string *)tag; + e9_printf("\t cmdline:"); + e9_printf("\t\t string=%s", cmdline->string); + break; + } + + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: { + struct multiboot_tag_string *name = (struct multiboot_tag_string *)tag; + e9_printf("\t bootloader_name:"); + e9_printf("\t\t string=%s", name->string); + break; + } + + case MULTIBOOT_TAG_TYPE_ACPI_OLD: { + struct multiboot_tag_old_acpi *old_acpi = (struct multiboot_tag_old_acpi *)tag; + e9_printf("\t acpi_old:"); + e9_printf("\t\t rsdp=%s", old_acpi->rsdp); + break; + } + + case MULTIBOOT_TAG_TYPE_ACPI_NEW: { + struct multiboot_tag_new_acpi *new_acpi = (struct multiboot_tag_new_acpi *)tag; + e9_printf("\t acpi_new:"); + e9_printf("\t\t rsdp=%s", new_acpi->rsdp); + break; + } + + case MULTIBOOT_TAG_TYPE_MODULE: { + struct multiboot_tag_module *module = (struct multiboot_tag_module *)tag; + e9_printf("\t module:"); + e9_printf("\t\t mod_start=%x", module->mod_start); + e9_printf("\t\t mod_end=%x", module->mod_end); + e9_printf("\t\t cmdline=%s", module->cmdline); + break; + } + + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: { + struct multiboot_tag_basic_meminfo *meminfo = (struct multiboot_tag_basic_meminfo *)tag; + e9_printf("\t basic_meminfo:"); + e9_printf("\t\t mem_lower=%x", meminfo->mem_lower); + e9_printf("\t\t mem_upper=%x", meminfo->mem_upper); + break; + } + + // unimplemented(Andy-Python-Programmer): MULTIBOOT_TAG_TYPE_BOOTDEV + + case MULTIBOOT_TAG_TYPE_MMAP: { + struct multiboot_tag_mmap *mmap = (struct multiboot_tag_mmap *)tag; + e9_printf("\t mmap:"); + e9_printf("\t\t entry_size=%d", mmap->entry_size); + e9_printf("\t\t entry_version=%d", mmap->entry_version); + e9_printf("\t\t entries:"); + + struct multiboot_mmap_entry *m = (struct multiboot_mmap_entry *)(mmap->entries); + + size_t entry_count = (mmap->size - sizeof(struct multiboot_tag_mmap)) / sizeof(struct multiboot_mmap_entry); + e9_printf("\t\t entry count: %d", entry_count); + + for (size_t i = 0; i < entry_count; i++) { + e9_printf("\t\t\t addr=%x", m[i].addr); + e9_printf("\t\t\t len=%x", m[i].len); + e9_printf("\t\t\t type=%x", m[i].type); + } + + break; + } + + case MULTIBOOT_TAG_TYPE_EFI_MMAP: { + struct multiboot_tag_efi_mmap *mmap = (struct multiboot_tag_efi_mmap *)tag; + e9_printf("\t efi_mmap:"); + e9_printf("\t\t descr_vers=%d", mmap->descr_vers); + e9_printf("\t\t descr_size=%d", mmap->descr_size); + e9_printf("\t\t size=%d", mmap->size); + e9_printf("\t\t entries:"); + + struct memory_descriptor { + uint32_t type; + uint32_t pad; + uint64_t physical_start; + uint64_t virtual_start; + uint64_t pages; + uint64_t attribute; + }; + + size_t entry_count = (mmap->size - sizeof(struct multiboot_tag_efi_mmap)) / mmap->descr_size; + e9_printf("\t\t entry count: %d", entry_count); + + for (size_t i = 0; i < entry_count; i++) { + struct memory_descriptor *m = (struct memory_descriptor *)(mmap->efi_mmap + i * mmap->descr_size); + + e9_printf("\t\t\t type=%x", m->type); + e9_printf("\t\t\t physical_start=%x", m->physical_start); + e9_printf("\t\t\t virtual_start=%x", m->virtual_start); + e9_printf("\t\t\t pages=%x", m->pages); + e9_printf("\t\t\t attribute=%x", m->attribute); + } + + break; + } + + // unimplemented(Andy-Python-Programmer): MULTIBOOT_TAG_TYPE_VBE + + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: { + struct multiboot_tag_framebuffer *fb = (struct multiboot_tag_framebuffer *)tag; + + e9_printf("\t framebuffer:"); + e9_printf("\t\t framebuffer_pitch: %d", fb->common.framebuffer_pitch); + e9_printf("\t\t framebuffer_width: %d", fb->common.framebuffer_width); + e9_printf("\t\t framebuffer_height: %d", fb->common.framebuffer_height); + e9_printf("\t\t framebuffer_bpp: %d", fb->common.framebuffer_bpp); + e9_printf("\t\t framebuffer_type: %d", fb->common.framebuffer_type); + e9_printf("\t\t framebuffer_address: %x", fb->common.framebuffer_addr); + + switch (fb->common.framebuffer_type) { + case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: { + e9_printf("\t\t framebuffer_red_field_position: %x", fb->framebuffer_red_field_position); + e9_printf("\t\t framebuffer_red_mask_size: %x", fb->framebuffer_red_mask_size); + e9_printf("\t\t framebuffer_green_field_position: %x", fb->framebuffer_green_field_position); + e9_printf("\t\t framebuffer_green_mask_size: %x", fb->framebuffer_green_mask_size); + e9_printf("\t\t framebuffer_blue_field_position: %x", fb->framebuffer_blue_field_position); + e9_printf("\t\t framebuffer_blue_mask_size: %x", fb->framebuffer_blue_mask_size); + break; + } + + // Rest are unimplemented(Andy-Python-Programmer): + } + + break; + } + + case MULTIBOOT_TAG_TYPE_NETWORK: { + struct multiboot_tag_network *network = (struct multiboot_tag_network *)tag; + e9_printf("\t network tag exists"); + break; + } + } + + add_size = tag->size; + + // Align the size to 8 bytes. + if ((add_size % 8) != 0) + add_size += (8 - add_size % 8); + } + +out: + for (;;); +} diff --git a/limine/test/multiboot2.ld b/limine/test/multiboot2.ld new file mode 100644 index 0000000..c602227 --- /dev/null +++ b/limine/test/multiboot2.ld @@ -0,0 +1,35 @@ +ENTRY(_start) + +SECTIONS { + . = 1M; + + .boot : + { + /* Ensure that the multiboot header is at the beginning! */ + *(.multiboot_header) + } + + . = ALIGN(4K); + .text : + { + *(.text .text.*) + } + + . = ALIGN(4K); + .rodata : + { + *(.rodata .rodata.*) + } + + . = ALIGN(4K); + .data : + { + *(.data .data.*) + } + + . = ALIGN(4K); + .bss : + { + *(.bss .bss.*) + } +} diff --git a/limine/test/multiboot2_trampoline.asm b/limine/test/multiboot2_trampoline.asm new file mode 100644 index 0000000..8c22ba5 --- /dev/null +++ b/limine/test/multiboot2_trampoline.asm @@ -0,0 +1,45 @@ +extern multiboot2_main + +global _start + +section .multiboot_header +header_start: + dd 0xe85250d6 ; Magic number (multiboot 2) + dd 0 ; Architecture 0 (protected mode i386) + dd header_end - header_start ; Header length + dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) ; Checksum + +align 8 +framebuffer_tag_start: + dw 5 ; type + dw 1 ; flags + dd framebuffer_tag_end - framebuffer_tag_start ; size + dd 800 ; width + dd 600 ; height + dd 32 ; depth +framebuffer_tag_end: + +align 8 + ; Required end tag: + dw 0 ; type + dw 0 ; flags + dw 8 ; size +header_end: + +section .text +bits 32 + +_start: + cli + + mov esp, stack_top + + push ebx + push eax + + call multiboot2_main ; Jump to our multiboot test kernel + +section .bss +stack_bottom: + resb 4096 * 16 +stack_top: diff --git a/limine/test/multiboot_trampoline.asm b/limine/test/multiboot_trampoline.asm new file mode 100644 index 0000000..4160faa --- /dev/null +++ b/limine/test/multiboot_trampoline.asm @@ -0,0 +1,37 @@ +%define MULTIBOOT_HEADER_MAGIC 0x1badb002 + +; Flags: +; +; bit 2: request framebuffer +%define MULTIBOOT_HEADER_FLAGS (1 << 2) + +extern multiboot_main + +global _start + +section .multiboot_header + +align 4 +header_start: + dd MULTIBOOT_HEADER_MAGIC ; Magic number (multiboot 1) + dd MULTIBOOT_HEADER_FLAGS ; Flags + dd -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) ; Checksum +header_end: + +section .text +bits 32 + +_start: + cli + + mov esp, stack_top + + push ebx + push eax + + call multiboot_main ; Jump to our multiboot test kernel + +section .bss +stack_bottom: + resb 4096 * 16 +stack_top: diff --git a/limine/test/test.mk b/limine/test/test.mk new file mode 100644 index 0000000..2e02810 --- /dev/null +++ b/limine/test/test.mk @@ -0,0 +1,163 @@ +override CC := $(CC_FOR_TARGET) +override CFLAGS := -O2 -g -Wall -Wextra +override LDFLAGS := +override LD := $(LD_FOR_TARGET) + +override CC_IS_CLANG := $(shell ! $(CC) --version 2>/dev/null | $(GREP) -q '^Target: '; echo $$?) + +ifeq ($(ARCH),x86) +ifeq ($(CC_IS_CLANG),1) +override CC += \ + -target x86_64-unknown-none-elf +endif +override LDFLAGS += \ + -m elf_x86_64 +endif +ifeq ($(ARCH),aarch64) +ifeq ($(CC_IS_CLANG),1) +override CC += \ + -target aarch64-unknown-none-elf +endif +override LDFLAGS += \ + -m aarch64elf +endif +ifeq ($(ARCH),riscv64) +ifeq ($(CC_IS_CLANG),1) +override CC += \ + -target riscv64-unknown-none-elf +endif +override LDFLAGS += \ + -m elf64lriscv +endif +ifeq ($(ARCH),loongarch64) +ifeq ($(CC_IS_CLANG),1) +override CC += \ + -target loongarch64-unknown-none-elf +endif +override LDFLAGS += \ + -m elf64loongarch +endif + +override LDFLAGS += \ + -Tlinker.ld \ + -nostdlib \ + -zmax-page-size=0x1000 \ + -pie \ + -ztext + +override LDFLAGS_MB2 := \ + -m elf_i386 \ + -Tmultiboot2.ld \ + -nostdlib \ + -zmax-page-size=0x1000 \ + -static + +override LDFLAGS_MB1 := \ + -m elf_i386 \ + -Tmultiboot.ld \ + -nostdlib \ + -zmax-page-size=0x1000 \ + -static + +override CFLAGS += \ + -std=c11 \ + -nostdinc \ + -ffreestanding \ + -fno-stack-protector \ + -fno-stack-check \ + -fno-lto \ + -fPIE \ + -I. \ + -I../limine-protocol/include \ + -I../flanterm/src \ + -isystem ../freestnd-c-hdrs/include \ + -D_LIMINE_PROTO \ + -DLIMINE_API_REVISION=4 + +ifeq ($(ARCH),x86) +override CFLAGS += \ + -m64 \ + -march=x86-64 \ + -mabi=sysv \ + -mgeneral-regs-only \ + -mno-red-zone +endif + +ifeq ($(ARCH),aarch64) +override CFLAGS += \ + -mcpu=generic \ + -march=armv8-a+nofp+nosimd \ + -mgeneral-regs-only +endif + +ifeq ($(ARCH),riscv64) +override CFLAGS += \ + -march=rv64imac \ + -mabi=lp64 \ + -mno-relax +override LDFLAGS += \ + --no-relax +endif + +ifeq ($(ARCH),loongarch64) +override CFLAGS += \ + -march=loongarch64 \ + -mabi=lp64s \ + -mfpu=none \ + -msimd=none +endif + +override CFLAGS_MB := \ + -std=c11 \ + -nostdinc \ + -ffreestanding \ + -fno-stack-protector \ + -fno-stack-check \ + -fno-lto \ + -fno-PIC \ + -m32 \ + -march=i686 \ + -mabi=sysv \ + -mgeneral-regs-only \ + -I. \ + -I../common/protos \ + -isystem ../freestnd-c-hdrs/include + +ifeq ($(ARCH),x86) +all: test.elf multiboot2.elf multiboot.elf +else +all: test.elf +endif + +flanterm.o: ../flanterm/src/flanterm.c + $(CC) $(CFLAGS) -c $< -o $@ + +flanterm_fb.o: ../flanterm/src/flanterm_backends/fb.c + $(CC) $(CFLAGS) -c $< -o $@ + +test.elf: limine.o e9print.o memory.o flanterm.o flanterm_fb.o + $(LD) $(LDFLAGS) $^ -o $@ + +multiboot2.elf: multiboot2_trampoline.o + $(CC) $(CFLAGS_MB) -c memory.c -o memory.o + $(CC) $(CFLAGS_MB) -c multiboot2.c -o multiboot2.o + $(CC) $(CFLAGS_MB) -c e9print.c -o e9print.o + $(LD) $(LDFLAGS_MB2) $^ memory.o multiboot2.o e9print.o -o $@ + +multiboot.elf: multiboot_trampoline.o + $(CC) $(CFLAGS_MB) -c memory.c -o memory.o + $(CC) $(CFLAGS_MB) -c multiboot.c -o multiboot.o + $(CC) $(CFLAGS_MB) -c e9print.c -o e9print.o + $(LD) $(LDFLAGS_MB1) $^ memory.o multiboot.o e9print.o -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.asm + nasm -felf32 -F dwarf -g $< -o $@ + +clean: + rm -rf test.elf limine.o e9print.o memory.o + rm -rf flanterm.o flanterm_fb.o + rm -rf multiboot2.o multiboot2.elf multiboot2_trampoline.o + rm -rf multiboot.o multiboot_trampoline.o multiboot.elf diff --git a/limine/version.sh b/limine/version.sh new file mode 100755 index 0000000..fe6d4b3 --- /dev/null +++ b/limine/version.sh @@ -0,0 +1,29 @@ +#! /bin/sh + +LC_ALL=C +export LC_ALL + +srcdir="$(dirname "$0")" +test -z "$srcdir" && srcdir=. + +cd "$srcdir" + +if test -f version; then + printf '%s' "$(cat version)" + exit 0 +fi + +if ! test -d .git || ! git log -n1 --pretty='%h' >/dev/null 2>&1; then + printf 'UNVERSIONED' + exit 0 +fi + +tmpfile="$(mktemp)" + +if ! git describe --exact-match --tags $(git log -n1 --pretty='%h') >"$tmpfile" 2>/dev/null; then + echo g$(git log -n1 --pretty='%h') >"$tmpfile" +fi + +printf '%s' "$(sed 's/^v//g' <"$tmpfile")" + +rm -f "$tmpfile"