tabs/bootloader/main.asm

324 lines
4.8 KiB
NASM
Raw Normal View History

2019-12-30 15:02:43 +00:00
DISK_ID EQU 0x80
2019-12-27 16:57:36 +00:00
2019-12-30 12:59:02 +00:00
KERNEL_START EQU 0x100000
ELF_START EQU 0x8000
2019-12-30 15:02:43 +00:00
NUM_SECTORS EQU 21
2019-12-28 10:46:56 +00:00
org 0x7C00
bits 16
jmp .start
2019-12-30 15:02:43 +00:00
2019-12-28 14:19:48 +00:00
[bits 16]
; Function: check_a20
;
; Returns: 0 in ax if the a20 line is disabled (memory wraps around)
; 1 in ax if the a20 line is enabled (memory does not wrap around)
check_a20:
cli
xor ax, ax ; ax = 0
mov es, ax
not ax ; ax = 0xFFFF
mov ds, ax
mov di, 0x0500
mov si, 0x0510
mov al, byte [es:di]
2019-12-30 15:46:57 +00:00
mov bx, ax
2019-12-28 14:19:48 +00:00
mov al, byte [ds:si]
mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
cmp byte [es:di], 0xFF
mov byte [ds:si], al
2019-12-30 15:46:57 +00:00
mov ax, bx
2019-12-28 14:19:48 +00:00
mov byte [es:di], al
mov ax, 0
2019-12-30 15:46:57 +00:00
je .check_a20__exit
2019-12-28 14:19:48 +00:00
mov ax, 1
2019-12-30 15:46:57 +00:00
.check_a20__exit:
2019-12-28 14:19:48 +00:00
ret
;;; Print string at bx
2019-12-28 14:19:48 +00:00
puts:
2019-12-28 10:46:56 +00:00
mov al, '!'
mov ah, 0x0e
int 0x10
.puts_loop:
mov al, [bx] ; load the character
cmp al, 0 ; check for null byte
je .end_puts_loop
mov ah, 0x0e
int 0x10 ; print charachter
inc bx
jmp .puts_loop
.end_puts_loop:
ret
;;; Print bl as hex
2019-12-28 14:19:48 +00:00
puthex:
; Print high bits
mov dl, bl
shr dl, 4
cmp dl, 9
jg .puthex_high_alpha
; It's < 10, print as digit
mov al, dl
add al, 0x30
mov ah, 0x0e
int 0x10
jmp .puthex_low
.puthex_high_alpha:
; It's >= 10, print as letter
mov al, dl
add al, 0x57 ; 0x57 = (0x61 - 10)
mov ah, 0x0e
int 0x10
.puthex_low:
; Print low bits
mov dl, bl
and dl, 0b1111
cmp dl, 9
jg .puthex_low_alpha
; It's < 10, print as digit
mov al, dl
add al, 0x30
mov ah, 0x0e
int 0x10
jmp .puthex_end
.puthex_low_alpha:
; It's >= 10, print as letter
mov al, dl
add al, 0x57 ; 0x57 = (0x61 - 10)
mov ah, 0x0e
int 0x10
.puthex_end:
ret
.start:
2019-12-30 13:45:55 +00:00
; disable vga cursor
mov ah, 0x01
mov ch, 0b00100000
mov cl, 0b00000000
int 0x10
; reset disk system
mov ah, 0x00
2019-12-27 16:57:36 +00:00
mov dl, DISK_ID
int 0x13
; print result on error
jnc .reset_disk_no_error
mov bl, ah
2019-12-28 14:19:48 +00:00
call puthex
.reset_disk_no_error:
2019-12-30 15:02:43 +00:00
mov cx, 2 ; start at sector 2 (skip bootloader)
2019-12-30 12:59:02 +00:00
mov bx, ELF_START ; write to ELF_START
2019-12-28 13:41:19 +00:00
.read_disk_loop:
2019-12-27 16:57:36 +00:00
; Read sector dl into memory
mov ah, 0x02
mov al, 0x01 ; number of sectors
mov ch, 0x00 ; cylinder number, low 8 bits
2019-12-27 16:57:36 +00:00
; mov cl, 0x01 ; cylinder number, high 2 bits + sector number (6 bits)
mov dh, 0x00 ; head number
2019-12-27 16:57:36 +00:00
mov dl, DISK_ID ; drive number
int 0x13
2019-12-30 15:02:43 +00:00
; exit on error
2019-12-27 16:57:36 +00:00
jc .read_disk_error
2019-12-30 15:02:43 +00:00
cmp cx, NUM_SECTORS
jge .read_disk_end ; we have reached the sector limit, time to boot
2019-12-28 13:41:19 +00:00
inc cl ; next sector
add bx, 512 ; add 512 to data start so we don't overwrite sectors
2019-12-27 16:57:36 +00:00
jmp .read_disk_loop
2019-12-30 15:02:43 +00:00
2019-12-27 16:57:36 +00:00
.read_disk_error:
2019-12-30 15:02:43 +00:00
mov bl, ah
call puthex
.read_disk_end:
2019-12-28 14:19:48 +00:00
call check_a20
cmp ax, 1
je .a20_enabled
mov bx, .str_no_A20
call puts
jmp .end
.a20_enabled:
2019-12-28 15:44:11 +00:00
; disable interrupts
cli
2019-12-28 14:19:48 +00:00
2019-12-28 15:44:11 +00:00
; reset ds
xor ax, ax
mov ds, ax
; load the GDT
lgdt [.gdt_desc]
; switch to protected mode
mov eax, cr0
or eax, 1
mov cr0, eax
; jump to 32 bit code
jmp 0x08:.clear_pipe
[BITS 32]
.clear_pipe:
; set Data Segment and Stack segment
mov ax, 10h
mov ds, ax
mov ss, ax
; set up stack
mov esp, 0x090000
2019-12-30 12:59:02 +00:00
; parse elf file at ELF_START to KERNEL_START
; verify ELF header
mov esi, ELF_START
cmp dword [esi], 464C457Fh ; ELF magic
jne .invalid_elf
cmp word [esi+4], 0101h ; lsb 32 bit, little endian
jne .invalid_elf
cmp word [esi+18], 03 ; x86 architecture
jne .invalid_elf
; read the entrypoint and store it
mov eax, dword [esi+0x18] ; program entry position
mov dword [.entrypoint], eax
mov cx, word [esi+0x2C] ; read phnum (number of program headers)
mov eax, dword [esi+0x1C] ; read phoff (offset of program header)
; ebx is now ELF_START, esi jumps to the start of the program header
mov ebx, esi
add esi, eax
; set up for loop
sub esi, 0x20
inc cx
.elf_ph_loop:
add esi, 0x20
dec cx
jz .invalid_elf ; there is no valid code block
cmp word [esi], 1 ; check if p_type is loadable
jne .elf_ph_loop
; add offset to ebx (ebx = pointer to code)
add ebx, dword [esi+0x04]
; store p_filesz (ecx = size of the segment)
mov ecx, dword [esi+0x10]
2019-12-28 20:05:22 +00:00
2019-12-30 12:59:02 +00:00
; calculate size of bss (edx = size of bss)
mov edx, dword [esi+0x14]
sub edx, ecx
2019-12-28 16:25:29 +00:00
2019-12-30 12:59:02 +00:00
; check if ecx is zero
or ecx, ecx
jz .invalid_elf
; set source
mov esi, ebx
; set destination
mov edi, KERNEL_START
; repeat ecx/4 times (because it moves 4 bytes at a time)
shr ecx, 2
; copy
repnz movsd
; jump to start of kernel
jmp [.entrypoint]
2019-12-28 14:19:48 +00:00
.end:
2019-12-28 14:19:48 +00:00
jmp .end
2019-12-30 12:59:02 +00:00
.invalid_elf:
mov byte [ds:0x0B8000], 'E'
mov byte [ds:0x0B8001], 0x40
mov byte [ds:0x0B8002], 'L'
mov byte [ds:0x0B8003], 0x40
mov byte [ds:0x0B8004], 'F'
mov byte [ds:0x0B8005], 0x40
jmp .end
2019-12-28 14:19:48 +00:00
.data:
.str_no_A20:
2019-12-30 15:02:43 +00:00
db "A20 fault"
2019-12-28 15:44:11 +00:00
db 0
.gdt:
dd 0
dd 0
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
.gdt_end:
.gdt_desc:
dw .gdt_end - .gdt - 1
dd .gdt
2019-12-30 12:59:02 +00:00
.entrypoint: dd 0
2019-12-27 16:57:36 +00:00
; print padding nullbytes
times 510 - ($ - $$) db 0
2019-12-27 16:57:36 +00:00
; write magic string
dw 0xAA55