diff options
Diffstat (limited to 'boot')
-rw-r--r-- | boot/bios.inc | 128 | ||||
-rw-r--r-- | boot/boot.asm | 144 | ||||
-rw-r--r-- | boot/page.inc | 44 | ||||
-rw-r--r-- | boot/segdesc.inc | 40 |
4 files changed, 356 insertions, 0 deletions
diff --git a/boot/bios.inc b/boot/bios.inc new file mode 100644 index 0000000..ccc94e3 --- /dev/null +++ b/boot/bios.inc @@ -0,0 +1,128 @@ +macro bios_a20_enable { + mov ax, 0x2401 + int 0x15 +} + +macro bios_vga_write_mode_3 { + mov ax, 0x03 + int 0x10 +} + +; assumes dl = drive number +macro bios_disk_read dst*, cyl*, head*, sector*, count*, err_label* { + local read_disk + + assert 0 <= cyl & cyl <= 1023 + assert 0 <= head & head <= 15 + assert 1 <= sector & sector <= 17 + assert 1 <= count & count <= 128 + + ; try to read disk up to 3 times + mov di, 3 + +read_disk: + ; ah = 0x02, al = number of sectors to read + mov ax, (0x0200 or count) + + ; es:bx = pointer to destination buffer + if dst = 0 + xor bx, bx + else + mov bx, dst + end if + + ; ch = track/cylinder number, cl = sector number + mov cx, ((cyl and 0xff) shl 8) or ((cyl and 0x300) shl 6) or sector + + ; dh = head number + if head = 0 + xor dh, dh + else + mov dh, head + end if + + int 0x13 + + jc .error + cmp ax, count + jne .error + + jmp .exit +.error: + dec di + jz err_label + + ; reset disk on error + xor ah, ah + stc + int 0x13 + jc err_label + jmp read_disk +.exit: +} + +macro bios_e820_mmap mmap*, err_label* { + local e820, e820_entry +e820: + virtual at di + label e820_entry + .base dq ? + .length dq ? + .type dd ? + .ext_attr dd ? + .sizeof = $ - e820_entry + end virtual + + mov di, mmap + 4 + xor ebx, ebx + xor bp, bp + + mov edx, 0x0534d4150 + mov eax, 0xe820 + mov byte [.ext_attr], 0x01 + mov ecx, .sizeof + int 0x15 + jc err_label + + mov edx, 0x0534d4150 + cmp eax, edx + jne err_label + + test ebx, ebx + jz err_label + + jmp @f + +.e820_loop: + mov ax, 0xe820 + mov byte [.ext_attr], 0x01 + mov ecx, .sizeof + int 0x15 + jc err_label + + mov edx, 0x0534d4150 +@@: + jcxz .skip_entry + + ; ACPI < 3.0 + cmp cl, (.sizeof-4) + jbe .not_ext + + ; ignore data flag + test byte [.ext_attr], 0x01 + jz .skip_entry + +.not_ext: + mov ecx, dword [.length] + or ecx, dword [.length + 4] + jz .skip_entry + + inc bp + add di, .sizeof +.skip_entry: + test ebx, ebx + jnz .e820_loop + + ; store e820 mmap entry count at beginning + mov [mmap], bp +} diff --git a/boot/boot.asm b/boot/boot.asm new file mode 100644 index 0000000..eedde07 --- /dev/null +++ b/boot/boot.asm @@ -0,0 +1,144 @@ +format ELF64 + +PML4_TABLE = 0x1000 +E820_MMAP = 0xf000 + +MSR_EFER = 0xc0000080 + +include 'bios.inc' +include 'page.inc' +include 'segdesc.inc' + +section '.text.boot' executable + +org 0x7c00 +use16 + jmp far 0x0000:@f ; ensure cs = 0x0000 +@@: + xor ax, ax + mov ds, ax + mov es, ax + + ; put stack directly bellow bootloader + mov ss, ax + mov sp, $$ + cld + + mov [boot_disk], dl + + bios_disk_read ($$+0x200), 0, 0, 2, 57, disk_error + + bios_e820_mmap E820_MMAP, e820_error + + bios_a20_enable + bios_vga_write_mode_3 + + page_table_init_real PML4_TABLE + + ; disable IRQs + mov al, 0xff + out 0xa1, al + out 0x21, al + + lidt [dummy_idtr] + + ; enable PAE (1<<5) and PGE (1<<7) + mov eax, (1 shl 7) or (1 shl 5) + mov cr4, eax + + ; point cr3 at PML4 + mov edx, edi + mov cr3, edx + + ; enable long mode + mov ecx, MSR_EFER + rdmsr + or eax, (1 shl 8) + wrmsr + + ; enable protected mode + mov ebx, cr0 + or ebx, (1 shl 31) or (1 shl 0) + mov cr0, ebx + + lgdt [gdtr] + + jmp far (gdt.kcode-gdt):longmode + +e820_error: + ; change beginning of error message ("disk" -> "e820") + mov dword [errmsg], 0x30323865 +disk_error: + mov si, errmsg + mov ah, 0x0e + jmp @f +.loop: + int 0x10 +@@: + lodsb + test al, al + jnz .loop + + ; wait until keypress, then jump to reset vector (reboot) + xor ah, ah + int 0x16 + jmp far 0xffff:0x0000 + +use64 +longmode: + mov ax, (gdt.kdata-gdt) + mov ds, ax + mov es, ax + mov ss, ax + + cli + + ; copy kernel to 0x100000 + mov ecx, (57*0x200)/8 + mov esi, 0x7e00 + mov edi, 0x100000 + rep movsq + + xor eax, eax + xor ebx, ebx + xor ecx, ecx + xor edx, edx + xor edi, edi + + ; put stack at top of addressable memory + mov esp, 0x200000 + mov ebp, esp + + jmp 0x100000 + +align 4 +dw 0 +label gdtr fword + .limit dw (gdt.sizeof - 1) + .addr dd gdt + +align 4 +errmsg db "disk error: press any key to reboot",0 + +align 8 +gdt: + .null dq 0 + .kcode seg_desc 0, 0, (GDT_CODE_64 or SEG_PRIV0) + .kdata seg_desc 0, 0, (GDT_DATA_64 or SEG_PRIV0) + .ucode seg_desc 0, 0, (GDT_CODE_64 or SEG_PRIV3) + .udata seg_desc 0, 0, (GDT_DATA_64 or SEG_PRIV3) + .sizeof = $ - gdt + +align 4 +boot_disk db 0, 0 +label dummy_idtr fword + .limit dw 0 + .addr dd 0 + +if ($-$$) <= 510 + db 510-($-$$) dup 0 +else + display "ERR: boot sector image exceeds 512 bytes",10 + assert ($-$$) <= 510 +end if +db 0x55,0xaa diff --git a/boot/page.inc b/boot/page.inc new file mode 100644 index 0000000..ec2a8ad --- /dev/null +++ b/boot/page.inc @@ -0,0 +1,44 @@ +PAGE_SIZE = 0x1000 + +PAGE_PRESENT = (1 shl 0) +PAGE_WRITE = (1 shl 1) + +macro page_table_init_real pml4_table* { + local page_table + + ; zero out page table + mov edi, pml4_table + push di + mov ecx, (PAGE_SIZE*4)/4 + xor eax, eax + cld + rep stosd + pop di + + virtual at di + label page_table + .PML4T rb PAGE_SIZE + .PDPT rb PAGE_SIZE + .PDT rb PAGE_SIZE + .PT rb PAGE_SIZE + .sizeof = $ - page_table + end virtual + + ; create first entry + mov word [.PML4T], (pml4_table + .PDPT - page_table) or PAGE_PRESENT or PAGE_WRITE + mov word [.PDPT], (pml4_table + .PDT - page_table) or PAGE_PRESENT or PAGE_WRITE + mov word [.PDT], (pml4_table + .PT - page_table) or PAGE_PRESENT or PAGE_WRITE + + ; identity map pages + push di + mov di, (pml4_table + .PT - page_table) + mov ax, (PAGE_PRESENT or PAGE_WRITE) +@@: + mov [di], eax + add eax, PAGE_SIZE + add di, 8 + cmp di, pml4_table + .sizeof + jb @b + + pop di +} diff --git a/boot/segdesc.inc b/boot/segdesc.inc new file mode 100644 index 0000000..b4d14dc --- /dev/null +++ b/boot/segdesc.inc @@ -0,0 +1,40 @@ +; segment type +SEG_ACCESSED = 0001b +SEG_CODE_READ = 0010b +SEG_DATA_WRITE = 0010b +SEG_CODE_CONFORM = 0100b +SEG_DATA_EXPDOWN = 0100b +SEG_EXECUTE = 1000b + +; privilege level +SEG_PRIV0 = (00b shl 0x05) +SEG_PRIV1 = (01b shl 0x05) +SEG_PRIV2 = (10b shl 0x05) +SEG_PRIV3 = (11b shl 0x05) + +; other flags +SEG_DESCTYPE = (1b shl 0x04) ; descriptor type +SEG_PRES = (1b shl 0x07) ; present +SEG_SAVL = (1b shl 0x0C) ; available for system use +SEG_LONG = (1b shl 0x0D) ; long mode +SEG_SIZE = (1b shl 0x0E) ; size +SEG_GRAN = (1b shl 0x0F) ; granularity + +; segment descriptor flag sets for convenience +GDT_CODE_32 = SEG_DESCTYPE or SEG_PRES or SEG_SIZE or SEG_GRAN or SEG_EXECUTE or SEG_CODE_READ +GDT_DATA_32 = SEG_DESCTYPE or SEG_PRES or SEG_SIZE or SEG_GRAN or SEG_DATA_WRITE +GDT_CODE_64 = SEG_DESCTYPE or SEG_PRES or SEG_LONG or SEG_EXECUTE or SEG_CODE_READ +GDT_DATA_64 = SEG_DESCTYPE or SEG_PRES or SEG_LONG or SEG_DATA_WRITE + +struc seg_desc base*, limit*, flag* { + assert 0x0 <= limit & limit <= 0x000fffff + assert 0x0 <= base & base <= 0xffffffff + assert (flag or 0xf0ff) = 0xf0ff + + . dq ((limit shl 0x20) and 0x000f000000000000) or \ + ((flag shl 0x28) and 0x00f0ff0000000000) or \ + ((base shl 0x10) and 0x000000ff00000000) or \ + ((base shl 0x20) and 0xff00000000000000) or \ + ((base shl 0x10) and 0x00000000ffff0000) or \ + ((limit shl 0x00) and 0x000000000000ffff) +} |