commit 08f9c590e399ed312a03073f7c97b2d6bd8f3229 Author: mid <> Date: Fri May 1 12:43:49 2026 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65f5dc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.bin +/kernel diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..9bb6273 --- /dev/null +++ b/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -e + +export LC_ALL=C + +nasm -fbin -o 1.bin src/boot/x86/1.asm +nasm -DSTAGE2_SIZE=$(stat --printf="%s" 1.bin) -fbin -o 0.bin src/boot/x86/0.asm + +i386-unknown-elf-gcc -s -Wl,--export-dynamic -fno-pic -flto -Os -mgeneral-regs-only -Isrc/kernel/ -ffreestanding -nostdlib -nostartfiles -o kernel src/kernel/x86/ppm.c src/kernel/x86/vpm.c src/kernel/x86/log.c src/kernel/tinyubsan.c src/kernel/main.c -fsanitize=undefined + +dd if=/dev/zero of=drive.bin bs=1024 count=10240 +/bin/echo -e 'mklabel msdos\nmkpart primary 8192b -1s\nquit\n' | parted drive.bin +[[ "$(udisksctl loop-setup -f drive.bin)" =~ (/dev/loop[0-9]+) ]] && LOOPDEV="${BASH_REMATCH[1]}" +sudo mkfs.ext2 -E root_perms=777 -b 1024 "${LOOPDEV}p1" +sleep 0.5 +[[ "$(udisksctl mount -b ${LOOPDEV}p1)" =~ (/run/.+) ]] && MOUNTDIR="${BASH_REMATCH[1]}" +cp ./kernel "${MOUNTDIR}/kernel" +udisksctl unmount -b "${LOOPDEV}p1" +udisksctl loop-delete -b "${LOOPDEV}" + +# Copy first stage bootloader without overwriting MBR +dd if=0.bin of=drive.bin bs=1 count=440 seek=0 conv=notrunc,nocreat + +# Copy second stage bootloader, hope it's not too fat +dd if=1.bin of=drive.bin bs=1 seek=512 conv=notrunc,nocreat diff --git a/src/boot/x86/0.asm b/src/boot/x86/0.asm new file mode 100644 index 0000000..e200cab --- /dev/null +++ b/src/boot/x86/0.asm @@ -0,0 +1,53 @@ +bits 16 +org 0x7C00 + + jmp 0x0:_start +_start: + mov ax, 0 + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + mov ax, 0x2403 + int 0x15 + jc a20_ns + test ah, ah + jnz a20_ns + + mov ax, 0x2402 + int 0x15 + jc a20_failed + test ah, ah + jnz a20_failed + test al, al + jnz a20_activated + + mov ax, 0x2401 + int 0x15 + jc a20_failed + test ah, ah + jnz a20_failed +a20_activated: + + mov ah, 0x02 + mov al, (STAGE2_SIZE + 511) / 512 + mov ch, 0 + mov dh, 0 + mov dl, 0x80 + mov cl, 2 + mov bx, 0x1000 + int 0x13 + + jmp 0x0:0x1000 + +a20_ns: +a20_failed: + mov ax, 0xB800 + mov ds, ax + mov [0], byte 'L' + jmp $ + +times 510 - ($ - $$) db 0 +dw 0xAA55 diff --git a/src/boot/x86/1.asm b/src/boot/x86/1.asm new file mode 100644 index 0000000..999201a --- /dev/null +++ b/src/boot/x86/1.asm @@ -0,0 +1,337 @@ +bits 16 +org 0x1000 + +; This bootloader should get the kernel in a fully running state, +; instead of the kernel initializing itself +; +; It attempts to load a file named "kernel" from the root of an ext2 +; partition, and set necessary state by peeking into the kernel's +; exported symbols, e.g. ppm_Bitmap. +; +; See 1_elf.asm and 1_fs.asm. + +MBR_PARTITION_TABLE equ 0x7DBE + +KERNEL_INITIAL_LOAD equ 0xA000 +KERNEL_BASE equ 0x2000 + +PAGE_PRESENT equ 1 +PAGE_RW equ 2 +PAGE_GLOBAL equ 256 + +start: + mov esp, 0x1000 + cld + + push 0xB800 + pop fs + mov bx, 0 +.clear_screen: + mov [fs:bx], byte ' ' + add bx, 2 + cmp bx, 4000 + jne .clear_screen + + ; Get first partition's starting LBA + mov eax, MBR_PARTITION_TABLE + mov ebx, [eax + 0x08] + mov [PARTITION_START], ebx + + call ext2_test + jnc .exist + mov eax, msg_partition_not_found + call print_string + jmp $ +.exist: + + call find_kernel_inode + test eax, eax + jnz .kernel_found + mov eax, msg_kernel_not_found + call print_string + jmp $ +.kernel_found: + + call load_kernel + jnc .kernel_loaded + mov eax, msg_kernel_fail + call print_string + jmp $ +.kernel_loaded: + + ; Use edi to dynamically allocate anything with an unknown size + ; Start allocation immediately after the kernel's image + + mov edi, [KERNEL_END] + add edi, 4095 + and edi, ~4095 + + ; Create template page directory and page table + ; Necessary as the kernel will use this in vpm_create_space + + mov eax, 0 + mov ecx, 8192 + rep stosb + + sub edi, 8192 + + mov dword [edi + 0], edi ; First 4MB of memory will be identity mapped, with the exception of 0 - 0x1000 + add dword [edi + 0], 4096 | PAGE_PRESENT | PAGE_RW | PAGE_GLOBAL + mov dword [edi + 4092], edi ; Recursively map last 4MB + add dword [edi + 4092], PAGE_PRESENT | PAGE_RW | PAGE_GLOBAL + + mov ecx, 1 + mov eax, 4096 | PAGE_PRESENT | PAGE_RW +.identity_map_lup: + mov dword [edi + 4096 + ecx * 4], eax + add eax, 4096 + inc ecx + cmp ecx, 1024 + jb .identity_map_lup + + mov cr3, edi + + add edi, 8192 + + ; Get memory map, setup ppm_Bitmap + + mov eax, string_ppm_Bitmap + call elf_find_symbol + mov eax, [eax + ELFSym.st_value] + add eax, KERNEL_BASE + mov [eax], edi + + xor cx, cx + xor dx, dx + mov ax, 0xE801 ; do not care enough for E820 at the moment + int 0x15 + jc short .err + cmp ah, 0x86 + je short .err + cmp ah, 0x80 + jne short .nerr +.err: + mov eax, msg_memory_fail + call print_string + jmp $ +.nerr: + jcxz .useax + mov ax, cx ; amount of mem starting at 1MB, in kB units + mov bx, dx ; amount of mem starting at 16MB, in 64kB units +.useax: + mov [edi], dword 0x100000 + mov [edi + 4], dword 0 + movzx eax, ax + shr eax, 2 ; divide by 4 to get 4kB (page) units + mov [edi + 8], eax + mov [edi + 12], dword 0 + add edi, 16 + mov ecx, eax + add ecx, 7 + shr ecx, 3 ; ecx = number of bytes belonging to bitmap + mov al, 0 + rep stosb + + ; Save kernel's k_init + + mov eax, string_k_init + call elf_find_symbol + test eax, eax + jnz .k_init_found + mov eax, msg_kernel_fail + call print_dword_hex + jmp $ +.k_init_found: + mov eax, [eax + ELFSym.st_value] + add eax, KERNEL_BASE + mov [FUNC_k_init], eax + + ; Up to now, our stack is within 0-0x1000, which will be unmapped in + ; protected mode, so lastly, we allocate 8kB for the kernel stack + add edi, 8192 + 15 + add edi, ~15 + mov esp, edi + + ; nasm doesn't support arithmetic on labels so we have to + ; dynamically compute the TSS segment's base + mov eax, TSS + mov [GDT + 42], ax + shr eax, 16 + mov [GDT + 44], al + mov [GDT + 47], ah + + ; Finally enter protected mode + cli + lgdt [GDT] + mov eax, cr0 + or eax, 0x80000001 + mov cr0, eax + jmp 0x08:.inPE +bits 32 +.inPE: + mov ax, 0x10 + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ax, 0x28 + ltr ax + + mov eax, [FUNC_k_init] + call eax + jmp $ + +bits 16 + +; compare C-strings in eax and ebx +; after calling, eax and ebx point to respective null terminators +streq: + push edx +.scanlup: + mov dl, [eax] + cmp dl, [ebx] + jnz .end + test dl, dl + jz .end + cmp byte [ebx], 0 + jz .end + + inc eax + inc ebx + jmp .scanlup +.end: + pushfd +.inceaxlup: + cmp byte [eax], 0 + jz .endier + inc eax + jmp .inceaxlup +.endier: +.incebxlup: + cmp byte [ebx], 0 + jz .endierier + inc ebx + jmp .incebxlup +.endierier: + popfd + pop edx + ret + +print_char: + push ebx + mov ebx, [.offset] + mov [fs:ebx], al + pop ebx + add [.offset], dword 2 + ret +.offset dd 0 + +print_string: + push ebx + push eax + mov ebx, eax +.lup: + cmp byte [ebx], 0 + je .end + movzx eax, byte [ebx] + call print_char + inc ebx + jmp .lup +.end: + pop eax + pop ebx + ret + +; in eax = dword to print +print_dword_hex: + pushad + mov ecx, 8 +.lup: + rol eax, 4 + push eax + and al, 15 + cmp al, 10 + jb .d + add al, 'A' - '0' - 10 +.d: + add al, '0' + call print_char + pop eax + dec ecx + jnz .lup + mov al, 10 + call print_char + popad + ret + +%include "src/boot/x86/1_elf.asm" +%include "src/boot/x86/1_fs.asm" + +DAPACK: + db 0x10 + db 0 +.blkcnt: dw 0 +.db_add: dw READ_LBA_RESULT + dw 0 +.d_lba: dd 0 + dd 0 + +GDT: + ; Use null descriptor space for gdtr + dw GDT.end - GDT - 1 + dd GDT + dw 0 + ; Kernel code (08) + db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00 + ; Kernel data (10) + db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00 + ; User code (18) + db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFA, 0xCF, 0x00 + ; User data (20) + db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF2, 0xCF, 0x00 + ; TSS(filled later) (28) + db 0x07, 0x01, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00 +.end: + +TSS: + dd 0 +TSS.esp0: + dd 0 +TSS.ss0: + dd 0 + times 96 db 0 + +elf_DYNSTR_TABLE: dd 0 +elf_DYNSYM_TABLE: dd 0 +elf_DYNSYMS: dd 0 + +READ_LBA_RESULT: times 512 db 0 +PARTITION_START: dd 0 +INODES_PER_GROUP: dd 0 +INODE_SIZE: dd 0 +KERNEL_END: dd 0 + +FUNC_k_init: dd 0 + +string_reldyn: db ".rel.dyn", 0 +string_dynstr: db ".dynstr", 0 +string_dynsym: db ".dynsym", 0 +string_ppm_Bitmap: db "ppm_Bitmap", 0 +string_scheduler_spawn: db "scheduler_spawn", 0 +string_k_init: db "k_init", 0 + +msg_partition_not_found: db "Failed to find partition.", 0 +msg_kernel_not_found: db "Failed to find kernel.", 0 +msg_kernel_fail: db "Failed to load kernel.", 0 +msg_memory_fail: db "Failed to determine memory.", 0 + +; Align to sector size +times (512 - ($ - $$) % 512) % 512 db 0 + +AFTER_THIS equ $ + +BLOCK_SCRATCH1 equ AFTER_THIS +ext2_read_inode_bytes_SCRATCH equ (AFTER_THIS + 1024) +ext2_get_inode_data_SCRATCH equ (ext2_read_inode_bytes_SCRATCH + 1024) diff --git a/src/boot/x86/1_elf.asm b/src/boot/x86/1_elf.asm new file mode 100644 index 0000000..97e3751 --- /dev/null +++ b/src/boot/x86/1_elf.asm @@ -0,0 +1,279 @@ +struc ELFHeader + .e_ident resb 16 + .e_type resw 1 + .e_machine resw 1 + .e_version resd 1 + .e_entry resd 1 + .e_phoff resd 1 + .e_shoff resd 1 + .e_flags resd 1 + .e_ehsize resw 1 + .e_phentsize resw 1 + .e_phnum resw 1 + .e_shentsize resw 1 + .e_shnum resw 1 + .e_shstrndx resw 1 +endstruc + +struc ELFSection + .sh_name resd 1 + .sh_type resd 1 + .sh_flags resd 1 + .sh_addr resd 1 + .sh_offset resd 1 + .sh_size resd 1 + .sh_link resd 1 + .sh_info resd 1 + .sh_addralign resd 1 + .sh_entsize resd 1 +endstruc + +struc ELFProgram + .p_type resd 1 + .p_offset resd 1 + .p_vaddr resd 1 + .p_paddr resd 1 + .p_filesz resd 1 + .p_memsz resd 1 + .p_flags resd 1 + .p_align resd 1 +endstruc + +struc ELFSym + .st_name resd 1 + .st_value resd 1 + .st_size resd 1 + .st_info resb 1 + .st_other resb 1 + .st_shndx resw 1 +endstruc + +; in eax = kernel file inode +load_kernel: + pushad + + mov edi, ext2_get_inode_data_SCRATCH + call ext2_get_inode_data + + mov esi, ext2_get_inode_data_SCRATCH + mov eax, 0 + mov ecx, [ext2_get_inode_data_SCRATCH + 4] ; filesize + mov edi, KERNEL_INITIAL_LOAD + call ext2_read_inode_bytes + + cmp dword [KERNEL_INITIAL_LOAD], 0x464C457F ;ELF signature + je .pass_signature + popad + stc + ret +.pass_signature: + + ; follow the LOAD program header instructions + + movzx ecx, word [KERNEL_INITIAL_LOAD + ELFHeader.e_phnum] + movzx ebp, word [KERNEL_INITIAL_LOAD + ELFHeader.e_phentsize] + mov eax, KERNEL_INITIAL_LOAD + add eax, [eax + ELFHeader.e_phoff] + +.lup0: + cmp dword [eax + ELFProgram.p_type], 1 ; PT_LOAD + jne .skip0 + pushad + + mov esi, [eax + ELFProgram.p_offset] + + mov ecx, [eax + ELFProgram.p_memsz] + add ecx, esi + add ecx, 4095 + and ecx, ~4095 + and esi, ~4095 + sub ecx, esi + + add esi, KERNEL_INITIAL_LOAD + + mov edi, [eax + ELFProgram.p_vaddr] + and edi, ~4095 + add edi, KERNEL_BASE + + test ecx, ecx + jz .skipcopy + rep movsb + .skipcopy: + + mov edi, [eax + ELFProgram.p_vaddr] + add edi, KERNEL_BASE + add edi, [eax + ELFProgram.p_filesz] + + mov ecx, [eax + ELFProgram.p_memsz] + sub ecx, [eax + ELFProgram.p_filesz] + + test ecx, ecx + jz .skipbss + mov eax, 0 + rep stosb + .skipbss: + + cmp edi, [KERNEL_END] + jbe .maxkernelend + mov [KERNEL_END], edi + .maxkernelend: + + popad + .skip0: + add eax, ebp + dec ecx + jnz .lup0 + + ; fix up the relocations + + mov esi, KERNEL_INITIAL_LOAD + mov eax, string_reldyn + call elf_find_array_section_by_name +.lup2: + mov ebx, [eax] + add dword [KERNEL_BASE + ebx], KERNEL_BASE + add eax, ebp + + dec ecx + jnz .lup2 + + ; Save .dynstr for elf_find_symbol to use + + mov esi, KERNEL_INITIAL_LOAD + mov eax, string_dynstr + call elf_find_array_section_by_name + mov [elf_DYNSTR_TABLE], eax + + ; Save .dynsym for elf_find_symbol to use + + mov esi, KERNEL_INITIAL_LOAD + mov eax, string_dynsym + call elf_find_array_section_by_name + mov [elf_DYNSYM_TABLE], eax + mov [elf_DYNSYMS], ecx + + popad + clc + ret + +; in esi = ptr to elf file +; in eax = section name ptr +; out eax = ptr to section data (or 0) +; out ebp = section entry size +; out ecx = section entry count +elf_find_array_section_by_name: + push ebx + push edx + + ; ebx = section name ptr + mov ebx, eax + + ; ebp = section table entry size + movzx ebp, word [esi + ELFHeader.e_shentsize] + + movzx eax, word [esi + ELFHeader.e_shstrndx] + mul ebp + + ; edx = section table + mov edx, [esi + ELFHeader.e_shoff] + add edx, esi + + ; eax = string table + add eax, edx + mov eax, [eax + ELFSection.sh_offset] + add eax, esi + + ; ecx = section index counter + mov ecx, 0 +.lup: + push eax + push ebx + add eax, [edx + ELFSection.sh_name] + call streq + pop ebx + pop eax + je .found + + add edx, ebp + inc ecx + cmp cx, word [esi + ELFHeader.e_shnum] + jb .lup + + mov eax, 0 + jmp .notfound +.found: + + ; eax = section header + mov eax, ecx + mul ebp + add eax, [esi + ELFHeader.e_shoff] + add eax, esi + + ; ebp = section entry size + mov ebp, [eax + ELFSection.sh_entsize] + + ; ecx = entry count + mov ecx, 0 + + test ebp, ebp + jz .zeroentitysize + push eax + mov eax, [eax + ELFSection.sh_size] + mov edx, 0 + div ebp + mov ecx, eax + pop eax +.zeroentitysize: + + mov eax, [eax + ELFSection.sh_offset] + add eax, esi + +.notfound: + pop edx + pop ebx + + ret + +elf_find_symbol: + cmp dword [elf_DYNSTR_TABLE], 0 + jnz .inited1 + ; table not loaded + ret +.inited1: + cmp dword [elf_DYNSYM_TABLE], 0 + jnz .inited2 + ; table not loaded + ret +.inited2: + + push edx + push ecx + push ebx + + mov edx, [elf_DYNSYM_TABLE] + mov ecx, 0 + +.lup: + mov ebx, [elf_DYNSTR_TABLE] + add ebx, [edx + ELFSym.st_name] + push eax + call streq + pop eax + je .found + + add edx, 16 ; symbol size + inc ecx + cmp ecx, [elf_DYNSYMS] + jne .lup + + ; symbol not found + mov edx, 0 + +.found: + mov eax, edx + + pop ebx + pop ecx + pop edx + + ret diff --git a/src/boot/x86/1_fs.asm b/src/boot/x86/1_fs.asm new file mode 100644 index 0000000..11596c2 --- /dev/null +++ b/src/boot/x86/1_fs.asm @@ -0,0 +1,248 @@ +; Only searches first block of root directory +find_kernel_inode: + push edi + push esi + push ecx + + mov eax, 2 + mov edi, ext2_get_inode_data_SCRATCH + call ext2_get_inode_data + + mov esi, ext2_get_inode_data_SCRATCH + mov eax, 0 + mov edi, BLOCK_SCRATCH1 + call ext2_read_inode_block + + mov eax, 0 ; null inode + +.lup: + cmp byte [edi + 6], 6 ; length of "kernel" + jne .skip + cmp [edi + 8], dword 'kern' + jne .skip + cmp [edi + 12], word 'el' + jne .skip + mov eax, [edi + 0] ; inode + jmp .end +.skip: + movzx ecx, word [edi + 4] ; record length + add edi, ecx + cmp edi, BLOCK_SCRATCH1 + 1024 + jb .lup + +.end: + pop ecx + pop esi + pop edi + ret + +; in esi = ptr to inode structure +; in eax = start offset byte +; in ecx = number of bytes to read +; in es:edi = pointer to destination +ext2_read_inode_bytes: + pushad + + push eax + and eax, 1023 + mov [.BLOCK_OFFSET], eax + pop eax + + mov [.BYTES_REMAINING], ecx + + lea edx, [eax + ecx - 1] + shr edx, 10 + shr eax, 10 + + mov ebp, eax + + ; ebp = start block + ; eax = counter + ; edx = end block (inclusive) +.lup: + push edi + mov edi, ext2_read_inode_bytes_SCRATCH + call ext2_read_inode_block + + mov esi, edi + mov ecx, 1024 + pop edi + + cmp eax, ebp + jne .not_start + ; dont copy entire block, only what we need + add esi, [.BLOCK_OFFSET] + sub ecx, [.BLOCK_OFFSET] +.not_start: + +.copy: + ; ensure byte count < remaining + cmp ecx, [.BYTES_REMAINING] + jbe .nomin + mov ecx, [.BYTES_REMAINING] +.nomin: + sub [.BYTES_REMAINING], ecx + rep movsb + + inc eax + cmp eax, edx + jbe .lup + + popad + ret +.BLOCK_OFFSET dd 0 +.BYTES_REMAINING dd 0 + +; in esi = ptr to inode structure +; in eax = block number +; in es:edi = pointer to destination +ext2_read_inode_block: + pushad + + cmp eax, 12 + jae .skip + mov eax, [esi + 40 + eax * 4] + shl eax, 10 + mov ecx, 1024 + call read_absolute_bytes +.skip: + + popad + ret + +; in eax = inode number +; in es:edi = pointer to destination +ext2_get_inode_data: + pushad + mov edx, 0 + dec eax + div dword [INODES_PER_GROUP] + ; edx is now inode index in group + ; eax is now block group index + call ext2_get_inode_table_index_from_block_group_index + ; eax is now inode table block index + shl eax, 10 + imul edx, dword [INODE_SIZE] + ; edx is now inode offset within block group (bytes) + add eax, edx + mov ecx, [INODE_SIZE] + call read_absolute_bytes + popad + ret + +ext2_get_inode_table_index_from_block_group_index: + push eax + mov eax, 4 + call read_lba + pop eax + shl eax, 5 ;*32 + mov eax, [READ_LBA_RESULT + eax + 8] + ret + +ext2_test: + push eax + mov eax, 2 + call read_lba + cmp [READ_LBA_RESULT + 56], word 0xEF53 ; ext2 signature + jne .fail + cmp [READ_LBA_RESULT + 24], word 0x0000 ; 1024 bytes per block + jne .fail +.signature_pass: + mov eax, [READ_LBA_RESULT + 40] + mov [INODES_PER_GROUP], eax + movzx eax, word [READ_LBA_RESULT + 88] + mov [INODE_SIZE], eax + pop eax + clc + ret +.fail: + pop eax + stc + ret + +; in eax = starting byte +; in ecx = number of bytes to read +; in es:edi = pointer to destination +read_absolute_bytes: + pushad + + cmp ecx, 0 + jne .not_empty + ; nothing to copy + ret +.not_empty: + + push eax + and eax, 511 + mov [.BLOCK_OFFSET], eax + pop eax + mov [.BYTES_REMAINING], ecx + + lea edx, [eax + ecx - 1] + shr edx, 9 + shr eax, 9 + + mov ebp, eax + + ; ebp = start sector + ; eax = counter + ; edx = end sector (inclusive) +.lup: + ;call print_dword_hex + call read_lba + + mov esi, READ_LBA_RESULT + mov ecx, 512 + + cmp eax, ebp + jne .not_start + ; dont copy entire sector, only what we need + add esi, [.BLOCK_OFFSET] + sub ecx, [.BLOCK_OFFSET] +.not_start: + +.copy: + ; ensure byte count < remaining + cmp ecx, [.BYTES_REMAINING] + jbe .nomin + mov ecx, [.BYTES_REMAINING] +.nomin: + sub [.BYTES_REMAINING], ecx + rep movsb + + inc eax + cmp eax, edx + jbe .lup + + popad + ret +.BLOCK_OFFSET dd 0 +.BYTES_REMAINING dd 0 + +; in eax = lba number +; out carry = error +read_lba: + push eax + push esi + push edx + add eax, dword [PARTITION_START] +.int: + mov [DAPACK.blkcnt], word 1 + mov [DAPACK.d_lba], dword eax + mov si, DAPACK + mov ah, 0x42 + mov dl, 0x80 + int 0x13 + cld + pop edx + pop esi + pop eax + ret + +; in eax = lba number +; out carry = error +read_absolute_lba: + push eax + push esi + push edx + jmp read_lba.int diff --git a/src/kernel/log.h b/src/kernel/log.h new file mode 100644 index 0000000..aaf43b9 --- /dev/null +++ b/src/kernel/log.h @@ -0,0 +1,3 @@ +#pragma once + +void print(const char *fmt, ...); diff --git a/src/kernel/main.c b/src/kernel/main.c new file mode 100644 index 0000000..071c817 --- /dev/null +++ b/src/kernel/main.c @@ -0,0 +1,17 @@ +#include"log.h" + +#include + +void k_init() { + uint8_t *ptr = (void*) 0xB8000; + ptr[0] = 'Y'; + ptr[2] = 'o'; + ptr[4] = ','; + ptr[6] = ' '; + ptr[8] = 'b'; + ptr[10] = 'i'; + ptr[12] = 't'; + ptr[14] = 'c'; + ptr[16] = 'h'; + ptr[18] = '.'; +} diff --git a/src/kernel/ppm.h b/src/kernel/ppm.h new file mode 100644 index 0000000..1549c6c --- /dev/null +++ b/src/kernel/ppm.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +typedef struct __attribute__((packed)) { + uint64_t start_ptr; + uint64_t page_count; + uint8_t bitmap[]; +} PPMBitmap; + +extern PPMBitmap *ppm_Bitmap; + +size_t ppm_alloc(size_t page_count); +bool ppm_free(size_t start_ptr, size_t page_count); + +size_t ppm_page_size(); diff --git a/src/kernel/tinyubsan.c b/src/kernel/tinyubsan.c new file mode 100644 index 0000000..f487932 --- /dev/null +++ b/src/kernel/tinyubsan.c @@ -0,0 +1,180 @@ +#include + +struct tu_source_location +{ + const char *file; + uint32_t line; + uint32_t column; +}; + +struct tu_type_descriptor +{ + uint16_t kind; + uint16_t info; + char name[]; +}; + +struct tu_overflow_data +{ + struct tu_source_location location; + struct tu_type_descriptor *type; +}; + +struct tu_shift_out_of_bounds_data +{ + struct tu_source_location location; + struct tu_type_descriptor *left_type; + struct tu_type_descriptor *right_type; +}; + +struct tu_invalid_value_data +{ + struct tu_source_location location; + struct tu_type_descriptor *type; +}; + +struct tu_array_out_of_bounds_data +{ + struct tu_source_location location; + struct tu_type_descriptor *array_type; + struct tu_type_descriptor *index_type; +}; + +struct tu_type_mismatch_v1_data +{ + struct tu_source_location location; + struct tu_type_descriptor *type; + unsigned char log_alignment; + unsigned char type_check_kind; +}; + +struct tu_negative_vla_data +{ + struct tu_source_location location; + struct tu_type_descriptor *type; +}; + +struct tu_nonnull_return_data +{ + struct tu_source_location location; +}; + +struct tu_nonnull_arg_data +{ + struct tu_source_location location; +}; + +struct tu_unreachable_data +{ + struct tu_source_location location; +}; + +struct tu_invalid_builtin_data +{ + struct tu_source_location location; + unsigned char kind; +}; + +void print(const char *fmt, ...); + +#ifdef __cplusplus +extern "C" +{ +#endif + static void tu_print_location(const char *message, struct tu_source_location loc) + { + print("tinyubsan: %s at file %s, line %d, column %d\n", message, loc.file, loc.line, loc.column); + } + + void __ubsan_handle_add_overflow(struct tu_overflow_data *data) + { + tu_print_location("addition overflow", data->location); + } + + void __ubsan_handle_sub_overflow(struct tu_overflow_data *data) + { + tu_print_location("subtraction overflow", data->location); + } + + void __ubsan_handle_mul_overflow(struct tu_overflow_data *data) + { + tu_print_location("multiplication overflow", data->location); + } + + void __ubsan_handle_divrem_overflow(struct tu_overflow_data *data) + { + tu_print_location("division overflow", data->location); + } + + void __ubsan_handle_negate_overflow(struct tu_overflow_data *data) + { + tu_print_location("negation overflow", data->location); + } + + void __ubsan_handle_pointer_overflow(struct tu_overflow_data *data) + { + tu_print_location("pointer overflow", data->location); + } + + void __ubsan_handle_shift_out_of_bounds(struct tu_shift_out_of_bounds_data *data, void *lhs, void *rhs) + { + tu_print_location("shift out of bounds", data->location); + } + + void __ubsan_handle_load_invalid_value(struct tu_invalid_value_data *data) + { + tu_print_location("invalid load value", data->location); + } + + void __ubsan_handle_out_of_bounds(struct tu_array_out_of_bounds_data *data) + { + tu_print_location("array out of bounds", data->location); + } + + void __ubsan_handle_type_mismatch_v1(struct tu_type_mismatch_v1_data *data, uintptr_t ptr) + { + if (!ptr) + { + tu_print_location("use of NULL pointer", data->location); + } + + else if (ptr & ((1 << data->log_alignment) - 1)) + { + tu_print_location("use of misaligned pointer", data->location); + } + else + { + tu_print_location("no space for object", data->location); + } + } + + void __ubsan_handle_vla_bound_not_positive(struct tu_negative_vla_data *data) + { + tu_print_location("variable-length argument is negative", data->location); + } + + void __ubsan_handle_nonnull_return(struct tu_nonnull_return_data *data) + { + tu_print_location("non-null return is null", data->location); + } + + void __ubsan_handle_nonnull_arg(struct tu_nonnull_arg_data *data) + { + tu_print_location("non-null argument is null", data->location); + } + + void __ubsan_handle_builtin_unreachable(struct tu_unreachable_data *data) + { + + tu_print_location("unreachable code reached", data->location); + } + + void __ubsan_handle_invalid_builtin(struct tu_invalid_builtin_data *data) + { + + tu_print_location("invalid builtin", data->location); + } + +#ifdef __cplusplus +} +#endif diff --git a/src/kernel/vpm.h b/src/kernel/vpm.h new file mode 100644 index 0000000..aa66d96 --- /dev/null +++ b/src/kernel/vpm.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include + +uintptr_t vpm_map(uintptr_t cr3, bool kernel, uintptr_t virt, uintptr_t phys, size_t len_bytes); +void vpm_unmap(uintptr_t cr3, uintptr_t virt, size_t len_bytes); diff --git a/src/kernel/x86/log.c b/src/kernel/x86/log.c new file mode 100644 index 0000000..177d0b6 --- /dev/null +++ b/src/kernel/x86/log.c @@ -0,0 +1,51 @@ +#include + +#include +#include + +typedef struct { + char buf[256]; + size_t i; +} Ctx; + +static void add_char(Ctx *ctx, char c) { + if(ctx->i < sizeof(ctx->buf)) { + ctx->buf[ctx->i++] = c; + } +} + +void print(const char *fmt, ...) { + Ctx ctx = {}; + + va_list args; + va_start(args, fmt); + + while(*fmt) { + if(*fmt == '%') { + fmt++; + + if(*fmt == 0) { + break; + } + + if(*fmt == 's') { + fmt++; + + const char *src = va_arg(args, const char*); + while(*src) { + add_char(&ctx, *src); + src++; + } + } else { + fmt++; + } + } else { + add_char(&ctx, *fmt); + fmt++; + } + } + + for(size_t i = 0; i < ctx.i; i++) { + asm volatile("outb %%al, $0xE9" :: "a"(ctx.buf[i]):); + } +} diff --git a/src/kernel/x86/ppm.c b/src/kernel/x86/ppm.c new file mode 100644 index 0000000..9c49b78 --- /dev/null +++ b/src/kernel/x86/ppm.c @@ -0,0 +1,93 @@ +#include + +PPMBitmap *ppm_Bitmap; + +static bool ppmbitmap_page_free(PPMBitmap *bm, size_t pg_idx) { + size_t byte_idx = pg_idx / 8; + int bit_idx = pg_idx % 8; + return (byte_idx >> bit_idx) & 1; +} + +static bool ppm_find_free_range(size_t page_count, PPMBitmap **ret_bm, size_t *ret_pg_i) { + if(page_count == 0) { + return false; + } + + PPMBitmap *bm = ppm_Bitmap; + while(bm->page_count) { + size_t byte_count = (bm->page_count + 7) / 8; + + size_t run = 0; + for(size_t i = 0; i < bm->page_count; i++) { + if(ppmbitmap_page_free(bm, i)) { + run++; + if(run == page_count) { + *ret_bm = bm; + *ret_pg_i = i - run + 1; + return true; + } + } else { + run = 0; + } + } + + bm = (void*) ((uintptr_t) bm + sizeof(*bm) + byte_count); + } + + return false; +} + +size_t ppm_alloc(size_t page_count) { + PPMBitmap *bm; + size_t pg_start; + if(!ppm_find_free_range(page_count, &bm, &pg_start)) { + return 0; + } + + for(size_t pg = pg_start; pg < pg_start + page_count; pg++) { + size_t byte_idx = pg / 8; + int bit_idx = pg % 8; + bm->bitmap[byte_idx] |= (1 << bit_idx); + } + + return bm->start_ptr + pg_start * ppm_page_size(); +} + +bool ppm_free(size_t start_ptr, size_t page_count) { + if(start_ptr % ppm_page_size() != 0) { + return false; + } + + size_t end_ptr = start_ptr + page_count * ppm_page_size(); + + PPMBitmap *bm = ppm_Bitmap; + while(bm->page_count) { + if( + (bm->start_ptr <= start_ptr && start_ptr < bm->start_ptr + bm->page_count * ppm_page_size()) + && (bm->start_ptr <= end_ptr && end_ptr < bm->start_ptr + bm->page_count * ppm_page_size()) + ) { + break; + } + + bm = (void*) ((uintptr_t) bm + sizeof(*bm) + (bm->page_count + 7) / 8); + } + + if(bm->page_count == 0) { + return false; + } + + size_t start_pg = (start_ptr - bm->start_ptr) / ppm_page_size(); + + for(size_t pg = 0; pg < page_count; pg++) { + size_t byte_idx = (pg + start_pg) / 8; + int bit_idx = (pg + start_pg) % 8; + + bm->bitmap[byte_idx] &= ~(1 << bit_idx); + } + + return true; +} + +size_t ppm_page_size() { + return 4096; +} diff --git a/src/kernel/x86/vpm.c b/src/kernel/x86/vpm.c new file mode 100644 index 0000000..0c2eb64 --- /dev/null +++ b/src/kernel/x86/vpm.c @@ -0,0 +1,23 @@ +#include"vpm.h" + +static void invlpg(uintptr_t ptr) { +// if(INVLPG_SUPPORTED) { + asm volatile("invlpg %0" : : "m"(*(char*) ptr) : "memory"); +// } else { +// print("Attempt to invlpg on 386\n"); +// } +} + +static void *move_4k_window(uintptr_t ptr) { + *(volatile uint32_t*) 0xFFFFFFF8 = ptr | 3; + invlpg(0xFFFFE000); + return (void*) 0xFFFFE000; +} + +/*uintptr_t vpm_map(uintptr_t cr3, bool kernel, uintptr_t virt, uintptr_t phys, size_t len_bytes) { + move_window(cr3); +} + +void vpm_unmap(uintptr_t cr3, uintptr_t virt, size_t len_bytes) { + +}*/