From ff9dae24ff70d306acfc6a423794f4dba59cf587 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 11 Dec 2024 18:20:46 -0800 Subject: [PATCH] Added bios loader --- bios/bootloader.asm | 31 ++++++ bios/cpuid.asm | 176 +++++++++++++++++++++++++++++++ bios/gdt.asm | 128 +++++++++++++++++++++++ bios/iso.asm | 161 +++++++++++++++++++++++++++++ bios/long_mode.asm | 56 ++++++++++ bios/print.asm | 67 ++++++++++++ bios/protected_mode.asm | 36 +++++++ bios/stage1.asm | 222 ++++++++++++++++++++++++++++++++++++++++ bios/stage2.asm | 134 ++++++++++++++++++++++++ bios/thunk.asm | 149 +++++++++++++++++++++++++++ 10 files changed, 1160 insertions(+) create mode 100644 bios/bootloader.asm create mode 100644 bios/cpuid.asm create mode 100644 bios/gdt.asm create mode 100644 bios/iso.asm create mode 100644 bios/long_mode.asm create mode 100644 bios/print.asm create mode 100644 bios/protected_mode.asm create mode 100644 bios/stage1.asm create mode 100644 bios/stage2.asm create mode 100644 bios/thunk.asm diff --git a/bios/bootloader.asm b/bios/bootloader.asm new file mode 100644 index 0000000..8b59b20 --- /dev/null +++ b/bios/bootloader.asm @@ -0,0 +1,31 @@ +sectalign off + +; stage 1 is sector 0, loaded at 0x7C00 +%include "stage1.asm" + +; GPT area from sector 1 to 33, loaded at 0x7E00 +times (33*512) db 0 + +; stage 2, loaded at 0xC000 +stage2: + %include "stage2.asm" + align 512, db 0 +stage2.end: + +; the maximum size of stage2 is 4 KiB +times (4*1024)-($-stage2) db 0 + +; ISO compatibility, uses up space until 0x12400 +%include "iso.asm" + +times 3072 db 0 ; Pad to 0x13000 + +; stage3, loaded at 0x13000 +stage3: +; %defstr STAGE3_STR %[STAGE3] +; incbin STAGE3_STR +; align 512, db 0 +.end: + +; the maximum size of the boot loader portion is 384 KiB +times (384*1024)-($-$$) db 0 diff --git a/bios/cpuid.asm b/bios/cpuid.asm new file mode 100644 index 0000000..e4aeb90 --- /dev/null +++ b/bios/cpuid.asm @@ -0,0 +1,176 @@ +SECTION .text +USE16 + +cpuid_required_features: + .edx equ cpuid_edx.fpu | cpuid_edx.pse | cpuid_edx.pge | cpuid_edx.fxsr + .ecx equ 0 + +cpuid_check: + ; If bit 21 of EFLAGS can be changed, then CPUID is supported + pushfd ;Save EFLAGS + pushfd ;Store EFLAGS + xor dword [esp],0x00200000 ;Invert the ID bit in stored EFLAGS + popfd ;Load stored EFLAGS (with ID bit inverted) + pushfd ;Store EFLAGS again (ID bit may or may not be inverted) + pop eax ;eax = modified EFLAGS (ID bit may or may not be inverted) + xor eax,[esp] ;eax = whichever bits were changed + popfd ;Restore original EFLAGS + test eax,0x00200000 ;eax = zero if ID bit can't be changed, else non-zero + jz .no_cpuid + + mov eax, 1 + cpuid + + and edx, cpuid_required_features.edx + cmp edx, cpuid_required_features.edx + jne .error + + and ecx, cpuid_required_features.ecx + cmp ecx, cpuid_required_features.ecx + jne .error + + ret + +.no_cpuid: + mov si, .msg_cpuid + call print + + mov si, .msg_line + call print + + jmp .halt + +.error: + push ecx + push edx + + mov si, .msg_features + call print + + mov si, .msg_line + call print + + mov si, .msg_edx + call print + + pop ebx + push ebx + shr ebx, 16 + call print_hex + + pop ebx + call print_hex + + mov si, .msg_must_contain + call print + + mov ebx, cpuid_required_features.edx + shr ebx, 16 + call print_hex + + mov ebx, cpuid_required_features.edx + call print_hex + + mov si, .msg_line + call print + + mov si, .msg_ecx + call print + + pop ebx + push ebx + shr ebx, 16 + call print_hex + + pop ebx + call print_hex + + mov si, .msg_must_contain + call print + + mov ebx, cpuid_required_features.ecx + shr ebx, 16 + call print_hex + + mov ebx, cpuid_required_features.ecx + call print_hex + + mov si, .msg_line + call print + +.halt: + cli + hlt + jmp .halt + +.msg_cpuid: db "CPUID not supported",0 +.msg_features: db "Required CPU features are not present",0 +.msg_line: db 13,10,0 +.msg_edx: db "EDX ",0 +.msg_ecx: db "ECX ",0 +.msg_must_contain: db " must contain ",0 + +cpuid_edx: + .fpu equ 1 << 0 + .vme equ 1 << 1 + .de equ 1 << 2 + .pse equ 1 << 3 + .tsc equ 1 << 4 + .msr equ 1 << 5 + .pae equ 1 << 6 + .mce equ 1 << 7 + .cx8 equ 1 << 8 + .apic equ 1 << 9 + .sep equ 1 << 11 + .mtrr equ 1 << 12 + .pge equ 1 << 13 + .mca equ 1 << 14 + .cmov equ 1 << 15 + .pat equ 1 << 16 + .pse_36 equ 1 << 17 + .psn equ 1 << 18 + .clfsh equ 1 << 19 + .ds equ 1 << 21 + .acpi equ 1 << 22 + .mmx equ 1 << 23 + .fxsr equ 1 << 24 + .sse equ 1 << 25 + .sse2 equ 1 << 26 + .ss equ 1 << 27 + .htt equ 1 << 28 + .tm equ 1 << 29 + .ia64 equ 1 << 30 + .pbe equ 1 << 31 + +cpuid_ecx: + .sse3 equ 1 << 0 + .pclmulqdq equ 1 << 1 + .dtes64 equ 1 << 2 + .monitor equ 1 << 3 + .ds_cpl equ 1 << 4 + .vmx equ 1 << 5 + .smx equ 1 << 6 + .est equ 1 << 7 + .tm2 equ 1 << 8 + .ssse3 equ 1 << 9 + .cnxt_id equ 1 << 10 + .sdbg equ 1 << 11 + .fma equ 1 << 12 + .cmpxchg16b equ 1 << 13 + .xtpr equ 1 << 14 + .pdcm equ 1 << 15 + .pcid equ 1 << 17 + .dca equ 1 << 18 + .sse4_1 equ 1 << 19 + .sse4_2 equ 1 << 20 + .x2apic equ 1 << 21 + .movbe equ 1 << 22 + .popcnt equ 1 << 23 + .tsc_deadline equ 1 << 24 + .aes equ 1 << 25 + .xsave equ 1 << 26 + .osxsave equ 1 << 27 + .avx equ 1 << 28 + .f16c equ 1 << 29 + .rdrand equ 1 << 30 + .hypervisor equ 1 << 31 diff --git a/bios/gdt.asm b/bios/gdt.asm new file mode 100644 index 0000000..925954a --- /dev/null +++ b/bios/gdt.asm @@ -0,0 +1,128 @@ +SECTION .text ; cannot use .data + +struc GDTEntry + .limitl resw 1 + .basel resw 1 + .basem resb 1 + .attribute resb 1 + .flags__limith resb 1 + .baseh resb 1 +endstruc + +gdt_attr: + .present equ 1 << 7 + .ring1 equ 1 << 5 + .ring2 equ 1 << 6 + .ring3 equ 1 << 5 | 1 << 6 + .user equ 1 << 4 +;user + .code equ 1 << 3 +; code + .conforming equ 1 << 2 + .readable equ 1 << 1 +; data + .expand_down equ 1 << 2 + .writable equ 1 << 1 + .accessed equ 1 << 0 +;system +; legacy + .tssAvailabe16 equ 0x1 + .ldt equ 0x2 + .tssBusy16 equ 0x3 + .call16 equ 0x4 + .task equ 0x5 + .interrupt16 equ 0x6 + .trap16 equ 0x7 + .tssAvailabe32 equ 0x9 + .tssBusy32 equ 0xB + .call32 equ 0xC + .interrupt32 equ 0xE + .trap32 equ 0xF +; long mode + .ldt32 equ 0x2 + .tssAvailabe64 equ 0x9 + .tssBusy64 equ 0xB + .call64 equ 0xC + .interrupt64 equ 0xE + .trap64 equ 0xF + +gdt_flag: + .granularity equ 1 << 7 + .available equ 1 << 4 +;user + .default_operand_size equ 1 << 6 +; code + .long_mode equ 1 << 5 +; data + .reserved equ 1 << 5 + +gdtr: + dw gdt.end + 1 ; size + dq gdt ; offset + +gdt: +.null equ $ - gdt + dq 0 + +.lm64_code equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code + at GDTEntry.flags__limith, db gdt_flag.long_mode + at GDTEntry.baseh, db 0 +iend + +.lm64_data equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 +; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it + at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable + at GDTEntry.flags__limith, db 0 + at GDTEntry.baseh, db 0 +iend + +.pm32_code equ $ - gdt + istruc GDTEntry + at GDTEntry.limitl, dw 0xFFFF + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable + at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size + at GDTEntry.baseh, db 0 + iend + +.pm32_data equ $ - gdt + istruc GDTEntry + at GDTEntry.limitl, dw 0xFFFF + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable + at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size + at GDTEntry.baseh, db 0 + iend + +.pm16_code equ $ - gdt + istruc GDTEntry + at GDTEntry.limitl, dw 0xFFFF + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable + at GDTEntry.flags__limith, db 0xF + at GDTEntry.baseh, db 0 + iend + +.pm16_data equ $ - gdt + istruc GDTEntry + at GDTEntry.limitl, dw 0xFFFF + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable + at GDTEntry.flags__limith, db 0xF + at GDTEntry.baseh, db 0 + iend + +.end equ $ - gdt diff --git a/bios/iso.asm b/bios/iso.asm new file mode 100644 index 0000000..d8fe2a8 --- /dev/null +++ b/bios/iso.asm @@ -0,0 +1,161 @@ +; Simple ISO emulation with el torito + +; Fill until CD sector 0x10 +times (0x10*2048)-($-$$) db 0 + +; Volume record +;TODO: fill in more fields +iso_volume_record: +db 1 ; Type volume record +db "CD001" ; Identifier +db 1 ; Version +db 0 ; Unused +times 32 db ' ' ; System identifier +.volume_id: ; Volume identifier +db 'Redox OS' +times 32-($-.volume_id) db ' ' +times 8 db 0 ; Unused +db 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 ; Volume space size (0x15) +times 32 db 0 ; Unused +db 0x01, 0x00, 0x00, 0x01 ; Volume set size +db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number +db 0x00, 0x08, 0x08, 0x00 ; Logical block size in little and big endian + +times 156-($-iso_volume_record) db 0 + +; Root directory entry +.root_directory: +db 0x22 ; Length of entry +db 0x00 ; Length of extended attributes +db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14) +db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent +db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time +db 0x02 ; File flags +db 0x00 ; Interleaved file unit size +db 0x00 ; Interleaved gap size +db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number +db 0x01 ; Length of file identifier +db 0x00 ; File identifier + +times 128 db ' ' ; Volume set identifier +times 128 db ' ' ; Publisher identifier +times 128 db ' ' ; Data preparer identifier +times 128 db ' ' ; Application identifier +times 37 db ' ' ; Copyright file ID +times 37 db ' ' ; Abstract file ID +times 37 db ' ' ; Bibliographic file ID + +times 881-($-iso_volume_record) db 0 + +db 1 ; File structure version + +; Fill until CD sector 0x11 +times (0x11*2048)-($-$$) db 0 + +; Boot record +iso_boot_record: +db 0 ; Type boot record +db "CD001" ; Identifier +db 1 ; Version +db "EL TORITO SPECIFICATION" ; Boot system identifier +times 0x47-($ - iso_boot_record) db 0 ; Padding +dd 0x13 ; Sector of boot catalog + +; Fill until CD sector 0x12 +times (0x12*2048)-($-$$) db 0 + +; Terminator +iso_terminator: +db 0xFF ; Type terminator +db "CD001" ; Identifier +db 1 ; Version + +; Fill until CD sector 0x13 +times (0x13*2048)-($-$$) db 0 + +; Boot catalog +iso_boot_catalog: + +; Validation entry +.validation: +db 1 ; Header ID +db 0 ; Platform ID (x86) +dw 0 ; Reserved +times 24 db 0 ; ID string +dw 0x55aa ; Checksum +dw 0xaa55 ; Key + +; Default entry +.default: +db 0x88 ; Bootable +db 4 ; Hard drive emulation +dw 0 ; Load segment (0 is platform default) +db 0xEE ; Partition type (0xEE is protective MBR) +db 0 ; Unused +dw 1 ; Sector count +dd 0 ; Start address for virtual disk +times 20 db 0 ; Padding + +; EFI section header entry +.efi_section_header: +db 0x91 ; Final header +db 0xEF ; Platform ID (EFI) +dw 1 ; Number of section header entries +times 28 db 0 ; ID string + +; EFI section entry +.efi_section_entry: +db 0x88 ; Bootable +db 0 ; No emulation +dw 0 ; Load segment (0 is platform default) +db 0 ; Partition type (not used) +db 0 ; Unused +dw 512 ; Sector count (1 MiB = 512 CD sectors) +dd 512 ; Start address for virtual disk (1 MiB = 512 CD sectors) +times 20 db 0 ; Padding + +; Fill until CD sector 0x14 +times (0x14*2048)-($-$$) db 0 + +iso_root_directory: +.self: +db 0x22 ; Length of entry +db 0x00 ; Length of extended attributes +db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14) +db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent +db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time +db 0x02 ; File flags +db 0x00 ; Interleaved file unit size +db 0x00 ; Interleaved gap size +db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number +db 0x01 ; Length of file identifier +db 0x00 ; File identifier + +.parent: +db 0x22 ; Length of entry +db 0x00 ; Length of extended attributes +db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14) +db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent +db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time +db 0x02 ; File flags +db 0x00 ; Interleaved file unit size +db 0x00 ; Interleaved gap size +db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number +db 0x01 ; Length of file identifier +db 0x01 ; File identifier + +.boot_cat: +db 0x2C ; Length of entry +db 0x00 ; Length of extended attributes +db 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13 ; Location of extent (0x13) +db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent +db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time +db 0x00 ; File flags +db 0x00 ; Interleaved file unit size +db 0x00 ; Interleaved gap size +db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number +db 0x0A ; Length of file identifier +db "BOOT.CAT;1",0 ; File identifier + +; Fill until CD sector 0x15 +times (0x15*2048)-($-$$) db 0 diff --git a/bios/long_mode.asm b/bios/long_mode.asm new file mode 100644 index 0000000..e0adb04 --- /dev/null +++ b/bios/long_mode.asm @@ -0,0 +1,56 @@ +SECTION .text +USE32 + +long_mode: +.func: dq 0 +.page_table: dd 0 + +.entry: + ; disable interrupts + cli + + ; disable paging + mov eax, cr0 + and eax, 0x7FFFFFFF + mov cr0, eax + + ; enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension + mov eax, cr4 + or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4 + mov cr4, eax + + ; load long mode GDT + lgdt [gdtr] + + ; enable long mode + mov ecx, 0xC0000080 ; Read from the EFER MSR. + rdmsr + or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit. + wrmsr + + ; set page table + mov eax, [.page_table] + mov cr3, eax + + ; enabling paging and protection simultaneously + mov eax, cr0 + or eax, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode + mov cr0, eax + + ; far jump to enable Long Mode and load CS with 64 bit segment + jmp gdt.lm64_code:.inner + +USE64 + +.inner: + ; load all the other segments with 64 bit data segments + mov rax, gdt.lm64_data + mov ds, rax + mov es, rax + mov fs, rax + mov gs, rax + mov ss, rax + + ; jump to specified function + mov rax, [.func] + jmp rax diff --git a/bios/print.asm b/bios/print.asm new file mode 100644 index 0000000..5e958a3 --- /dev/null +++ b/bios/print.asm @@ -0,0 +1,67 @@ +SECTION .text +USE16 + +; provide function for printing in x86 real mode + +; print a string and a newline +; CLOBBER +; ax +print_line: + mov al, 13 + call print_char + mov al, 10 + jmp print_char + +; print a string +; IN +; si: points at zero-terminated String +; CLOBBER +; si, ax +print: + pushf + cld +.loop: + lodsb + test al, al + jz .done + call print_char + jmp .loop +.done: + popf + ret + +; print a character +; IN +; al: character to print +print_char: + pusha + mov bx, 7 + mov ah, 0x0e + int 0x10 + popa + ret + +; print a number in hex +; IN +; bx: the number +; CLOBBER +; al, cx +print_hex: + mov cx, 4 +.lp: + mov al, bh + shr al, 4 + + cmp al, 0xA + jb .below_0xA + + add al, 'A' - 0xA - '0' +.below_0xA: + add al, '0' + + call print_char + + shl bx, 4 + loop .lp + + ret diff --git a/bios/protected_mode.asm b/bios/protected_mode.asm new file mode 100644 index 0000000..8edc621 --- /dev/null +++ b/bios/protected_mode.asm @@ -0,0 +1,36 @@ +SECTION .text +USE16 + +protected_mode: + +.func: dd 0 + +.entry: + ; disable interrupts + cli + + ; load protected mode GDT + lgdt [gdtr] + + ; set protected mode bit of cr0 + mov eax, cr0 + or eax, 1 + mov cr0, eax + + ; far jump to load CS with 32 bit segment + jmp gdt.pm32_code:.inner + +USE32 + +.inner: + ; load all the other segments with 32 bit data segments + mov eax, gdt.pm32_data + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + ; jump to specified function + mov eax, [.func] + jmp eax diff --git a/bios/stage1.asm b/bios/stage1.asm new file mode 100644 index 0000000..d2175e1 --- /dev/null +++ b/bios/stage1.asm @@ -0,0 +1,222 @@ +ORG 0x7C00 +SECTION .text +USE16 + +stage1: ; dl comes with disk + ; initialize segment registers + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + + ; initialize stack + mov sp, 0x7C00 + + ; initialize CS + push ax + push word .set_cs + retf + +.set_cs: + + ; save disk number + mov [disk], dl + + mov si, stage_msg + call print + mov al, '1' + call print_char + call print_line + + ; read CHS gemotry + ; CL (bits 0-5) = maximum sector number + ; CL (bits 6-7) = high bits of max cylinder number + ; CH = low bits of maximum cylinder number + ; DH = maximum head number + mov ah, 0x08 + mov dl, [disk] + xor di, di + int 0x13 + jc error ; carry flag set on error + mov bl, ch + mov bh, cl + shr bh, 6 + mov [chs.c], bx + shr dx, 8 + inc dx ; returns heads - 1 + mov [chs.h], dx + and cl, 0x3f + mov [chs.s], cl + + mov eax, (stage2 - stage1) / 512 + mov bx, stage2 + mov cx, (stage3.end - stage2) / 512 + mov dx, 0 + call load + + mov si, stage_msg + call print + mov al, '2' + call print_char + call print_line + + jmp stage2.entry + +; load some sectors from disk to a buffer in memory +; buffer has to be below 1MiB +; IN +; ax: start sector +; bx: offset of buffer +; cx: number of sectors (512 Bytes each) +; dx: segment of buffer +; CLOBBER +; ax, bx, cx, dx, si +; TODO rewrite to (eventually) move larger parts at once +; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end) +load: + cmp cx, 127 + jbe .good_size + + pusha + mov cx, 127 + call load + popa + add eax, 127 + add dx, 127 * 512 / 16 + sub cx, 127 + + jmp load +.good_size: + mov [DAPACK.addr], eax + mov [DAPACK.buf], bx + mov [DAPACK.count], cx + mov [DAPACK.seg], dx + + call print_dapack + + cmp byte [chs.s], 0 + jne .chs + ;INT 0x13 extended read does not work on CDROM! + mov dl, [disk] + mov si, DAPACK + mov ah, 0x42 + int 0x13 + jc error ; carry flag set on error + ret + +.chs: + ; calculate CHS + xor edx, edx + mov eax, [DAPACK.addr] + div dword [chs.s] ; divide by sectors + mov ecx, edx ; move sector remainder to ecx + xor edx, edx + div dword [chs.h] ; divide by heads + ; eax has cylinders, edx has heads, ecx has sectors + + ; Sector cannot be greater than 63 + inc ecx ; Sector is base 1 + cmp ecx, 63 + ja error_chs + + ; Head cannot be greater than 255 + cmp edx, 255 + ja error_chs + + ; Cylinder cannot be greater than 1023 + cmp eax, 1023 + ja error_chs + + ; Move CHS values to parameters + mov ch, al + shl ah, 6 + and cl, 0x3f + or cl, ah + shl dx, 8 + + ; read from disk using CHS + mov al, [DAPACK.count] + mov ah, 0x02 ; disk read (CHS) + mov bx, [DAPACK.buf] + mov dl, [disk] + push es ; save ES + mov es, [DAPACK.seg] + int 0x13 + pop es ; restore EC + jc error ; carry flag set on error + ret + +print_dapack: + mov bx, [DAPACK.addr + 2] + call print_hex + + mov bx, [DAPACK.addr] + call print_hex + + mov al, '#' + call print_char + + mov bx, [DAPACK.count] + call print_hex + + mov al, ' ' + call print_char + + mov bx, [DAPACK.seg] + call print_hex + + mov al, ':' + call print_char + + mov bx, [DAPACK.buf] + call print_hex + + call print_line + + ret + +error_chs: + mov ah, 0 + +error: + call print_line + + mov bh, 0 + mov bl, ah + call print_hex + + mov al, ' ' + call print_char + + mov si, error_msg + call print + call print_line +.halt: + cli + hlt + jmp .halt + +%include "print.asm" + +stage_msg: db "Stage ",0 +error_msg: db "ERROR",0 + +disk: db 0 + +chs: +.c: dd 0 +.h: dd 0 +.s: dd 0 + +DAPACK: + db 0x10 + db 0 +.count: dw 0 ; int 13 resets this to # of blocks actually read/written +.buf: dw 0 ; memory buffer destination address (0:7c00) +.seg: dw 0 ; in memory page zero +.addr: dq 0 ; put the lba to read in this spot + +times 446-($-$$) db 0 +partitions: times 4 * 16 db 0 +db 0x55 +db 0xaa diff --git a/bios/stage2.asm b/bios/stage2.asm new file mode 100644 index 0000000..1ef0344 --- /dev/null +++ b/bios/stage2.asm @@ -0,0 +1,134 @@ +SECTION .text +USE16 + +stage2.entry: + ; check for required features + call cpuid_check + + ; enable A20-Line via IO-Port 92, might not work on all motherboards + in al, 0x92 + or al, 2 + out 0x92, al + + mov dword [protected_mode.func], stage3.entry + jmp protected_mode.entry + +%include "cpuid.asm" +%include "gdt.asm" +%include "long_mode.asm" +%include "protected_mode.asm" +%include "thunk.asm" + +USE32 + +stage3.entry: + ; stage3 stack at 448 KiB (512KiB minus 64KiB disk buffer) + mov esp, 0x70000 + + ; push arguments + mov eax, thunk.int16 + push eax + mov eax, thunk.int15 + push eax + mov eax, thunk.int13 + push eax + mov eax, thunk.int10 + push eax + xor eax, eax + mov al, [disk] + push eax + mov eax, kernel.entry + push eax + mov eax, [stage3 + 0x18] + call eax +.halt: + cli + hlt + jmp .halt + +kernel: +.stack: dq 0 +.func: dq 0 +.args: dq 0 + +.entry: + ; page_table: usize + mov eax, [esp + 4] + mov [long_mode.page_table], eax + + ; stack: u64 + mov eax, [esp + 8] + mov [.stack], eax + mov eax, [esp + 12] + mov [.stack + 4], eax + + ; func: u64 + mov eax, [esp + 16] + mov [.func], eax + mov eax, [esp + 20] + mov [.func + 4], eax + + ; args: *const KernelArgs + mov eax, [esp + 24] + mov [.args], eax + + ; long_mode: usize + mov eax, [esp + 28] + test eax, eax + jz .inner32 + + mov eax, .inner64 + mov [long_mode.func], eax + jmp long_mode.entry + +.inner32: + ; disable paging + mov eax, cr0 + and eax, 0x7FFFFFFF + mov cr0, eax + + ;TODO: PAE (1 << 5) + ; enable FXSAVE/FXRSTOR, Page Global, and Page Size Extension + mov eax, cr4 + or eax, 1 << 9 | 1 << 7 | 1 << 4 + mov cr4, eax + + ; set page table + mov eax, [long_mode.page_table] + mov cr3, eax + + ; enabling paging and protection simultaneously + mov eax, cr0 + ; Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode + or eax, 1 << 31 | 1 << 16 | 1 + mov cr0, eax + + ; enable FPU + ;TODO: move to Rust + mov eax, cr0 + and al, 11110011b ; Clear task switched (3) and emulation (2) + or al, 00100010b ; Set numeric error (5) monitor co-processor (1) + mov cr0, eax + fninit + + mov esp, [.stack] + mov eax, [.args] + push eax + mov eax, [.func] + call eax +.halt32: + cli + hlt + jmp .halt32 + +USE64 + +.inner64: + mov rsp, [.stack] + mov rax, [.func] + mov rdi, [.args] + call rax +.halt64: + cli + hlt + jmp .halt64 diff --git a/bios/thunk.asm b/bios/thunk.asm new file mode 100644 index 0000000..4423132 --- /dev/null +++ b/bios/thunk.asm @@ -0,0 +1,149 @@ +SECTION .text +USE32 + +thunk: +.int10: + mov dword [.func], .int10_real + jmp .enter + +.int13: + mov dword [.func], .int13_real + jmp .enter + +.int15: + mov dword [.func], .int15_real + jmp .enter + +.int16: + mov dword [.func], .int16_real + jmp .enter + +.func: dd 0 +.esp: dd 0 +.cr0: dd 0 + +.enter: + ; save flags + pushfd + + ; save registers + pushad + + ; save esp + mov [.esp], esp + + ; load gdt + lgdt [gdtr] + + ; far jump to protected mode 16-bit + jmp gdt.pm16_code:.pm16 + +.exit: + ; set segment selectors to 32-bit protected mode + mov eax, gdt.pm32_data + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + ; restore esp + mov esp, [.esp] + + ; restore registers + popad + + ; restore flags + popfd + + ; return + ret + +USE16 + +.int10_real: + int 0x10 + ret + +.int13_real: + int 0x13 + ret + +.int15_real: + int 0x15 + ret + +.int16_real: + int 0x16 + ret + +.pm16: + ; set segment selectors to protected mode 16-bit + mov eax, gdt.pm16_data + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + ; save cr0 + mov eax, cr0 + mov [.cr0], eax + + ; disable paging and protected mode + and eax, 0x7FFFFFFE + mov cr0, eax + + ; far jump to real mode + jmp 0:.real + +.real: + ; set segment selectors to real mode + mov eax, 0 + mov ds, eax + mov es, eax + mov fs, eax + mov gs, eax + mov ss, eax + + ; set stack + mov esp, 0x7C00 - 64 + + ; load registers and ES + pop es + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + + ; enable interrupts + sti + + ; call real mode function + call [.func] + + ; disable interrupts + cli + + ; save registers and ES + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + push es + + ; load gdt (BIOS sometimes overwrites this) + lgdt [gdtr] + + ; restore cr0, will enable protected mode + mov eax, [.cr0] + mov cr0, eax + + ; far jump to protected mode 32-bit + jmp gdt.pm32_code:.exit