The new FAT32 bootloader has reached feature parity with the old bootloader (can
actually load the kernel). The new bootloader also brings some nice improvements
over the old bootloader such as loading the kernel at 1MB (fixing the potential
issue that when the kernel grows large enough it will overwrite the EBDA and
upper RAM before it ever happened) and automatically adapting to the kernel
size, and as such I believe that there is no reason to keep the old bootloader
in the source tree.
+++ /dev/null
-; Some routines for managing the A20 line
-
-; Check if A20 is enabled
-; Based on <https://wiki.osdev.org/A20_Line#Testing_the_A20_line>
-check_a20:
- pushf
- push ds
- push es
- push di
- push si
-
- cli
-
- xor ax, ax
- mov es, ax
-
- not ax
- mov ds, ax
-
- mov di, 500h
- mov si, 510h
-
- mov al, byte [es:di]
- push ax
-
- mov al, byte [ds:si]
- push ax
-
- mov byte [es:di], 0
- mov byte [ds:si], 0FFh
-
- cmp byte [es:di], 0FFh
-
- pop ax
- mov byte [ds:si], al
-
- pop ax
- mov byte [es:di], al
-
- mov ax, 0
- je .exit
- mov ax, 1
-
-.exit
- pop si
- pop di
- pop es
- pop ds
- popf
- ret
-
-a20_i8042_wait
- in al, 64h
- test al, 2
- jnz a20_i8042_wait
- ret
-
-a20_i8042_wait2
- in al, 64h
- test al, 1
- jnz a20_i8042_wait2
- ret
-
-enable_a20
- mov ax, 2401h
- int 15h
- call check_a20
- cmp ax, 1
- jne .fail
-.success
- ret
-.fail
- cli ; disable interrupts
-
- call a20_i8042_wait
- mov al, 0ADh ; disable 1st PS/2 port
- out 64h, al
-
- call a20_i8042_wait
- mov al, 0D0h ; read controller output port
- out 64h, al
-
- call a20_i8042_wait2
- in al, 60h ; do the actual read
- push ax ; save the register
-
- call a20_i8042_wait
- mov al, 0D1h ; write byte to output port
- out 64h, al
-
- call a20_i8042_wait
- pop ax ; restore the register
- or al, 2 ; toggle A20 bit
- out 60h, al
-
- call a20_i8042_wait
- mov al, 0AEh ; reenable 1st PS/2 port
- out 64h, al
-
- call a20_i8042_wait
- sti ; enable interrupts
- ret
-
- call check_a20
- cmp ax, 1
- jne .fail2
- ret
-.fail2
- mov si, A20_FAIL
- call print
- jmp $
-
-A20_FAIL db "A20 enable fail, not booting!", 0
+++ /dev/null
- bits 16 ; boot sectors run in real mode
- org 7C00h ; BIOS loads us at 0x7c00
-
-KERNEL_OFFSET equ 1000h ; where we will load our kernel
-
- mov [BOOT_DRIVE], dl ; BIOS puts the number of our boot drive in dl, so we will want to remember this
-
- mov bp, 9000h ; initialize stack at a spot sufficiently distanced from this code
- mov sp, bp
-
- mov di, 0
-
- xor ax, ax ; clear accumulator
- int 13h ; BIOS disk services, with a cleared accumulator this will reset the disk controller
- jc reset_error ; halt if controller reset fails
-
- mov ah, 2 ; instruct BIOS's interrupt 13h to read sectors
- mov al, 30 ; load 30 sectors, might be excessive (for now) but it's still good to do so
- xor ch, ch ; read from 1st cylinder
- mov cl, 2 ; start reading from 2nd sector, right after the boot sector
- xor dh, dh ; read from 1st head
- xor bx, bx ; clear B register
- mov es, bx ; clear extended segment
- mov bx, KERNEL_OFFSET ; put the sectors in our desired offset
- ; dl holds the number of the drive to read from, but BIOS already filled this in
- int 13h ; do the read
- jc read_error ; halt if read fails
- cmp al, 10 ; make sure we actually read 10 sectors
- jl read_error ; halt if we didn't
-
- call check_a20
- cmp ax, 1
- jne a20_is_off
-
-a20_is_on
- jmp switch_to_pm
-a20_is_off
- call enable_a20
- jmp switch_to_pm
-
-reset_error
- mov bx, 0B800h
- mov es, bx
- mov byte [es:di], '1'
- jmp $
-
-read_error
- mov bx, 0B800h
- mov es, bx
- mov byte [es:di], '2'
- jmp $
-
-%include "a20.s"
-%include "print.s"
-%include "protected.s"
-
-BOOT_DRIVE resb 1
-
- times 510-($-$$) db 0
- dw 0AA55h ; MBR signature
+++ /dev/null
-; ds:si - pointer to ASCIIZ string
-print:
- pusha
- pushf
- cld
- xor bh, bh
- mov ah, 0Eh
-.loop:
- lodsb
- cmp al, 0
- je .done
- int 10h
- jmp .loop
-.done:
- mov al, 0Dh
- int 10h
- mov al, 0Ah
- int 10h
- popf
- popa
- ret
+++ /dev/null
-; Everything between real mode and the C kernel
-
- bits 16
-
-switch_to_pm
- mov bx, 0B800h
- mov es, bx ; set extra segment to starting address of video RAM
- mov byte [es:0], 'L' ; print an L to screen, to let us know that we actually got here
-
- cli ; disable interrupts
- xor ax, ax ; clear accumulator
- mov ds, ax ; clear data segment, this makes sure the next instruction reads from the right place
- lgdt [gdt_desc] ; load the Global Descriptor Table
-
- mov eax, cr0 ; move Control Register 0 to accumulator
- or eax, 1 ; flip the bit which controls memory protection
- mov cr0, eax ; move the accumulator back to CR0
- jmp CODE_SEG:protected ; not quite there yet, need to do a far jump to clean the pipeline from any 16-bit instructions
- ; note that the jump does not have to be physically far away, it just needs to use a segmented address
-
- bits 32 ; we are finally in 32-bit mode
-protected
- mov ax, DATA_SEG ; put the selector for the data segment in the accumulator
- ; in real mode, segmentation works by taking one of these segment registers, shifting it right by 4 bits or 1 hex digit
- ; and then adding a 16-bit offset to form a 20-bit address
- ; example: 1234h:5678h
- ; 1234h is shifted to 12340h, 12340h + 5678h is 179B8h
- ; in 32-bit protected mode, these segment registers do not hold the segment address itself, but a selector in the GDT
- ; so we have to update the segment registers accordingly
- mov ds, ax
- mov ss, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- ; reinitialize the stack at a safe location
- mov ebp, 090000h
- mov esp, ebp
-
- ; transfer control to the kernel
- call KERNEL_OFFSET
-
- ; above call should not return in normal circumstances, but if it does hang forever
- jmp $
-
-video_memory equ 0B8000h
-
-; the actual Global Descriptor Table
-; refer to Volume 3, Chapter 2, 2.1.1 of Intel's 64 and IA-32 Software Developer's Manual for more info
-gdt_start
-null_seg
- dq 0
-code_seg
- dw 0FFFFh
- dw 0
- db 0
- db 10011010b
- db 11001111b
- db 0
-data_seg
- dw 0FFFFh
- dw 0
- db 0
- db 10010010b
- db 11001111b
- db 0
-gdt_end
-gdt_desc
- dw gdt_end - gdt_start - 1
- dd gdt_start
-CODE_SEG equ code_seg - gdt_start
-DATA_SEG equ data_seg - gdt_start
-