├── host ├── .gitignore ├── hgen.sh └── host.mk ├── test ├── .gitignore ├── bg.jpg ├── e9print.h ├── device_tree.dts ├── multiboot.ld ├── multiboot2.ld ├── multiboot_trampoline.asm ├── linker.ld ├── memory.c ├── multiboot2_trampoline.asm ├── limine.conf ├── e9print.c └── multiboot.c ├── logo.png ├── screenshot.png ├── common ├── lib │ ├── time.h │ ├── trace.h │ ├── bli.h │ ├── stb_image.c │ ├── rand.h │ ├── uri.h │ ├── gterm.h │ ├── print.h │ ├── fdt.h │ ├── pe.h │ ├── elsewhere.h │ ├── guid.h │ ├── spinup.asm_bios_ia32 │ ├── getchar.h │ ├── real.h │ ├── image.h │ ├── config.h │ ├── mem.asm_x86_64 │ ├── libc.h │ ├── fb.h │ ├── macros.aarch64_asm.h │ ├── spinup.asm_uefi_ia32 │ ├── fdt.c │ ├── spinup.asm_riscv64 │ ├── memory.s2.c │ ├── mem.s2.asm_ia32 │ ├── term.h │ ├── spinup.asm_uefi_x86_64 │ ├── image.c │ ├── panic.s2.c │ ├── fb.c │ ├── time.c │ ├── trace.s2.c │ ├── bli.c │ ├── elf.h │ ├── rand.c │ ├── spinup.asm_aarch64 │ ├── guid.c │ ├── part.h │ ├── libc.c │ ├── sleep.asm_bios_ia32 │ ├── elsewhere.c │ ├── misc.s2.c │ ├── real.s2.asm_bios_ia32 │ ├── libc.s2.c │ └── spinup.asm_loongarch64 ├── sys │ ├── a20.h │ ├── dummy_isr.asm_ia32 │ ├── pic.h │ ├── e820.h │ ├── sbi.asm_riscv64 │ ├── dummy_isr.asm_x86_64 │ ├── gdt.h │ ├── int_thunks.s2.asm_bios_ia32 │ ├── lapic.h │ ├── idt.s2.c │ ├── e820.s2.c │ ├── pic.c │ ├── sbi.h │ ├── exceptions.s2.c │ ├── idt.h │ ├── smp.h │ ├── a20.s2.c │ ├── idt.c │ ├── gdt.s2.c │ ├── smp_trampoline.asm_riscv64 │ └── smp_trampoline.asm_aarch64 ├── libc-compat │ ├── stdlib.h │ └── string.h ├── protos │ ├── limine.h │ ├── chainload.h │ ├── multiboot.h │ ├── multiboot_32.asm_x86 │ ├── linux_64.asm_uefi_x86_64 │ ├── linux_32.asm_x86 │ ├── multiboot_reloc.asm_x86 │ ├── multiboot1.h │ ├── limine_32.asm_x86 │ └── linux.h ├── mm │ ├── mtrr.h │ ├── pmm.h │ └── pmm.c ├── crypt │ └── blake2b.h ├── efi_thunk.asm_uefi_loongarch64 ├── efi_thunk.asm_uefi_aarch64 ├── drivers │ ├── serial.h │ ├── vbe.h │ ├── gop.h │ ├── disk.h │ ├── vga_textmode.h │ ├── edid.h │ ├── serial.c │ └── edid.c ├── efi_thunk.asm_uefi_ia32 ├── efi_thunk.asm_uefi_x86_64 ├── fs │ ├── iso9660.h │ ├── fat32.h │ └── file.h ├── efi_thunk.asm_uefi_riscv64 ├── menu.h ├── menu_thunk.asm_loongarch64 ├── menu_thunk.asm_x86_64 ├── menu_thunk.asm_ia32 ├── menu_thunk.asm_aarch64 ├── menu_thunk.asm_riscv64 ├── entry_asm.s2.asm_bios_ia32 ├── pxe │ ├── tftp.h │ ├── pxe_asm.s2.asm_bios_ia32 │ ├── pxe.s2.c │ └── pxe.h ├── gensyms.sh ├── linker_uefi_ia32.ld.in ├── linker_uefi_x86_64.ld.in ├── linker_uefi_aarch64.ld.in ├── linker_uefi_loongarch64.ld.in ├── linker_uefi_riscv64.ld.in ├── linker_bios.ld.in ├── stb_image.patch └── entry.s2.c ├── config.h.in ├── bochsrc ├── .editorconfig ├── decompressor ├── entry.asm ├── linker.ld ├── main.c ├── mem.asm └── decompressor.mk ├── man └── man1 │ └── limine.1.in ├── .typos.toml ├── .forgejo └── workflows │ ├── pr_branch_check.yml │ ├── qa.yml │ ├── check.yml │ └── binary-release.yml ├── LICENSES └── LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt ├── version.sh ├── stage1 ├── gdt.asm ├── cd │ ├── read_2k_sectors.asm │ └── bootsect.asm ├── pxe │ └── bootsect.asm └── hdd │ └── disk.asm ├── .gitignore ├── COPYING ├── INSTALL.md ├── FAQ.md └── 3RDPARTY.md /host/.gitignore: -------------------------------------------------------------------------------- 1 | limine 2 | limine.exe 3 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | test.o 2 | test.elf 3 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limine-bootloader/limine/HEAD/logo.png -------------------------------------------------------------------------------- /test/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limine-bootloader/limine/HEAD/test/bg.jpg -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limine-bootloader/limine/HEAD/screenshot.png -------------------------------------------------------------------------------- /common/lib/time.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__TIME_H__ 2 | #define LIB__TIME_H__ 3 | 4 | #include 5 | 6 | uint64_t time(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /common/lib/trace.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__TRACE_H__ 2 | #define LIB__TRACE_H__ 3 | 4 | #include 5 | 6 | void print_stacktrace(size_t *base_ptr); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /common/sys/a20.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__A20_H__ 2 | #define SYS__A20_H__ 3 | 4 | #include 5 | 6 | bool a20_check(void); 7 | bool a20_enable(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | #define LIMINE_VERSION "@PACKAGE_VERSION@" 5 | #define LIMINE_COPYRIGHT "@LIMINE_COPYRIGHT@" 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /common/libc-compat/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBC_COMPAT__STDLIB_H__ 2 | #define LIBC_COMPAT__STDLIB_H__ 3 | 4 | unsigned long strtoul(const char *str, char **end, int base); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /common/lib/bli.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__BLI_H__ 2 | #define LIB__BLI_H__ 3 | 4 | #if defined (UEFI) 5 | 6 | void init_bli(void); 7 | void bli_on_boot(void); 8 | 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /common/protos/limine.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOS__LIMINE_H__ 2 | #define PROTOS__LIMINE_H__ 3 | 4 | #include 5 | 6 | noreturn void limine_load(char *config, char *cmdline); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /common/protos/chainload.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOS__CHAINLOAD_H__ 2 | #define PROTOS__CHAINLOAD_H__ 3 | 4 | #include 5 | 6 | noreturn void chainload(char *config, char *cmdline); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /common/lib/stb_image.c: -------------------------------------------------------------------------------- 1 | #pragma GCC diagnostic ignored "-Wunused-parameter" 2 | #pragma GCC diagnostic ignored "-Wunused-function" 3 | 4 | #define STB_IMAGE_IMPLEMENTATION 5 | 6 | #include 7 | -------------------------------------------------------------------------------- /common/lib/rand.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__RAND_H__ 2 | #define LIB__RAND_H__ 3 | 4 | #include 5 | 6 | void srand(uint32_t s); 7 | 8 | uint32_t rand32(void); 9 | uint64_t rand64(void); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /common/mm/mtrr.h: -------------------------------------------------------------------------------- 1 | #ifndef MM__MTRR_H__ 2 | #define MM__MTRR_H__ 3 | 4 | #if defined (__x86_64__) || defined (__i386__) 5 | 6 | void mtrr_save(void); 7 | void mtrr_restore(void); 8 | 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /common/crypt/blake2b.h: -------------------------------------------------------------------------------- 1 | #ifndef CRYPT__BLAKE2B_H__ 2 | #define CRYPT__BLAKE2B_H__ 3 | 4 | #include 5 | 6 | #define BLAKE2B_OUT_BYTES 64 7 | 8 | void blake2b(void *out, const void *in, size_t in_len); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /common/efi_thunk.asm_uefi_loongarch64: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .global efi_main 4 | .extern uefi_entry 5 | efi_main: 6 | move $fp, $r0 7 | move $ra, $r0 8 | b uefi_entry 9 | 10 | .section .note.GNU-stack,"",%progbits 11 | -------------------------------------------------------------------------------- /test/e9print.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void e9_putc(char c); 7 | void e9_print(const char *msg); 8 | void e9_puts(const char *msg); 9 | void e9_printf(const char *format, ...); 10 | -------------------------------------------------------------------------------- /common/efi_thunk.asm_uefi_aarch64: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .global efi_main 4 | .extern uefi_entry 5 | 6 | efi_main: 7 | mov x30, xzr 8 | mov x29, xzr 9 | 10 | b uefi_entry 11 | 12 | .section .note.GNU-stack,"",%progbits 13 | -------------------------------------------------------------------------------- /bochsrc: -------------------------------------------------------------------------------- 1 | cpu: count=2, reset_on_triple_fault=0 2 | memory: guest=1024, host=1024 3 | ata0-master: type=disk, path="test.hdd", mode=flat 4 | boot: c 5 | clock: sync=realtime, rtc_sync=1, time0=utc 6 | port_e9_hack: enabled=1 7 | magic_break: enabled=1 8 | -------------------------------------------------------------------------------- /common/drivers/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVERS__SERIAL_H__ 2 | #define DRIVERS__SERIAL_H__ 3 | 4 | #if defined (BIOS) 5 | 6 | #include 7 | 8 | void serial_out(uint8_t b); 9 | int serial_in(void); 10 | 11 | #endif 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /common/efi_thunk.asm_uefi_ia32: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | global efi_main 4 | extern uefi_entry 5 | efi_main: 6 | xor eax, eax 7 | mov [esp], eax 8 | jmp uefi_entry 9 | 10 | section .note.GNU-stack noalloc noexec nowrite progbits 11 | -------------------------------------------------------------------------------- /common/efi_thunk.asm_uefi_x86_64: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | global efi_main 4 | extern uefi_entry 5 | efi_main: 6 | xor eax, eax 7 | mov [rsp], rax 8 | jmp uefi_entry 9 | 10 | section .note.GNU-stack noalloc noexec nowrite progbits 11 | -------------------------------------------------------------------------------- /common/sys/dummy_isr.asm_ia32: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | extern lapic_eoi 4 | 5 | global dummy_isr 6 | dummy_isr: 7 | pusha 8 | call lapic_eoi 9 | popa 10 | iretd 11 | 12 | section .note.GNU-stack noalloc noexec nowrite progbits 13 | -------------------------------------------------------------------------------- /common/sys/pic.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__PIC_H__ 2 | #define SYS__PIC_H__ 3 | 4 | #include 5 | 6 | void pic_eoi(int irq); 7 | void pic_flush(void); 8 | void pic_set_mask(int line, bool status); 9 | void pic_mask_all(void); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /common/fs/iso9660.h: -------------------------------------------------------------------------------- 1 | #ifndef FS__ISO9660_H__ 2 | #define FS__ISO9660_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct file_handle *iso9660_open(struct volume *vol, const char *path); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /common/efi_thunk.asm_uefi_riscv64: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .global efi_main 4 | .extern uefi_entry 5 | efi_main: 6 | .option norelax 7 | mv fp, zero 8 | mv ra, zero 9 | j uefi_entry 10 | 11 | .section .note.GNU-stack,"",%progbits 12 | -------------------------------------------------------------------------------- /common/fs/fat32.h: -------------------------------------------------------------------------------- 1 | #ifndef FS__FAT32_H__ 2 | #define FS__FAT32_H__ 3 | 4 | #include 5 | #include 6 | 7 | char *fat32_get_label(struct volume *part); 8 | 9 | struct file_handle *fat32_open(struct volume *part, const char *path); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /common/lib/uri.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__URI_H__ 2 | #define LIB__URI_H__ 3 | 4 | #include 5 | #include 6 | 7 | bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash); 8 | struct file_handle *uri_open(char *uri); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /common/sys/e820.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__E820_H__ 2 | #define SYS__E820_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | extern struct memmap_entry e820_map[]; 9 | extern size_t e820_entries; 10 | 11 | void init_e820(void); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /common/lib/gterm.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__GTERM_H__ 2 | #define LIB__GTERM_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | bool gterm_init(struct fb_info **ret, size_t *_fbs_count, 9 | char *config, size_t width, size_t height); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /host/hgen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | 5 | LC_ALL=C 6 | export LC_ALL 7 | 8 | cat < 5 | #include 6 | 7 | extern bool verbose; 8 | 9 | void print(const char *fmt, ...); 10 | void vprint(const char *fmt, va_list args); 11 | 12 | #define printv(FMT, ...) do { \ 13 | if (verbose) print(FMT, ##__VA_ARGS__); \ 14 | } while (0) 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /common/drivers/vbe.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVERS__VBE_H__ 2 | #define DRIVERS__VBE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | bool init_vbe(struct fb_info *ret, 10 | uint16_t target_width, uint16_t target_height, uint16_t target_bpp); 11 | 12 | struct fb_info *vbe_get_mode_list(size_t *count); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /test/device_tree.dts: -------------------------------------------------------------------------------- 1 | // Example device tree. 2 | 3 | /dts-v1/; 4 | 5 | / { 6 | soc { 7 | limine_node: limine@deadbeef { 8 | reg = <0xdeadbeef 0x1000>; 9 | label = "KANKER"; 10 | }; 11 | fake_memory: memory@ffff0000 { 12 | reg = <0xffff0000 0xffff>; 13 | label = "This node will be removed by Limine."; 14 | }; 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | trim_trailing_whitespace = true 12 | max_line_length = 80 13 | 14 | [{Makefile,GNUmakefile*}] 15 | indent_style = tab 16 | 17 | [{*.toml,*.yml}] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /common/protos/multiboot.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOS__MULTIBOOT_H__ 2 | #define PROTOS__MULTIBOOT_H__ 3 | 4 | #include 5 | #include 6 | 7 | struct mb_reloc_stub { 8 | char jmp[4]; 9 | uint32_t magic; 10 | uint32_t entry_point; 11 | uint32_t mb_info_target; 12 | }; 13 | 14 | extern symbol multiboot_spinup_32; 15 | extern symbol multiboot_reloc_stub, multiboot_reloc_stub_end; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /decompressor/entry.asm: -------------------------------------------------------------------------------- 1 | extern bss_begin 2 | extern bss_end 3 | extern entry 4 | 5 | section .entry progbits alloc exec nowrite align=16 6 | 7 | global _start 8 | _start: 9 | cld 10 | 11 | ; Zero out .bss 12 | xor al, al 13 | mov edi, bss_begin 14 | mov ecx, bss_end 15 | sub ecx, bss_begin 16 | rep stosb 17 | 18 | jmp entry 19 | 20 | section .note.GNU-stack noalloc noexec nowrite progbits 21 | -------------------------------------------------------------------------------- /common/drivers/gop.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVERS__GOP_H__ 2 | #define DRIVERS__GOP_H__ 3 | 4 | #if defined (UEFI) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void init_gop(struct fb_info **ret, size_t *_fbs_count, 12 | uint64_t target_width, uint64_t target_height, uint16_t target_bpp); 13 | 14 | extern bool gop_force_16; 15 | 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /man/man1/limine.1.in: -------------------------------------------------------------------------------- 1 | .TH LIMINE 1 "version @PACKAGE_VERSION@" "@REGEN_DATE@" 2 | .SH NAME 3 | limine \- Multiplexer to several Limine-related utilities. 4 | .SH SYNOPSIS 5 | .B limine 6 | .RI " " 7 | .SH DESCRIPTION 8 | \fBlimine\fR provides a series of Limine-related utilities condensed in a single executable. 9 | .SH BUGS 10 | Please report bugs via 11 | .IR @PACKAGE_BUGREPORT@ . 12 | .SH HOMEPAGE 13 | .I @PACKAGE_URL@ 14 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | # Configuration for https://github.com/crate-ci/typos 2 | 3 | [files] 4 | extend-exclude = [ 5 | # Typos already uses .gitignore. These excludes are additional: 6 | ] 7 | 8 | [default.extend-words] 9 | hda = "hda" 10 | Nd = "Nd" 11 | sie = "sie" 12 | SIE = "SIE" 13 | ist = "ist" 14 | rela = "rela" 15 | RELA = "RELA" 16 | guid = "guid" 17 | GUID = "GUID" 18 | pn = "pn" 19 | hd = "hd" 20 | htpt = "htpt" 21 | 22 | [default.extend-identifiers] 23 | -------------------------------------------------------------------------------- /common/lib/fdt.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__FDT_H__ 2 | #define LIB__FDT_H__ 3 | 4 | #if !defined(__x86_64__) && !defined(__i386__) 5 | 6 | #include 7 | #include 8 | 9 | int fdt_set_chosen_string(void *fdt, const char *name, const char *value); 10 | int fdt_set_chosen_uint64(void *fdt, const char *name, uint64_t value); 11 | int fdt_set_chosen_uint32(void *fdt, const char *name, uint32_t value); 12 | 13 | #endif 14 | 15 | #endif // LIB__FDT_H__ 16 | -------------------------------------------------------------------------------- /common/sys/sbi.asm_riscv64: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .global sbicall 4 | sbicall: 5 | .option norelax 6 | mv t0, a0 7 | mv t1, a1 8 | mv a0, a2 9 | mv a1, a3 10 | mv a2, a4 11 | mv a3, a5 12 | mv a4, a6 13 | mv a5, a7 14 | mv a7, t0 15 | mv a6, t1 16 | ecall 17 | ret 18 | 19 | .section .note.GNU-stack,"",%progbits 20 | -------------------------------------------------------------------------------- /common/menu.h: -------------------------------------------------------------------------------- 1 | #ifndef MENU_H__ 2 | #define MENU_H__ 3 | 4 | #include 5 | #include 6 | 7 | #if defined(UEFI) 8 | bool reboot_to_fw_ui_supported(void); 9 | noreturn void reboot_to_fw_ui(void); 10 | #endif 11 | 12 | noreturn void menu(bool first_run); 13 | 14 | noreturn void boot(char *config); 15 | 16 | char *config_entry_editor(const char *title, const char *orig_entry); 17 | 18 | extern bool booting_from_editor; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /common/menu_thunk.asm_loongarch64: -------------------------------------------------------------------------------- 1 | .section .data 2 | 3 | .align 3 4 | stack_at_first_entry: 5 | .quad 0 6 | 7 | .section .text 8 | 9 | .global menu 10 | .extern _menu 11 | 12 | menu: 13 | la $t0, stack_at_first_entry 14 | ld.d $t1, $t0, 0 15 | beqz $t1, 1f 16 | move $sp, $t1 17 | b 2f 18 | 1: 19 | st.d $sp, $t0, 0 20 | 2: 21 | move $fp, $r0 22 | move $ra, $r0 23 | b _menu 24 | 25 | .section .note.GNU-stack,"",%progbits 26 | -------------------------------------------------------------------------------- /common/lib/pe.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__PE_H__ 2 | #define LIB__PE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int pe_bits(uint8_t *image); 9 | 10 | bool pe64_load(uint8_t *image, 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); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /common/menu_thunk.asm_x86_64: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | stack_at_first_entry: 4 | dq 0 5 | 6 | section .text 7 | 8 | global menu 9 | extern _menu 10 | menu: 11 | xor eax, eax 12 | cmp [rel stack_at_first_entry], rax 13 | jne .L1 14 | mov [rel stack_at_first_entry], rsp 15 | jmp .L2 16 | .L1: 17 | mov rsp, [rel stack_at_first_entry] 18 | .L2: 19 | push 0 20 | push 0 21 | jmp _menu 22 | 23 | section .note.GNU-stack noalloc noexec nowrite progbits 24 | -------------------------------------------------------------------------------- /.forgejo/workflows/pr_branch_check.yml: -------------------------------------------------------------------------------- 1 | name: Check that the PR is targetting trunk 2 | 3 | on: [ pull_request ] 4 | 5 | jobs: 6 | pr_branch_check: 7 | name: Check that the PR is targetting trunk 8 | runs-on: codeberg-tiny 9 | steps: 10 | - name: Check that the PR is targetting trunk 11 | if: ${{ forge.base_ref != 'trunk' }} 12 | run: | 13 | set -e 14 | echo "The PR is not targetting the trunk branch, please fix that." 15 | false 16 | -------------------------------------------------------------------------------- /common/lib/elsewhere.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__ELSEWHERE_H__ 2 | #define LIB__ELSEWHERE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct elsewhere_range { 9 | uint64_t elsewhere; 10 | uint64_t target; 11 | uint64_t length; 12 | }; 13 | 14 | bool elsewhere_append( 15 | bool flexible_target, 16 | struct elsewhere_range *ranges, uint64_t *ranges_count, 17 | void *elsewhere, uint64_t *target, size_t t_length); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /common/menu_thunk.asm_ia32: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | global menu 4 | extern _menu 5 | menu: 6 | pop eax 7 | call .L1 8 | .L1: 9 | pop eax 10 | add eax, .L3 - .L1 11 | cmp dword [eax], 0 12 | jne .L2 13 | mov [eax], esp 14 | jmp .L4 15 | 16 | .L2: 17 | mov edi, [esp] 18 | mov esp, [eax] 19 | push edi 20 | jmp .L4 21 | 22 | .L3: 23 | dq 0 24 | 25 | .L4: 26 | push 0 27 | jmp _menu 28 | 29 | section .note.GNU-stack noalloc noexec nowrite progbits 30 | -------------------------------------------------------------------------------- /LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt: -------------------------------------------------------------------------------- 1 | * Redistribution and use in source and binary forms, with or without 2 | * modification, are permitted provided that the following conditions 3 | * are met: 4 | * 1. Redistributions of source code must retain the above copyright 5 | * notice and this list of conditions, without modification. 6 | * 2. The name of the author may not be used to endorse or promote products 7 | * derived from this software without specific prior written permission. 8 | -------------------------------------------------------------------------------- /common/sys/dummy_isr.asm_x86_64: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | extern lapic_eoi 4 | 5 | global dummy_isr 6 | dummy_isr: 7 | push rax 8 | push rcx 9 | push rdx 10 | push rsi 11 | push rdi 12 | push r8 13 | push r9 14 | push r10 15 | push r11 16 | call lapic_eoi 17 | pop r11 18 | pop r10 19 | pop r9 20 | pop r8 21 | pop rdi 22 | pop rsi 23 | pop rdx 24 | pop rcx 25 | pop rax 26 | iretq 27 | 28 | section .note.GNU-stack noalloc noexec nowrite progbits 29 | -------------------------------------------------------------------------------- /common/menu_thunk.asm_aarch64: -------------------------------------------------------------------------------- 1 | .section .data 2 | 3 | .align 3 4 | stack_at_first_entry: 5 | .quad 0 6 | 7 | .section .text 8 | 9 | .global menu 10 | .extern _menu 11 | 12 | menu: 13 | adrp x8, stack_at_first_entry 14 | ldr x9, [x8, :lo12:stack_at_first_entry] 15 | cbz x9, 1f 16 | mov sp, x9 17 | b 2f 18 | 1: 19 | mov x9, sp 20 | str x9, [x8, :lo12:stack_at_first_entry] 21 | 2: 22 | mov x30, xzr 23 | mov x29, xzr 24 | 25 | b _menu 26 | 27 | .section .note.GNU-stack,"",%progbits 28 | -------------------------------------------------------------------------------- /common/menu_thunk.asm_riscv64: -------------------------------------------------------------------------------- 1 | .section .data 2 | 3 | .p2align 3 4 | stack_at_first_entry: 5 | .8byte 0 6 | 7 | .section .text 8 | 9 | .global menu 10 | .extern _menu 11 | 12 | menu: 13 | .option norelax 14 | lla t0, stack_at_first_entry 15 | ld t1, (t0) 16 | beqz t1, 1f 17 | mv sp, t1 18 | j 2f 19 | 1: sd sp, (t0) 20 | 2: mv fp, zero 21 | mv ra, zero 22 | j _menu 23 | 24 | .section .note.GNU-stack,"",%progbits 25 | -------------------------------------------------------------------------------- /common/lib/guid.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__GUID_H__ 2 | #define LIB__GUID_H__ 3 | 4 | #include 5 | #include 6 | 7 | struct guid { 8 | uint32_t a; 9 | uint16_t b; 10 | uint16_t c; 11 | uint8_t d[8]; 12 | }; 13 | 14 | bool is_valid_guid(const char *s); 15 | bool string_to_guid_be(struct guid *guid, const char *s); 16 | bool string_to_guid_mixed(struct guid *guid, const char *s); 17 | // Assumption: s must be big enough to fit 36 characters and a null byte 18 | void guid_to_string(const struct guid *guid, char *s); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /common/protos/multiboot_32.asm_x86: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | global multiboot_spinup_32: 5 | multiboot_spinup_32: 6 | sub esp, 6 7 | mov word [esp-2], 0x3ff 8 | mov dword [esp], 0 9 | lidt [esp-2] 10 | add esp, 6 11 | 12 | add esp, 4 ; return address 13 | 14 | pop ebx ; reloc_stub 15 | pop esi ; magic 16 | pop edi ; protocol_info 17 | pop ecx ; entry_point 18 | pop eax ; elf_ranges 19 | pop edx ; elf_ranges_count 20 | 21 | jmp ebx 22 | 23 | section .note.GNU-stack noalloc noexec nowrite progbits 24 | -------------------------------------------------------------------------------- /common/drivers/disk.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVERS__DISK_H__ 2 | #define DRIVERS__DISK_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if defined (UEFI) 10 | 11 | #include 12 | 13 | struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle); 14 | 15 | #endif 16 | 17 | enum { 18 | DISK_SUCCESS, 19 | DISK_NO_MEDIA, 20 | DISK_FAILURE 21 | }; 22 | 23 | void disk_create_index(void); 24 | int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /common/sys/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__GDT_H__ 2 | #define SYS__GDT_H__ 3 | 4 | #include 5 | 6 | struct gdtr { 7 | uint16_t limit; 8 | #if defined (__x86_64__) 9 | uint64_t ptr; 10 | #elif defined (__i386__) 11 | uint32_t ptr; 12 | uint32_t ptr_hi; 13 | #endif 14 | } __attribute__((packed)); 15 | 16 | struct gdt_desc { 17 | uint16_t limit; 18 | uint16_t base_low; 19 | uint8_t base_mid; 20 | uint8_t access; 21 | uint8_t granularity; 22 | uint8_t base_hi; 23 | } __attribute__((packed)); 24 | 25 | extern struct gdtr gdt; 26 | 27 | void init_gdt(void); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /test/multiboot.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS { 4 | . = 1M; 5 | 6 | .boot : 7 | { 8 | /* Ensure that the multiboot header is at the beginning! */ 9 | *(.multiboot_header) 10 | } 11 | 12 | . = ALIGN(4K); 13 | .text : 14 | { 15 | *(.text .text.*) 16 | } 17 | 18 | . = ALIGN(4K); 19 | .rodata : 20 | { 21 | *(.rodata .rodata.*) 22 | } 23 | 24 | . = ALIGN(4K); 25 | .data : 26 | { 27 | *(.data .data.*) 28 | } 29 | 30 | . = ALIGN(4K); 31 | .bss : 32 | { 33 | *(.bss .bss.*) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/multiboot2.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS { 4 | . = 1M; 5 | 6 | .boot : 7 | { 8 | /* Ensure that the multiboot header is at the beginning! */ 9 | *(.multiboot_header) 10 | } 11 | 12 | . = ALIGN(4K); 13 | .text : 14 | { 15 | *(.text .text.*) 16 | } 17 | 18 | . = ALIGN(4K); 19 | .rodata : 20 | { 21 | *(.rodata .rodata.*) 22 | } 23 | 24 | . = ALIGN(4K); 25 | .data : 26 | { 27 | *(.data .data.*) 28 | } 29 | 30 | . = ALIGN(4K); 31 | .bss : 32 | { 33 | *(.bss .bss.*) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/entry_asm.s2.asm_bios_ia32: -------------------------------------------------------------------------------- 1 | extern bss_begin 2 | extern bss_end 3 | extern entry 4 | extern gdt 5 | 6 | section .entry progbits alloc exec nowrite align=16 7 | 8 | global _start 9 | _start: 10 | cld 11 | 12 | ; Zero out .bss 13 | xor al, al 14 | mov edi, bss_begin 15 | mov ecx, bss_end 16 | sub ecx, bss_begin 17 | rep stosb 18 | 19 | lgdt [gdt] 20 | jmp 0x18:.reload_cs 21 | .reload_cs: 22 | mov eax, 0x20 23 | mov ds, ax 24 | mov es, ax 25 | mov fs, ax 26 | mov gs, ax 27 | mov ss, ax 28 | 29 | jmp entry 30 | 31 | section .note.GNU-stack noalloc noexec nowrite progbits 32 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | LC_ALL=C 4 | export LC_ALL 5 | 6 | srcdir="$(dirname "$0")" 7 | test -z "$srcdir" && srcdir=. 8 | 9 | cd "$srcdir" 10 | 11 | if test -f version; then 12 | printf '%s' "$(cat version)" 13 | exit 0 14 | fi 15 | 16 | if ! test -d .git || ! git log -n1 --pretty='%h' >/dev/null 2>&1; then 17 | printf 'UNVERSIONED' 18 | exit 0 19 | fi 20 | 21 | tmpfile="$(mktemp)" 22 | 23 | if ! git describe --exact-match --tags $(git log -n1 --pretty='%h') >"$tmpfile" 2>/dev/null; then 24 | echo g$(git log -n1 --pretty='%h') >"$tmpfile" 25 | fi 26 | 27 | printf '%s' "$(sed 's/^v//g' <"$tmpfile")" 28 | 29 | rm -f "$tmpfile" 30 | -------------------------------------------------------------------------------- /stage1/gdt.asm: -------------------------------------------------------------------------------- 1 | gdt: 2 | dw .size - 1 + 8 ; GDT size 3 | dd .start - 8 ; GDT start address 4 | 5 | .start: 6 | ; 32-bit code 7 | dw 0xffff ; Limit 8 | dw 0x0000 ; Base (low 16 bits) 9 | db 0x00 ; Base (mid 8 bits) 10 | db 10011011b ; Access 11 | db 11001111b ; Granularity 12 | db 0x00 ; Base (high 8 bits) 13 | 14 | ; 32-bit data 15 | dw 0xffff ; Limit 16 | dw 0x0000 ; Base (low 16 bits) 17 | db 0x00 ; Base (mid 8 bits) 18 | db 10010011b ; Access 19 | db 11001111b ; Granularity 20 | db 0x00 ; Base (high 8 bits) 21 | 22 | .end: 23 | 24 | .size: equ .end - .start 25 | -------------------------------------------------------------------------------- /common/lib/spinup.asm_bios_ia32: -------------------------------------------------------------------------------- 1 | section .rodata 2 | 3 | invalid_idt: 4 | dd 0, 0 5 | 6 | section .text 7 | 8 | extern flush_irqs 9 | 10 | global common_spinup 11 | bits 32 12 | common_spinup: 13 | cli 14 | 15 | lidt [invalid_idt] 16 | 17 | call flush_irqs 18 | 19 | xor eax, eax 20 | lldt ax 21 | 22 | ; We don't need the return address 23 | add esp, 4 24 | 25 | ; Get function address 26 | pop edi 27 | 28 | ; We don't need the argument count 29 | add esp, 4 30 | 31 | mov eax, 0x00000011 32 | mov cr0, eax 33 | 34 | xor eax, eax 35 | mov cr4, eax 36 | 37 | call edi 38 | 39 | section .note.GNU-stack noalloc noexec nowrite progbits 40 | -------------------------------------------------------------------------------- /.forgejo/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | name: QA 2 | 3 | on: [ merge_group, push, pull_request ] 4 | 5 | jobs: 6 | spellcheck: 7 | name: Spellcheck 8 | runs-on: codeberg-tiny 9 | container: archlinux:latest 10 | steps: 11 | - name: Create resolv.conf 12 | run: | 13 | set -ex 14 | echo 'nameserver 8.8.8.8' >/etc/resolv.conf 15 | echo 'nameserver 8.8.4.4' >>/etc/resolv.conf 16 | 17 | - name: Install dependencies 18 | run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs git typos 19 | 20 | - name: Checkout code 21 | uses: https://code.forgejo.org/actions/checkout@v6 22 | 23 | - name: Run spellchecker 24 | run: typos 25 | -------------------------------------------------------------------------------- /decompressor/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf32-i386) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text PT_LOAD FLAGS(0x05); 7 | rodata PT_LOAD FLAGS(0x04); 8 | data PT_LOAD FLAGS(0x06); 9 | } 10 | 11 | SECTIONS 12 | { 13 | . = 0x70000; 14 | 15 | .text : { 16 | *(.entry) 17 | *(.text .text.*) 18 | } :text 19 | 20 | .rodata : { 21 | *(.rodata .rodata.*) 22 | } :rodata 23 | 24 | .data : { 25 | *(.data .data.*) 26 | } :data 27 | 28 | .bss : { 29 | bss_begin = .; 30 | *(.bss .bss.*) 31 | *(COMMON) 32 | bss_end = .; 33 | } :data 34 | 35 | /DISCARD/ : { 36 | *(.eh_frame*) 37 | *(.note .note.*) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /common/drivers/vga_textmode.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVERS__VGA_TEXTMODE_H__ 2 | #define DRIVERS__VGA_TEXTMODE_H__ 3 | 4 | #if defined (BIOS) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define VD_COLS (80 * 2) 12 | #define VD_ROWS 25 13 | 14 | struct textmode_context { 15 | struct flanterm_context term; 16 | 17 | volatile uint8_t *video_mem; 18 | 19 | uint8_t *back_buffer; 20 | uint8_t *front_buffer; 21 | 22 | size_t cursor_offset; 23 | size_t old_cursor_offset; 24 | bool cursor_status; 25 | uint8_t text_palette; 26 | 27 | uint8_t saved_state_text_palette; 28 | size_t saved_state_cursor_offset; 29 | }; 30 | 31 | void vga_textmode_init(bool managed); 32 | 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /stage1/cd/read_2k_sectors.asm: -------------------------------------------------------------------------------- 1 | bits 16 2 | 3 | ; --- Read sectors from disk --- 4 | ; IN: 5 | ; eax <- start LBA (2k sectors) 6 | ; cx <- number of 2k sectors 7 | ; dl <- drive number 8 | ; ds <- ZERO 9 | ; bx <- buffer offset 10 | ; es <- buffer segment 11 | 12 | ; OUT: 13 | ; Carry if error 14 | 15 | align 8 16 | dapack: 17 | dapack_size: db 0x10 18 | dapack_null: db 0x00 19 | dapack_nblocks: dw 0 20 | dapack_offset: dw 0 21 | dapack_segment: dw 0 22 | dapack_LBA: dq 0 23 | 24 | read_2k_sectors: 25 | pusha 26 | mov dword [dapack_LBA], eax 27 | mov word [dapack_nblocks], cx 28 | mov word [dapack_offset], bx 29 | mov word [dapack_segment], es 30 | 31 | mov ah, 0x42 32 | mov si, dapack 33 | int 0x13 34 | popa 35 | ret 36 | -------------------------------------------------------------------------------- /decompressor/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | noreturn void entry(uint8_t *compressed_stage2, size_t stage2_size, uint8_t boot_drive, int pxe) { 7 | // The decompressor should decompress compressed_stage2 to address 0xf000. 8 | uint8_t *dest = (uint8_t *)0xf000; 9 | 10 | tinf_gzip_uncompress(dest, compressed_stage2, stage2_size); 11 | 12 | asm volatile ( 13 | "movl $0xf000, %%esp\n\t" 14 | "xorl %%ebp, %%ebp\n\t" 15 | "pushl %1\n\t" 16 | "pushl %0\n\t" 17 | "pushl $0\n\t" 18 | "pushl $0xf000\n\t" 19 | "ret\n\t" 20 | : 21 | : "r" ((uint32_t)boot_drive), "r" (pxe) 22 | : "memory" 23 | ); 24 | 25 | __builtin_unreachable(); 26 | } 27 | -------------------------------------------------------------------------------- /common/libc-compat/string.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBC_COMPAT__STRING_H__ 2 | #define LIBC_COMPAT__STRING_H__ 3 | 4 | #include 5 | 6 | void *memset(void *, int, size_t); 7 | void *memcpy(void *, const void *, size_t); 8 | int memcmp(const void *, const void *, size_t); 9 | void *memmove(void *, const void *, size_t); 10 | void *memchr(const void *, int, size_t); 11 | 12 | char *strcpy(char *, const char *); 13 | char *strncpy(char *, const char *, size_t); 14 | char *strchr(const char *, int); 15 | char *strrchr(const char *, int); 16 | size_t strlen(const char *); 17 | size_t strnlen(const char *, size_t); 18 | int strcmp(const char *, const char *); 19 | int strcasecmp(const char *, const char *); 20 | int strncmp(const char *, const char *, size_t); 21 | int strncasecmp(const char *, const char *, size_t); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /common/sys/int_thunks.s2.asm_bios_ia32: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | extern except 4 | 5 | %macro raise_exception 1 6 | align 16 7 | raise_exception_%1: 8 | cld 9 | %if %1 == 8 || %1 == 10 || %1 == 11 || %1 == 12 || %1 == 13 || %1 == 14 || %1 == 17 || %1 == 30 10 | pop eax 11 | %else 12 | xor eax, eax 13 | %endif 14 | push ebp 15 | mov ebp, esp 16 | push eax 17 | push %1 18 | call except 19 | %endmacro 20 | 21 | %assign i 0 22 | %rep 32 23 | raise_exception i 24 | %assign i i+1 25 | %endrep 26 | 27 | section .rodata 28 | 29 | %macro raise_exception_getaddr 1 30 | dd raise_exception_%1 31 | %endmacro 32 | 33 | global exceptions 34 | exceptions: 35 | %assign i 0 36 | %rep 32 37 | raise_exception_getaddr i 38 | %assign i i+1 39 | %endrep 40 | 41 | section .note.GNU-stack noalloc noexec nowrite progbits 42 | -------------------------------------------------------------------------------- /common/lib/getchar.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__GETCHAR_H__ 2 | #define LIB__GETCHAR_H__ 3 | 4 | #include 5 | 6 | #define GETCHAR_CURSOR_LEFT (-10) 7 | #define GETCHAR_CURSOR_RIGHT (-11) 8 | #define GETCHAR_CURSOR_UP (-12) 9 | #define GETCHAR_CURSOR_DOWN (-13) 10 | #define GETCHAR_DELETE (-14) 11 | #define GETCHAR_END (-15) 12 | #define GETCHAR_HOME (-16) 13 | #define GETCHAR_PGUP (-17) 14 | #define GETCHAR_PGDOWN (-18) 15 | #define GETCHAR_F10 (-19) 16 | #define GETCHAR_ESCAPE (-20) 17 | 18 | #if defined (BIOS) 19 | # define GETCHAR_RCTRL 0x4 20 | # define GETCHAR_LCTRL GETCHAR_RCTRL 21 | #elif defined (UEFI) 22 | # define GETCHAR_RCTRL EFI_RIGHT_CONTROL_PRESSED 23 | # define GETCHAR_LCTRL EFI_LEFT_CONTROL_PRESSED 24 | #endif 25 | 26 | int getchar(void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /common/lib/real.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__REAL_H__ 2 | #define LIB__REAL_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define rm_seg(x) ((uint16_t)(((int)x & 0xffff0) >> 4)) 8 | #define rm_off(x) ((uint16_t)(((int)x & 0x0000f) >> 0)) 9 | 10 | #define rm_desegment(seg, off) (((uint32_t)(seg) << 4) + (uint32_t)(off)) 11 | 12 | #define EFLAGS_CF (1 << 0) 13 | #define EFLAGS_ZF (1 << 6) 14 | 15 | struct rm_regs { 16 | uint16_t gs; 17 | uint16_t fs; 18 | uint16_t es; 19 | uint16_t ds; 20 | uint32_t eflags; 21 | uint32_t ebp; 22 | uint32_t edi; 23 | uint32_t esi; 24 | uint32_t edx; 25 | uint32_t ecx; 26 | uint32_t ebx; 27 | uint32_t eax; 28 | } __attribute__((packed)); 29 | 30 | void rm_int(uint8_t int_no, struct rm_regs *out_regs, struct rm_regs *in_regs); 31 | 32 | noreturn void rm_hcf(void); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /test/multiboot_trampoline.asm: -------------------------------------------------------------------------------- 1 | %define MULTIBOOT_HEADER_MAGIC 0x1badb002 2 | 3 | ; Flags: 4 | ; 5 | ; bit 2: request framebuffer 6 | %define MULTIBOOT_HEADER_FLAGS (1 << 2) 7 | 8 | extern multiboot_main 9 | 10 | global _start 11 | 12 | section .multiboot_header 13 | 14 | align 4 15 | header_start: 16 | dd MULTIBOOT_HEADER_MAGIC ; Magic number (multiboot 1) 17 | dd MULTIBOOT_HEADER_FLAGS ; Flags 18 | dd -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) ; Checksum 19 | header_end: 20 | 21 | section .text 22 | bits 32 23 | 24 | _start: 25 | cli 26 | 27 | mov esp, stack_top 28 | 29 | push ebx 30 | push eax 31 | 32 | call multiboot_main ; Jump to our multiboot test kernel 33 | 34 | section .bss 35 | stack_bottom: 36 | resb 4096 * 16 37 | stack_top: 38 | -------------------------------------------------------------------------------- /common/lib/image.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__IMAGE_H__ 2 | #define LIB__IMAGE_H__ 3 | 4 | #include 5 | #include 6 | 7 | struct image { 8 | size_t x_size; 9 | size_t y_size; 10 | int type; 11 | uint8_t *img; 12 | int bpp; 13 | int pitch; 14 | size_t img_width; // x_size = scaled size, img_width = bitmap size 15 | size_t img_height; 16 | size_t x_displacement; 17 | size_t y_displacement; 18 | uint32_t back_colour; 19 | }; 20 | 21 | enum { 22 | IMAGE_TILED, 23 | IMAGE_CENTERED, 24 | IMAGE_STRETCHED 25 | }; 26 | 27 | void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour); 28 | void image_make_stretched(struct image *image, int new_x_size, int new_y_size); 29 | struct image *image_open(struct file_handle *file); 30 | void image_close(struct image *image); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /common/sys/lapic.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__APIC_H__ 2 | #define SYS__APIC_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define LAPIC_REG_ICR0 0x300 9 | #define LAPIC_REG_ICR1 0x310 10 | #define LAPIC_REG_SPURIOUS 0x0f0 11 | #define LAPIC_REG_EOI 0x0b0 12 | #define LAPIC_REG_ID 0x020 13 | 14 | bool lapic_check(void); 15 | void lapic_eoi(void); 16 | uint32_t lapic_read(uint32_t reg); 17 | void lapic_write(uint32_t reg, uint32_t data); 18 | 19 | bool x2apic_check(void); 20 | bool x2apic_enable(void); 21 | uint64_t x2apic_read(uint32_t reg); 22 | void x2apic_write(uint32_t reg, uint64_t data); 23 | 24 | void init_io_apics(void); 25 | uint32_t io_apic_read(size_t io_apic, uint32_t reg); 26 | void io_apic_write(size_t io_apic, uint32_t reg, uint32_t value); 27 | uint32_t io_apic_gsi_count(size_t io_apic); 28 | void io_apic_mask_all(void); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /common/lib/config.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__CONFIG_H__ 2 | #define LIB__CONFIG_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | extern bool config_ready; 9 | extern bool bad_config; 10 | 11 | struct menu_entry { 12 | char name[64]; 13 | char *comment; 14 | struct menu_entry *parent; 15 | struct menu_entry *sub; 16 | bool expanded; 17 | char *body; 18 | struct menu_entry *next; 19 | }; 20 | 21 | struct conf_tuple { 22 | char *value1; 23 | char *value2; 24 | }; 25 | 26 | extern struct menu_entry *menu_tree; 27 | 28 | int init_config_disk(struct volume *part); 29 | bool init_config_smbios(void); 30 | int init_config(size_t config_size); 31 | 32 | char *config_get_value(const char *config, size_t index, const char *key); 33 | struct conf_tuple config_get_tuple(const char *config, size_t index, 34 | const char *key1, const char *key2); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /common/lib/mem.asm_x86_64: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | global memcpy 4 | memcpy: 5 | mov rcx, rdx 6 | mov rax, rdi 7 | rep movsb 8 | ret 9 | 10 | global memset 11 | memset: 12 | push rdi 13 | mov rax, rsi 14 | mov rcx, rdx 15 | rep stosb 16 | pop rax 17 | ret 18 | 19 | global memmove 20 | memmove: 21 | mov rcx, rdx 22 | mov rax, rdi 23 | 24 | cmp rdi, rsi 25 | ja .copy_backwards 26 | 27 | rep movsb 28 | jmp .done 29 | 30 | .copy_backwards: 31 | lea rdi, [rdi+rcx-1] 32 | lea rsi, [rsi+rcx-1] 33 | std 34 | rep movsb 35 | cld 36 | 37 | .done: 38 | ret 39 | 40 | global memcmp 41 | memcmp: 42 | mov rcx, rdx 43 | repe cmpsb 44 | je .equal 45 | 46 | mov al, byte [rdi-1] 47 | sub al, byte [rsi-1] 48 | movsx rax, al 49 | jmp .done 50 | 51 | .equal: 52 | xor eax, eax 53 | 54 | .done: 55 | ret 56 | 57 | section .note.GNU-stack noalloc noexec nowrite progbits 58 | -------------------------------------------------------------------------------- /common/sys/idt.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if defined (BIOS) 7 | 8 | static struct idt_entry idt_entries[32]; 9 | 10 | __attribute__((section(".realmode"))) 11 | struct idtr idt = { 12 | sizeof(idt_entries) - 1, 13 | (uintptr_t)idt_entries 14 | }; 15 | 16 | static void register_interrupt_handler(size_t vec, void *handler, uint8_t type) { 17 | uint32_t p = (uint32_t)handler; 18 | 19 | idt_entries[vec].offset_lo = (uint16_t)p; 20 | idt_entries[vec].selector = 0x18; 21 | idt_entries[vec].unused = 0; 22 | idt_entries[vec].type_attr = type; 23 | idt_entries[vec].offset_hi = (uint16_t)(p >> 16); 24 | } 25 | 26 | extern void *exceptions[]; 27 | 28 | void init_idt(void) { 29 | for (size_t i = 0; i < SIZEOF_ARRAY(idt_entries); i++) 30 | register_interrupt_handler(i, exceptions[i], 0x8e); 31 | 32 | asm volatile ("lidt %0" :: "m"(idt) : "memory"); 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /common/sys/e820.s2.c: -------------------------------------------------------------------------------- 1 | #if defined (BIOS) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAX_E820_ENTRIES 256 12 | 13 | struct memmap_entry e820_map[MAX_E820_ENTRIES]; 14 | size_t e820_entries = 0; 15 | 16 | void init_e820(void) { 17 | struct rm_regs r = {0}; 18 | 19 | for (size_t i = 0; i < MAX_E820_ENTRIES; i++) { 20 | struct memmap_entry entry; 21 | 22 | r.eax = 0xe820; 23 | r.ecx = 24; 24 | r.edx = 0x534d4150; 25 | r.edi = (uint32_t)&entry; 26 | rm_int(0x15, &r, &r); 27 | 28 | if (r.eflags & EFLAGS_CF) { 29 | e820_entries = i; 30 | return; 31 | } 32 | 33 | e820_map[i] = entry; 34 | 35 | if (!r.ebx) { 36 | e820_entries = ++i; 37 | return; 38 | } 39 | } 40 | 41 | panic(false, "Too many E820 entries!"); 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /common/sys/pic.c: -------------------------------------------------------------------------------- 1 | #if defined (__x86_64__) || defined (__i386__) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void pic_eoi(int irq) { 11 | if (irq >= 8) { 12 | outb(0xa0, 0x20); 13 | } 14 | 15 | outb(0x20, 0x20); 16 | } 17 | 18 | // Flush all potentially pending IRQs 19 | void pic_flush(void) { 20 | for (int i = 0; i < 16; i++) 21 | pic_eoi(i); 22 | } 23 | 24 | void pic_set_mask(int line, bool status) { 25 | uint16_t port; 26 | uint8_t value; 27 | 28 | if (line < 8) { 29 | port = 0x21; 30 | } else { 31 | port = 0xa1; 32 | line -= 8; 33 | } 34 | 35 | if (!status) 36 | value = inb(port) & ~((uint8_t)1 << line); 37 | else 38 | value = inb(port) | ((uint8_t)1 << line); 39 | 40 | outb(port, value); 41 | } 42 | 43 | void pic_mask_all(void) { 44 | outb(0xa1, 0xff); 45 | outb(0x21, 0xff); 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /common/lib/libc.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__LIBC_H__ 2 | #define LIB__LIBC_H__ 3 | 4 | #include 5 | #include 6 | 7 | bool isprint(int c); 8 | bool isspace(int c); 9 | bool isalpha(int c); 10 | bool isdigit(int c); 11 | 12 | int toupper(int c); 13 | int tolower(int c); 14 | 15 | int abs(int i); 16 | 17 | void *memset(void *, int, size_t); 18 | void *memcpy(void *, const void *, size_t); 19 | int memcmp(const void *, const void *, size_t); 20 | void *memmove(void *, const void *, size_t); 21 | void *memchr(const void *, int, size_t); 22 | 23 | char *strcpy(char *, const char *); 24 | char *strncpy(char *, const char *, size_t); 25 | char *strchr(const char *, int); 26 | char *strrchr(const char *, int); 27 | size_t strlen(const char *); 28 | size_t strnlen(const char *, size_t); 29 | int strcmp(const char *, const char *); 30 | int strcasecmp(const char *, const char *); 31 | int strncmp(const char *, const char *, size_t); 32 | int strncasecmp(const char *, const char *, size_t); 33 | int inet_pton(const char *src, void *dst); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /common/pxe/tftp.h: -------------------------------------------------------------------------------- 1 | #ifndef TFTP_H 2 | #define TFTP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined (BIOS) 9 | 10 | #define UNDI_GET_INFORMATION 0xC 11 | 12 | #define TFTP_OPEN 0x0020 13 | struct pxenv_open { 14 | uint16_t status; 15 | uint32_t sip; 16 | uint32_t gip; 17 | uint8_t name[128]; 18 | uint16_t port; 19 | uint16_t packet_size; 20 | } __attribute__((packed)); 21 | 22 | #define TFTP_READ 0x22 23 | struct pxenv_read { 24 | uint16_t status; 25 | uint16_t pn; 26 | uint16_t bsize; 27 | uint16_t boff; 28 | uint16_t bseg; 29 | } __attribute__((packed)); 30 | 31 | #define TFTP_GET_FILE_SIZE 0x25 32 | struct pxenv_get_file_size { 33 | uint16_t status; 34 | uint32_t sip; 35 | uint32_t gip; 36 | uint8_t name[128]; 37 | uint32_t file_size; 38 | } __attribute__((packed)); 39 | 40 | #define TFTP_CLOSE 0x21 41 | 42 | #endif 43 | 44 | struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /common/protos/linux_64.asm_uefi_x86_64: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | align 16 4 | linux_gdt64: 5 | dq 0 6 | 7 | dq 0 8 | 9 | dw 0xffff 10 | dw 0x0000 11 | db 0x00 12 | db 10011011b 13 | db 10101111b 14 | db 0x00 15 | 16 | dw 0xffff 17 | dw 0x0000 18 | db 0x00 19 | db 10010011b 20 | db 10001111b 21 | db 0x00 22 | 23 | .end: 24 | 25 | align 16 26 | linux_gdt64_ptr: 27 | dw (linux_gdt64.end - linux_gdt64) - 1 28 | dq linux_gdt64 29 | 30 | section .text 31 | 32 | bits 64 33 | 34 | global linux_spinup64 35 | linux_spinup64: 36 | cli 37 | cld 38 | 39 | lgdt [rel linux_gdt64_ptr] 40 | 41 | lea rbx, [rel .fj] 42 | push 0x10 43 | push rbx 44 | retfq 45 | 46 | .fj: 47 | mov eax, 0x18 48 | mov ds, eax 49 | mov es, eax 50 | mov fs, eax 51 | mov gs, eax 52 | mov ss, eax 53 | 54 | mov rax, rdi 55 | 56 | xor ebp, ebp 57 | xor edi, edi 58 | xor ebx, ebx 59 | 60 | jmp rax 61 | 62 | section .note.GNU-stack noalloc noexec nowrite progbits 63 | -------------------------------------------------------------------------------- /common/sys/sbi.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__SBI_H__ 2 | #define SYS__SBI_H__ 3 | 4 | #include 5 | #include 6 | 7 | struct sbiret { 8 | long error; 9 | long value; 10 | }; 11 | 12 | #define SBI_SUCCESS ((long)0) 13 | #define SBI_ERR_FAILED ((long)-1) 14 | #define SBI_ERR_NOT_SUPPORTED ((long)-2) 15 | #define SBI_ERR_INVALID_PARAM ((long)-3) 16 | #define SBI_ERR_DENIED ((long)-4) 17 | #define SBI_ERR_INVALID_ADDRESS ((long)-5) 18 | #define SBI_ERR_ALREADY_AVAILABLE ((long)-6) 19 | #define SBI_ERR_ALREADY_STARTED ((long)-7) 20 | #define SBI_ERR_ALREADY_STOPPED ((long)-8) 21 | 22 | extern struct sbiret sbicall(int eid, int fid, ...); 23 | 24 | #define SBI_EID_HSM 0x48534d 25 | 26 | static inline struct sbiret sbi_hart_start(unsigned long hartid, 27 | unsigned long start_addr, 28 | unsigned long opaque) { 29 | return sbicall(SBI_EID_HSM, 0, hartid, start_addr, opaque); 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /common/lib/fb.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__FB_H__ 2 | #define LIB__FB_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct resolution { 9 | uint64_t width; 10 | uint64_t height; 11 | uint16_t bpp; 12 | }; 13 | 14 | struct fb_info { 15 | uint64_t framebuffer_pitch; 16 | uint64_t framebuffer_width; 17 | uint64_t framebuffer_height; 18 | uint16_t framebuffer_bpp; 19 | uint8_t memory_model; 20 | uint8_t red_mask_size; 21 | uint8_t red_mask_shift; 22 | uint8_t green_mask_size; 23 | uint8_t green_mask_shift; 24 | uint8_t blue_mask_size; 25 | uint8_t blue_mask_shift; 26 | 27 | uint64_t framebuffer_addr; 28 | 29 | struct edid_info_struct *edid; 30 | 31 | uint64_t mode_count; 32 | struct fb_info *mode_list; 33 | }; 34 | 35 | extern struct fb_info *fb_fbs; 36 | extern size_t fb_fbs_count; 37 | 38 | void fb_init(struct fb_info **ret, size_t *_fbs_count, 39 | uint64_t target_width, uint64_t target_height, uint16_t target_bpp); 40 | 41 | void fb_clear(struct fb_info *fb); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /common/sys/exceptions.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if defined (BIOS) 7 | 8 | static const char *exception_names[] = { 9 | "Division", 10 | "Debug", 11 | "NMI", 12 | "Breakpoint", 13 | "Overflow", 14 | "Bound range exceeded", 15 | "Invalid opcode", 16 | "Device not available", 17 | "Double fault", 18 | "???", 19 | "Invalid TSS", 20 | "Segment not present", 21 | "Stack-segment fault", 22 | "General protection fault", 23 | "Page fault", 24 | "???", 25 | "x87", 26 | "Alignment check", 27 | "Machine check", 28 | "SIMD", 29 | "Virtualisation", 30 | "???", 31 | "???", 32 | "???", 33 | "???", 34 | "???", 35 | "???", 36 | "???", 37 | "???", 38 | "???", 39 | "Security" 40 | }; 41 | 42 | void except(uint32_t exception, uint32_t error_code, uint32_t ebp, uint32_t eip) { 43 | (void)ebp; 44 | panic(false, "%s exception at %x. Error code: %x", exception_names[exception], eip, error_code); 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /test/linker.ld: -------------------------------------------------------------------------------- 1 | PHDRS 2 | { 3 | headers PT_PHDR PHDRS; 4 | text PT_LOAD FILEHDR PHDRS; 5 | rodata PT_LOAD; 6 | data PT_LOAD; 7 | dynamic PT_DYNAMIC; 8 | } 9 | 10 | SECTIONS 11 | { 12 | . = SIZEOF_HEADERS; 13 | executable_start = . - SIZEOF_HEADERS; 14 | 15 | .text : { 16 | *(.text .text.*) 17 | } :text 18 | 19 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 20 | 21 | .rodata : { 22 | *(.rodata .rodata.*) 23 | *(.srodata .srodata.*) 24 | *(.sdata2 .sdata2.*) 25 | } :rodata 26 | 27 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 28 | 29 | .data : { 30 | *(.data .data.*) 31 | *(.sdata .sdata.*) 32 | 33 | KEEP(*(.limine_requests_start_marker)) 34 | KEEP(*(.limine_requests)) 35 | KEEP(*(.limine_requests_end_marker)) 36 | } :data 37 | 38 | .dynamic : { 39 | *(.dynamic) 40 | } :data :dynamic 41 | 42 | .bss : { 43 | *(.bss .bss.*) 44 | *(.sbss .sbss.*) 45 | *(COMMON) 46 | } :data 47 | 48 | /DISCARD/ : { 49 | *(.eh_frame*) 50 | *(.note .note.*) 51 | *(.interp) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /common/sys/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__IDT_H__ 2 | #define SYS__IDT_H__ 3 | 4 | #include 5 | 6 | #if defined (__i386__) 7 | 8 | struct idtr { 9 | uint16_t limit; 10 | uint32_t ptr; 11 | } __attribute__((packed)); 12 | 13 | struct idt_entry { 14 | uint16_t offset_lo; 15 | uint16_t selector; 16 | uint8_t unused; 17 | uint8_t type_attr; 18 | uint16_t offset_hi; 19 | } __attribute__((packed)); 20 | 21 | #elif defined (__x86_64__) 22 | 23 | struct idtr { 24 | uint16_t limit; 25 | uint64_t ptr; 26 | } __attribute__((packed)); 27 | 28 | struct idt_entry { 29 | uint16_t offset_lo; 30 | uint16_t selector; 31 | uint8_t ist; 32 | uint8_t type_attr; 33 | uint16_t offset_mid; 34 | uint32_t offset_hi; 35 | uint32_t reserved; 36 | } __attribute__((packed)); 37 | 38 | #endif 39 | 40 | #if defined (BIOS) 41 | 42 | extern struct idtr idt; 43 | 44 | void init_idt(void); 45 | 46 | #endif 47 | 48 | enum { 49 | IRQ_NO_FLUSH, 50 | IRQ_PIC_ONLY_FLUSH, 51 | IRQ_PIC_APIC_FLUSH 52 | }; 53 | 54 | extern int irq_flush_type; 55 | 56 | void init_flush_irqs(void); 57 | void flush_irqs(void); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /common/drivers/edid.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVERS__EDID_H__ 2 | #define DRIVERS__EDID_H__ 3 | 4 | #include 5 | 6 | struct edid_info_struct { 7 | uint8_t padding[8]; 8 | uint16_t manufacturer_id_be; 9 | uint16_t edid_id_code; 10 | uint32_t serial_num; 11 | uint8_t man_week; 12 | uint8_t man_year; 13 | uint8_t edid_version; 14 | uint8_t edid_revision; 15 | uint8_t video_input_type; 16 | uint8_t max_hor_size; 17 | uint8_t max_ver_size; 18 | uint8_t gamma_factor; 19 | uint8_t dpms_flags; 20 | uint8_t chroma_info[10]; 21 | uint8_t est_timings1; 22 | uint8_t est_timings2; 23 | uint8_t man_res_timing; 24 | uint16_t std_timing_id[8]; 25 | uint8_t det_timing_desc1[18]; 26 | uint8_t det_timing_desc2[18]; 27 | uint8_t det_timing_desc3[18]; 28 | uint8_t det_timing_desc4[18]; 29 | uint8_t unused; 30 | uint8_t checksum; 31 | } __attribute__((packed)); 32 | 33 | #if defined (UEFI) 34 | #include 35 | 36 | struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle); 37 | #endif 38 | 39 | #if defined (BIOS) 40 | struct edid_info_struct *get_edid_info(void); 41 | #endif 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /common/lib/macros.aarch64_asm.h: -------------------------------------------------------------------------------- 1 | // Branch to \el1 if in EL1, or to \el2 if in EL2 2 | // Uses \reg, halts if not in EL1 or EL2 3 | .macro PICK_EL reg, el1, el2 4 | mrs \reg, currentel 5 | and \reg, \reg, #0b1100 6 | 7 | cmp \reg, #0b0100 // EL1? 8 | b.eq \el1 9 | cmp \reg, #0b1000 // EL2? 10 | b.eq \el2 11 | 12 | // Halt otherwise 13 | msr daifset, #0b1111 14 | 99: 15 | wfi 16 | b 99b 17 | .endm 18 | 19 | 20 | // Zero out all general purpose registers apart from X0 21 | .macro ZERO_REGS_EXCEPT_X0 22 | mov x1, xzr 23 | mov x2, xzr 24 | mov x3, xzr 25 | mov x4, xzr 26 | mov x5, xzr 27 | mov x6, xzr 28 | mov x7, xzr 29 | mov x8, xzr 30 | mov x9, xzr 31 | mov x10, xzr 32 | mov x11, xzr 33 | mov x12, xzr 34 | mov x13, xzr 35 | mov x14, xzr 36 | mov x15, xzr 37 | mov x16, xzr 38 | mov x17, xzr 39 | mov x18, xzr 40 | mov x19, xzr 41 | mov x20, xzr 42 | mov x21, xzr 43 | mov x22, xzr 44 | mov x23, xzr 45 | mov x24, xzr 46 | mov x25, xzr 47 | mov x26, xzr 48 | mov x27, xzr 49 | mov x28, xzr 50 | mov x29, xzr 51 | mov x30, xzr 52 | .endm 53 | -------------------------------------------------------------------------------- /common/lib/spinup.asm_uefi_ia32: -------------------------------------------------------------------------------- 1 | extern _GLOBAL_OFFSET_TABLE_ 2 | 3 | extern gdt 4 | 5 | section .text 6 | 7 | extern flush_irqs 8 | 9 | global common_spinup 10 | bits 32 11 | common_spinup: 12 | cli 13 | 14 | push 0 15 | push 0 16 | lidt [esp] 17 | add esp, 8 18 | 19 | call .get_got 20 | .get_got: 21 | pop ebx 22 | add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc 23 | 24 | lgdt [ebx + gdt wrt ..gotoff] 25 | 26 | push dword 0x18 27 | call .p1 28 | .p1: 29 | pop eax 30 | add eax, 6 31 | push eax 32 | retfd 33 | 34 | .flush_cs: 35 | mov eax, 0x20 36 | mov ds, eax 37 | mov es, eax 38 | mov fs, eax 39 | mov gs, eax 40 | mov ss, eax 41 | 42 | call flush_irqs 43 | 44 | xor eax, eax 45 | lldt ax 46 | 47 | ; We don't need the return address 48 | add esp, 4 49 | 50 | ; Get function address 51 | pop edi 52 | 53 | ; We don't need the argument count 54 | add esp, 4 55 | 56 | mov eax, 0x00000011 57 | mov cr0, eax 58 | 59 | xor eax, eax 60 | mov cr4, eax 61 | 62 | call edi 63 | 64 | section .note.GNU-stack noalloc noexec nowrite progbits 65 | -------------------------------------------------------------------------------- /common/protos/linux_32.asm_x86: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | align 16 4 | linux_gdt: 5 | dq 0 6 | 7 | dq 0 8 | 9 | dw 0xffff 10 | dw 0x0000 11 | db 0x00 12 | db 10011011b 13 | db 11001111b 14 | db 0x00 15 | 16 | dw 0xffff 17 | dw 0x0000 18 | db 0x00 19 | db 10010011b 20 | db 11001111b 21 | db 0x00 22 | 23 | .end: 24 | 25 | align 16 26 | linux_gdt_ptr: 27 | dw (linux_gdt.end - linux_gdt) - 1 28 | dd 0 29 | 30 | bits 32 31 | 32 | section .text 33 | 34 | global linux_spinup 35 | linux_spinup: 36 | call .p0 37 | .p0: 38 | pop eax 39 | lea ebx, [eax - (linux_spinup.p0 - linux_gdt_ptr)] 40 | lea ecx, [eax - (linux_spinup.p0 - linux_gdt)] 41 | mov [ebx+2], ecx 42 | 43 | lgdt [ebx] 44 | 45 | push 0x10 46 | call .p1 47 | .p1: 48 | add dword [esp], .fj - .p1 49 | retfd 50 | 51 | .fj: 52 | mov eax, 0x18 53 | mov ds, eax 54 | mov es, eax 55 | mov fs, eax 56 | mov gs, eax 57 | mov ss, eax 58 | 59 | xor ebp, ebp 60 | xor edi, edi 61 | xor ebx, ebx 62 | 63 | mov esi, [esp+8] ; boot_params 64 | 65 | cld 66 | 67 | jmp [esp+4] 68 | 69 | section .note.GNU-stack noalloc noexec nowrite progbits 70 | -------------------------------------------------------------------------------- /stage1/pxe/bootsect.asm: -------------------------------------------------------------------------------- 1 | org 0x7c00 2 | bits 16 3 | 4 | start: 5 | cli 6 | cld 7 | jmp 0x0000:.initialise_cs 8 | .initialise_cs: 9 | xor ax, ax 10 | mov ds, ax 11 | mov es, ax 12 | mov ss, ax 13 | mov sp, 0x7c00 14 | 15 | lgdt [gdt] 16 | 17 | mov eax, cr0 18 | bts ax, 0 19 | mov cr0, eax 20 | 21 | jmp 0x08:.mode32 22 | bits 32 23 | .mode32: 24 | mov eax, 0x10 25 | mov ds, ax 26 | mov es, ax 27 | mov fs, ax 28 | mov gs, ax 29 | mov ss, ax 30 | 31 | push 0x1 32 | and edx, 0xff 33 | push edx 34 | 35 | push stage2.size 36 | push (stage2 - decompressor) + 0x70000 37 | 38 | mov esi, decompressor 39 | mov edi, 0x70000 40 | mov ecx, stage2.fullsize 41 | rep movsb 42 | 43 | call 0x70000 44 | 45 | ; Includes 46 | 47 | %include '../gdt.asm' 48 | 49 | ; ********************* Stage 2 ********************* 50 | 51 | decompressor: 52 | %strcat DECOMPRESSOR_PATH BUILDDIR, '/decompressor-build/decompressor.bin' 53 | incbin DECOMPRESSOR_PATH 54 | 55 | align 16 56 | stage2: 57 | %strcat STAGE2_PATH BUILDDIR, '/common-bios/stage2.bin.gz' 58 | incbin STAGE2_PATH 59 | .size: equ $ - stage2 60 | .fullsize: equ $ - decompressor 61 | -------------------------------------------------------------------------------- /test/memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void *memcpy(void *dest, const void *src, size_t n) { 5 | uint8_t *pdest = dest; 6 | const uint8_t *psrc = src; 7 | 8 | for (size_t i = 0; i < n; i++) { 9 | pdest[i] = psrc[i]; 10 | } 11 | 12 | return dest; 13 | } 14 | 15 | void *memset(void *s, int c, size_t n) { 16 | uint8_t *p = s; 17 | 18 | for (size_t i = 0; i < n; i++) { 19 | p[i] = (uint8_t)c; 20 | } 21 | 22 | return s; 23 | } 24 | 25 | void *memmove(void *dest, const void *src, size_t n) { 26 | uint8_t *pdest = dest; 27 | const uint8_t *psrc = src; 28 | 29 | if (src > dest) { 30 | for (size_t i = 0; i < n; i++) { 31 | pdest[i] = psrc[i]; 32 | } 33 | } else if (src < dest) { 34 | for (size_t i = n; i > 0; i--) { 35 | pdest[i-1] = psrc[i-1]; 36 | } 37 | } 38 | 39 | return dest; 40 | } 41 | 42 | int memcmp(const void *s1, const void *s2, size_t n) { 43 | const uint8_t *p1 = s1; 44 | const uint8_t *p2 = s2; 45 | 46 | for (size_t i = 0; i < n; i++) { 47 | if (p1[i] != p2[i]) 48 | return p1[i] < p2[i] ? -1 : 1; 49 | } 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /common/gensyms.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | 5 | LC_ALL=C 6 | export LC_ALL 7 | 8 | TMP0="$(mktemp)" 9 | 10 | cat >"$TMP0" </dev/null 16 | EOF 17 | 18 | chmod +x "$TMP0" 19 | 20 | "$TMP0" && set -o pipefail 21 | 22 | rm "$TMP0" 23 | 24 | TMP1="$(mktemp)" 25 | TMP2="$(mktemp)" 26 | TMP3="$(mktemp)" 27 | TMP4="$(mktemp)" 28 | 29 | trap 'rm -f "$TMP1" "$TMP2" "$TMP3" "$TMP4"' EXIT 30 | 31 | "$OBJDUMP_FOR_TARGET" -t "$1" | ( "$SED" '/[[:<:]]d[[:>:]]/d' 2>/dev/null || "$SED" '/\bd\b/d' ) | sort > "$TMP1" 32 | "$GREP" "F $4" < "$TMP1" | cut -d' ' -f1 > "$TMP2" 33 | "$GREP" "F $4" < "$TMP1" | "$AWK" 'NF{ print $NF }' > "$TMP3" 34 | 35 | echo ".section .$2_map" > "$TMP4" 36 | echo ".globl $2_map" >> "$TMP4" 37 | echo "$2_map:" >> "$TMP4" 38 | 39 | if [ "$3" = "32" ]; then 40 | paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.long 0x/g;s/$/"/g;s/#/\ 41 | .asciz "/g' >> "$TMP4" 42 | echo ".long 0xffffffff" >> "$TMP4" 43 | elif [ "$3" = "64" ]; then 44 | paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.quad 0x/g;s/$/"/g;s/#/\ 45 | .asciz "/g' >> "$TMP4" 46 | echo ".quad 0xffffffffffffffff" >> "$TMP4" 47 | fi 48 | 49 | echo '.section .note.GNU-stack,"",%progbits' >> "$TMP4" 50 | 51 | mv "$TMP4" "$2.map.S" 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # We don't want to ignore the following files 2 | !.clang-format 3 | !.editorconfig 4 | !.gitattributes 5 | !.gitignore 6 | !.typos.toml 7 | 8 | 9 | # Generated files 10 | *.o 11 | *.d 12 | *.a 13 | *.exe 14 | *.EFI 15 | *.bin 16 | *.bin.gz 17 | *.tar* 18 | *.elf 19 | *.hdd 20 | *.iso 21 | *.sys 22 | *.dtb 23 | *~ 24 | 25 | /bin 26 | /build 27 | /limine-protocol 28 | /picoefi 29 | /freestnd-c-hdrs 30 | /flanterm 31 | /common/lib/stb_image.h.nopatch 32 | /common/lib/stb_image.h 33 | /common/cc-runtime.s2.c 34 | /cc-runtime 35 | /decompressor/tinf 36 | /decompressor/cc-runtime.c 37 | /libfdt 38 | /tinf 39 | /edk2-ovmf 40 | /bochsout.txt 41 | /bx_enh_dbg.ini 42 | /test_image 43 | /configure 44 | /configure.ac.save 45 | /timestamps 46 | /build-aux 47 | /aclocal.m4 48 | /config.status 49 | /config.log 50 | /autom4te.cache 51 | /man/man1/limine.1 52 | /GNUmakefile 53 | /config.h 54 | /common-bios 55 | /common-uefi-ia32 56 | /common-uefi-x86-64 57 | /common-uefi-aarch64 58 | /common-uefi-riscv64 59 | /common-uefi-loongarch64 60 | /decompressor-build 61 | /stage1.stamp 62 | 63 | 64 | # For local development 65 | /.vscode 66 | /.gdb_history 67 | /tags 68 | /TAGS 69 | 70 | # Clang's compilation database file 71 | compile_commands.json 72 | 73 | # clangd caches 74 | /.clangd 75 | /.cache/clangd 76 | -------------------------------------------------------------------------------- /common/lib/fdt.c: -------------------------------------------------------------------------------- 1 | #if !defined(__i386__) && !defined(__x86_64__) 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static int fdt_get_or_add_chosen_node(void *fdt) { 8 | int offset = fdt_subnode_offset(fdt, 0, "chosen"); 9 | 10 | if (offset == -FDT_ERR_NOTFOUND) { 11 | offset = fdt_add_subnode(fdt, 0, "chosen"); 12 | } 13 | 14 | return offset; 15 | } 16 | 17 | int fdt_set_chosen_string(void *fdt, const char *name, const char *value) { 18 | int chosen_offset = fdt_get_or_add_chosen_node(fdt); 19 | if (chosen_offset < 0) { 20 | return chosen_offset; 21 | } 22 | 23 | return fdt_setprop_string(fdt, chosen_offset, name, value); 24 | } 25 | 26 | int fdt_set_chosen_uint64(void *fdt, const char *name, uint64_t value) { 27 | int chosen_offset = fdt_get_or_add_chosen_node(fdt); 28 | if (chosen_offset < 0) { 29 | return chosen_offset; 30 | } 31 | 32 | return fdt_setprop_u64(fdt, chosen_offset, name, value); 33 | } 34 | 35 | int fdt_set_chosen_uint32(void *fdt, const char *name, uint32_t value) { 36 | int chosen_offset = fdt_get_or_add_chosen_node(fdt); 37 | if (chosen_offset < 0) { 38 | return chosen_offset; 39 | } 40 | 41 | return fdt_setprop_u32(fdt, chosen_offset, name, value); 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /common/fs/file.h: -------------------------------------------------------------------------------- 1 | #ifndef FS__FILE_H__ 2 | #define FS__FILE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #if defined (UEFI) 9 | # include 10 | #endif 11 | 12 | extern bool case_insensitive_fopen; 13 | 14 | bool fs_get_guid(struct guid *guid, struct volume *part); 15 | char *fs_get_label(struct volume *part); 16 | 17 | struct file_handle { 18 | bool is_memfile; 19 | bool readall; 20 | struct volume *vol; 21 | char *path; 22 | size_t path_len; 23 | void *fd; 24 | void (*read)(void *fd, void *buf, uint64_t loc, uint64_t count); 25 | void (*close)(void *fd); 26 | uint64_t size; 27 | #if defined (UEFI) 28 | EFI_HANDLE efi_part_handle; 29 | #endif 30 | bool pxe; 31 | uint32_t pxe_ip; 32 | uint16_t pxe_port; 33 | }; 34 | 35 | struct file_handle *fopen(struct volume *part, const char *filename); 36 | void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count); 37 | void fclose(struct file_handle *fd); 38 | void *freadall(struct file_handle *fd, uint32_t type); 39 | void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs 40 | #if defined (__i386__) 41 | , void (*memcpy_to_64)(uint64_t dst, void *src, size_t count) 42 | #endif 43 | ); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /test/multiboot2_trampoline.asm: -------------------------------------------------------------------------------- 1 | extern multiboot2_main 2 | 3 | global _start 4 | 5 | section .multiboot_header 6 | header_start: 7 | dd 0xe85250d6 ; Magic number (multiboot 2) 8 | dd 0 ; Architecture 0 (protected mode i386) 9 | dd header_end - header_start ; Header length 10 | dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) ; Checksum 11 | 12 | align 8 13 | framebuffer_tag_start: 14 | dw 5 ; type 15 | dw 1 ; flags 16 | dd framebuffer_tag_end - framebuffer_tag_start ; size 17 | dd 800 ; width 18 | dd 600 ; height 19 | dd 32 ; depth 20 | framebuffer_tag_end: 21 | 22 | align 8 23 | ; Required end tag: 24 | dw 0 ; type 25 | dw 0 ; flags 26 | dw 8 ; size 27 | header_end: 28 | 29 | section .text 30 | bits 32 31 | 32 | _start: 33 | cli 34 | 35 | mov esp, stack_top 36 | 37 | push ebx 38 | push eax 39 | 40 | call multiboot2_main ; Jump to our multiboot test kernel 41 | 42 | section .bss 43 | stack_bottom: 44 | resb 4096 * 16 45 | stack_top: 46 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-2025 Mintsuki and contributors. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /common/lib/spinup.asm_riscv64: -------------------------------------------------------------------------------- 1 | 2 | .section .text 3 | 4 | .global riscv_spinup 5 | riscv_spinup: 6 | .option norelax 7 | csrci sstatus, 0x2 8 | csrw sie, zero 9 | 10 | lla t0, 0f 11 | add t0, t0, a3 12 | csrw stvec, t0 13 | csrw satp, a2 14 | sfence.vma 15 | unimp 16 | .align 4 17 | 0: 18 | csrw stvec, zero 19 | 20 | mv t0, a0 21 | mv sp, a1 22 | 23 | mv a0, zero 24 | mv a1, zero 25 | mv a2, zero 26 | mv a3, zero 27 | mv a4, zero 28 | mv a5, zero 29 | mv a6, zero 30 | mv a7, zero 31 | mv s0, zero 32 | mv s1, zero 33 | mv s2, zero 34 | mv s3, zero 35 | mv s4, zero 36 | mv s5, zero 37 | mv s6, zero 38 | mv s7, zero 39 | mv s8, zero 40 | mv s9, zero 41 | mv s10, zero 42 | mv s11, zero 43 | mv t1, zero 44 | mv t2, zero 45 | mv t3, zero 46 | mv t4, zero 47 | mv t5, zero 48 | mv t6, zero 49 | mv tp, zero 50 | mv gp, zero 51 | mv ra, zero 52 | 53 | jr t0 54 | 55 | .section .note.GNU-stack,"",%progbits 56 | -------------------------------------------------------------------------------- /common/lib/memory.s2.c: -------------------------------------------------------------------------------- 1 | #if !defined (__x86_64__) && !defined (__i386__) 2 | 3 | #include 4 | #include 5 | 6 | void *memcpy(void *dest, const void *src, size_t n) { 7 | uint8_t *pdest = (uint8_t *)dest; 8 | const uint8_t *psrc = (const uint8_t *)src; 9 | 10 | for (size_t i = 0; i < n; i++) { 11 | pdest[i] = psrc[i]; 12 | } 13 | 14 | return dest; 15 | } 16 | 17 | void *memset(void *s, int c, size_t n) { 18 | uint8_t *p = (uint8_t *)s; 19 | 20 | for (size_t i = 0; i < n; i++) { 21 | p[i] = (uint8_t)c; 22 | } 23 | 24 | return s; 25 | } 26 | 27 | void *memmove(void *dest, const void *src, size_t n) { 28 | uint8_t *pdest = (uint8_t *)dest; 29 | const uint8_t *psrc = (const uint8_t *)src; 30 | 31 | if (src > dest) { 32 | for (size_t i = 0; i < n; i++) { 33 | pdest[i] = psrc[i]; 34 | } 35 | } else if (src < dest) { 36 | for (size_t i = n; i > 0; i--) { 37 | pdest[i-1] = psrc[i-1]; 38 | } 39 | } 40 | 41 | return dest; 42 | } 43 | 44 | int memcmp(const void *s1, const void *s2, size_t n) { 45 | const uint8_t *p1 = (const uint8_t *)s1; 46 | const uint8_t *p2 = (const uint8_t *)s2; 47 | 48 | for (size_t i = 0; i < n; i++) { 49 | if (p1[i] != p2[i]) { 50 | return p1[i] < p2[i] ? -1 : 1; 51 | } 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /decompressor/mem.asm: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | global memcpy 4 | memcpy: 5 | push esi 6 | push edi 7 | mov eax, dword [esp+12] 8 | mov edi, eax 9 | mov esi, dword [esp+16] 10 | mov ecx, dword [esp+20] 11 | rep movsb 12 | pop edi 13 | pop esi 14 | ret 15 | 16 | global memset 17 | memset: 18 | push edi 19 | mov edx, dword [esp+8] 20 | mov edi, edx 21 | mov eax, dword [esp+12] 22 | mov ecx, dword [esp+16] 23 | rep stosb 24 | mov eax, edx 25 | pop edi 26 | ret 27 | 28 | global memmove 29 | memmove: 30 | push esi 31 | push edi 32 | mov eax, dword [esp+12] 33 | mov edi, eax 34 | mov esi, dword [esp+16] 35 | mov ecx, dword [esp+20] 36 | 37 | cmp edi, esi 38 | ja .copy_backwards 39 | 40 | rep movsb 41 | jmp .done 42 | 43 | .copy_backwards: 44 | lea edi, [edi+ecx-1] 45 | lea esi, [esi+ecx-1] 46 | std 47 | rep movsb 48 | cld 49 | 50 | .done: 51 | pop edi 52 | pop esi 53 | ret 54 | 55 | global memcmp 56 | memcmp: 57 | push esi 58 | push edi 59 | mov edi, dword [esp+12] 60 | mov esi, dword [esp+16] 61 | mov ecx, dword [esp+20] 62 | repe cmpsb 63 | je .equal 64 | mov al, byte [edi-1] 65 | sub al, byte [esi-1] 66 | movsx eax, al 67 | jmp .done 68 | 69 | .equal: 70 | xor eax, eax 71 | 72 | .done: 73 | pop edi 74 | pop esi 75 | ret 76 | 77 | section .note.GNU-stack noalloc noexec nowrite progbits 78 | -------------------------------------------------------------------------------- /common/drivers/serial.c: -------------------------------------------------------------------------------- 1 | #if defined (BIOS) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static bool serial_initialised = false; 12 | static uint32_t serial_baudrate; 13 | 14 | static void serial_initialise(void) { 15 | if (serial_initialised || config_ready == false) { 16 | return; 17 | } 18 | 19 | char *baudrate_s = config_get_value(NULL, 0, "SERIAL_BAUDRATE"); 20 | if (baudrate_s == NULL) { 21 | serial_baudrate = 115200; 22 | } else { 23 | serial_baudrate = strtoui(baudrate_s, NULL, 10); 24 | } 25 | 26 | // Init com1 27 | outb(0x3f8 + 3, 0x00); 28 | outb(0x3f8 + 1, 0x00); 29 | outb(0x3f8 + 3, 0x80); 30 | 31 | uint16_t divisor = (uint16_t)(115200 / serial_baudrate); 32 | outb(0x3f8 + 0, divisor & 0xff); 33 | outb(0x3f8 + 1, (divisor >> 8) & 0xff); 34 | 35 | outb(0x3f8 + 1, 0x00); 36 | outb(0x3f8 + 3, 0x03); 37 | outb(0x3f8 + 2, 0xc7); 38 | outb(0x3f8 + 4, 0x0b); 39 | 40 | serial_initialised = true; 41 | } 42 | 43 | void serial_out(uint8_t b) { 44 | serial_initialise(); 45 | 46 | while ((inb(0x3f8 + 5) & 0x20) == 0); 47 | outb(0x3f8, b); 48 | } 49 | 50 | int serial_in(void) { 51 | serial_initialise(); 52 | 53 | if ((inb(0x3f8 + 5) & 0x01) == 0) { 54 | return -1; 55 | } 56 | return inb(0x3f8); 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /common/lib/mem.s2.asm_ia32: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | global memcpy 4 | memcpy: 5 | push esi 6 | push edi 7 | mov eax, dword [esp+12] 8 | mov edi, eax 9 | mov esi, dword [esp+16] 10 | mov ecx, dword [esp+20] 11 | rep movsb 12 | pop edi 13 | pop esi 14 | ret 15 | 16 | global memset 17 | memset: 18 | push edi 19 | mov edx, dword [esp+8] 20 | mov edi, edx 21 | mov eax, dword [esp+12] 22 | mov ecx, dword [esp+16] 23 | rep stosb 24 | mov eax, edx 25 | pop edi 26 | ret 27 | 28 | global memmove 29 | memmove: 30 | push esi 31 | push edi 32 | mov eax, dword [esp+12] 33 | mov edi, eax 34 | mov esi, dword [esp+16] 35 | mov ecx, dword [esp+20] 36 | 37 | cmp edi, esi 38 | ja .copy_backwards 39 | 40 | rep movsb 41 | jmp .done 42 | 43 | .copy_backwards: 44 | lea edi, [edi+ecx-1] 45 | lea esi, [esi+ecx-1] 46 | std 47 | rep movsb 48 | cld 49 | 50 | .done: 51 | pop edi 52 | pop esi 53 | ret 54 | 55 | global memcmp 56 | memcmp: 57 | push esi 58 | push edi 59 | mov edi, dword [esp+12] 60 | mov esi, dword [esp+16] 61 | mov ecx, dword [esp+20] 62 | repe cmpsb 63 | je .equal 64 | mov al, byte [edi-1] 65 | sub al, byte [esi-1] 66 | movsx eax, al 67 | jmp .done 68 | 69 | .equal: 70 | xor eax, eax 71 | 72 | .done: 73 | pop edi 74 | pop esi 75 | ret 76 | 77 | section .note.GNU-stack noalloc noexec nowrite progbits 78 | -------------------------------------------------------------------------------- /common/protos/multiboot_reloc.asm_x86: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | bits 32 4 | 5 | global multiboot_reloc_stub 6 | multiboot_reloc_stub: 7 | jmp .code 8 | 9 | times 4-($-multiboot_reloc_stub) db 0 10 | 11 | ; EBX = self 12 | ; ESI = magic value 13 | ; EDI = protocol info 14 | ; ECX = entry point 15 | ; EAX = ranges 16 | ; EDX = ranges count 17 | 18 | .code: 19 | mov esp, ebx 20 | add esp, .mini_stack_top - multiboot_reloc_stub 21 | 22 | push edi 23 | push esi 24 | push ecx 25 | 26 | .ranges_loop: 27 | test edx, edx ; Loop until we're done 28 | jz .ranges_loop_out 29 | 30 | mov esi, [eax] ; ESI = range.elsewhere 31 | mov edi, [eax+8] ; EDI = range.target 32 | mov ecx, [eax+16] ; ECX = range.length 33 | rep movsb ; Copy range to target location 34 | 35 | add eax, 24 ; Move to the next range 36 | 37 | dec edx 38 | jmp .ranges_loop 39 | 40 | .ranges_loop_out: 41 | ; We're done relocating! 42 | pop ecx 43 | pop esi 44 | pop edi 45 | 46 | push ecx 47 | 48 | mov eax, esi ; EAX = magic value 49 | mov ebx, edi ; EBX = protocol info 50 | xor ecx, ecx 51 | xor edx, edx 52 | xor esi, esi 53 | xor edi, edi 54 | xor ebp, ebp 55 | 56 | ret 57 | 58 | align 16 59 | .mini_stack: 60 | times 3 dq 0 61 | .mini_stack_top: 62 | 63 | global multiboot_reloc_stub_end 64 | multiboot_reloc_stub_end: 65 | 66 | section .note.GNU-stack noalloc noexec nowrite progbits 67 | -------------------------------------------------------------------------------- /common/sys/smp.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__SMP_H__ 2 | #define SYS__SMP_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #define LIMINE_NO_POINTERS 9 | #include 10 | 11 | #if defined (__x86_64__) || defined (__i386__) 12 | 13 | struct limine_mp_info *init_smp(size_t *cpu_count, 14 | uint32_t *_bsp_lapic_id, 15 | int paging_mode, 16 | pagemap_t pagemap, 17 | bool x2apic, 18 | bool nx, 19 | uint64_t hhdm, 20 | bool wp); 21 | 22 | #elif defined (__aarch64__) 23 | 24 | struct limine_mp_info *init_smp(const char *config, 25 | size_t *cpu_count, 26 | uint64_t *bsp_mpidr, 27 | pagemap_t pagemap, 28 | uint64_t mair, 29 | uint64_t tcr, 30 | uint64_t sctlr, 31 | uint64_t hhdm_offset); 32 | 33 | #elif defined (__riscv) 34 | 35 | struct limine_mp_info *init_smp(size_t *cpu_count, 36 | pagemap_t pagemap, 37 | uint64_t hhdm_offset); 38 | 39 | #elif defined (__loongarch64) 40 | #else 41 | #error Unknown architecture 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /common/lib/term.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__TERM_H__ 2 | #define LIB__TERM_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum { 10 | _NOT_READY, 11 | GTERM, 12 | TEXTMODE, 13 | FALLBACK 14 | }; 15 | 16 | #if defined (BIOS) 17 | extern int current_video_mode; 18 | #endif 19 | 20 | extern struct flanterm_context **terms; 21 | extern size_t terms_i; 22 | 23 | extern int term_backend; 24 | 25 | #define TERM_CTX_SIZE ((uint64_t)(-1)) 26 | #define TERM_CTX_SAVE ((uint64_t)(-2)) 27 | #define TERM_CTX_RESTORE ((uint64_t)(-3)) 28 | #define TERM_FULL_REFRESH ((uint64_t)(-4)) 29 | #define TERM_OOB_OUTPUT_GET ((uint64_t)(-10)) 30 | #define TERM_OOB_OUTPUT_SET ((uint64_t)(-11)) 31 | 32 | #define FOR_TERM(...) do { \ 33 | for (size_t FOR_TERM_i = 0; FOR_TERM_i < terms_i; FOR_TERM_i++) { \ 34 | struct flanterm_context *TERM = terms[FOR_TERM_i]; \ 35 | __VA_ARGS__ \ 36 | ; \ 37 | } \ 38 | } while (0) 39 | 40 | static inline void reset_term(void) { 41 | for (size_t i = 0; i < terms_i; i++) { 42 | struct flanterm_context *term = terms[i]; 43 | 44 | print("\e[2J\e[H"); 45 | flanterm_context_reinit(term); 46 | term->cursor_enabled = true; 47 | term->double_buffer_flush(term); 48 | } 49 | } 50 | 51 | static inline void set_cursor_pos_helper(size_t x, size_t y) { 52 | print("\e[%u;%uH", (int)y + 1, (int)x + 1); 53 | } 54 | 55 | void term_notready(void); 56 | void term_fallback(void); 57 | void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /stage1/hdd/disk.asm: -------------------------------------------------------------------------------- 1 | ; ***************************** 2 | ; Reads bytes from disk 3 | ; ***************************** 4 | 5 | ; IN: 6 | ; EAX = Start address to load low 32 7 | ; EBP = Start address to load high 32 8 | ; DL = Drive number 9 | ; ES = Buffer segment 10 | ; BX = Buffer offset 11 | ; ECX = Byte count 12 | 13 | ; OUT: 14 | ; Carry if error 15 | 16 | read_sectors: 17 | pusha 18 | 19 | mov si, .da_struct 20 | 21 | mov word [si], 16 22 | mov word [si+2], 1 23 | mov word [si+4], bx 24 | mov word [si+6], es 25 | 26 | push dx 27 | push si 28 | 29 | push eax 30 | push ebp 31 | 32 | ; Get bytes per sector 33 | mov ah, 0x48 34 | mov si, .drive_params 35 | mov word [si], 30 ; buf_size 36 | int 0x13 37 | jc .done 38 | mov bp, word [si+24] ; bytes_per_sect 39 | 40 | ; ECX byte count to CX sector count 41 | mov ax, cx 42 | shr ecx, 16 43 | mov dx, cx 44 | xor cx, cx 45 | div bp 46 | test dx, dx 47 | setnz cl 48 | add cx, ax 49 | 50 | pop edx 51 | pop eax 52 | 53 | pop si 54 | 55 | ; EBP:EAX address to EAX LBA sector 56 | div ebp 57 | mov dword [si+8], eax 58 | mov dword [si+12], 0 59 | 60 | pop dx 61 | 62 | .loop: 63 | mov ah, 0x42 64 | 65 | clc 66 | int 0x13 67 | jc .done 68 | 69 | add word [si+4], bp 70 | xor ebx, ebx 71 | inc dword [si+8] 72 | seto bl 73 | add dword [si+12], ebx 74 | 75 | loop .loop 76 | 77 | .done: 78 | popa 79 | ret 80 | 81 | .da_struct: equ 0x8000 82 | .drive_params: equ 0x8010 83 | -------------------------------------------------------------------------------- /test/limine.conf: -------------------------------------------------------------------------------- 1 | # Some example macros 2 | ${TEST_KERNEL}=boot():/boot/test.elf 3 | ${WALLPAPER_PATH}=boot():/boot/bg.jpg 4 | 5 | default_entry: 1 6 | timeout: 3 7 | verbose: yes 8 | 9 | wallpaper: ${WALLPAPER_PATH} 10 | backdrop: 008080 11 | 12 | interface_help_colour: 3 13 | 14 | /Limine Test 15 | comment: Test of the Limine boot protocol. ${ARCH} ${FW_TYPE} 16 | 17 | protocol: limine 18 | path: ${TEST_KERNEL} 19 | cmdline: This is an example command line. 20 | 21 | module_path: ${WALLPAPER_PATH} 22 | module_string: This is the first module. 23 | 24 | module_path: boot():/boot/bg.jpg 25 | 26 | /Multiboot2 Test 27 | comment: Test of the multiboot2 boot protocol. 28 | 29 | protocol: multiboot2 30 | kernel_path: boot():/boot/multiboot2.elf 31 | kernel_cmdline: This is an example kernel command line. 32 | 33 | module_path: boot():/boot/bg.jpg 34 | module_string: This is the first module. 35 | 36 | /EFI Chainloading 37 | comment: Test EFI image chainloading. 38 | 39 | protocol: efi_chainload 40 | image_path: boot():/EFI/BOOT/BOOTX64.EFI 41 | 42 | /BIOS Chainloading 43 | comment: Test BIOS chainloading. 44 | 45 | protocol: bios_chainload 46 | drive: 1 47 | 48 | /+Legacy 49 | comment: Directory containing legacy entries. 50 | 51 | //Multiboot1 Test 52 | comment: Test of the multiboot1 boot protocol. 53 | 54 | protocol: multiboot1 55 | kernel_path: boot():/boot/multiboot.elf 56 | kernel_cmdline: This is an example kernel command line. 57 | 58 | module_path: boot():/boot/bg.jpg 59 | module_string: This is the first module. 60 | -------------------------------------------------------------------------------- /common/linker_uefi_ia32.ld.in: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf32-i386) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text PT_LOAD FLAGS(0x05); 7 | rodata PT_LOAD FLAGS(0x04); 8 | data PT_LOAD FLAGS(0x06); 9 | dynamic PT_DYNAMIC FLAGS(0x06); 10 | } 11 | 12 | SECTIONS 13 | { 14 | . = 0; 15 | __slide = .; 16 | __image_base = ABSOLUTE(.); 17 | __image_size = ABSOLUTE(__image_end - __image_base); 18 | 19 | .text : { 20 | KEEP(*(.pe_header)) 21 | 22 | . = ALIGN(0x1000); 23 | 24 | __text_start = ABSOLUTE(.); 25 | *(.text .text.*) 26 | } :text 27 | 28 | . = ALIGN(0x1000); 29 | __text_end = ABSOLUTE(.); 30 | __text_size = ABSOLUTE(__text_end - __text_start); 31 | 32 | .rodata : { 33 | __reloc_start = ABSOLUTE(.); 34 | *(.dummy_reloc) 35 | 36 | . = ALIGN(0x1000); 37 | __reloc_end = ABSOLUTE(.); 38 | __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); 39 | 40 | __data_start = ABSOLUTE(.); 41 | *(.rodata .rodata.*) 42 | 43 | #ifdef LINKER_NOMAP 44 | full_map = .; 45 | #else 46 | *(.full_map) 47 | #endif 48 | } :rodata 49 | 50 | .data : { 51 | data_begin = .; 52 | *(.data .data.*) 53 | } :data 54 | 55 | .bss : { 56 | *(.bss .bss.*) 57 | *(COMMON) 58 | data_end = .; 59 | } :data 60 | 61 | .no_unwind : { 62 | *(.no_unwind) 63 | } :data 64 | 65 | .dynamic : { 66 | *(.dynamic) 67 | } :data :dynamic 68 | 69 | __data_end = ABSOLUTE(ALIGN(0x1000)); 70 | __data_size = ABSOLUTE(__data_end - __data_start); 71 | 72 | __image_end = ABSOLUTE(ALIGN(0x1000)); 73 | 74 | /DISCARD/ : { 75 | *(.eh_frame*) 76 | *(.note .note.*) 77 | *(.interp) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /common/linker_uefi_x86_64.ld.in: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-x86-64) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text PT_LOAD FLAGS(0x05); 7 | rodata PT_LOAD FLAGS(0x04); 8 | data PT_LOAD FLAGS(0x06); 9 | dynamic PT_DYNAMIC FLAGS(0x06); 10 | } 11 | 12 | SECTIONS 13 | { 14 | . = 0; 15 | __slide = .; 16 | __image_base = ABSOLUTE(.); 17 | __image_size = ABSOLUTE(__image_end - __image_base); 18 | 19 | .text : { 20 | KEEP(*(.pe_header)) 21 | 22 | . = ALIGN(0x1000); 23 | 24 | __text_start = ABSOLUTE(.); 25 | *(.text .text.*) 26 | } :text 27 | 28 | . = ALIGN(0x1000); 29 | __text_end = ABSOLUTE(.); 30 | __text_size = ABSOLUTE(__text_end - __text_start); 31 | 32 | .rodata : { 33 | __reloc_start = ABSOLUTE(.); 34 | *(.dummy_reloc) 35 | 36 | . = ALIGN(0x1000); 37 | __reloc_end = ABSOLUTE(.); 38 | __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); 39 | 40 | __data_start = ABSOLUTE(.); 41 | *(.rodata .rodata.*) 42 | 43 | #ifdef LINKER_NOMAP 44 | full_map = .; 45 | #else 46 | *(.full_map) 47 | #endif 48 | } :rodata 49 | 50 | .data : { 51 | data_begin = .; 52 | *(.data .data.*) 53 | } :data 54 | 55 | .bss : { 56 | *(.bss .bss.*) 57 | *(COMMON) 58 | data_end = .; 59 | } :data 60 | 61 | .no_unwind : { 62 | *(.no_unwind) 63 | } :data 64 | 65 | .dynamic : { 66 | *(.dynamic) 67 | } :data :dynamic 68 | 69 | __data_end = ABSOLUTE(ALIGN(0x1000)); 70 | __data_size = ABSOLUTE(__data_end - __data_start); 71 | 72 | __image_end = ABSOLUTE(ALIGN(0x1000)); 73 | 74 | /DISCARD/ : { 75 | *(.eh_frame*) 76 | *(.note .note.*) 77 | *(.interp) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /common/linker_uefi_aarch64.ld.in: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-littleaarch64) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text PT_LOAD FLAGS(0x05); 7 | rodata PT_LOAD FLAGS(0x04); 8 | data PT_LOAD FLAGS(0x06); 9 | dynamic PT_DYNAMIC FLAGS(0x06); 10 | } 11 | 12 | SECTIONS 13 | { 14 | . = 0; 15 | __slide = .; 16 | __image_base = ABSOLUTE(.); 17 | __image_size = ABSOLUTE(__image_end - __image_base); 18 | 19 | .text : { 20 | KEEP(*(.pe_header)) 21 | 22 | . = ALIGN(0x1000); 23 | 24 | __text_start = ABSOLUTE(.); 25 | *(.text .text.*) 26 | } :text 27 | 28 | . = ALIGN(0x1000); 29 | __text_end = ABSOLUTE(.); 30 | __text_size = ABSOLUTE(__text_end - __text_start); 31 | 32 | .rodata : { 33 | __reloc_start = ABSOLUTE(.); 34 | *(.dummy_reloc) 35 | 36 | . = ALIGN(0x1000); 37 | __reloc_end = ABSOLUTE(.); 38 | __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); 39 | 40 | __data_start = ABSOLUTE(.); 41 | *(.rodata .rodata.*) 42 | 43 | #ifdef LINKER_NOMAP 44 | full_map = .; 45 | #else 46 | *(.full_map) 47 | #endif 48 | } :rodata 49 | 50 | .data : { 51 | data_begin = .; 52 | *(.data .data.*) 53 | } :data 54 | 55 | .bss : { 56 | *(.bss .bss.*) 57 | *(COMMON) 58 | data_end = .; 59 | } :data 60 | 61 | .no_unwind : { 62 | *(.no_unwind) 63 | } :data 64 | 65 | .dynamic : { 66 | *(.dynamic) 67 | } :data :dynamic 68 | 69 | __data_end = ABSOLUTE(ALIGN(0x1000)); 70 | __data_size = ABSOLUTE(__data_end - __data_start); 71 | 72 | __image_end = ABSOLUTE(ALIGN(0x1000)); 73 | 74 | /DISCARD/ : { 75 | *(.eh_frame*) 76 | *(.note .note.*) 77 | *(.interp) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /common/linker_uefi_loongarch64.ld.in: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-loongarch) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text PT_LOAD FLAGS(0x05); 7 | rodata PT_LOAD FLAGS(0x04); 8 | data PT_LOAD FLAGS(0x06); 9 | dynamic PT_DYNAMIC FLAGS(0x06); 10 | } 11 | 12 | SECTIONS 13 | { 14 | . = 0; 15 | __slide = .; 16 | __image_base = ABSOLUTE(.); 17 | __image_size = ABSOLUTE(__image_end - __image_base); 18 | 19 | .text : { 20 | KEEP(*(.pe_header)) 21 | 22 | . = ALIGN(0x1000); 23 | 24 | __text_start = ABSOLUTE(.); 25 | *(.text .text.*) 26 | } :text 27 | 28 | . = ALIGN(0x1000); 29 | __text_end = ABSOLUTE(.); 30 | __text_size = ABSOLUTE(__text_end - __text_start); 31 | 32 | .rodata : { 33 | __reloc_start = ABSOLUTE(.); 34 | *(.dummy_reloc) 35 | 36 | . = ALIGN(0x1000); 37 | __reloc_end = ABSOLUTE(.); 38 | __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); 39 | 40 | __data_start = ABSOLUTE(.); 41 | *(.rodata .rodata.*) 42 | 43 | #ifdef LINKER_NOMAP 44 | full_map = .; 45 | #else 46 | *(.full_map) 47 | #endif 48 | } :rodata 49 | 50 | .data : { 51 | data_begin = .; 52 | *(.data .data.*) 53 | } :data 54 | 55 | .bss : { 56 | *(.bss .bss.*) 57 | *(COMMON) 58 | data_end = .; 59 | } :data 60 | 61 | .no_unwind : { 62 | *(.no_unwind) 63 | } :data 64 | 65 | .dynamic : { 66 | *(.dynamic) 67 | } :data :dynamic 68 | 69 | __data_end = ABSOLUTE(ALIGN(0x1000)); 70 | __data_size = ABSOLUTE(__data_end - __data_start); 71 | 72 | __image_end = ABSOLUTE(ALIGN(0x1000)); 73 | 74 | /DISCARD/ : { 75 | *(.eh_frame*) 76 | *(.note .note.*) 77 | *(.interp) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /common/sys/a20.s2.c: -------------------------------------------------------------------------------- 1 | #if defined (BIOS) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | bool a20_check(void) { 11 | bool ret = false; 12 | uint16_t orig = mminw(0x7dfe); 13 | 14 | mmoutw(0x7dfe, 0x1234); 15 | 16 | if (mminw(0x7dfe) != mminw(0x7dfe + 0x100000)) { 17 | ret = true; 18 | goto out; 19 | } 20 | 21 | mmoutw(0x7dfe, ~mminw(0x7dfe)); 22 | 23 | if (mminw(0x7dfe) != mminw(0x7dfe + 0x100000)) { 24 | ret = true; 25 | goto out; 26 | } 27 | 28 | out: 29 | mmoutw(0x7dfe, orig); 30 | return ret; 31 | } 32 | 33 | // Keyboard controller method code below taken from: 34 | // https://wiki.osdev.org/A20_Line 35 | 36 | bool a20_enable(void) { 37 | if (a20_check()) 38 | return true; 39 | 40 | // BIOS method 41 | struct rm_regs r = {0}; 42 | r.eax = 0x2401; 43 | rm_int(0x15, &r, &r); 44 | 45 | if (a20_check()) 46 | return true; 47 | 48 | // Keyboard controller method 49 | while (inb(0x64) & 2); 50 | outb(0x64, 0xad); 51 | while (inb(0x64) & 2); 52 | outb(0x64, 0xd0); 53 | while (!(inb(0x64) & 1)); 54 | uint8_t b = inb(0x60); 55 | while (inb(0x64) & 2); 56 | outb(0x64, 0xd1); 57 | while (inb(0x64) & 2); 58 | outb(0x60, b | 2); 59 | while (inb(0x64) & 2); 60 | outb(0x64, 0xae); 61 | while (inb(0x64) & 2); 62 | 63 | if (a20_check()) 64 | return true; 65 | 66 | // Fast A20 method 67 | b = inb(0x92); 68 | if ((b & 0x02) == 0) { 69 | b &= ~0x01; 70 | b |= 0x02; 71 | outb(0x92, b); 72 | } 73 | 74 | if (a20_check()) 75 | return true; 76 | 77 | return false; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /common/lib/spinup.asm_uefi_x86_64: -------------------------------------------------------------------------------- 1 | section .rodata 2 | 3 | invalid_idt: 4 | dq 0, 0 5 | 6 | section .text 7 | 8 | extern flush_irqs 9 | 10 | %macro push32 1 11 | sub rsp, 4 12 | mov dword [rsp], %1 13 | %endmacro 14 | 15 | extern gdt 16 | 17 | global common_spinup 18 | bits 64 19 | common_spinup: 20 | cli 21 | 22 | lgdt [rel gdt] 23 | lidt [rel invalid_idt] 24 | 25 | lea rbx, [rel .reload_cs] 26 | 27 | push 0x28 28 | push rbx 29 | retfq 30 | .reload_cs: 31 | mov eax, 0x30 32 | mov ds, eax 33 | mov es, eax 34 | mov fs, eax 35 | mov gs, eax 36 | mov ss, eax 37 | 38 | push r8 39 | push r9 40 | push rcx 41 | push rdx 42 | push rsi 43 | push rdi 44 | call flush_irqs 45 | pop rdi 46 | pop rsi 47 | pop rdx 48 | pop rcx 49 | pop r9 50 | pop r8 51 | 52 | mov rbp, rsp 53 | 54 | cmp esi, 4 55 | jle .no_stack_args 56 | 57 | .push_stack_args: 58 | dec esi 59 | mov eax, [rbp + 8 + rsi*8] 60 | push32 eax 61 | test esi, esi 62 | jnz .push_stack_args 63 | 64 | .no_stack_args: 65 | push32 r9d 66 | push32 r8d 67 | push32 ecx 68 | push32 edx 69 | 70 | lea rbx, [rel .go_32] 71 | 72 | push 0x18 73 | push rbx 74 | retfq 75 | 76 | bits 32 77 | .go_32: 78 | mov eax, 0x20 79 | mov ds, ax 80 | mov es, ax 81 | mov fs, ax 82 | mov gs, ax 83 | mov ss, ax 84 | 85 | xor eax, eax 86 | lldt ax 87 | 88 | mov eax, 0x00000011 89 | mov cr0, eax 90 | 91 | mov ecx, 0xc0000080 92 | xor eax, eax 93 | xor edx, edx 94 | wrmsr 95 | 96 | xor eax, eax 97 | mov cr4, eax 98 | 99 | call edi 100 | 101 | section .note.GNU-stack noalloc noexec nowrite progbits 102 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Build and Install Instructions 2 | 3 | > **NOTE:** This document is about building and installing Limine. 4 | > For information about deployment for usage, see [USAGE.md](USAGE.md). 5 | 6 | ## Prerequisites 7 | 8 | In order to build Limine, the following programs have to be installed: 9 | common UNIX tools (also known as `coreutils`), 10 | `GNU make`, `grep`, `sed`, `find`, `awk`, `gzip`, `nasm`, `mtools` 11 | (optional, necessary to build `limine-uefi-cd.bin`). 12 | Furthermore, `gcc` or `llvm/clang` must also be installed, alongside 13 | the respective binutils. 14 | 15 | ## Configure 16 | 17 | If using a release tarball (recommended, see 18 | https://codeberg.org/Limine/Limine/releases), run `./configure` directly. 19 | 20 | If checking out from the repository, run `./bootstrap` first in order to 21 | download the necessary [dependencies](3RDPARTY.md) and generate the configure 22 | script (`GNU autoconf` required). 23 | 24 | `./configure` takes arguments and environment variables; for more information 25 | on these, run `./configure --help`. 26 | 27 | > **NOTE:** `./configure` by default does not build any Limine port. Make sure 28 | > to read the output of `./configure --help` and enable any or all ports! 29 | 30 | Limine supports both in-tree and out-of-tree builds. Simply run the `configure` 31 | script from the directory you wish to execute the build in. The following 32 | `make` commands are supposed to be run inside the build directory. 33 | 34 | ## Building 35 | 36 | To build Limine, run: 37 | ```bash 38 | make # (or gmake where applicable) 39 | ``` 40 | 41 | ## Installing 42 | 43 | This step will install Limine files to `share`, `include`, and `bin` 44 | directories in the specified prefix (default is `/usr/local`, see 45 | `./configure --help`. 46 | 47 | To install Limine, run: 48 | ```bash 49 | make install # (or gmake where applicable) 50 | ``` 51 | -------------------------------------------------------------------------------- /common/lib/image.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour) { 10 | image->type = IMAGE_CENTERED; 11 | 12 | image->x_displacement = frame_x_size / 2 - image->x_size / 2; 13 | image->y_displacement = frame_y_size / 2 - image->y_size / 2; 14 | image->back_colour = back_colour; 15 | } 16 | 17 | 18 | void image_make_stretched(struct image *image, int new_x_size, int new_y_size) { 19 | image->type = IMAGE_STRETCHED; 20 | 21 | image->x_size = new_x_size; 22 | image->y_size = new_y_size; 23 | } 24 | 25 | struct image *image_open(struct file_handle *file) { 26 | struct image *image = ext_mem_alloc(sizeof(struct image)); 27 | 28 | image->type = IMAGE_TILED; 29 | 30 | void *src = ext_mem_alloc(file->size); 31 | 32 | fread(file, src, 0, file->size); 33 | 34 | int x, y, bpp; 35 | 36 | image->img = stbi_load_from_memory(src, file->size, &x, &y, &bpp, 4); 37 | 38 | pmm_free(src, file->size); 39 | 40 | if (image->img == NULL) { 41 | pmm_free(image, sizeof(struct image)); 42 | return NULL; 43 | } 44 | 45 | // Convert ABGR to XRGB 46 | uint32_t *pptr = (void *)image->img; 47 | for (int i = 0; i < x * y; i++) { 48 | pptr[i] = (pptr[i] & 0x0000ff00) | ((pptr[i] & 0x00ff0000) >> 16) | ((pptr[i] & 0x000000ff) << 16); 49 | } 50 | 51 | image->x_size = x; 52 | image->y_size = y; 53 | image->pitch = x * 4; 54 | image->bpp = 32; 55 | image->img_width = x; 56 | image->img_height = y; 57 | 58 | return image; 59 | } 60 | 61 | void image_close(struct image *image) { 62 | stbi_image_free(image->img); 63 | pmm_free(image, sizeof(struct image)); 64 | } 65 | -------------------------------------------------------------------------------- /common/linker_uefi_riscv64.ld.in: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-littleriscv) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text PT_LOAD FLAGS(0x05); 7 | rodata PT_LOAD FLAGS(0x04); 8 | data PT_LOAD FLAGS(0x06); 9 | dynamic PT_DYNAMIC FLAGS(0x06); 10 | } 11 | 12 | SECTIONS 13 | { 14 | . = 0; 15 | __slide = .; 16 | __image_base = ABSOLUTE(.); 17 | __image_size = ABSOLUTE(__image_end - __image_base); 18 | 19 | .text : { 20 | KEEP(*(.pe_header)) 21 | 22 | . = ALIGN(0x1000); 23 | 24 | __text_start = ABSOLUTE(.); 25 | *(.text .text.*) 26 | } :text 27 | 28 | . = ALIGN(0x1000); 29 | __text_end = ABSOLUTE(.); 30 | __text_size = ABSOLUTE(__text_end - __text_start); 31 | 32 | .rodata : { 33 | __reloc_start = ABSOLUTE(.); 34 | *(.dummy_reloc) 35 | 36 | . = ALIGN(0x1000); 37 | __reloc_end = ABSOLUTE(.); 38 | __reloc_size = ABSOLUTE(__reloc_end - __reloc_start); 39 | 40 | __data_start = ABSOLUTE(.); 41 | *(.rodata .rodata.*) 42 | *(.srodata .srodata.*) 43 | *(.sdata2 .sdata2.*) 44 | 45 | #ifdef LINKER_NOMAP 46 | full_map = .; 47 | #else 48 | *(.full_map) 49 | #endif 50 | } :rodata 51 | 52 | .data : { 53 | data_begin = .; 54 | *(.data .data.*) 55 | *(.sdata .sdata.*) 56 | } :data 57 | 58 | .bss : { 59 | *(.bss .bss.*) 60 | *(.sbss .sbss.*) 61 | *(COMMON) 62 | data_end = .; 63 | } :data 64 | 65 | .no_unwind : { 66 | *(.no_unwind) 67 | } :data 68 | 69 | .dynamic : { 70 | *(.dynamic) 71 | } :data :dynamic 72 | 73 | __data_end = ABSOLUTE(ALIGN(0x1000)); 74 | __data_size = ABSOLUTE(__data_end - __data_start); 75 | 76 | __image_end = ABSOLUTE(ALIGN(0x1000)); 77 | 78 | /DISCARD/ : { 79 | *(.eh_frame*) 80 | *(.note .note.*) 81 | *(.interp) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ### Why not support filesystem X or feature Y? (eg: LUKS, LVM) 4 | 5 | The idea with Limine is to remove the responsibility of parsing filesystems and 6 | formats, aside from the bare minimum necessities (eg: FAT*, ISO9660), from the 7 | bootloader itself. It is a needless duplication of efforts to have bootloaders 8 | support all possible filesystems and formats, and it leads to massive, bloated 9 | bootloaders as a result (eg: GRUB2). 10 | 11 | What is needed is to simply make sure the bootloader is capable of reading its 12 | own files, configuration, and be able to load kernel/module files from disk. 13 | The kernel should be responsible for parsing everything else as it sees fit. 14 | 15 | ### What about LUKS? What about security? Encrypt the kernel! 16 | 17 | Simply put, this is unnecessary. Putting the kernel/modules in a readable FAT32 18 | partition and letting Limine know about their BLAKE2B checksums in the config 19 | file provides as much security as encrypting the kernel does. 20 | 21 | ### What if a malicious actor modifies the config file? 22 | 23 | While this is a pointless effort on legacy x86 BIOS, it is a reasonable 24 | expectation to secure the boot sequence on UEFI systems with Secure Boot. 25 | Limine provides a way to modify its own EFI executable to bake in the BLAKE2B 26 | checksum of the config file itself. The EFI executable can then get signed with 27 | a key added to the firmware's keychain. This prevents modifications to the 28 | config file (and in turn the checksums contained there) from going unnoticed. 29 | 30 | ### I do not want to have a separate FAT boot partition! What can I do? 31 | 32 | It is `$year_following_2012` now and most PCs are equipped with UEFI and simply 33 | won't boot without a FAT EFI system partition anyways. 34 | It is not unreasonable to share the EFI system partition with the OS's /boot 35 | and store kernels, initramfses, and any other files needed for boot there. 36 | -------------------------------------------------------------------------------- /common/pxe/pxe_asm.s2.asm_bios_ia32: -------------------------------------------------------------------------------- 1 | section .realmode 2 | 3 | global pxe_call 4 | global set_pxe_fp 5 | 6 | set_pxe_fp: 7 | mov eax, [esp + 4] 8 | mov [pxe_call.pxe_fp], eax 9 | ret 10 | 11 | pxe_call: 12 | ; Save GDT in case BIOS overwrites it 13 | sgdt [.gdt] 14 | 15 | ; Save IDT 16 | sidt [.idt] 17 | 18 | ; Load BIOS IVT 19 | lidt [.rm_idt] 20 | 21 | ; Save non-scratch GPRs 22 | push ebx 23 | push esi 24 | push edi 25 | push ebp 26 | 27 | lea ebp, [esp + 20] 28 | 29 | ; Jump to real mode 30 | jmp 0x08:.bits16 31 | .bits16: 32 | bits 16 33 | mov ax, 0x10 34 | mov ds, ax 35 | mov es, ax 36 | mov fs, ax 37 | mov gs, ax 38 | mov ss, ax 39 | mov eax, cr0 40 | and al, 0xfe 41 | mov cr0, eax 42 | jmp 0x00:.cszero 43 | .cszero: 44 | xor ax, ax 45 | mov ss, ax 46 | mov ds, ax 47 | mov es, ax 48 | mov fs, ax 49 | mov gs, ax 50 | 51 | sti 52 | 53 | push word [bp + 4] 54 | push word [bp + 8] 55 | push word [bp + 0] 56 | call far [.pxe_fp] 57 | add sp, 6 58 | 59 | cli 60 | 61 | ; Restore GDT 62 | o32 lgdt [cs:.gdt] 63 | 64 | ; Restore IDT 65 | o32 lidt [cs:.idt] 66 | 67 | ; Jump back to pmode 68 | mov ebx, cr0 69 | or bl, 1 70 | mov cr0, ebx 71 | jmp 0x18:.bits32 72 | .bits32: 73 | bits 32 74 | mov bx, 0x20 75 | mov ds, bx 76 | mov es, bx 77 | mov fs, bx 78 | mov gs, bx 79 | mov ss, bx 80 | 81 | and eax, 0xffff 82 | 83 | ; Restore non-scratch GPRs 84 | pop ebp 85 | pop edi 86 | pop esi 87 | pop ebx 88 | 89 | ; Exit 90 | ret 91 | 92 | align 16 93 | .pxe_fp: dd 0 94 | .gdt: dq 0 95 | .idt: dq 0 96 | .rm_idt: dw 0x3ff 97 | dd 0 98 | 99 | section .note.GNU-stack noalloc noexec nowrite progbits 100 | -------------------------------------------------------------------------------- /common/pxe/pxe.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #if defined (BIOS) 7 | #include 8 | #elif defined (UEFI) 9 | #include 10 | #endif 11 | 12 | #if defined (BIOS) 13 | 14 | void set_pxe_fp(uint32_t fp); 15 | 16 | struct volume *pxe_bind_volume(void) { 17 | struct volume *volume = ext_mem_alloc(sizeof(struct volume)); 18 | 19 | volume->pxe = true; 20 | 21 | return volume; 22 | } 23 | 24 | void pxe_init(void) { 25 | //pxe installation check 26 | struct rm_regs r = { 0 }; 27 | r.ebx = 0; 28 | r.ecx = 0; 29 | r.eax = 0x5650; 30 | r.es = 0; 31 | 32 | rm_int(0x1a, &r, &r); 33 | if ((r.eax & 0xffff) != 0x564e) { 34 | panic(false, "PXE installation check failed"); 35 | } 36 | 37 | struct pxenv* pxenv = { 0 }; 38 | 39 | pxenv = (struct pxenv*)((r.es << 4) + (r.ebx & 0xffff)); 40 | if (memcmp(pxenv->signature, PXE_SIGNATURE, sizeof(pxenv->signature)) != 0) { 41 | panic(false, "PXENV structure signature corrupted"); 42 | } 43 | 44 | if (pxenv->version < 0x201) { 45 | //we won't support pxe < 2.1, grub does this too and it seems to work fine 46 | panic(false, "pxe version too old"); 47 | } 48 | 49 | struct bangpxe* bangpxe = (struct bangpxe*)((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4) + (pxenv->pxe_ptr & 0xffff)); 50 | 51 | if (memcmp(bangpxe->signature, PXE_BANGPXE_SIGNATURE, 52 | sizeof(bangpxe->signature)) 53 | != 0) { 54 | panic(false, "!pxe signature corrupted"); 55 | } 56 | set_pxe_fp(bangpxe->rm_entry); 57 | printv("pxe: Successfully initialized\n"); 58 | } 59 | 60 | #elif defined (UEFI) 61 | 62 | struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code) { 63 | struct volume *volume = ext_mem_alloc(sizeof(struct volume)); 64 | 65 | volume->efi_handle = efi_handle; 66 | volume->pxe_base_code = pxe_base_code; 67 | volume->pxe = true; 68 | 69 | return volume; 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /common/sys/idt.c: -------------------------------------------------------------------------------- 1 | #if defined (__x86_64__) || defined (__i386__) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static struct idt_entry *dummy_idt = NULL; 13 | 14 | void dummy_isr(void); 15 | 16 | void init_flush_irqs(void) { 17 | size_t dummy_idt_size = 256 * sizeof(struct idt_entry); 18 | dummy_idt = ext_mem_alloc(dummy_idt_size); 19 | 20 | for (size_t i = 0; i < 256; i++) { 21 | dummy_idt[i].offset_lo = (uint16_t)(uintptr_t)dummy_isr; 22 | dummy_idt[i].type_attr = 0x8e; 23 | #if defined (__i386__) 24 | dummy_idt[i].selector = 0x18; 25 | dummy_idt[i].offset_hi = (uint16_t)((uintptr_t)dummy_isr >> 16); 26 | #elif defined (__x86_64__) 27 | dummy_idt[i].selector = 0x28; 28 | dummy_idt[i].offset_mid = (uint16_t)((uintptr_t)dummy_isr >> 16); 29 | dummy_idt[i].offset_hi = (uint32_t)((uintptr_t)dummy_isr >> 32); 30 | #endif 31 | } 32 | } 33 | 34 | int irq_flush_type = IRQ_NO_FLUSH; 35 | 36 | void flush_irqs(void) { 37 | switch (irq_flush_type) { 38 | case IRQ_PIC_ONLY_FLUSH: 39 | pic_flush(); 40 | // FALLTHRU 41 | case IRQ_NO_FLUSH: 42 | return; 43 | case IRQ_PIC_APIC_FLUSH: 44 | break; 45 | default: 46 | panic(false, "Invalid IRQ flush type"); 47 | } 48 | 49 | struct idtr old_idt; 50 | asm volatile ("sidt %0" : "=m"(old_idt) :: "memory"); 51 | 52 | struct idtr new_idt = { 53 | 256 * sizeof(struct idt_entry) - 1, 54 | (uintptr_t)dummy_idt 55 | }; 56 | asm volatile ("lidt %0" :: "m"(new_idt) : "memory"); 57 | 58 | // Flush the legacy PIC so we know the remaining ints come from the LAPIC 59 | pic_flush(); 60 | 61 | asm volatile ("sti" ::: "memory"); 62 | 63 | // Delay a while to make sure we catch ALL pending IRQs 64 | delay(10000000); 65 | 66 | asm volatile ("cli" ::: "memory"); 67 | 68 | asm volatile ("lidt %0" :: "m"(old_idt) : "memory"); 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /common/sys/gdt.s2.c: -------------------------------------------------------------------------------- 1 | #if defined (__x86_64__) || defined (__i386__) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static struct gdt_desc gdt_descs[] = { 9 | {0}, 10 | 11 | { 12 | .limit = 0xffff, 13 | .base_low = 0x0000, 14 | .base_mid = 0x00, 15 | .access = 0b10011011, 16 | .granularity = 0b00000000, 17 | .base_hi = 0x00 18 | }, 19 | 20 | { 21 | .limit = 0xffff, 22 | .base_low = 0x0000, 23 | .base_mid = 0x00, 24 | .access = 0b10010011, 25 | .granularity = 0b00000000, 26 | .base_hi = 0x00 27 | }, 28 | 29 | { 30 | .limit = 0xffff, 31 | .base_low = 0x0000, 32 | .base_mid = 0x00, 33 | .access = 0b10011011, 34 | .granularity = 0b11001111, 35 | .base_hi = 0x00 36 | }, 37 | 38 | { 39 | .limit = 0xffff, 40 | .base_low = 0x0000, 41 | .base_mid = 0x00, 42 | .access = 0b10010011, 43 | .granularity = 0b11001111, 44 | .base_hi = 0x00 45 | }, 46 | 47 | { 48 | .limit = 0x0000, 49 | .base_low = 0x0000, 50 | .base_mid = 0x00, 51 | .access = 0b10011011, 52 | .granularity = 0b00100000, 53 | .base_hi = 0x00 54 | }, 55 | 56 | { 57 | .limit = 0x0000, 58 | .base_low = 0x0000, 59 | .base_mid = 0x00, 60 | .access = 0b10010011, 61 | .granularity = 0b00000000, 62 | .base_hi = 0x00 63 | } 64 | }; 65 | 66 | #if defined (BIOS) 67 | __attribute__((section(".realmode"))) 68 | #endif 69 | struct gdtr gdt = { 70 | sizeof(gdt_descs) - 1, 71 | (uintptr_t)gdt_descs, 72 | #if defined (__i386__) 73 | 0 74 | #endif 75 | }; 76 | 77 | #if defined (UEFI) 78 | void init_gdt(void) { 79 | struct gdt_desc *gdt_copy = ext_mem_alloc(sizeof(gdt_descs)); 80 | memcpy(gdt_copy, gdt_descs, sizeof(gdt_descs)); 81 | gdt.ptr = (uintptr_t)gdt_copy; 82 | } 83 | #endif 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /common/drivers/edid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if defined (BIOS) 11 | 12 | #include 13 | 14 | struct edid_info_struct *get_edid_info(void) { 15 | static struct edid_info_struct *buf = NULL; 16 | 17 | if (buf == NULL) 18 | buf = conv_mem_alloc(sizeof(struct edid_info_struct)); 19 | 20 | struct rm_regs r = {0}; 21 | 22 | r.eax = 0x4f15; 23 | r.ebx = 0x0001; 24 | r.edi = (uint32_t)rm_off(buf); 25 | r.ds = (uint32_t)rm_seg(buf); 26 | r.es = r.ds; 27 | rm_int(0x10, &r, &r); 28 | 29 | if ((r.eax & 0x00ff) != 0x4f) 30 | goto fail; 31 | if ((r.eax & 0xff00) != 0) 32 | goto fail; 33 | 34 | for (size_t i = 0; i < sizeof(struct edid_info_struct); i++) 35 | if (((uint8_t *)buf)[i] != 0) 36 | goto success; 37 | 38 | fail: 39 | printv("edid: Could not fetch EDID data.\n"); 40 | return NULL; 41 | 42 | success: 43 | printv("edid: Success.\n"); 44 | return buf; 45 | } 46 | 47 | #endif 48 | 49 | #if defined (UEFI) 50 | 51 | #include 52 | 53 | struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle) { 54 | struct edid_info_struct *buf = ext_mem_alloc(sizeof(struct edid_info_struct)); 55 | 56 | EFI_STATUS status; 57 | 58 | EFI_EDID_ACTIVE_PROTOCOL *edid = NULL; 59 | EFI_GUID edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID; 60 | 61 | status = gBS->HandleProtocol(gop_handle, &edid_guid, (void **)&edid); 62 | 63 | if (status) 64 | goto fail; 65 | 66 | if (edid->SizeOfEdid < sizeof(struct edid_info_struct)) 67 | goto fail; 68 | 69 | memcpy(buf, edid->Edid, sizeof(struct edid_info_struct)); 70 | 71 | for (size_t i = 0; i < sizeof(struct edid_info_struct); i++) 72 | if (((uint8_t *)buf)[i] != 0) 73 | goto success; 74 | 75 | fail: 76 | printv("edid: Could not fetch EDID data.\n"); 77 | return NULL; 78 | 79 | success: 80 | printv("edid: Success.\n"); 81 | return buf; 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /common/lib/panic.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #if defined (UEFI) 8 | # include 9 | #endif 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | noreturn void panic(bool allow_menu, const char *fmt, ...) { 18 | va_list args; 19 | 20 | va_start(args, fmt); 21 | 22 | quiet = false; 23 | 24 | if ( 25 | #if defined (BIOS) 26 | stage3_loaded == true && 27 | #endif 28 | term_backend == _NOT_READY) { 29 | term_fallback(); 30 | } 31 | 32 | if ( 33 | #if defined (BIOS) 34 | stage3_loaded == true && 35 | #endif 36 | term_backend != FALLBACK) { 37 | print("\033[31mPANIC\033[37;1m\033[0m: "); 38 | } else { 39 | print("PANIC: "); 40 | } 41 | vprint(fmt, args); 42 | 43 | va_end(args); 44 | 45 | print("\n"); 46 | print_stacktrace(NULL); 47 | 48 | if ( 49 | #if defined (BIOS) 50 | stage3_loaded == true && 51 | #elif defined (UEFI) 52 | efi_boot_services_exited == false && 53 | #endif 54 | allow_menu == true) { 55 | print("Press a key to return to %s.", booting_from_editor ? "editor" : "menu"); 56 | 57 | getchar(); 58 | 59 | // This fixes a crash 60 | term_notready(); 61 | 62 | menu(false); 63 | /* 64 | fb_clear(&fbinfo); 65 | 66 | // release all uefi memory and return to firmware 67 | pmm_release_uefi_mem(); 68 | gBS->Exit(efi_image_handle, EFI_ABORTED, 0, NULL); 69 | */ 70 | } else { 71 | #if defined (BIOS) 72 | print("Press CTRL+ALT+DEL to reboot."); 73 | rm_hcf(); 74 | #elif defined (UEFI) 75 | print("System halted."); 76 | for (;;) { 77 | #if defined (__x86_64__) || defined (__i386__) 78 | asm ("hlt"); 79 | #elif defined (__aarch64__) || defined (__riscv) 80 | asm ("wfi"); 81 | #elif defined (__loongarch64) 82 | asm ("idle 0"); 83 | #else 84 | #error Unknown architecture 85 | #endif 86 | } 87 | #endif 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/e9print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #if defined (_LIMINE_PROTO) 6 | #include 7 | extern struct flanterm_context *ft_ctx; 8 | #endif 9 | 10 | static const char CONVERSION_TABLE[] = "0123456789abcdef"; 11 | 12 | void e9_putc(char c) { 13 | #if defined (__x86_64__) || defined (__i386__) 14 | __asm__ __volatile__ ("outb %0, %1" :: "a" (c), "Nd" (0xe9) : "memory"); 15 | #endif 16 | #if defined (_LIMINE_PROTO) 17 | if (ft_ctx != NULL) { 18 | flanterm_write(ft_ctx, &c, 1); 19 | } 20 | #endif 21 | } 22 | 23 | void e9_print(const char *msg) { 24 | for (size_t i = 0; msg[i]; i++) { 25 | e9_putc(msg[i]); 26 | } 27 | } 28 | 29 | void e9_puts(const char *msg) { 30 | e9_print(msg); 31 | e9_putc('\n'); 32 | } 33 | 34 | static void e9_printhex(size_t num) { 35 | int i; 36 | char buf[17]; 37 | 38 | if (!num) { 39 | e9_print("0x0"); 40 | return; 41 | } 42 | 43 | buf[16] = 0; 44 | 45 | for (i = 15; num; i--) { 46 | buf[i] = CONVERSION_TABLE[num % 16]; 47 | num /= 16; 48 | } 49 | 50 | i++; 51 | e9_print("0x"); 52 | e9_print(&buf[i]); 53 | } 54 | 55 | static void e9_printdec(size_t num) { 56 | int i; 57 | char buf[21] = {0}; 58 | 59 | if (!num) { 60 | e9_putc('0'); 61 | return; 62 | } 63 | 64 | for (i = 19; num; i--) { 65 | buf[i] = (num % 10) + 0x30; 66 | num = num / 10; 67 | } 68 | 69 | i++; 70 | e9_print(buf + i); 71 | } 72 | 73 | void e9_printf(const char *format, ...) { 74 | va_list argp; 75 | va_start(argp, format); 76 | 77 | while (*format != '\0') { 78 | if (*format == '%') { 79 | format++; 80 | if (*format == 'x') { 81 | e9_printhex(va_arg(argp, size_t)); 82 | } else if (*format == 'd') { 83 | e9_printdec(va_arg(argp, size_t)); 84 | } else if (*format == 's') { 85 | e9_print(va_arg(argp, char*)); 86 | } 87 | } else { 88 | e9_putc(*format); 89 | } 90 | format++; 91 | } 92 | 93 | e9_putc('\n'); 94 | va_end(argp); 95 | } 96 | -------------------------------------------------------------------------------- /common/lib/fb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct fb_info *fb_fbs; 10 | size_t fb_fbs_count = 0; 11 | 12 | void fb_init(struct fb_info **ret, size_t *_fbs_count, 13 | uint64_t target_width, uint64_t target_height, uint16_t target_bpp) { 14 | #if defined (BIOS) 15 | *ret = ext_mem_alloc(sizeof(struct fb_info)); 16 | if (init_vbe(*ret, target_width, target_height, target_bpp)) { 17 | *_fbs_count = 1; 18 | 19 | (*ret)->edid = get_edid_info(); 20 | size_t mode_count; 21 | (*ret)->mode_list = vbe_get_mode_list(&mode_count); 22 | (*ret)->mode_count = mode_count; 23 | } else { 24 | *_fbs_count = 0; 25 | pmm_free(*ret, sizeof(struct fb_info)); 26 | } 27 | #elif defined (UEFI) 28 | init_gop(ret, _fbs_count, target_width, target_height, target_bpp); 29 | #endif 30 | 31 | fb_fbs = *ret; 32 | fb_fbs_count = *_fbs_count; 33 | } 34 | 35 | void fb_clear(struct fb_info *fb) { 36 | for (size_t y = 0; y < fb->framebuffer_height; y++) { 37 | switch (fb->framebuffer_bpp) { 38 | case 32: { 39 | uint32_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr; 40 | size_t row = (y * fb->framebuffer_pitch) / 4; 41 | for (size_t x = 0; x < fb->framebuffer_width; x++) { 42 | fbp[row + x] = 0; 43 | } 44 | break; 45 | } 46 | case 16: { 47 | uint16_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr; 48 | size_t row = (y * fb->framebuffer_pitch) / 2; 49 | for (size_t x = 0; x < fb->framebuffer_width; x++) { 50 | fbp[row + x] = 0; 51 | } 52 | break; 53 | } 54 | default: { 55 | uint8_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr; 56 | size_t row = y * fb->framebuffer_pitch; 57 | for (size_t x = 0; x < fb->framebuffer_width * fb->framebuffer_bpp; x++) { 58 | fbp[row + x] = 0; 59 | } 60 | break; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /stage1/cd/bootsect.asm: -------------------------------------------------------------------------------- 1 | org 0x7c00 2 | bits 16 3 | 4 | jmp skip_bpb 5 | nop 6 | 7 | ; El Torito Boot Information Table 8 | ; ↓ Set by mkisofs 9 | times 8-($-$$) db 0 10 | boot_info: 11 | bi_PVD dd 0 12 | bi_boot_LBA dd 0 13 | bi_boot_len dd 0 14 | bi_checksum dd 0 15 | bi_reserved times 40 db 0 16 | 17 | times 90-($-$$) db 0 18 | 19 | skip_bpb: 20 | cli 21 | cld 22 | jmp 0x0000:.initialise_cs 23 | .initialise_cs: 24 | xor si, si 25 | mov ds, si 26 | mov es, si 27 | mov ss, si 28 | mov sp, 0x7c00 29 | sti 30 | 31 | ; int 13h? 32 | mov ah, 0x41 33 | mov bx, 0x55aa 34 | int 0x13 35 | jc err.0 36 | cmp bx, 0xaa55 37 | jne err.1 38 | 39 | ; --- Load the decompressor --- 40 | mov eax, dword [bi_boot_LBA] 41 | add eax, 1 42 | mov ecx, stage2.fullsize / 2048 43 | ; DECOMPRESSOR_LOCATION = 0x70000 = 0x7000:0x0000 44 | push 0x7000 45 | pop es 46 | xor bx, bx 47 | call read_2k_sectors 48 | jc err.2 49 | 50 | ; Enable GDT 51 | lgdt [gdt] 52 | cli 53 | mov eax, cr0 54 | or al, 1 55 | mov cr0, eax 56 | 57 | jmp 0x08:pmode 58 | 59 | err: 60 | .2: 61 | inc si 62 | .1: 63 | inc si 64 | .0: 65 | add si, '0' | (0x4f << 8) 66 | 67 | push 0xb800 68 | pop es 69 | mov word [es:0], si 70 | 71 | sti 72 | .h: hlt 73 | jmp .h 74 | 75 | %include 'read_2k_sectors.asm' 76 | %include '../gdt.asm' 77 | 78 | bits 32 79 | pmode: 80 | mov eax, 0x10 81 | mov ds, ax 82 | mov es, ax 83 | mov fs, ax 84 | mov gs, ax 85 | mov ss, ax 86 | 87 | ; Time to handle control over to the decompressor 88 | push 2 89 | and edx, 0xff 90 | push edx ; Boot drive 91 | push stage2.size 92 | push (stage2 - decompressor) + 0x70000 93 | call 0x70000 94 | 95 | ; Align stage2 to 2K ON DISK 96 | times 2048-($-$$) db 0 97 | decompressor: 98 | %strcat DECOMPRESSOR_PATH BUILDDIR, '/decompressor-build/decompressor.bin' 99 | incbin DECOMPRESSOR_PATH 100 | 101 | align 16 102 | stage2: 103 | %strcat STAGE2_PATH BUILDDIR, '/common-bios/stage2.bin.gz' 104 | incbin STAGE2_PATH 105 | .size: equ $ - stage2 106 | 107 | times ((($-$$)+2047) & ~2047)-($-$$) db 0 108 | .fullsize: equ $ - decompressor 109 | -------------------------------------------------------------------------------- /common/lib/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #if defined (BIOS) 5 | # include 6 | #elif defined (UEFI) 7 | # include 8 | #endif 9 | #include 10 | 11 | // Julian date calculation from https://en.wikipedia.org/wiki/Julian_day 12 | static int get_jdn(int days, int months, int years) { 13 | return (1461 * (years + 4800 + (months - 14)/12))/4 + (367 * 14 | (months - 2 - 12 * ((months - 14)/12)))/12 - 15 | (3 * ((years + 4900 + (months - 14)/12)/100))/4 16 | + days - 32075; 17 | } 18 | 19 | static uint64_t get_unix_epoch(uint8_t seconds, uint8_t minutes, uint8_t hours, 20 | uint8_t days, uint8_t months, uint16_t years) { 21 | uint64_t jdn_current = get_jdn(days, months, years); 22 | uint64_t jdn_1970 = get_jdn(1, 1, 1970); 23 | 24 | uint64_t jdn_diff = jdn_current - jdn_1970; 25 | 26 | return (jdn_diff * (60 * 60 * 24)) + hours * 3600 + minutes * 60 + seconds; 27 | } 28 | 29 | #if defined (BIOS) 30 | uint64_t time(void) { 31 | struct rm_regs r; 32 | 33 | again: 34 | r = (struct rm_regs){0}; 35 | r.eax = 0x0400; 36 | rm_int(0x1a, &r, &r); 37 | 38 | uint8_t day = bcd_to_int( r.edx & 0x00ff); 39 | uint8_t month = bcd_to_int((r.edx & 0xff00) >> 8); 40 | uint16_t year = bcd_to_int( r.ecx & 0x00ff) + 41 | /* century */ bcd_to_int((r.ecx & 0xff00) >> 8) * 100; 42 | 43 | r = (struct rm_regs){0}; 44 | r.eax = 0x0200; 45 | rm_int(0x1a, &r, &r); 46 | 47 | uint8_t second = bcd_to_int((r.edx & 0xff00) >> 8); 48 | uint8_t minute = bcd_to_int( r.ecx & 0x00ff); 49 | uint8_t hour = bcd_to_int((r.ecx & 0xff00) >> 8); 50 | 51 | // Check RTC day wraparound 52 | r = (struct rm_regs){0}; 53 | r.eax = 0x0400; 54 | rm_int(0x1a, &r, &r); 55 | if (bcd_to_int(r.edx & 0x00ff) != day) { 56 | goto again; 57 | } 58 | 59 | return get_unix_epoch(second, minute, hour, day, month, year); 60 | } 61 | #endif 62 | 63 | #if defined (UEFI) 64 | uint64_t time(void) { 65 | EFI_TIME time; 66 | EFI_STATUS status = gRT->GetTime(&time, NULL); 67 | 68 | if (status != 0) { 69 | return 0; 70 | } 71 | 72 | return get_unix_epoch(time.Second, time.Minute, time.Hour, 73 | time.Day, time.Month, time.Year); 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /common/lib/trace.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if defined (BIOS) 12 | extern symbol stage2_map; 13 | #elif defined (UEFI) 14 | extern symbol __slide; 15 | #endif 16 | 17 | extern symbol full_map; 18 | 19 | static char *trace_address(size_t *off, size_t addr) { 20 | char *limine_map; 21 | 22 | #if defined (BIOS) 23 | if (!stage3_loaded) 24 | limine_map = stage2_map; 25 | else 26 | limine_map = full_map; 27 | #elif defined (UEFI) 28 | limine_map = full_map; 29 | 30 | addr -= (size_t)__slide; 31 | #endif 32 | 33 | uintptr_t prev_addr = 0; 34 | char *prev_sym = NULL; 35 | 36 | for (size_t i = 0;;) { 37 | if (*((uintptr_t *)&limine_map[i]) >= addr) { 38 | *off = addr - prev_addr; 39 | return prev_sym; 40 | } 41 | prev_addr = *((uintptr_t *)&limine_map[i]); 42 | i += sizeof(uintptr_t); 43 | prev_sym = &limine_map[i]; 44 | while (limine_map[i++] != 0); 45 | } 46 | } 47 | 48 | void print_stacktrace(size_t *base_ptr) { 49 | if (base_ptr == NULL) { 50 | asm volatile ( 51 | #if defined (__i386__) 52 | "movl %%ebp, %0" 53 | #elif defined (__x86_64__) 54 | "movq %%rbp, %0" 55 | #elif defined (__aarch64__) 56 | "mov %0, x29" 57 | #elif defined (__riscv) 58 | "mv %0, fp; addi %0, %0, -16" 59 | #elif defined (__loongarch64) 60 | "move %0, $fp; addi.d %0, %0, -16" 61 | #endif 62 | : "=r"(base_ptr) 63 | :: "memory" 64 | ); 65 | } 66 | print("Stacktrace:\n"); 67 | for (;;) { 68 | size_t old_bp = base_ptr[0]; 69 | size_t ret_addr = base_ptr[1]; 70 | if (!ret_addr) 71 | break; 72 | size_t off; 73 | char *name = trace_address(&off, ret_addr); 74 | if (name) 75 | print(" [%p] <%s+%p>\n", ret_addr, name, off); 76 | else 77 | print(" [%p]\n", ret_addr); 78 | if (!old_bp) 79 | break; 80 | #if defined (__riscv) || defined (__loongarch64) 81 | base_ptr = (void *)(old_bp - 16); 82 | #else 83 | base_ptr = (void*)old_bp; 84 | #endif 85 | } 86 | print("End of trace. "); 87 | } 88 | -------------------------------------------------------------------------------- /common/protos/multiboot1.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOS__MULTIBOOT1_H__ 2 | #define PROTOS__MULTIBOOT1_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define MULTIBOOT1_HEADER_MAGIC 0x1BADB002 9 | 10 | struct multiboot1_header { 11 | uint32_t magic; 12 | uint32_t flags; 13 | uint32_t checksum; 14 | 15 | uint32_t header_addr; 16 | uint32_t load_addr; 17 | uint32_t load_end_addr; 18 | uint32_t bss_end_addr; 19 | uint32_t entry_addr; 20 | 21 | uint32_t fb_mode; 22 | uint32_t fb_width; 23 | uint32_t fb_height; 24 | uint32_t fb_bpp; 25 | }; 26 | 27 | struct multiboot1_elf_sections { 28 | uint32_t num; 29 | uint32_t size; 30 | uint32_t addr; 31 | uint32_t shndx; 32 | }; 33 | 34 | struct multiboot1_info { 35 | uint32_t flags; 36 | 37 | uint32_t mem_lower; 38 | uint32_t mem_upper; 39 | 40 | uint32_t boot_device; 41 | 42 | uint32_t cmdline; 43 | 44 | uint32_t mods_count; 45 | uint32_t mods_addr; 46 | 47 | struct multiboot1_elf_sections elf_sect; 48 | 49 | uint32_t mmap_length; 50 | uint32_t mmap_addr; 51 | 52 | uint32_t drives_length; 53 | uint32_t drivers_addr; 54 | 55 | uint32_t rom_config_table; 56 | 57 | uint32_t bootloader_name; 58 | 59 | uint32_t apm_table; 60 | 61 | uint32_t vbe_control_info; 62 | uint32_t vbe_mode_info; 63 | uint16_t vbe_mode; 64 | uint16_t vbe_interface_seg; 65 | uint16_t vbe_interface_off; 66 | uint16_t vbe_interface_len; 67 | 68 | uint64_t fb_addr; 69 | uint32_t fb_pitch; 70 | uint32_t fb_width; 71 | uint32_t fb_height; 72 | uint8_t fb_bpp; 73 | uint8_t fb_type; 74 | uint16_t fb_reserved; 75 | 76 | uint8_t fb_red_mask_shift; 77 | uint8_t fb_red_mask_size; 78 | uint8_t fb_green_mask_shift; 79 | uint8_t fb_green_mask_size; 80 | uint8_t fb_blue_mask_shift; 81 | uint8_t fb_blue_mask_size; 82 | }; 83 | 84 | struct multiboot1_module { 85 | uint32_t begin; 86 | uint32_t end; 87 | uint32_t cmdline; 88 | uint32_t pad; 89 | }; 90 | 91 | struct multiboot1_mmap_entry { 92 | uint32_t size; 93 | uint64_t addr; 94 | uint64_t len; 95 | uint32_t type; 96 | } __attribute__((packed)); 97 | 98 | noreturn void multiboot1_load(char *config, char *cmdline); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /common/lib/bli.c: -------------------------------------------------------------------------------- 1 | #if defined (UEFI) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define LIMINE_BRAND L"Limine " LIMINE_VERSION 13 | 14 | static EFI_GUID bli_vendor_guid = { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } }; 15 | 16 | // The buffer must be at least 21 bytes long 17 | void uint64_to_decwstr(uint64_t value, wchar_t *buf) { 18 | wchar_t tmp[21]; 19 | size_t i = 0; 20 | 21 | if (buf == NULL) { 22 | return; 23 | } 24 | 25 | if (value == 0) { 26 | buf[0] = '0'; 27 | buf[1] = '\0'; 28 | return; 29 | } 30 | 31 | // Convert digits in reverse order 32 | while (value > 0) { 33 | tmp[i++] = '0' + (value % 10); 34 | value /= 10; 35 | } 36 | 37 | // Reverse the string into the buffer 38 | for (size_t j = 0; j < i; j++) { 39 | buf[j] = tmp[i - j - 1]; 40 | } 41 | buf[i] = '\0'; 42 | } 43 | 44 | void bli_set_loader_time(wchar_t *variable, uint64_t time) { 45 | if (time == 0) 46 | return; 47 | 48 | wchar_t time_wstr[21]; 49 | uint64_to_decwstr(time, time_wstr); 50 | 51 | gRT->SetVariable(variable, 52 | &bli_vendor_guid, 53 | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 54 | sizeof(time_wstr), 55 | time_wstr); 56 | } 57 | 58 | void init_bli(void) { 59 | bli_set_loader_time(L"LoaderTimeInitUSec", usec_at_bootloader_entry); 60 | 61 | gRT->SetVariable(L"LoaderInfo", 62 | &bli_vendor_guid, 63 | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 64 | sizeof(LIMINE_BRAND), 65 | LIMINE_BRAND); 66 | 67 | char part_uuid_str[37]; 68 | guid_to_string(&boot_volume->part_guid, part_uuid_str); 69 | 70 | // Convert part_uuid_str to a wide-char string 71 | wchar_t part_uuid[37]; 72 | for (size_t i = 0; i < 37; i++) { 73 | part_uuid[i] = (wchar_t) part_uuid_str[i]; 74 | } 75 | 76 | gRT->SetVariable(L"LoaderDevicePartUUID", 77 | &bli_vendor_guid, 78 | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 79 | sizeof(part_uuid), 80 | part_uuid); 81 | } 82 | 83 | void bli_on_boot(void) { 84 | bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec()); 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /common/lib/elf.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__ELF_H__ 2 | #define LIB__ELF_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000) 10 | 11 | #define ELF_PF_X 1 12 | #define ELF_PF_W 2 13 | #define ELF_PF_R 4 14 | 15 | struct elf_section_hdr_info { 16 | uint32_t section_entry_size; 17 | uint32_t str_section_idx; 18 | uint32_t num; 19 | uint32_t section_offset; 20 | }; 21 | 22 | int elf_bits(uint8_t *elf); 23 | 24 | struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf); 25 | struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf); 26 | 27 | bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide); 28 | bool elf64_load(uint8_t *elf, 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); 29 | 30 | bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point, 31 | struct elsewhere_range **ranges); 32 | bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point, 33 | struct elsewhere_range **ranges); 34 | 35 | struct elf64_hdr { 36 | uint8_t ident[16]; 37 | uint16_t type; 38 | uint16_t machine; 39 | uint32_t version; 40 | uint64_t entry; 41 | uint64_t phoff; 42 | uint64_t shoff; 43 | uint32_t flags; 44 | uint16_t hdr_size; 45 | uint16_t phdr_size; 46 | uint16_t ph_num; 47 | uint16_t shdr_size; 48 | uint16_t sh_num; 49 | uint16_t shstrndx; 50 | }; 51 | 52 | struct elf64_shdr { 53 | uint32_t sh_name; 54 | uint32_t sh_type; 55 | uint64_t sh_flags; 56 | uint64_t sh_addr; 57 | uint64_t sh_offset; 58 | uint64_t sh_size; 59 | uint32_t sh_link; 60 | uint32_t sh_info; 61 | uint64_t sh_addralign; 62 | uint64_t sh_entsize; 63 | }; 64 | 65 | struct elf32_shdr { 66 | uint32_t sh_name; 67 | uint32_t sh_type; 68 | uint32_t sh_flags; 69 | uint32_t sh_addr; 70 | uint32_t sh_offset; 71 | uint32_t sh_size; 72 | uint32_t sh_link; 73 | uint32_t sh_info; 74 | uint32_t sh_addralign; 75 | uint32_t sh_entsize; 76 | }; 77 | 78 | struct elf64_sym { 79 | uint32_t st_name; 80 | uint8_t st_info; 81 | uint8_t st_other; 82 | uint16_t st_shndx; 83 | uint64_t st_value; 84 | uint64_t st_size; 85 | }; 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /common/sys/smp_trampoline.asm_riscv64: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .global smp_trampoline_start 4 | smp_trampoline_start: 5 | .option norelax 6 | // The AP begins executing here with the following state: 7 | // satp = 0 8 | // sstatus.SIE = 0 9 | // a0 = hartid 10 | // a1 = struct trampoline_passed_info * 11 | // 12 | // All other registers are undefined. 13 | 14 | #define smp_tpl_booted_flag 0 15 | #define smp_tpl_satp 8 16 | #define smp_tpl_info_struct 16 17 | #define smp_tpl_hhdm_offset 24 18 | 19 | ld a0, smp_tpl_info_struct(a1) 20 | ld t1, smp_tpl_hhdm_offset(a1) 21 | 22 | // Set `stvec` so we page fault into the higher half after loading `satp`. 23 | lla t0, 0f 24 | add t0, t1, t0 25 | csrw stvec, t0 26 | ld t0, smp_tpl_satp(a1) 27 | csrw satp, t0 28 | sfence.vma 29 | unimp 30 | 0: 31 | // Relocate the smp_info and passed_info pointers to the higher half. 32 | add a0, t1, a0 33 | add a1, t1, a1 34 | 35 | // Tell the BSP we've started. 36 | li t0, 1 37 | fence rw, w 38 | sd t0, (a1) 39 | 40 | // Zero all the things. 41 | // Preserve a0 42 | mv a1, zero 43 | mv a2, zero 44 | mv a3, zero 45 | mv a4, zero 46 | mv a5, zero 47 | mv a6, zero 48 | mv a7, zero 49 | mv s0, zero 50 | mv s1, zero 51 | mv s2, zero 52 | mv s3, zero 53 | mv s4, zero 54 | mv s5, zero 55 | mv s6, zero 56 | mv s7, zero 57 | mv s8, zero 58 | mv s9, zero 59 | mv s10, zero 60 | mv s11, zero 61 | mv t1, zero 62 | mv t2, zero 63 | mv t3, zero 64 | mv t4, zero 65 | mv t5, zero 66 | mv t6, zero 67 | mv tp, zero 68 | mv ra, zero 69 | 70 | csrw sie, zero 71 | csrw stvec, zero 72 | 73 | // Wait for kernel to tell us where to go. 74 | 0: .4byte 0x0100000f // pause 75 | ld t0, 24(a0) 76 | fence r, rw 77 | beqz t0, 0b 78 | 79 | // Load sp from reserved field of info struct 80 | ld sp, 16(a0) 81 | 82 | jr t0 83 | 84 | .section .note.GNU-stack,"",%progbits 85 | -------------------------------------------------------------------------------- /common/mm/pmm.h: -------------------------------------------------------------------------------- 1 | #ifndef MM__PMM_H__ 2 | #define MM__PMM_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct memmap_entry { 9 | uint64_t base; 10 | uint64_t length; 11 | uint32_t type; 12 | uint32_t unused; 13 | }; 14 | 15 | #define MEMMAP_USABLE 1 16 | #define MEMMAP_RESERVED 2 17 | #define MEMMAP_ACPI_RECLAIMABLE 3 18 | #define MEMMAP_ACPI_NVS 4 19 | #define MEMMAP_BAD_MEMORY 5 20 | #define MEMMAP_BOOTLOADER_RECLAIMABLE 0x1000 21 | #define MEMMAP_KERNEL_AND_MODULES 0x1001 22 | #define MEMMAP_FRAMEBUFFER 0x1002 23 | #define MEMMAP_ACPI_TABLES 0x1003 24 | #define MEMMAP_EFI_RECLAIMABLE 0x2000 25 | 26 | struct meminfo { 27 | size_t uppermem; 28 | size_t lowermem; 29 | }; 30 | 31 | struct meminfo mmap_get_info(size_t mmap_count, struct memmap_entry *mmap); 32 | 33 | #if defined (BIOS) 34 | extern struct memmap_entry memmap[]; 35 | extern size_t memmap_entries; 36 | #endif 37 | 38 | #if defined (UEFI) 39 | extern struct memmap_entry *memmap; 40 | extern size_t memmap_entries; 41 | 42 | extern struct memmap_entry *untouched_memmap; 43 | extern size_t untouched_memmap_entries; 44 | #endif 45 | 46 | extern bool allocations_disallowed; 47 | 48 | void init_memmap(void); 49 | struct memmap_entry *get_memmap(size_t *entries); 50 | struct memmap_entry *get_raw_memmap(size_t *entry_count); 51 | void print_memmap(struct memmap_entry *mm, size_t size); 52 | uint64_t pmm_check_type(uint64_t addr); 53 | bool memmap_alloc_range_in(struct memmap_entry *m, size_t *_count, 54 | uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool do_panic, bool simulation, bool new_entry); 55 | bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool panic, bool simulation, bool new_entry); 56 | void pmm_randomise_memory(void); 57 | 58 | void *ext_mem_alloc(size_t count); 59 | void *ext_mem_alloc_type(size_t count, uint32_t type); 60 | void *ext_mem_alloc_type_aligned(size_t count, uint32_t type, size_t alignment); 61 | void *ext_mem_alloc_type_aligned_mode(size_t count, uint32_t type, size_t alignment, bool allow_high_allocs); 62 | 63 | void *conv_mem_alloc(size_t count); 64 | 65 | void pmm_free(void *ptr, size_t length); 66 | void *pmm_realloc(void *old_ptr, size_t old_size, size_t new_size); 67 | 68 | #if defined (UEFI) 69 | void pmm_release_uefi_mem(void); 70 | #endif 71 | 72 | bool check_usable_memory(uint64_t base, uint64_t top); 73 | void pmm_sanitise_entries(struct memmap_entry *m, size_t *_count, bool align_entries); 74 | 75 | extern bool pmm_sanitiser_keep_first_page; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /common/linker_bios.ld.in: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf32-i386) 2 | ENTRY(_start) 3 | 4 | PHDRS 5 | { 6 | text_s2 PT_LOAD FLAGS(0x05); 7 | rodata_s2 PT_LOAD FLAGS(0x04); 8 | data_s2 PT_LOAD FLAGS(0x06); 9 | text_s3 PT_LOAD FLAGS(0x05); 10 | rodata_s3 PT_LOAD FLAGS(0x04); 11 | data_s3 PT_LOAD FLAGS(0x06); 12 | } 13 | 14 | SECTIONS 15 | { 16 | . = 0xf000; 17 | 18 | .text.stage2 : { 19 | *(.entry) 20 | *(.realmode) 21 | *.s2.o(.text .text.*) 22 | } :text_s2 23 | 24 | .rodata.stage2 : { 25 | *.s2.o(.rodata .rodata.*) 26 | 27 | build_id_s2 = .; 28 | KEEP(*build-id.s2.o(*)) 29 | 30 | #ifdef LINKER_STAGE2ONLY 31 | /* stage2 missing symbols overrides */ 32 | stage2_map = .; 33 | stage3_common = .; 34 | build_id_s3 = .; 35 | full_map = .; 36 | getchar = .; 37 | menu = .; 38 | booting_from_editor = .; 39 | flanterm_write = .; 40 | term_backend = .; 41 | term_fallback = .; 42 | term_notready = .; 43 | terms = .; 44 | terms_i = .; 45 | usec_at_bootloader_entry = .; 46 | serial_out = .; 47 | stage3_addr = .; 48 | #else 49 | #ifdef LINKER_NOS2MAP 50 | stage2_map = .; 51 | #else 52 | *(.stage2_map) 53 | #endif 54 | #endif 55 | } :rodata_s2 56 | 57 | .data.stage2 : { 58 | s2_data_begin = .; 59 | *.s2.o(.data .data.*) 60 | s2_data_end = .; 61 | } :data_s2 62 | 63 | .no_unwind.stage2 : { 64 | *.s2.o(.no_unwind) 65 | } :data_s2 66 | 67 | #ifndef LINKER_STAGE2ONLY 68 | .text.stage3 : { 69 | stage3_addr = .; 70 | *(.text .text.*) 71 | } :text_s3 72 | 73 | .rodata.stage3 : { 74 | *(.rodata .rodata.*) 75 | 76 | build_id_s3 = .; 77 | KEEP(*build-id.s3.o(*)) 78 | 79 | #ifdef LINKER_NOMAP 80 | full_map = .; 81 | #else 82 | *(.full_map) 83 | #endif 84 | } :rodata_s3 85 | 86 | .data.stage3 : { 87 | data_begin = .; 88 | *(.data .data.*) 89 | data_end = .; 90 | } :data_s3 91 | 92 | .no_unwind.stage3 : { 93 | *(.no_unwind) 94 | } :data_s3 95 | #endif 96 | 97 | .note.gnu.build-id : { 98 | *(.note.gnu.build-id) 99 | limine_bios_sys_size = . - 0xf000; 100 | } :data_s3 101 | 102 | .bss : { 103 | bss_begin = .; 104 | *(.bss .bss.*) 105 | *(COMMON) 106 | bss_end = .; 107 | } :data_s3 108 | 109 | /DISCARD/ : { 110 | *(.eh_frame*) 111 | *(.note .note.*) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /common/lib/rand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // TODO: Find where this mersenne twister implementation is inspired from 11 | // and properly credit the original author(s). 12 | 13 | static bool rand_initialised = false; 14 | 15 | #define n ((int)624) 16 | #define m ((int)397) 17 | #define matrix_a ((uint32_t)0x9908b0df) 18 | #define msb ((uint32_t)0x80000000) 19 | #define lsbs ((uint32_t)0x7fffffff) 20 | 21 | static uint32_t *status; 22 | static int ctr; 23 | 24 | static void init_rand(void) { 25 | uint32_t seed = ((uint32_t)0xc597060c * (uint32_t)rdtsc()) 26 | * ((uint32_t)0xce86d624) 27 | ^ ((uint32_t)0xee0da130 * (uint32_t)rdtsc()); 28 | 29 | // TODO(qookie): aarch64 also has an optional HW random number generator 30 | #if defined (__x86_64__) || defined(__i386__) 31 | uint32_t eax, ebx, ecx, edx; 32 | 33 | // Check for rdseed 34 | if (cpuid(0x07, 0, &eax, &ebx, &ecx, &edx) && (ebx & (1 << 18))) { 35 | seed *= (seed ^ rdseed(uint32_t)); 36 | } else if (cpuid(0x01, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 30))) { 37 | seed *= (seed ^ rdrand(uint32_t)); 38 | } 39 | #endif 40 | 41 | status = ext_mem_alloc(n * sizeof(uint32_t)); 42 | 43 | srand(seed); 44 | 45 | rand_initialised = true; 46 | } 47 | 48 | void srand(uint32_t s) { 49 | status[0] = s; 50 | for (ctr = 1; ctr < n; ctr++) 51 | status[ctr] = (1812433253 * (status[ctr - 1] ^ (status[ctr - 1] >> 30)) + ctr); 52 | } 53 | 54 | uint32_t rand32(void) { 55 | if (!rand_initialised) 56 | init_rand(); 57 | 58 | const uint32_t mag01[2] = {0, matrix_a}; 59 | 60 | if (ctr >= n) { 61 | for (int kk = 0; kk < n - m; kk++) { 62 | uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs); 63 | status[kk] = status[kk + m] ^ (y >> 1) ^ mag01[y & 1]; 64 | } 65 | 66 | for (int kk = n - m; kk < n - 1; kk++) { 67 | uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs); 68 | status[kk] = status[kk + (m - n)] ^ (y >> 1) ^ mag01[y & 1]; 69 | } 70 | 71 | uint32_t y = (status[n - 1] & msb) | (status[0] & lsbs); 72 | status[n - 1] = status[m - 1] ^ (y >> 1) ^ mag01[y & 1]; 73 | 74 | ctr = 0; 75 | } 76 | 77 | uint32_t res = status[ctr++]; 78 | 79 | res ^= (res >> 11); 80 | res ^= (res << 7) & 0x9d2c5680; 81 | res ^= (res << 15) & 0xefc60000; 82 | res ^= (res >> 18); 83 | 84 | return res; 85 | } 86 | 87 | uint64_t rand64(void) { 88 | return (((uint64_t)rand32()) << 32) | (uint64_t)rand32(); 89 | } 90 | -------------------------------------------------------------------------------- /decompressor/decompressor.mk: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | 3 | override SPACE := $(subst ,, ) 4 | 5 | override MKESCAPE = $(subst $(SPACE),\ ,$(1)) 6 | override SHESCAPE = $(subst ','\'',$(1)) 7 | override OBJESCAPE = $(subst .a ,.a' ',$(subst .o ,.o' ',$(call SHESCAPE,$(1)))) 8 | 9 | override CC_FOR_TARGET_IS_CLANG := $(shell ! $(CC_FOR_TARGET) --version 2>/dev/null | $(GREP) -q '^Target: '; echo $$?) 10 | 11 | ifeq ($(CC_FOR_TARGET_IS_CLANG),1) 12 | override CC_FOR_TARGET += \ 13 | -target i686-unknown-none-elf 14 | endif 15 | 16 | override CFLAGS_FOR_TARGET += \ 17 | -Os \ 18 | -Wall \ 19 | -Wextra \ 20 | -Wshadow \ 21 | -Wvla \ 22 | $(WERROR_FLAG) \ 23 | -std=gnu11 \ 24 | -nostdinc \ 25 | -ffreestanding \ 26 | -ffunction-sections \ 27 | -fdata-sections \ 28 | -fno-stack-protector \ 29 | -fno-stack-check \ 30 | -fomit-frame-pointer \ 31 | -fno-strict-aliasing \ 32 | -fno-lto \ 33 | -fno-PIC \ 34 | -m32 \ 35 | -march=i686 \ 36 | -mabi=sysv \ 37 | -mno-80387 \ 38 | -mno-mmx 39 | 40 | override CPPFLAGS_FOR_TARGET := \ 41 | -I . \ 42 | -I tinf \ 43 | -isystem ../freestnd-c-hdrs/include \ 44 | $(CPPFLAGS_FOR_TARGET) \ 45 | -MMD \ 46 | -MP 47 | 48 | override LDFLAGS_FOR_TARGET += \ 49 | -m elf_i386 \ 50 | -nostdlib \ 51 | -z max-page-size=0x1000 \ 52 | --gc-sections \ 53 | -static \ 54 | -T linker.ld 55 | 56 | override NASMFLAGS_FOR_TARGET := \ 57 | -f elf32 \ 58 | $(patsubst -g,-g -F dwarf,$(NASMFLAGS_FOR_TARGET)) \ 59 | -Wall \ 60 | -w-unknown-warning \ 61 | -w-reloc \ 62 | $(WERROR_FLAG) 63 | 64 | override C_FILES := $(shell find . -type f -name '*.c' | LC_ALL=C sort) 65 | override ASM_FILES := $(shell find . -type f -name '*.asm' | LC_ALL=C sort) 66 | override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(ASM_FILES:.asm=.o) $(C_FILES:.c=.o)) 67 | override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d)) 68 | 69 | .PHONY: all 70 | all: $(call MKESCAPE,$(BUILDDIR))/decompressor.bin 71 | 72 | $(call MKESCAPE,$(BUILDDIR))/decompressor.bin: $(OBJ) 73 | $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$(BUILDDIR))/decompressor.elf' 74 | $(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$(BUILDDIR))/decompressor.elf' '$(call SHESCAPE,$@)' 75 | 76 | -include $(HEADER_DEPS) 77 | 78 | $(call MKESCAPE,$(BUILDDIR))/%.o: %.c 79 | $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" 80 | $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)' 81 | 82 | $(call MKESCAPE,$(BUILDDIR))/%.o: %.asm 83 | $(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')" 84 | nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)' 85 | -------------------------------------------------------------------------------- /.forgejo/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Check for compilation failures 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Check for compilation failures 8 | runs-on: codeberg-small 9 | container: archlinux:latest 10 | 11 | steps: 12 | - name: Create resolv.conf 13 | run: | 14 | set -ex 15 | echo 'nameserver 8.8.8.8' >/etc/resolv.conf 16 | echo 'nameserver 8.8.4.4' >>/etc/resolv.conf 17 | 18 | - name: Install dependencies 19 | run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs base-devel git autoconf automake nasm curl mtools llvm clang lld 20 | 21 | - name: Checkout code 22 | uses: https://code.forgejo.org/actions/checkout@v6 23 | 24 | - name: Build the bootloader (LLVM, default) 25 | run: ./bootstrap && ./configure --enable-werror --enable-all && make all && make distclean 26 | 27 | - name: Set cross GCC version 28 | run: echo "GCC_VERSION=15.2.0" >> $FORGEJO_ENV 29 | 30 | - name: Download GCC cross toolchains 31 | run: | 32 | set -e 33 | for i in aarch64 loongarch64 riscv64 x86_64; do 34 | ( 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 ) & 35 | WAITING_ON_PIDS="$WAITING_ON_PIDS $!" 36 | done 37 | for pid in $WAITING_ON_PIDS; do 38 | wait $pid 39 | done 40 | 41 | - name: Build the bootloader (GNU, x86) 42 | 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 43 | 44 | - name: Build the bootloader (GNU, aarch64) 45 | 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 46 | 47 | - name: Build the bootloader (GNU, riscv64) 48 | 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 49 | 50 | - name: Build the bootloader (GNU, loongarch64) 51 | 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 52 | -------------------------------------------------------------------------------- /common/lib/spinup.asm_aarch64: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | .section .text 4 | 5 | // noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr, 6 | // uint64_t mair, uint64_t tcr, uint64_t ttbr0, 7 | // uint64_t ttbr1, uint64_t direct_map_offset) 8 | // Potentially drop to EL1 from EL2 (and also disable trapping to EL2), then 9 | // configure EL1 state and jump to kernel. 10 | 11 | .global enter_in_el1 12 | enter_in_el1: 13 | msr spsel, #0 14 | mov sp, x1 15 | 16 | PICK_EL x8, 0f, 2f 17 | 0: 18 | // Switch to the new page tables 19 | 20 | // Point the EL1t handler to the continuation, such that after we page fault, 21 | // execution continues and the kernel is entered. 22 | adrp x8, 1f 23 | add x8, x8, #:lo12:1f 24 | add x8, x8, x7 25 | msr vbar_el1, x8 26 | isb 27 | dsb sy 28 | isb 29 | 30 | // Switch the page table registers 31 | msr mair_el1, x3 32 | msr tcr_el1, x4 33 | msr ttbr0_el1, x5 34 | msr ttbr1_el1, x6 35 | msr sctlr_el1, x2 36 | isb 37 | dsb sy 38 | isb 39 | 40 | // Jump to the higher half mapping in case we didn't immediately crash 41 | br x8 42 | 43 | // Alignment required by VBAR register 44 | .align 11 45 | 1: 46 | // Zero out VBAR to avoid confusion 47 | msr vbar_el1, xzr 48 | 49 | // Enter kernel in EL1 50 | mov x8, #0x3c4 51 | msr spsr_el1, x8 52 | msr elr_el1, x0 53 | 54 | mov x0, xzr 55 | ZERO_REGS_EXCEPT_X0 56 | 57 | eret 58 | 59 | 2: 60 | // Check HCR_EL2.E2H 61 | mrs x8, hcr_el2 62 | tbnz x8, #34, 3f 63 | 64 | // Configure EL1 state (normal silicon) 65 | msr mair_el1, x3 66 | msr tcr_el1, x4 67 | msr ttbr0_el1, x5 68 | msr ttbr1_el1, x6 69 | msr sctlr_el1, x2 70 | dsb sy 71 | isb 72 | b 4f 73 | 74 | 3: 75 | // Configure EL1 state (apple silicon) 76 | msr s3_5_c10_c2_0, x3 // MAIR_EL12 77 | msr s3_5_c2_c0_2, x4 // TCR_EL12 78 | msr s3_5_c2_c0_0, x5 // TTBR0_EL12 79 | msr s3_5_c2_c0_1, x6 // TTBR1_EL12 80 | msr s3_5_c1_c0_0, x2 // SCTLR_EL12 81 | dsb sy 82 | isb 83 | 84 | 4: 85 | 86 | // Configure EL2-specific state for EL1 87 | 88 | // Don't trap counters to EL2 89 | mrs x8, cnthctl_el2 90 | orr x8, x8, #3 91 | msr cnthctl_el2, x8 92 | msr cntvoff_el2, xzr 93 | 94 | // Enable AArch64 in EL1 95 | ldr x8, =0x80000002 96 | msr hcr_el2, x8 97 | 98 | // Don't trap FP/SIMD to EL2 99 | mov x8, #0x33FF 100 | msr cptr_el2, x8 101 | msr hstr_el2, xzr 102 | 103 | // Enter kernel in EL1 104 | mov x8, #0x3c4 105 | msr spsr_el2, x8 106 | msr elr_el2, x0 107 | 108 | mov x0, xzr 109 | ZERO_REGS_EXCEPT_X0 110 | 111 | eret 112 | 113 | .section .note.GNU-stack,"",%progbits 114 | -------------------------------------------------------------------------------- /common/stb_image.patch: -------------------------------------------------------------------------------- 1 | --- common/lib/stb_image.h 2023-03-05 18:57:03.930649341 +0100 2 | +++ common/lib/stb_image.h 2023-03-05 18:55:53.767318782 +0100 3 | @@ -127,6 +127,54 @@ 4 | #ifndef STBI_INCLUDE_STB_IMAGE_H 5 | #define STBI_INCLUDE_STB_IMAGE_H 6 | 7 | +#include 8 | +#include 9 | +#include 10 | +#include 11 | + 12 | +#define STBI_ASSERT(x) 13 | + 14 | +#define STBI_MALLOC(x) ({ \ 15 | + size_t STBI_MALLOC_alloc_size = (x); \ 16 | + STBI_MALLOC_alloc_size += 16; \ 17 | + void *STBI_MALLOC_buf = ext_mem_alloc(STBI_MALLOC_alloc_size); \ 18 | + size_t *STBI_MALLOC_alloc_size_ptr = STBI_MALLOC_buf; \ 19 | + *STBI_MALLOC_alloc_size_ptr = STBI_MALLOC_alloc_size; \ 20 | + STBI_MALLOC_buf + 16; \ 21 | +}) 22 | + 23 | +#define STBI_FREE(x) do { \ 24 | + void *STBI_FREE_buf = (x); \ 25 | + if (STBI_FREE_buf == NULL) { \ 26 | + break; \ 27 | + } \ 28 | + STBI_FREE_buf -= 16; \ 29 | + size_t *STBI_FREE_alloc_size_ptr = STBI_FREE_buf; \ 30 | + size_t STBI_FREE_alloc_size = *STBI_FREE_alloc_size_ptr; \ 31 | + pmm_free(STBI_FREE_buf, STBI_FREE_alloc_size); \ 32 | +} while (0) 33 | + 34 | +#define STBI_REALLOC(x, y) ({ \ 35 | + void *STBI_REALLOC_buf = (x); \ 36 | + size_t STBI_REALLOC_alloc_size = (y); \ 37 | + void *STBI_REALLOC_new_buf = STBI_MALLOC(STBI_REALLOC_alloc_size); \ 38 | + if (STBI_REALLOC_buf != NULL) { \ 39 | + size_t STBI_REALLOC_old_size = (*(size_t *)((void *)STBI_REALLOC_buf - 16)) - 16; \ 40 | + memcpy(STBI_REALLOC_new_buf, STBI_REALLOC_buf, \ 41 | + MIN(STBI_REALLOC_alloc_size, STBI_REALLOC_old_size)); \ 42 | + STBI_FREE(STBI_REALLOC_buf); \ 43 | + } \ 44 | + STBI_REALLOC_new_buf; \ 45 | +}) 46 | + 47 | +#define STBI_NO_THREAD_LOCALS 48 | +#define STBI_NO_STDIO 49 | +#define STBI_NO_SIMD 50 | +#define STBI_NO_LINEAR 51 | +#define STBI_ONLY_JPEG 52 | +#define STBI_ONLY_PNG 53 | +#define STBI_ONLY_BMP 54 | + 55 | // DOCUMENTATION 56 | // 57 | // Limitations: 58 | @@ -381,7 +429,7 @@ 59 | STBI_rgb_alpha = 4 60 | }; 61 | 62 | -#include 63 | +// #include 64 | typedef unsigned char stbi_uc; 65 | typedef unsigned short stbi_us; 66 | 67 | @@ -584,8 +632,8 @@ 68 | 69 | #include 70 | #include // ptrdiff_t on osx 71 | -#include 72 | -#include 73 | +// #include 74 | +// #include 75 | #include 76 | 77 | #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) 78 | @@ -1574,10 +1622,12 @@ 79 | STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } 80 | #endif 81 | 82 | +/* 83 | static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; 84 | 85 | STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } 86 | STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } 87 | +*/ 88 | 89 | 90 | ////////////////////////////////////////////////////////////////////////////// 91 | -------------------------------------------------------------------------------- /common/pxe/pxe.h: -------------------------------------------------------------------------------- 1 | #ifndef PXE_H 2 | #define PXE_H 3 | 4 | #include 5 | #include 6 | 7 | #define DHCP_ACK_PACKET_LEN 296 8 | 9 | extern uint8_t cached_dhcp_packet[DHCP_ACK_PACKET_LEN]; 10 | extern bool cached_dhcp_ack_valid; 11 | 12 | #if defined (BIOS) 13 | 14 | struct volume *pxe_bind_volume(void); 15 | void pxe_init(void); 16 | int pxe_call(uint16_t opcode, uint16_t buf_seg, uint16_t buf_off); 17 | 18 | #define MAC_ADDR_LEN 16 19 | typedef uint8_t MAC_ADDR_t[MAC_ADDR_LEN]; 20 | 21 | struct bootph { 22 | uint8_t opcode; 23 | uint8_t Hardware; 24 | uint8_t Hardlen; 25 | uint8_t Gatehops; 26 | uint32_t ident; 27 | uint16_t seconds; 28 | uint16_t Flags; 29 | uint32_t cip; 30 | uint32_t yip; 31 | uint32_t sip; 32 | uint32_t gip; 33 | MAC_ADDR_t CAddr; 34 | uint8_t Sname[64]; 35 | uint8_t bootfile[128]; 36 | union bootph_vendor { 37 | uint8_t d[1024]; 38 | struct bootph_vendor_v { 39 | uint8_t magic[4]; 40 | uint32_t flags; 41 | uint8_t pad[52]; 42 | } v; 43 | } vendor; 44 | }; 45 | 46 | struct PXENV_UNDI_GET_INFORMATION { 47 | uint16_t Status; 48 | uint16_t BaseIo; 49 | uint16_t IntNumber; 50 | uint16_t MaxTranUnit; 51 | uint16_t HwType; 52 | uint16_t HwAddrLen; 53 | uint8_t CurrentNodeAddress[16]; 54 | uint8_t PermNodeAddress[16]; 55 | uint16_t ROMAddress; 56 | uint16_t RxBufCt; 57 | uint16_t TxBufCt; 58 | }; 59 | 60 | #define PXE_SIGNATURE "PXENV+" 61 | struct pxenv { 62 | uint8_t signature[6]; 63 | uint16_t version; 64 | uint8_t length; 65 | uint8_t checksum; 66 | uint32_t rm_entry; 67 | uint32_t pm_offset; 68 | uint16_t pm_selector; 69 | uint16_t stack_seg; 70 | uint16_t stack_size; 71 | uint16_t bc_code_seg; 72 | uint16_t bc_code_size; 73 | uint16_t bc_data_seg; 74 | uint16_t bc_data_size; 75 | uint16_t undi_data_seg; 76 | uint16_t undi_data_size; 77 | uint16_t undi_code_seg; 78 | uint16_t undi_code_size; 79 | uint32_t pxe_ptr; 80 | } __attribute__((packed)); 81 | 82 | #define PXE_BANGPXE_SIGNATURE "!PXE" 83 | struct bangpxe { 84 | uint8_t signature[4]; 85 | uint8_t length; 86 | uint8_t chksum; 87 | uint8_t rev; 88 | uint8_t reserved; 89 | uint32_t undiromid; 90 | uint32_t baseromid; 91 | uint32_t rm_entry; 92 | uint32_t pm_entry; 93 | } __attribute__((packed)); 94 | 95 | #define PXENV_GET_CACHED_INFO 0x0071 96 | #define PXENV_PACKET_TYPE_CACHED_REPLY 3 97 | struct pxenv_get_cached_info { 98 | uint16_t status; 99 | uint16_t packet_type; 100 | uint16_t buffer_size; 101 | uint32_t buffer; 102 | uint16_t buffer_limit; 103 | } __attribute__((packed)); 104 | 105 | #elif defined (UEFI) 106 | 107 | struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code); 108 | 109 | #endif 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /common/mm/pmm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static bool full_overlap_check(uint64_t base1, uint64_t top1, 9 | uint64_t base2, uint64_t top2) { 10 | return ((base1 >= base2 && base1 < top2) 11 | && (top1 > base2 && top1 <= top2)); 12 | } 13 | 14 | bool check_usable_memory(uint64_t base, uint64_t top) { 15 | uint64_t overlap_remaining = top - base; 16 | 17 | for (size_t i = 0; i < memmap_entries; i++) { 18 | if (memmap[i].type != MEMMAP_USABLE 19 | #if defined (UEFI) 20 | && memmap[i].type != MEMMAP_EFI_RECLAIMABLE 21 | #endif 22 | && memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE 23 | && memmap[i].type != MEMMAP_KERNEL_AND_MODULES) { 24 | continue; 25 | } 26 | 27 | uint64_t memmap_top = memmap[i].base + memmap[i].length; 28 | 29 | if (full_overlap_check(base, top, memmap[i].base, memmap_top)) { 30 | return true; 31 | } 32 | 33 | // Count how many bytes from a real-RAM entry overlap our range 34 | if ((memmap[i].base >= base && memmap[i].base < top) 35 | || (memmap_top > base && memmap_top <= top)) { 36 | uint64_t overlap_bottom = base; 37 | if (memmap[i].base >= base && memmap[i].base < top) { 38 | overlap_bottom = memmap[i].base; 39 | } 40 | 41 | uint64_t overlap_top = top; 42 | if (memmap_top > base && memmap_top <= top) { 43 | overlap_top = memmap_top; 44 | } 45 | 46 | uint64_t overlap_size = overlap_top - overlap_bottom; 47 | overlap_remaining -= overlap_size; 48 | 49 | if (overlap_remaining == 0) { 50 | return true; 51 | } 52 | } 53 | } 54 | 55 | return false; 56 | } 57 | 58 | void pmm_randomise_memory(void) { 59 | print("pmm: Randomising memory contents..."); 60 | 61 | for (size_t i = 0; i < memmap_entries; i++) { 62 | if (memmap[i].type != MEMMAP_USABLE) 63 | continue; 64 | 65 | #if defined (BIOS) 66 | // We're not going to randomise memory above 4GiB from protected mode, 67 | // are we? 68 | if (memmap[i].base >= 0x100000000) { 69 | continue; 70 | } 71 | #endif 72 | 73 | uint8_t *ptr = (void *)(uintptr_t)memmap[i].base; 74 | size_t len = memmap[i].length; 75 | 76 | for (size_t j = 0;;) { 77 | uint32_t random = rand32(); 78 | uint8_t *rnd_data = (void *)&random; 79 | if (j >= len) 80 | break; 81 | ptr[j++] = rnd_data[0]; 82 | if (j >= len) 83 | break; 84 | ptr[j++] = rnd_data[1]; 85 | if (j >= len) 86 | break; 87 | ptr[j++] = rnd_data[2]; 88 | if (j >= len) 89 | break; 90 | ptr[j++] = rnd_data[3]; 91 | } 92 | } 93 | 94 | print("\n"); 95 | } 96 | -------------------------------------------------------------------------------- /common/protos/limine_32.asm_x86: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | 5 | global limine_spinup_32 6 | limine_spinup_32: 7 | ; If available, set PAT as: 8 | ; PAT0 -> WB (06) 9 | ; PAT1 -> WT (04) 10 | ; PAT2 -> UC- (07) 11 | ; PAT3 -> UC (00) 12 | ; PAT4 -> WP (05) 13 | ; PAT5 -> WC (01) 14 | mov eax, 1 15 | xor ecx, ecx 16 | cpuid 17 | test edx, 1 << 16 18 | jz .no_pat 19 | mov eax, 0x00070406 20 | mov edx, 0x00000105 21 | mov ecx, 0x277 22 | wrmsr 23 | .no_pat: 24 | 25 | ; Enable EFER.NXE 26 | cmp dword [esp+32], 0 ; nx_available 27 | je .no_nx 28 | mov ecx, 0xc0000080 29 | rdmsr 30 | bts eax, 11 31 | wrmsr 32 | .no_nx: 33 | 34 | ; Enable CR4.LA57 35 | cmp dword [esp+4], 0 ; level5pg 36 | je .no_la57 37 | mov eax, cr4 38 | bts eax, 12 39 | mov cr4, eax 40 | .no_la57: 41 | 42 | ; Enable CR0.WP 43 | mov eax, cr0 44 | bts eax, 16 45 | mov cr0, eax 46 | 47 | cld 48 | 49 | mov eax, [esp+8] ; pagemap_top_lv 50 | mov cr3, eax 51 | 52 | ; Enable CR4.PAE 53 | mov eax, cr4 54 | bts eax, 5 55 | mov cr4, eax 56 | 57 | ; Enable EFER.LME 58 | mov ecx, 0xc0000080 59 | rdmsr 60 | bts eax, 8 61 | wrmsr 62 | 63 | ; Enable CR0.PG 64 | mov eax, cr0 65 | bts eax, 31 66 | mov cr0, eax 67 | 68 | ; Go 64 69 | push 0x28 70 | call .p1 71 | .p1: 72 | add dword [esp], .mode64 - .p1 73 | retfd 74 | 75 | bits 64 76 | .mode64: 77 | mov eax, 0x30 78 | mov ds, eax 79 | mov es, eax 80 | mov fs, eax 81 | mov gs, eax 82 | mov ss, eax 83 | 84 | ; Load 64-bit GDT 85 | mov eax, [rsp+28] ; local_gdt 86 | lgdt [rax] 87 | 88 | ; Jump to higher half 89 | mov rax, qword [rsp+36] 90 | add rsp, rax 91 | call .p2 92 | .p2: 93 | add qword [rsp], .hh - .p2 94 | add qword [rsp], rax 95 | retq 96 | .hh: 97 | 98 | cmp dword [rsp+44], 1 99 | jb .no_unmap_lower_half 100 | 101 | ; Unmap lower half entirely 102 | mov rsi, cr3 103 | lea rdi, [rsi + rax] 104 | mov rcx, 256 105 | xor rax, rax 106 | rep stosq 107 | mov cr3, rsi 108 | 109 | .no_unmap_lower_half: 110 | 111 | ; Push fake return address 112 | mov rsi, [rsp+20] ; stack 113 | sub rsi, 8 114 | mov qword [rsi], 0 115 | 116 | ; Prepare iretq frame 117 | mov rax, qword [rsp+12] ; entry_point 118 | push 0x30 119 | push rsi 120 | pushfq 121 | push 0x28 122 | push rax 123 | 124 | ; Zero out all GPRs 125 | xor eax, eax 126 | xor ebx, ebx 127 | xor ecx, ecx 128 | xor edx, edx 129 | xor esi, esi 130 | xor edi, edi 131 | xor ebp, ebp 132 | xor r8d, r8d 133 | xor r9d, r9d 134 | xor r10d, r10d 135 | xor r11d, r11d 136 | xor r12d, r12d 137 | xor r13d, r13d 138 | xor r14d, r14d 139 | xor r15d, r15d 140 | 141 | iretq 142 | 143 | section .note.GNU-stack noalloc noexec nowrite progbits 144 | -------------------------------------------------------------------------------- /common/lib/guid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | bool is_valid_guid(const char *s) { 8 | for (size_t i = 0; ; i++) { 9 | switch (i) { 10 | case 8: 11 | case 13: 12 | case 18: 13 | case 23: 14 | if (s[i] != '-') 15 | return false; 16 | break; 17 | case 36: 18 | return s[i] == 0; 19 | default: 20 | if (digit_to_int(s[i]) == -1) 21 | return false; 22 | break; 23 | } 24 | } 25 | } 26 | 27 | static void guid_convert_le_cluster(uint8_t *dest, const char *s, int len) { 28 | size_t p = 0; 29 | for (int i = len - 1; i >= 0; i--) { 30 | int val = digit_to_int(s[i]); 31 | 32 | i % 2 ? (dest[p] = val) : (dest[p++] |= val << 4); 33 | } 34 | } 35 | 36 | static void guid_convert_be_cluster(uint8_t *dest, const char *s, int len) { 37 | size_t p = 0; 38 | for (int i = 0; i < len; i++) { 39 | int val = digit_to_int(s[i]); 40 | 41 | i % 2 ? (dest[p++] |= val) : (dest[p] = val << 4); 42 | } 43 | } 44 | 45 | bool string_to_guid_be(struct guid *guid, const char *s) { 46 | if (!is_valid_guid(s)) 47 | return false; 48 | 49 | guid_convert_be_cluster((uint8_t *)guid + 0, s + 0, 8); 50 | guid_convert_be_cluster((uint8_t *)guid + 4, s + 9, 4); 51 | guid_convert_be_cluster((uint8_t *)guid + 6, s + 14, 4); 52 | guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4); 53 | guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12); 54 | 55 | return true; 56 | } 57 | 58 | bool string_to_guid_mixed(struct guid *guid, const char *s) { 59 | if (!is_valid_guid(s)) 60 | return false; 61 | 62 | guid_convert_le_cluster((uint8_t *)guid + 0, s + 0, 8); 63 | guid_convert_le_cluster((uint8_t *)guid + 4, s + 9, 4); 64 | guid_convert_le_cluster((uint8_t *)guid + 6, s + 14, 4); 65 | guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4); 66 | guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12); 67 | 68 | return true; 69 | } 70 | 71 | static void uint_to_hex(uint32_t num, char *dest, int len) { 72 | const char digits[] = "0123456789abcdef"; 73 | for (int i = 0; i < len; i++) { 74 | dest[i] = digits[(num >> ((len - 1 - i) * 4)) & 0xf]; 75 | } 76 | } 77 | 78 | void guid_to_string(const struct guid *guid, char *s) { 79 | uint_to_hex(guid->a, s, 8); 80 | s[8] = '-'; 81 | uint_to_hex(guid->b, s + 9, 4); 82 | s[13] = '-'; 83 | uint_to_hex(guid->c, s + 14, 4); 84 | s[18] = '-'; 85 | uint_to_hex(guid->d[0], s + 19, 2); 86 | uint_to_hex(guid->d[1], s + 21, 2); 87 | s[23] = '-'; 88 | uint_to_hex(guid->d[2], s + 24, 2); 89 | uint_to_hex(guid->d[3], s + 26, 2); 90 | uint_to_hex(guid->d[4], s + 28, 2); 91 | uint_to_hex(guid->d[5], s + 30, 2); 92 | uint_to_hex(guid->d[6], s + 32, 2); 93 | uint_to_hex(guid->d[7], s + 34, 2); 94 | s[36] = 0; 95 | } 96 | -------------------------------------------------------------------------------- /common/lib/part.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB__PART_H__ 2 | #define LIB__PART_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #if defined (UEFI) 9 | # include 10 | # include 11 | #endif 12 | 13 | #define NO_PARTITION (-1) 14 | #define INVALID_TABLE (-2) 15 | #define END_OF_TABLE (-3) 16 | 17 | struct volume { 18 | #if defined (UEFI) 19 | EFI_HANDLE efi_handle; 20 | 21 | // Block storage 22 | EFI_HANDLE efi_part_handle; 23 | EFI_BLOCK_IO *block_io; 24 | 25 | // PXE 26 | EFI_PXE_BASE_CODE_PROTOCOL *pxe_base_code; 27 | 28 | bool unique_sector_valid; 29 | uint8_t unique_sector_b2b[BLAKE2B_OUT_BYTES]; 30 | #elif defined (BIOS) 31 | int drive; 32 | #endif 33 | 34 | size_t fastest_xfer_size; 35 | 36 | int index; 37 | 38 | bool is_optical; 39 | bool pxe; 40 | 41 | int partition; 42 | int sector_size; 43 | struct volume *backing_dev; 44 | 45 | int max_partition; 46 | 47 | int cache_status; 48 | uint8_t *cache; 49 | uint64_t cached_block; 50 | 51 | uint64_t first_sect; 52 | uint64_t sect_count; 53 | 54 | bool guid_valid; 55 | struct guid guid; 56 | bool part_guid_valid; 57 | struct guid part_guid; 58 | bool fslabel_valid; 59 | char *fslabel; 60 | }; 61 | 62 | bool is_valid_mbr(struct volume *volume); 63 | 64 | extern struct volume **volume_index; 65 | extern size_t volume_index_i; 66 | 67 | bool gpt_get_guid(struct guid *guid, struct volume *volume); 68 | uint32_t mbr_get_id(struct volume *volume); 69 | 70 | int part_get(struct volume *part, struct volume *volume, int partition); 71 | 72 | struct volume *volume_get_by_guid(struct guid *guid); 73 | struct volume *volume_get_by_fslabel(char *fslabel); 74 | struct volume *volume_get_by_coord(bool optical, int drive, int partition); 75 | #if defined (BIOS) 76 | struct volume *volume_get_by_bios_drive(int drive); 77 | #endif 78 | 79 | bool volume_read(struct volume *part, void *buffer, uint64_t loc, uint64_t count); 80 | 81 | #define volume_iterate_parts(_VOLUME_, _BODY_) do { \ 82 | struct volume *_VOLUME = _VOLUME_; \ 83 | if (_VOLUME->pxe) { \ 84 | do { \ 85 | struct volume *_PART = _VOLUME; \ 86 | _BODY_ \ 87 | } while (0); \ 88 | } else { \ 89 | while (_VOLUME->backing_dev != NULL) { \ 90 | _VOLUME = _VOLUME->backing_dev; \ 91 | } \ 92 | \ 93 | int _PART_CNT = -1; \ 94 | for (size_t _PARTNO = 0; ; _PARTNO++) { \ 95 | if (_PART_CNT > _VOLUME->max_partition) \ 96 | break; \ 97 | \ 98 | struct volume *_PART = volume_get_by_coord(_VOLUME->is_optical, \ 99 | _VOLUME->index, _PARTNO); \ 100 | if (_PART == NULL) \ 101 | continue; \ 102 | \ 103 | _PART_CNT++; \ 104 | \ 105 | _BODY_ \ 106 | } \ 107 | } \ 108 | } while (0) 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /common/protos/linux.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOS__LINUX_H__ 2 | #define PROTOS__LINUX_H__ 3 | 4 | #include 5 | 6 | struct screen_info { 7 | uint8_t orig_x; /* 0x00 */ 8 | uint8_t orig_y; /* 0x01 */ 9 | uint16_t ext_mem_k; /* 0x02 */ 10 | uint16_t orig_video_page; /* 0x04 */ 11 | uint8_t orig_video_mode; /* 0x06 */ 12 | uint8_t orig_video_cols; /* 0x07 */ 13 | uint8_t flags; /* 0x08 */ 14 | uint8_t unused2; /* 0x09 */ 15 | uint16_t orig_video_ega_bx;/* 0x0a */ 16 | uint16_t unused3; /* 0x0c */ 17 | uint8_t orig_video_lines; /* 0x0e */ 18 | uint8_t orig_video_isVGA; /* 0x0f */ 19 | uint16_t orig_video_points;/* 0x10 */ 20 | 21 | /* VESA graphic mode -- linear frame buffer */ 22 | uint16_t lfb_width; /* 0x12 */ 23 | uint16_t lfb_height; /* 0x14 */ 24 | uint16_t lfb_depth; /* 0x16 */ 25 | uint32_t lfb_base; /* 0x18 */ 26 | uint32_t lfb_size; /* 0x1c */ 27 | uint16_t cl_magic, cl_offset; /* 0x20 */ 28 | uint16_t lfb_linelength; /* 0x24 */ 29 | uint8_t red_size; /* 0x26 */ 30 | uint8_t red_pos; /* 0x27 */ 31 | uint8_t green_size; /* 0x28 */ 32 | uint8_t green_pos; /* 0x29 */ 33 | uint8_t blue_size; /* 0x2a */ 34 | uint8_t blue_pos; /* 0x2b */ 35 | uint8_t rsvd_size; /* 0x2c */ 36 | uint8_t rsvd_pos; /* 0x2d */ 37 | uint16_t vesapm_seg; /* 0x2e */ 38 | uint16_t vesapm_off; /* 0x30 */ 39 | uint16_t pages; /* 0x32 */ 40 | uint16_t vesa_attributes; /* 0x34 */ 41 | uint32_t capabilities; /* 0x36 */ 42 | uint32_t ext_lfb_base; /* 0x3a */ 43 | uint8_t _reserved[2]; /* 0x3e */ 44 | } __attribute__((packed)); 45 | 46 | #define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ 47 | #define VIDEO_TYPE_CGA 0x11 /* CGA Display */ 48 | #define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ 49 | #define VIDEO_TYPE_EGAC 0x21 /* EGA in Color Mode */ 50 | #define VIDEO_TYPE_VGAC 0x22 /* VGA+ in Color Mode */ 51 | #define VIDEO_TYPE_VLFB 0x23 /* VESA VGA in graphic mode */ 52 | 53 | #define VIDEO_TYPE_PICA_S3 0x30 /* ACER PICA-61 local S3 video */ 54 | #define VIDEO_TYPE_MIPS_G364 0x31 /* MIPS Magnum 4000 G364 video */ 55 | #define VIDEO_TYPE_SGI 0x33 /* Various SGI graphics hardware */ 56 | 57 | #define VIDEO_TYPE_TGAC 0x40 /* DEC TGA */ 58 | 59 | #define VIDEO_TYPE_SUN 0x50 /* Sun frame buffer. */ 60 | #define VIDEO_TYPE_SUNPCI 0x51 /* Sun PCI based frame buffer. */ 61 | 62 | #define VIDEO_TYPE_PMAC 0x60 /* PowerMacintosh frame buffer. */ 63 | 64 | #define VIDEO_TYPE_EFI 0x70 /* EFI graphic mode */ 65 | 66 | #define VIDEO_FLAGS_NOCURSOR (1 << 0) /* The video mode has no cursor set */ 67 | 68 | #define VIDEO_CAPABILITY_SKIP_QUIRKS (1 << 0) 69 | #define VIDEO_CAPABILITY_64BIT_BASE (1 << 1) /* Frame buffer base is 64-bit */ 70 | 71 | noreturn void linux_load(char *config, char *cmdline); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /common/lib/libc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Slightly adapted strtoul() implementation from FreeBSD. 10 | // https://github.com/freebsd/freebsd-src/blob/de1aa3dab23c06fec962a14da3e7b4755c5880cf/lib/libc/stdlib/strtoul.c 11 | unsigned long strtoul(const char *nptr, char **endptr, int base) { 12 | const char *s; 13 | unsigned long acc; 14 | char c; 15 | unsigned long cutoff; 16 | int neg, any, cutlim; 17 | 18 | s = nptr; 19 | do { 20 | c = *s++; 21 | } while (isspace((unsigned char)c)); 22 | if (c == '-') { 23 | neg = 1; 24 | c = *s++; 25 | } else { 26 | neg = 0; 27 | if (c == '+') 28 | c = *s++; 29 | } 30 | if ((base == 0 || base == 16) && 31 | c == '0' && (*s == 'x' || *s == 'X') && 32 | ((s[1] >= '0' && s[1] <= '9') || 33 | (s[1] >= 'A' && s[1] <= 'F') || 34 | (s[1] >= 'a' && s[1] <= 'f'))) { 35 | c = s[1]; 36 | s += 2; 37 | base = 16; 38 | } 39 | if (base == 0) 40 | base = c == '0' ? 8 : 10; 41 | acc = any = 0; 42 | if (base < 2 || base > 36) 43 | goto noconv; 44 | 45 | cutoff = ULONG_MAX / base; 46 | cutlim = ULONG_MAX % base; 47 | for ( ; ; c = *s++) { 48 | if (c >= '0' && c <= '9') 49 | c -= '0'; 50 | else if (c >= 'A' && c <= 'Z') 51 | c -= 'A' - 10; 52 | else if (c >= 'a' && c <= 'z') 53 | c -= 'a' - 10; 54 | else 55 | break; 56 | if (c >= base) 57 | break; 58 | if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) 59 | any = -1; 60 | else { 61 | any = 1; 62 | acc *= base; 63 | acc += c; 64 | } 65 | } 66 | if (any < 0) { 67 | acc = ULONG_MAX; 68 | //errno = ERANGE; 69 | } else if (!any) { 70 | noconv: 71 | ;//errno = EINVAL; 72 | } else if (neg) 73 | acc = -acc; 74 | if (endptr != NULL) 75 | *endptr = (char *)(any ? s - 1 : nptr); 76 | return (acc); 77 | } 78 | 79 | size_t strnlen(const char *str, size_t maxlen) { 80 | size_t len; 81 | 82 | for (len = 0; len < maxlen && str[len]; len++); 83 | 84 | return len; 85 | } 86 | 87 | void *memchr(const void *ptr, int ch, size_t n) { 88 | uint8_t *p = (uint8_t *)ptr; 89 | 90 | for (size_t i = 0; i < n; i++) { 91 | if (p[i] == ch) { 92 | return (void *)ptr + i; 93 | } 94 | } 95 | 96 | return NULL; 97 | } 98 | 99 | char *strchr(const char *str, int ch) { 100 | for (size_t i = 0; str[i]; i++) { 101 | if (str[i] == ch) { 102 | return (char *)str + i; 103 | } 104 | } 105 | 106 | return NULL; 107 | } 108 | 109 | char *strrchr(const char *str, int ch) { 110 | char *p = NULL; 111 | 112 | for (size_t i = 0; str[i]; i++) { 113 | if (str[i] == ch) { 114 | p = (char *)str + i; 115 | } 116 | } 117 | 118 | return p; 119 | } 120 | -------------------------------------------------------------------------------- /common/lib/sleep.asm_bios_ia32: -------------------------------------------------------------------------------- 1 | section .realmode 2 | 3 | int_08_ticks_counter: dd 0 4 | int_08_callback: dd 0 5 | 6 | int_08_isr: 7 | bits 16 8 | pushf 9 | inc dword [cs:int_08_ticks_counter] 10 | popf 11 | jmp far [cs:int_08_callback] 12 | bits 32 13 | 14 | extern getchar_internal 15 | 16 | global _pit_sleep_and_quit_on_keypress 17 | _pit_sleep_and_quit_on_keypress: 18 | ; Hook int 0x08 19 | mov edx, dword [0x08*4] 20 | mov dword [int_08_callback], edx 21 | mov dword [0x08*4], int_08_isr 22 | 23 | ; pit_ticks in edx 24 | mov edx, dword [esp+4] 25 | 26 | mov dword [int_08_ticks_counter], 0 27 | 28 | ; Save GDT in case BIOS overwrites it 29 | sgdt [.gdt] 30 | 31 | ; Save IDT 32 | sidt [.idt] 33 | 34 | ; Load BIOS IVT 35 | lidt [.rm_idt] 36 | 37 | ; Save non-scratch GPRs 38 | push ebx 39 | push esi 40 | push edi 41 | push ebp 42 | 43 | ; Jump to real mode 44 | jmp 0x08:.bits16 45 | .bits16: 46 | bits 16 47 | mov ax, 0x10 48 | mov ds, ax 49 | mov es, ax 50 | mov fs, ax 51 | mov gs, ax 52 | mov ss, ax 53 | mov eax, cr0 54 | and al, 0xfe 55 | mov cr0, eax 56 | jmp 0x00:.cszero 57 | .cszero: 58 | xor ax, ax 59 | mov ds, ax 60 | mov es, ax 61 | mov fs, ax 62 | mov gs, ax 63 | mov ss, ax 64 | 65 | sti 66 | 67 | mov byte [.mods], 0 68 | mov byte [.ascii], 0 69 | mov byte [.scan], 0 70 | 71 | .loop: 72 | cmp dword [int_08_ticks_counter], edx 73 | je .done 74 | 75 | push ecx 76 | push edx 77 | mov ah, 0x01 78 | xor al, al 79 | int 0x16 80 | pop edx 81 | pop ecx 82 | 83 | jz .loop 84 | 85 | ; on keypress 86 | xor ax, ax 87 | int 0x16 88 | mov byte [.ascii], al 89 | mov byte [.scan], ah 90 | 91 | mov ax, 0x0200 92 | int 0x16 93 | test al, 0x04 94 | jz .done 95 | 96 | ; ctrl handling 97 | mov byte [.mods], 0x04 98 | add byte [.ascii], 0x60 99 | 100 | .done: 101 | cli 102 | 103 | ; Restore GDT 104 | o32 lgdt [ss:.gdt] 105 | 106 | ; Restore IDT 107 | o32 lidt [ss:.idt] 108 | 109 | ; Jump back to pmode 110 | mov ebx, cr0 111 | or bl, 1 112 | mov cr0, ebx 113 | jmp 0x18:.bits32 114 | .bits32: 115 | bits 32 116 | mov bx, 0x20 117 | mov ds, bx 118 | mov es, bx 119 | mov fs, bx 120 | mov gs, bx 121 | mov ss, bx 122 | 123 | ; Restore non-scratch GPRs 124 | pop ebp 125 | pop edi 126 | pop esi 127 | pop ebx 128 | 129 | ; Dehook int 0x08 130 | mov edx, dword [int_08_callback] 131 | mov dword [0x08*4], edx 132 | 133 | cmp byte [.scan], 0 134 | je .fail 135 | 136 | push dword [.mods] 137 | push dword [.ascii] 138 | push dword [.scan] 139 | call getchar_internal 140 | add esp, 3*4 141 | 142 | ret 143 | 144 | .fail: 145 | xor eax, eax 146 | ret 147 | 148 | .gdt: dq 0 149 | .idt: dq 0 150 | .rm_idt: dw 0x3ff 151 | dd 0 152 | 153 | .mods: dd 0 154 | .ascii: dd 0 155 | .scan: dd 0 156 | 157 | section .note.GNU-stack noalloc noexec nowrite progbits 158 | -------------------------------------------------------------------------------- /3RDPARTY.md: -------------------------------------------------------------------------------- 1 | # 3rd Party Software Acknowledgments 2 | 3 | The Limine project depends on several other projects. 4 | 5 | (For readers with access to source code, know that these are pulled in by the 6 | `./bootstrap` script, or, in the case of release tarballs, are shipped 7 | alongside the core Limine code in the tarballs themselves, similar to 8 | `./bootstrap` having already been run.) 9 | 10 | These additional projects are NOT covered by the License as contained inside 11 | the `COPYING` file as present at the root of the source tree, or, for installed 12 | copies, present at `${DOCDIR}/COPYING` (assuming the file has not been 13 | otherwise removed by the packager). These are instead licensed as described by 14 | each individual project's documentation present in each project's dedicated 15 | subdirectory or license header(s) in the source tree. For readers without access 16 | to the source code, one can read the following for a quick overview of licenses 17 | that Limine is distributed under: 18 | 19 | A non-binding, informal summary of all projects Limine depends on, and the 20 | licenses used by said projects, in SPDX format, is as follows: 21 | 22 | - [cc-runtime](https://codeberg.org/OSDev/cc-runtime) 23 | (Apache-2.0 WITH LLVM-exception) is used to provide runtime libgcc-like 24 | routines. 25 | 26 | - [0BSD Freestanding C Headers](https://codeberg.org/OSDev/freestnd-c-hdrs-0bsd) 27 | (0BSD) provide GCC and Clang compatible freestanding C headers. 28 | 29 | - [Limine Boot Protocol](https://codeberg.org/Limine/limine-protocol) 30 | (0BSD) has the C/C++ header and the specification text of the Limine Boot 31 | Protocol. 32 | 33 | - [PicoEFI](https://codeberg.org/PicoEFI/PicoEFI) (multiple licenses, see list 34 | below) provides headers and build-time support for UEFI. 35 | - BSD-2-Clause 36 | - BSD-2-Clause-Patent 37 | - BSD-3-Clause 38 | - LicenseRef-scancode-bsd-no-disclaimer-unmodified 39 | - MIT 40 | 41 | For more information about the 42 | LicenseRef-scancode-bsd-no-disclaimer-unmodified license used by parts of 43 | PicoEFI, see 44 | https://scancode-licensedb.aboutcode.org/bsd-no-disclaimer-unmodified.html 45 | and the LicenseRef file 46 | [here](LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt), 47 | in case of viewing this file from inside the source tree, alternatively at 48 | `${DOCDIR}/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt` 49 | in case of installed copies, assuming the file has not been otherwise 50 | removed by the packager. 51 | 52 | - [tinf](https://github.com/jibsen/tinf) (Zlib) is used in early x86 BIOS 53 | stages for GZIP decompression of stage2. 54 | 55 | - [Flanterm](https://codeberg.org/Mintsuki/Flanterm) (BSD-2-Clause) is used for 56 | text related screen drawing. 57 | 58 | - [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) (MIT) is 59 | used for wallpaper image loading. 60 | 61 | - [libfdt](https://codeberg.org/OSDev/libfdt) (BSD-2-Clause) is used for 62 | manipulating Flat Device Trees. 63 | 64 | Note that some of these projects, or parts of them, are provided under 65 | dual-licensing, in which case, in the above list, the only license mentioned is 66 | the one chosen by the Limine developers. Refer to each individual project's 67 | documentation for details. 68 | -------------------------------------------------------------------------------- /common/lib/elsewhere.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static bool elsewhere_overlap_check(uint64_t base1, uint64_t top1, 9 | uint64_t base2, uint64_t top2) { 10 | return ((base1 >= base2 && base1 < top2) 11 | || (top1 > base2 && top1 <= top2)); 12 | } 13 | 14 | bool elsewhere_append( 15 | bool flexible_target, 16 | struct elsewhere_range *ranges, uint64_t *ranges_count, 17 | void *elsewhere, uint64_t *target, size_t t_length) { 18 | // original target of -1 means "allocate after top of all ranges" 19 | // flexible target is ignored 20 | flexible_target = true; 21 | if (*target == (uint64_t)-1) { 22 | uint64_t top = 0; 23 | 24 | for (size_t i = 0; i < *ranges_count; i++) { 25 | uint64_t r_top = ranges[i].target + ranges[i].length; 26 | 27 | if (top < r_top) { 28 | top = r_top; 29 | } 30 | } 31 | 32 | *target = ALIGN_UP(top, 4096); 33 | } 34 | 35 | uint64_t max_retries = 0x10000; 36 | 37 | retry: 38 | if (max_retries-- == 0) { 39 | return false; 40 | } 41 | 42 | for (size_t i = 0; i < *ranges_count; i++) { 43 | uint64_t t_top = *target + t_length; 44 | 45 | // Does it overlap with other elsewhere ranges targets? 46 | { 47 | uint64_t base = ranges[i].target; 48 | uint64_t length = ranges[i].length; 49 | uint64_t top = base + length; 50 | 51 | if (elsewhere_overlap_check(base, top, *target, t_top)) { 52 | if (!flexible_target) { 53 | return false; 54 | } 55 | *target = ALIGN_UP(top, 4096); 56 | goto retry; 57 | } 58 | } 59 | 60 | // Does it overlap with other elsewhere ranges sources? 61 | { 62 | uint64_t base = ranges[i].elsewhere; 63 | uint64_t length = ranges[i].length; 64 | uint64_t top = base + length; 65 | 66 | if (elsewhere_overlap_check(base, top, *target, t_top)) { 67 | if (!flexible_target) { 68 | return false; 69 | } 70 | *target += 0x1000; 71 | goto retry; 72 | } 73 | } 74 | 75 | // Make sure it is memory that actually exists. 76 | if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE, 77 | MEMMAP_USABLE, false, true, false)) { 78 | if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE, 79 | MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, false)) { 80 | if (!flexible_target) { 81 | return false; 82 | } 83 | *target += 0x1000; 84 | goto retry; 85 | } 86 | } 87 | } 88 | 89 | // Add the elsewhere range 90 | ranges[*ranges_count].elsewhere = (uintptr_t)elsewhere; 91 | ranges[*ranges_count].target = *target; 92 | ranges[*ranges_count].length = t_length; 93 | *ranges_count += 1; 94 | 95 | return true; 96 | } 97 | -------------------------------------------------------------------------------- /common/lib/misc.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | bool verbose = false; 7 | bool quiet = false; 8 | bool serial = false; 9 | bool hash_mismatch_panic = false; 10 | 11 | uint8_t bcd_to_int(uint8_t val) { 12 | return (val & 0x0f) + ((val & 0xf0) >> 4) * 10; 13 | } 14 | uint8_t int_to_bcd(uint8_t val) { 15 | return (val % 10) | (val / 10) << 4; 16 | } 17 | 18 | int digit_to_int(char c) { 19 | if (c >= 'a' && c <= 'f') { 20 | return (c - 'a') + 10; 21 | } 22 | if (c >= 'A' && c <= 'F') { 23 | return (c - 'A') + 10; 24 | } 25 | if (c >= '0' && c <= '9'){ 26 | return c - '0'; 27 | } 28 | 29 | return -1; 30 | } 31 | 32 | uint64_t strtoui(const char *s, const char **end, int base) { 33 | uint64_t n = 0; 34 | for (size_t i = 0; ; i++) { 35 | int d = digit_to_int(s[i]); 36 | if (d == -1) { 37 | if (end != NULL) 38 | *end = &s[i]; 39 | break; 40 | } 41 | n = n * base + d; 42 | } 43 | return n; 44 | } 45 | 46 | void get_absolute_path(char *path_ptr, const char *path, const char *pwd) { 47 | char *orig_ptr = path_ptr; 48 | 49 | if (!*path) { 50 | strcpy(path_ptr, pwd); 51 | return; 52 | } 53 | 54 | if (*path != '/') { 55 | strcpy(path_ptr, pwd); 56 | path_ptr += strlen(path_ptr); 57 | } else { 58 | *path_ptr = '/'; 59 | path_ptr++; 60 | path++; 61 | } 62 | 63 | goto first_run; 64 | 65 | for (;;) { 66 | switch (*path) { 67 | case '/': 68 | path++; 69 | first_run: 70 | if (*path == '/') continue; 71 | if ((!strncmp(path, ".\0", 2)) 72 | || (!strncmp(path, "./\0", 3))) { 73 | goto term; 74 | } 75 | if ((!strncmp(path, "..\0", 3)) 76 | || (!strncmp(path, "../\0", 4))) { 77 | while (*path_ptr != '/') path_ptr--; 78 | if (path_ptr == orig_ptr) path_ptr++; 79 | goto term; 80 | } 81 | if (!strncmp(path, "../", 3)) { 82 | while (*path_ptr != '/') path_ptr--; 83 | if (path_ptr == orig_ptr) path_ptr++; 84 | path += 2; 85 | *path_ptr = 0; 86 | continue; 87 | } 88 | if (!strncmp(path, "./", 2)) { 89 | path += 1; 90 | continue; 91 | } 92 | if (((path_ptr - 1) != orig_ptr) && (*(path_ptr - 1) != '/')) { 93 | *path_ptr = '/'; 94 | path_ptr++; 95 | } 96 | continue; 97 | case '\0': 98 | term: 99 | if ((*(path_ptr - 1) == '/') && ((path_ptr - 1) != orig_ptr)) 100 | path_ptr--; 101 | *path_ptr = 0; 102 | return; 103 | default: 104 | *path_ptr = *path; 105 | path++; 106 | path_ptr++; 107 | continue; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /common/lib/real.s2.asm_bios_ia32: -------------------------------------------------------------------------------- 1 | section .realmode 2 | 3 | global rm_hcf 4 | rm_hcf: 5 | ; Load BIOS IVT 6 | lidt [.rm_idt] 7 | 8 | ; Jump to real mode 9 | jmp 0x08:.bits16 10 | .bits16: 11 | bits 16 12 | mov ax, 0x10 13 | mov ds, ax 14 | mov es, ax 15 | mov fs, ax 16 | mov gs, ax 17 | mov ss, ax 18 | mov eax, cr0 19 | btr ax, 0 20 | mov cr0, eax 21 | jmp 0x00:.cszero 22 | .cszero: 23 | xor ax, ax 24 | mov ds, ax 25 | mov es, ax 26 | mov fs, ax 27 | mov gs, ax 28 | mov ss, ax 29 | 30 | sti 31 | .hang: 32 | hlt 33 | jmp .hang 34 | bits 32 35 | 36 | .rm_idt: dw 0x3ff 37 | dd 0 38 | 39 | global rm_int 40 | rm_int: 41 | ; Self-modifying code: int $int_no 42 | mov al, byte [esp+4] 43 | mov byte [.int_no], al 44 | 45 | ; Save out_regs 46 | mov eax, dword [esp+8] 47 | mov dword [.out_regs], eax 48 | 49 | ; Save in_regs 50 | mov eax, dword [esp+12] 51 | mov dword [.in_regs], eax 52 | 53 | ; Save GDT in case BIOS overwrites it 54 | sgdt [.gdt] 55 | 56 | ; Save IDT 57 | sidt [.idt] 58 | 59 | ; Load BIOS IVT 60 | lidt [.rm_idt] 61 | 62 | ; Save non-scratch GPRs 63 | push ebx 64 | push esi 65 | push edi 66 | push ebp 67 | 68 | ; Jump to real mode 69 | jmp 0x08:.bits16 70 | .bits16: 71 | bits 16 72 | mov ax, 0x10 73 | mov ds, ax 74 | mov es, ax 75 | mov fs, ax 76 | mov gs, ax 77 | mov ss, ax 78 | mov eax, cr0 79 | and al, 0xfe 80 | mov cr0, eax 81 | jmp 0x00:.cszero 82 | .cszero: 83 | xor ax, ax 84 | mov ss, ax 85 | 86 | ; Load in_regs 87 | mov dword [ss:.esp], esp 88 | mov esp, dword [ss:.in_regs] 89 | pop gs 90 | pop fs 91 | pop es 92 | pop ds 93 | popfd 94 | pop ebp 95 | pop edi 96 | pop esi 97 | pop edx 98 | pop ecx 99 | pop ebx 100 | pop eax 101 | mov esp, dword [ss:.esp] 102 | 103 | sti 104 | 105 | ; Indirect interrupt call 106 | db 0xcd 107 | .int_no: 108 | db 0 109 | 110 | cli 111 | 112 | ; Load out_regs 113 | mov dword [ss:.esp], esp 114 | mov esp, dword [ss:.out_regs] 115 | lea esp, [esp + 10*4] 116 | push eax 117 | push ebx 118 | push ecx 119 | push edx 120 | push esi 121 | push edi 122 | push ebp 123 | pushfd 124 | push ds 125 | push es 126 | push fs 127 | push gs 128 | mov esp, dword [ss:.esp] 129 | 130 | ; Restore GDT 131 | o32 lgdt [ss:.gdt] 132 | 133 | ; Restore IDT 134 | o32 lidt [ss:.idt] 135 | 136 | ; Jump back to pmode 137 | mov eax, cr0 138 | or al, 1 139 | mov cr0, eax 140 | jmp 0x18:.bits32 141 | .bits32: 142 | bits 32 143 | mov ax, 0x20 144 | mov ds, ax 145 | mov es, ax 146 | mov fs, ax 147 | mov gs, ax 148 | mov ss, ax 149 | 150 | ; Restore non-scratch GPRs 151 | pop ebp 152 | pop edi 153 | pop esi 154 | pop ebx 155 | 156 | ; Exit 157 | ret 158 | 159 | align 16 160 | .esp: dd 0 161 | .out_regs: dd 0 162 | .in_regs: dd 0 163 | .gdt: dq 0 164 | .idt: dq 0 165 | .rm_idt: dw 0x3ff 166 | dd 0 167 | 168 | section .note.GNU-stack noalloc noexec nowrite progbits 169 | -------------------------------------------------------------------------------- /.forgejo/workflows/binary-release.yml: -------------------------------------------------------------------------------- 1 | name: Binary release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | name: Build and upload binary artifacts 11 | runs-on: codeberg-medium 12 | container: archlinux:latest 13 | 14 | steps: 15 | - name: Create resolv.conf 16 | run: | 17 | set -ex 18 | echo 'nameserver 8.8.8.8' >/etc/resolv.conf 19 | echo 'nameserver 8.8.4.4' >>/etc/resolv.conf 20 | 21 | - name: Install dependencies 22 | run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs base-devel gnupg git autoconf automake nasm curl mtools llvm clang lld mingw-w64-gcc 23 | 24 | - name: Import GPG public key 25 | run: gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821 26 | 27 | - name: Import GPG private key 28 | run: echo "$MINTSUKI_PRIVATE_KEY" | gpg --batch --import 29 | env: 30 | MINTSUKI_PRIVATE_KEY: ${{ secrets.MINTSUKI_PRIVATE_KEY }} 31 | 32 | - name: Checkout code 33 | uses: https://code.forgejo.org/actions/checkout@v6 34 | with: 35 | fetch-depth: '0' 36 | 37 | - name: Git config 38 | run: | 39 | set -e 40 | git config --global --add safe.directory "$FORGEJO_WORKSPACE" 41 | git config --global user.name 'Mintsuki' 42 | git config --global user.email 'mintsuki@protonmail.com' 43 | git config --global user.signingkey 05D29860D0A0668AAEFB9D691F3C021BECA23821 44 | 45 | - name: Get tag name 46 | run: echo "TAG_NAME=$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" >> $FORGEJO_ENV 47 | 48 | - name: Get branch name 49 | run: echo "BRANCH_NAME=$(echo "$TAG_NAME" | grep -o 'v[0-9]\+\.')x" >> $FORGEJO_ENV 50 | 51 | - name: Regenerate 52 | run: ./bootstrap 53 | 54 | - name: Create build dir 55 | run: mkdir -p build 56 | 57 | - name: Configure 58 | run: cd build && ../configure --enable-all 59 | 60 | - name: Build the bootloader 61 | run: make -C build -j$(nproc) 62 | 63 | - name: Clean limine 64 | run: rm build/bin/limine 65 | 66 | - name: Build limine for Windows 67 | run: make -C build/bin CC="i686-w64-mingw32-gcc" CFLAGS="-O2 -pipe" CPPFLAGS="-D__USE_MINGW_ANSI_STDIO" limine 68 | 69 | - name: Strip limine for Windows 70 | run: i686-w64-mingw32-strip build/bin/limine.exe 71 | 72 | - name: Copy LICENSE to bin 73 | run: cp COPYING build/bin/LICENSE 74 | 75 | - name: Remove limine-bios-hdd.bin 76 | run: rm build/bin/limine-bios-hdd.bin 77 | 78 | - name: Push binaries to binary branch 79 | run: | 80 | set -e 81 | git remote set-url origin https://x-access-token:${{ secrets.FORGEJO_TOKEN }}@codeberg.org/Limine/Limine.git 82 | git fetch --all 83 | git checkout $BRANCH_NAME-binary || git checkout --orphan $BRANCH_NAME-binary 84 | rm -rf $(ls -a | grep -v '^\.git$' | grep -v '^\.\.$' | grep -v '^\.$' | grep -v '^build$') 85 | cp -r build/bin/. ./ 86 | rm -rf build 87 | git add -f . 88 | git commit -m "Binary release $TAG_NAME" -S 89 | git push origin $BRANCH_NAME-binary 90 | git tag $TAG_NAME-binary -s -m $TAG_NAME-binary 91 | git push origin $TAG_NAME-binary 92 | -------------------------------------------------------------------------------- /common/sys/smp_trampoline.asm_aarch64: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | .set tpl_booted_flag, -64 4 | .set tpl_hhdm_offset, -56 5 | .set tpl_ttbr0, -48 6 | .set tpl_ttbr1, -40 7 | .set tpl_mair, -32 8 | .set tpl_tcr, -24 9 | .set tpl_sctlr, -16 10 | .set tpl_info_struct, -8 11 | 12 | .section .text 13 | 14 | .global smp_trampoline_start 15 | smp_trampoline_start: 16 | bl .L_entry 17 | .L_entry: 18 | // Mask IRQs 19 | msr daifset, #0b1111 20 | 21 | // Address to next page (since our offsets into the boot data are negative) 22 | add x1, x30, #0xFFC 23 | 24 | ldr x0, [x1, tpl_info_struct] 25 | ldr x2, [x1, tpl_sctlr] 26 | ldr x3, [x1, tpl_mair] 27 | ldr x4, [x1, tpl_tcr] 28 | ldr x5, [x1, tpl_ttbr0] 29 | ldr x6, [x1, tpl_ttbr1] 30 | ldr x7, [x1, tpl_hhdm_offset] 31 | 32 | PICK_EL x8, 1f, 0f 33 | 0: 34 | // Configure EL2-specific state for EL1 35 | 36 | // Configure EL1 page tables 37 | msr mair_el1, x3 38 | msr tcr_el1, x4 39 | msr ttbr0_el1, x5 40 | msr ttbr1_el1, x6 41 | msr sctlr_el1, x2 42 | isb 43 | dsb sy 44 | isb 45 | 46 | // Don't trap counters to EL2 47 | mrs x8, cnthctl_el2 48 | orr x8, x8, #3 49 | msr cnthctl_el2, x8 50 | msr cntvoff_el2, xzr 51 | 52 | // Enable AArch64 in EL1 53 | mov x8, xzr 54 | orr x8, x8, #(1 << 31) 55 | orr x8, x8, #(1 << 1) 56 | msr hcr_el2, x8 57 | 58 | // Don't trap FP/SIMD to EL2 59 | mov x8, #0x33FF 60 | msr cptr_el2, x8 61 | msr hstr_el2, xzr 62 | 63 | // Run rest of trampoline in EL1 64 | mov x8, #0x3c4 65 | msr spsr_el2, x8 66 | adrp x8, 3f 67 | add x8, x8, :lo12:2f 68 | add x8, x8, x7 // Add HHDM offset 69 | msr elr_el2, x8 70 | 71 | eret 72 | 73 | 1: 74 | msr spsel, #0 75 | 76 | // Switch to the new page tables 77 | 78 | // Point the EL1t handler to the continuation, such that after we page fault, 79 | // execution continues as expected. 80 | adrp x8, 2f 81 | add x8, x8, #:lo12:2f 82 | add x8, x8, x7 83 | msr vbar_el1, x8 84 | isb 85 | dsb sy 86 | isb 87 | 88 | // Switch the page table registers 89 | msr mair_el1, x3 90 | msr tcr_el1, x4 91 | msr ttbr0_el1, x5 92 | msr ttbr1_el1, x6 93 | msr sctlr_el1, x2 94 | isb 95 | dsb sy 96 | isb 97 | 98 | // Jump to the higher half mapping in case we didn't immediately crash 99 | br x8 100 | 101 | // Alignment required by VBAR register 102 | .align 12 103 | 2: 104 | // Zero out VBAR to avoid confusion 105 | msr vbar_el1, xzr 106 | 107 | 3: 108 | // Add HHDM offset to data pointer 109 | add x1, x1, x7 110 | 111 | // Notify BSP we are alive 112 | mov x8, #1 113 | add x9, x1, tpl_booted_flag 114 | stlr x8, [x9] 115 | 116 | // Wait for BSP to tell us where to go 117 | // Add HHDM offset to our info struct pointer 118 | add x0, x0, x7 119 | add x9, x0, #24 120 | 4: 121 | ldar x8, [x9] 122 | cbnz x8, 5f 123 | yield 124 | b 4b 125 | 126 | 5: 127 | msr elr_el1, x8 128 | 129 | msr spsel, #0 130 | ldr x8, [x0, #16] 131 | mov sp, x8 132 | 133 | // Enter kernel 134 | mov x8, #0x3c4 135 | msr spsr_el1, x8 136 | 137 | ZERO_REGS_EXCEPT_X0 138 | 139 | eret 140 | 141 | smp_trampoline_end: 142 | 143 | .section .rodata 144 | 145 | .global smp_trampoline_size 146 | smp_trampoline_size: 147 | .quad smp_trampoline_end - smp_trampoline_start 148 | 149 | .section .note.GNU-stack,"",%progbits 150 | -------------------------------------------------------------------------------- /common/lib/libc.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | bool isprint(int c) { 8 | return c >= ' ' && c <= '~'; 9 | } 10 | 11 | bool isspace(int c) { 12 | return (c >= '\t' && c <= 0xD) || c == ' '; 13 | } 14 | 15 | bool isalpha(int c) { 16 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 17 | } 18 | 19 | bool isdigit(int c) { 20 | return c >= '0' && c <= '9'; 21 | } 22 | 23 | int toupper(int c) { 24 | if (c >= 'a' && c <= 'z') { 25 | return c - 0x20; 26 | } 27 | return c; 28 | } 29 | 30 | int tolower(int c) { 31 | if (c >= 'A' && c <= 'Z') { 32 | return c + 0x20; 33 | } 34 | return c; 35 | } 36 | 37 | int abs(int i) { 38 | return i < 0 ? -i : i; 39 | } 40 | 41 | char *strcpy(char *dest, const char *src) { 42 | size_t i; 43 | 44 | for (i = 0; src[i]; i++) 45 | dest[i] = src[i]; 46 | 47 | dest[i] = 0; 48 | 49 | return dest; 50 | } 51 | 52 | char *strncpy(char *dest, const char *src, size_t n) { 53 | size_t i; 54 | 55 | for (i = 0; i < n && src[i]; i++) 56 | dest[i] = src[i]; 57 | for ( ; i < n; i++) 58 | dest[i] = 0; 59 | 60 | return dest; 61 | } 62 | 63 | int strcmp(const char *s1, const char *s2) { 64 | for (size_t i = 0; ; i++) { 65 | unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; 66 | if (c1 != c2) { 67 | return c1 < c2 ? -1 : 1; 68 | } 69 | if (c1 == 0) { 70 | return 0; 71 | } 72 | } 73 | } 74 | 75 | int strcasecmp(const char *s1, const char *s2) { 76 | for (size_t i = 0; ; i++) { 77 | unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; 78 | if (tolower(c1) != tolower(c2)) { 79 | return c1 < c2 ? -1 : 1; 80 | } 81 | if (c1 == 0) { 82 | return 0; 83 | } 84 | } 85 | } 86 | 87 | int strncmp(const char *s1, const char *s2, size_t n) { 88 | for (size_t i = 0; i < n; i++) { 89 | unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; 90 | if (c1 != c2) { 91 | return c1 < c2 ? -1 : 1; 92 | } 93 | if (c1 == 0) { 94 | return 0; 95 | } 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | int strncasecmp(const char *s1, const char *s2, size_t n) { 102 | for (size_t i = 0; i < n; i++) { 103 | unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i]; 104 | if (tolower(c1) != tolower(c2)) { 105 | return c1 < c2 ? -1 : 1; 106 | } 107 | if (c1 == 0) { 108 | return 0; 109 | } 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | size_t strlen(const char *str) { 116 | size_t len; 117 | 118 | for (len = 0; str[len]; len++); 119 | 120 | return len; 121 | } 122 | 123 | int inet_pton(const char *src, void *dst) { 124 | uint8_t array[4]; 125 | const char *current = src; 126 | 127 | for (int i = 0; i < 4; i++) { 128 | const char *newcur; 129 | uint64_t value = strtoui(current, &newcur, 10); 130 | if (current == newcur) 131 | return -1; 132 | current = newcur; 133 | if (*current == 0 && i < 3) 134 | return -1; 135 | if (value > 255) 136 | return -1; 137 | current++; 138 | array[i] = value; 139 | } 140 | memcpy(dst, array, 4); 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /common/lib/spinup.asm_loongarch64: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | #define PAGE_SHIFT 12 4 | #define PT_SHIFT (PAGE_SHIFT - 3) 5 | #define PT_BASE(level) (PAGE_SHIFT + PT_SHIFT * (level)) 6 | 7 | #define MAKE_PWCL(Dir2_width, Dir2_base, Dirl_width, Dirl_base, PTwidth, PTbase) \ 8 | ((Dir2_width) << 25) | ((Dir2_base) << 20) | ((Dirl_width) << 15) | \ 9 | ((Dirl_base) << 10) | ((PTwidth) << 5) | ((PTbase) << 0) 10 | 11 | #define MAKE_PWCH(Dir4_width, Dir4_base, Dir3_width, Dir3_base) \ 12 | ((Dir4_width) << 18) | ((Dir4_base) << 12) | ((Dir3_width) << 6) | \ 13 | ((Dir3_base) << 0) 14 | 15 | #define CSR_CRMD 0x00 16 | #define CSR_EENTRY 0xc 17 | #define CSR_PGDL 0x19 18 | #define CSR_PGDH 0x1a 19 | #define CSR_PGD 0x1b 20 | #define CSR_PWCL 0x1c 21 | #define CSR_PWCH 0x1d 22 | #define CSR_STLBPS 0x1e 23 | #define CSR_TLBRENTRY 0x88 24 | #define CSR_TLBRSAVE 0x8b 25 | #define CSR_TLBREHI 0x8e 26 | #define CSR_MERRENTRY 0x93 27 | #define CSR_DMW0 0x180 28 | #define CSR_DMW1 0x181 29 | #define CSR_DMW2 0x182 30 | #define CSR_DMW3 0x183 31 | 32 | .global loongarch_spinup 33 | loongarch_spinup: 34 | li.d $t0, 0b010001 // MAT=01, PLV1..3=0, PLV0=1 35 | csrwr $t0, CSR_DMW0 36 | csrwr $zero, CSR_DMW1 37 | csrwr $zero, CSR_DMW2 38 | csrwr $zero, CSR_DMW3 39 | 40 | li.d $t0, 0b010110000 // DATF=01, DATM=01, PG=1, DA=0, IE=0, PLV=00 41 | csrwr $t0, CSR_CRMD 42 | 43 | invtlb 0, $zero, $zero 44 | li.d $t0, PAGE_SHIFT 45 | csrwr $t0, CSR_STLBPS 46 | csrwr $t0, CSR_TLBREHI 47 | 48 | csrwr $a2, CSR_PGDL 49 | csrwr $a3, CSR_PGDH 50 | 51 | li.d $t0, MAKE_PWCL(PT_SHIFT, PT_BASE(2), PT_SHIFT, PT_BASE(1), PT_SHIFT, PT_BASE(0)) 52 | csrwr $t0, CSR_PWCL 53 | li.d $t0, MAKE_PWCH(0, 0, PT_SHIFT, PT_BASE(3)) 54 | csrwr $t0, CSR_PWCH 55 | 56 | la $t0, loongarch_handle_refill 57 | csrwr $t0, CSR_TLBRENTRY 58 | 59 | csrwr $zero, CSR_EENTRY 60 | csrwr $zero, CSR_MERRENTRY 61 | 62 | move $t0, $a0 63 | move $sp, $a1 64 | 65 | move $ra, $zero 66 | move $tp, $zero 67 | move $a0, $zero 68 | move $a1, $zero 69 | move $a2, $zero 70 | move $a3, $zero 71 | move $a4, $zero 72 | move $a5, $zero 73 | move $a6, $zero 74 | move $a7, $zero 75 | move $t1, $zero 76 | move $t2, $zero 77 | move $t3, $zero 78 | move $t4, $zero 79 | move $t5, $zero 80 | move $t6, $zero 81 | move $t7, $zero 82 | move $t8, $zero 83 | move $fp, $zero 84 | move $s0, $zero 85 | move $s1, $zero 86 | move $s2, $zero 87 | move $s3, $zero 88 | move $s4, $zero 89 | move $s5, $zero 90 | move $s6, $zero 91 | move $s7, $zero 92 | move $s8, $zero 93 | 94 | jirl $zero, $t0, 0 95 | 96 | .global loongarch_handle_refill 97 | .align 12 98 | loongarch_handle_refill: 99 | csrwr $t0, CSR_TLBRSAVE 100 | csrrd $t0, CSR_PGD 101 | lddir $t0, $t0, 3 102 | lddir $t0, $t0, 2 103 | lddir $t0, $t0, 1 104 | ldpte $t0, 0 105 | ldpte $t0, 1 106 | tlbfill 107 | csrrd $t0, CSR_TLBRSAVE 108 | ertn 109 | 110 | .section .note.GNU-stack,"",%progbits 111 | -------------------------------------------------------------------------------- /test/multiboot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MULTIBOOT_BOOTLOADER_MAGIC 0x2badb002 6 | 7 | void multiboot_main(uint32_t magic, struct multiboot1_info *info) { 8 | if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { 9 | e9_printf("multiboot: Invalid magic: %x\n", magic); 10 | goto out; 11 | } 12 | 13 | e9_printf("Welcome to the multiboot1 test kernel: "); 14 | 15 | e9_printf("\t flags: %x", info->flags); 16 | 17 | e9_printf("\t mem_lower: %x", info->mem_lower); 18 | e9_printf("\t mem_upper: %x", info->mem_upper); 19 | 20 | e9_printf("\t boot_device: %x", info->boot_device); 21 | e9_printf("\t cmdline: %s", info->cmdline); 22 | 23 | { 24 | struct multiboot1_module *start = (struct multiboot1_module *)info->mods_addr; 25 | struct multiboot1_module *end = (struct multiboot1_module *)(info->mods_addr + info->mods_count); 26 | 27 | e9_printf("\t modules:"); 28 | for (struct multiboot1_module* entry = start; entry < end; entry++) { 29 | e9_printf("\t\t begin=%x", entry->begin); 30 | e9_printf("\t\t end=%x", entry->end); 31 | e9_printf("\t\t cmdline=%s", entry->cmdline); 32 | } 33 | } 34 | 35 | { 36 | struct multiboot1_mmap_entry *start = (struct multiboot1_mmap_entry *)info->mmap_addr; 37 | struct multiboot1_mmap_entry *end = (struct multiboot1_mmap_entry *)(info->mmap_addr + info->mmap_length); 38 | 39 | e9_printf("\t usable_entries_mmap:"); 40 | 41 | size_t total_mem = 0; 42 | 43 | // For now we only print the usable memory map entries since 44 | // printing the whole memory map blows my terminal up. We also 45 | // iterate through the available memory map entries and add up 46 | // to find the total amount of usable memory. 47 | for (struct multiboot1_mmap_entry* entry = start; entry < end; entry++) { 48 | // Check if the memory map entry is marked as usable! 49 | if (entry->type != 1) { 50 | continue; 51 | } 52 | 53 | e9_printf("\t\t addr=%x", entry->addr); 54 | e9_printf("\t\t length=%x", entry->len); 55 | e9_printf("\t\t type=Usable"); 56 | 57 | // Now this might be a bit confusing since but `entry->size` represents the 58 | // is the size of the associated structure in bytes and `entry->len` represents the 59 | // size of the memory region. 60 | total_mem += entry->len; 61 | } 62 | 63 | e9_printf("Total usable memory: %x", total_mem); 64 | } 65 | 66 | // TODO(Andy-Python-Programmer): Drives are unimplemented 67 | // TODO(Andy-Python-Programmer): ROM config is unimplemented 68 | 69 | e9_printf("\t bootloader_name: %s", info->bootloader_name); 70 | 71 | // TODO(Andy-Python-Programmer): APM table is unimplemented 72 | // TODO(Andy-Python-Programmer): VBE tag is unimplemented 73 | 74 | e9_printf("\t fb_addr: %x", info->fb_addr); 75 | e9_printf("\t fb_pitch: %x", info->fb_pitch); 76 | e9_printf("\t fb_width: %x", info->fb_width); 77 | e9_printf("\t fb_height: %x", info->fb_height); 78 | e9_printf("\t fb_bpp: %x", info->fb_bpp); 79 | e9_printf("\t fb_type: %x", info->fb_type); 80 | 81 | e9_printf("\t fb_red_mask_shift: %x", info->fb_red_mask_shift); 82 | e9_printf("\t fb_red_mask_size: %x", info->fb_red_mask_size); 83 | 84 | e9_printf("\t fb_green_mask_shift: %x", info->fb_green_mask_shift); 85 | e9_printf("\t fb_green_mask_size: %x", info->fb_green_mask_size); 86 | 87 | e9_printf("\t fb_blue_mask_shift: %x", info->fb_blue_mask_shift); 88 | e9_printf("\t fb_blue_mask_size: %x", info->fb_blue_mask_size); 89 | 90 | out: 91 | for (;;); 92 | } 93 | -------------------------------------------------------------------------------- /common/entry.s2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct volume *boot_volume; 28 | 29 | #if defined (BIOS) 30 | 31 | bool stage3_loaded = false; 32 | static bool stage3_found = false; 33 | 34 | extern symbol stage3_addr; 35 | extern symbol limine_bios_sys_size; 36 | extern symbol build_id_s2; 37 | extern symbol build_id_s3; 38 | 39 | static bool stage3_init(struct volume *part) { 40 | struct file_handle *stage3; 41 | 42 | bool old_cif = case_insensitive_fopen; 43 | case_insensitive_fopen = true; 44 | if (true 45 | && (stage3 = fopen(part, "/boot/limine/limine-bios.sys")) == NULL 46 | && (stage3 = fopen(part, "/boot/limine-bios.sys")) == NULL 47 | && (stage3 = fopen(part, "/limine/limine-bios.sys")) == NULL 48 | && (stage3 = fopen(part, "/limine-bios.sys")) == NULL 49 | ) { 50 | case_insensitive_fopen = old_cif; 51 | return false; 52 | } 53 | case_insensitive_fopen = old_cif; 54 | 55 | stage3_found = true; 56 | 57 | if (stage3->size != (size_t)limine_bios_sys_size) { 58 | print("limine-bios.sys size incorrect.\n"); 59 | return false; 60 | } 61 | 62 | fread(stage3, stage3_addr, 63 | (uintptr_t)stage3_addr - 0xf000, 64 | stage3->size - ((uintptr_t)stage3_addr - 0xf000)); 65 | 66 | fclose(stage3); 67 | 68 | if (memcmp(build_id_s2 + 16, build_id_s3 + 16, 20) != 0) { 69 | print("limine-bios.sys build ID mismatch.\n"); 70 | return false; 71 | } 72 | 73 | stage3_loaded = true; 74 | 75 | return true; 76 | } 77 | 78 | enum { 79 | BOOTED_FROM_HDD = 0, 80 | BOOTED_FROM_PXE = 1, 81 | BOOTED_FROM_CD = 2 82 | }; 83 | 84 | noreturn void entry(uint8_t boot_drive, int boot_from) { 85 | // XXX DO NOT MOVE A20 ENABLE CALL 86 | if (!a20_enable()) { 87 | panic(false, "Could not enable A20 line"); 88 | } 89 | 90 | uint64_t usec_at_entry = rdtsc_usec(); 91 | 92 | init_e820(); 93 | init_memmap(); 94 | 95 | init_idt(); 96 | 97 | disk_create_index(); 98 | 99 | if (boot_from == BOOTED_FROM_HDD || boot_from == BOOTED_FROM_CD) { 100 | boot_volume = volume_get_by_bios_drive(boot_drive); 101 | } else if (boot_from == BOOTED_FROM_PXE) { 102 | pxe_init(); 103 | boot_volume = pxe_bind_volume(); 104 | } 105 | 106 | if (boot_volume == NULL) { 107 | panic(false, "Could not determine boot drive"); 108 | } 109 | 110 | volume_iterate_parts(boot_volume, 111 | if (stage3_init(_PART)) { 112 | break; 113 | } 114 | ); 115 | 116 | if (!stage3_found) { 117 | print("\n" 118 | "!! Stage 3 file not found!\n" 119 | "!! Have you copied limine-bios.sys to the root, /boot, /limine, or /boot/limine\n" 120 | "!! directories of one of the partitions on the boot device?\n\n"); 121 | } 122 | 123 | if (!stage3_loaded) { 124 | panic(false, "Failed to load stage 3."); 125 | } 126 | 127 | term_fallback(); 128 | 129 | usec_at_bootloader_entry = usec_at_entry; 130 | stage3_common(); 131 | } 132 | 133 | #endif 134 | --------------------------------------------------------------------------------