luma/src/boot1.asm
2025-05-22 22:16:46 +03:00

865 lines
13 KiB
NASM

cpu 386
bits 16
org 0
%define BIT_READWRITE (1 << 1)
%define BIT_EXECUTABLE (1 << 3)
%define BIT_ISCODEORDATA (1 << 4)
%define BIT_PRESENT (1 << 7)
%define PBIT_PRESENT 1
%define PBIT_WRITABLE 2
%define PBIT_USERLEVEL 4
%define PBIT_WRITETHROUGH 8
%define PBIT_CACHEDISABLE 16
%define PBIT_ACCESSED 32
MALLOC_MEM equ 0x520
BOOT1_SEGMENT equ 0x3000
BOOT1_ADDR equ (BOOT1_SEGMENT * 0x10)
start:
mov [ld_lba.drive + 1], dl
mov ah, 8
mov di, 0
int 0x13
mov dl, dh
mov dh, 0
inc dx
mov [lba2chs.numheads + 1], dx
and cl, 0x3F
mov ch, 0
mov [lba2chs.spt + 1], cx
mov eax, [es:0x7C00 + 0x1BE + 8]
mov [get_inode.abs1], eax
mov [read_inode.abs1], eax
mov ecx, 40 * 25
mov edi, 0
.lup:
mov [fs:edi + ecx * 4 - 4], dword 0x0F000F00
dec ecx
jnz .lup
mov si, MSG_START
mov ax, MSG_START.end - MSG_START
call print16
mov edi, MALLOC_MEM ;Bump allocator pointer
add edi, 4096
mov [TSS.esp0], edi ;End of stack ~ beginning of memory map!!
a20:
cli
call .waito
mov al, 0xAD
out 0x64, al
call .waito
mov al, 0xD0
out 0x64, al
.waiti:
in al, 0x64
test al, 1
jz .waiti
in al, 0x60
push ax
call .waito
mov al, 0xD1
out 0x64, al
call .waito
pop ax
or al, 2
out 0x60, al
call .waito
mov al, 0xAE
out 0x64, al
call .waito
sti
jmp ramdetect
.waito:
in al, 0x64
test al, 2
jnz .waito
ret
ramdetect: ;Currently only detects RAM below 16MB, which will be enough for a while.
clc
int 0x12
jc .fail
mov [es:di + 0], dword 0
mov [es:di + 4], dword 0
movzx ecx, ax
shr cx, 2
mov [es:di + 8], ecx
mov [es:di + 12], byte 0
add di, 13
mov al, 0
add cx, 7
shr cx, 3
rep stosb
xor cx, cx
xor dx, dx
mov ax, 0xE801
int 0x15
jc .fail
cmp ah, 0x86
je .fail
cmp ah, 0x80
je .fail
jcxz .useax
mov ax, cx
mov bx, dx
.useax:
mov [es:di + 0], dword 0x100000
mov [es:di + 4], dword 0
movzx ecx, ax
shr ecx, 2
mov [es:di + 8], ecx
mov [es:di + 12], byte 0
add di, 13
mov al, 0
shr ecx, 3
rep stosb
jmp .foundMMap
.fail:
mov si, MSG_FAIL
mov ax, MSG_FAIL.end - MSG_FAIL
call print16
jmp $
.foundMMap:
mov si, MSG_DETECTED_MEMORY_MAP
mov ax, MSG_DETECTED_MEMORY_MAP.end - MSG_DETECTED_MEMORY_MAP
call print16
pg:
; Create page directory
add edi, 4095
and edi, ~4095
mov cr3, edi ;In advance
lea eax, [edi + 4096]
or eax, PBIT_PRESENT | PBIT_WRITABLE
mov [es:edi], eax
add edi, 4
mov eax, 0
mov ecx, 1023
rep stosd
mov ecx, 192
mov edx, PBIT_PRESENT | PBIT_WRITABLE
.lup:
mov [es:edi], edx
add edi, 4
add edx, 4096
dec ecx
jnz .lup
mov ecx, 1024 - 192
mov eax, 0
rep stosd
mov eax, cr3 ;Back to the page directory
mov [es:eax + 4092], edi
or dword [es:eax + 4092], PBIT_PRESENT | PBIT_WRITABLE
mov [es:edi + 4092], edi
or dword [es:edi + 4092], PBIT_PRESENT | PBIT_WRITABLE
add edi, 4096
mov esi, MSG_STEP3s
mov eax, MSG_STEP3s.end - MSG_STEP3s
call print16
loadkernel:
mov eax, 2 ;root
call get_inode
mov bx, di
call read_inode
mov eax, edi
mov esi, KERNEL_FILE
mov ecx, KERNEL_FILE.end - KERNEL_FILE
call find_in_dirnode
test eax, eax
jnz .found
mov si, MSG_STEP5f
mov ax, MSG_STEP5f.end - MSG_STEP5f
call print16
jmp $
.found:
call get_inode
mov bx, di
call read_inode
; Now edi is kernel location, ebx is after kernel.
call fix_module_addresses
mov [inPE.KERNEL_START], edi
mov edi, ebx ;Allocate modules after kernel
;~ mov [inPE.MODULES_START], edi
loadmods:
mov eax, 2
call get_inode
mov esi, [endSector + 32] ;filesize
push es
push ds
pop es
mov bx, endSector + 0x200 ;Will break if root directory is larger than 1KB.
call read_inode
pop es
mov eax, endSector + 0x200
.lup: ;Loop over files, the names of which end with ".mod"
movzx ecx, word [eax + 10] ;Length of name
lea edx, [eax + 12] ;Get name string
cmp ecx, 4 ;Test if shorter than 4 characters
jb .cont
.testext:
cmp [eax + ecx + 12 - 4], dword ".mod"
jne .cont
.testkrnl:
cmp [eax + 12], dword "krnl"
je .cont
.ismod:
inc dword [inPE.MOD_COUNT]
push eax
mov eax, [eax] ;inode
call get_inode
call page_align_es_edi
push esi
mov esi, cr3
mov cx, 1024
push ds
push word 0
pop ds
rep movsd ;Copy page directory to one local to process
pop ds
pop esi
mov bx, di
call read_inode ;edi = start, ebx = end
mov di, bx
pop eax
.cont:
movzx ecx, byte [eax + 9]
shl cx, 4
add eax, ecx
sub esi, ecx
jnz .lup
.end:
mov di, es
shl edi, 4
add edi, 4095
and edi, ~4095
mov [inPE.IMPORTANT_MEM_END], edi
xor ax, ax
mov es, ax
add ebx, edi
fixmmap: ; Make all module memories "used"
lea ecx, [es:ebx + 4095]
shr ecx, 12
mov ebx, ecx
and ebx, 7
shr ecx, 3
mov edi, [TSS.esp0]
add edi, 13
push edi
mov al, -1
rep stosb
mov ecx, ebx
mov al, 1
shl al, cl
dec al
mov [es:edi], al
pop edi
or [es:edi + (BOOT1_ADDR / 4096 / 8)], byte 3 ; TODO: Let boot1 get overwritten after load
modex:
mov ax, 0x13
int 0x10
mov dx, 0x3C4
mov al, 4
out dx, al
inc dx
mov al, 6
out dx, al
mov dx, 0x3D4
mov al, 0x14
out dx, al
inc dx
xor al, al
out dx, al
dec dx
mov al, 0x17
out dx, al
inc dx
mov al, 0xE3
out dx, al
dec dx
mov al, 0x11
out dx, al
inc dx
mov al, 0x2C
out dx, al
dec dx
mov al, 0x06
out dx, al
inc dx
mov al, 0x0D
out dx, al
dec dx
mov al, 0x07
out dx, al
inc dx
mov al, 0x3E
out dx, al
dec dx
mov al, 0x10
out dx, al
inc dx
mov al, 0xEA
out dx, al
dec dx
mov al, 0x11
out dx, al
inc dx
mov al, 0xAC
out dx, al
dec dx
mov al, 0x12
out dx, al
inc dx
mov al, 0xDF
out dx, al
dec dx
mov al, 0x15
out dx, al
inc dx
mov al, 0xE7
out dx, al
dec dx
mov al, 0x16
out dx, al
inc dx
mov al, 0x06
out dx, al
; ESP should be zero here.
push strict dword BOOT1_ADDR + inPE
mov esp, BOOT1_ADDR + 0x10000 + 0xFFFC
jmp enterPM
enterPM:
cli
or [GDT.R0CSLIMFLAGS], byte 64 ;Make 32-bit
lgdt [GDT]
mov eax, cr0
or eax, 1 | (1 << 31)
mov cr0, eax
jmp 8:dword BOOT1_ADDR + .inPM
.inPM:
bits 32
mov eax, 0x23
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov al, 0x10
mov ss, ax
ret
enterRM:
and [GDT.R0CSLIMFLAGS], byte ~64 ;Make 16-bit
lgdt [GDT]
jmp 8:.in16
.in16:
bits 16
mov eax, cr0
and eax, ~(1 | (1 << 31))
mov cr0, eax
jmp 0:.inRM
.inRM:
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
sti
ret
; Makes es:edi a normalized far pointer
normalize_es_edi:
push eax
push edi
mov ax, es
shr di, 4
add ax, di
mov es, ax
pop edi
and edi, 15
pop eax
ret
page_align_es_edi:
push eax
xor eax, eax
mov ax, es
shl eax, 4
add eax, edi
add eax, 4095
and eax, ~4095
shr eax, 4
mov es, ax
pop eax
xor edi, edi
ret
print16:
push di
push cx
push ax
.ind:
mov di, 0
mov cx, ax
.lup:
mov al, [ds:si]
mov [fs:di], al
inc si
inc di
inc di
dec cx
jnz .lup
mov [.ind + 1], di ;SMC
pop ax
pop cx
pop di
ret
ld_lba:
pushad
call lba2chs
mov ch, al
mov dh, dl
.drive:
mov dl, 0
mov ah, 2
mov al, 1
int 0x13
popad
ret
; In: AX = lba
; Out: CX = sector, AX = cylinder, DX = head
lba2chs:
push bx
.spt:
mov bx, 0xF157
xor dx, dx
div bx
mov cx, dx
inc cx
.numheads:
mov bx, 0x9001
xor dx, dx
div bx
pop bx
ret
; In: EAX = inode
; Out: *BOOT1_SEGMENT:endSector = inode struct
get_inode:
push eax
push ebx
.abs1 equ ($ + 2)
add eax, strict dword 0xABCDEF42
push es
push ds
pop es
mov bx, endSector
call ld_lba
pop es
pop ebx
pop eax
ret
; In: *BOOT1_SEGMENT:endSector = inode struct
; Out: *ES:BX = data
read_inode:
push edx
push ecx
push eax
push esi
push edi
push ebx
mov dl, [endSector + 8] ;extent count
mov ecx, endSector + 104 ;extent starts
.lup:
mov eax, [ecx] ;start
mov esi, [ecx + 48] ;size of extent
.abs1 equ ($ + 2)
add eax, strict dword 0x12345678
.lup2:
call ld_lba
add bx, 512
inc eax
dec esi
jnz .lup2
add ecx, 8
dec dl
jnz .lup
mov edi, [esp] ;dest address
lea esi, [edi + 176]
test dword [endSector + 28], 1 << 19
jz .doesntHaveInline
add esi, 512 - 176
.doesntHaveInline:
mov ecx, [endSector + 32] ;filesize
push ds
push es
pop ds
rep movsb
pop ds
add esp, 4 ;ignore old ebx, because read_inode should return the addr after
pop edi
pop esi
pop eax
pop ecx
pop edx
ret
; In: *ES:EAX = directory content buffer, *ESI = name to test, ECX = name length
; Out: EAX = inode or 0 if not found
find_in_dirnode:
push ebx
push edx
push edi
mov edi, [endSector + 32] ;filesize
.lup:
movzx edx, word [es:eax + 10] ;record name length
cmp dx, cx
.lup2:
dec dx
mov bl, [es:eax + 12 + edx]
cmp bl, [esi + edx]
jne .next
test dx, dx
jnz .lup2
jmp .found
.next:
movzx edx, byte [es:eax + 9] ;record length
shl dx, 4
add eax, edx
sub edi, edx
jnz .lup
mov eax, 0
jmp .end
.found:
mov eax, [es:eax]
.end:
pop edi
pop edx
pop ebx
ret
fix_module_addresses:
push esi
push ecx
push eax
push ebx
movzx ecx, word [es:edi + 4] ;amount of syms
lea esi, [es:edi + 12 + ecx * 4]
lea esi, [es:esi + ecx * 2]
movzx ecx, word [es:edi + 6] ;amount of relocs
mov eax, esi ;ptr to relocs
lea esi, [esi + ecx * 4] ;ptr to data
add esi, 15
and esi, ~15
mov [es:edi], esi ;Replace the MOD\0 magic header with the base address
test ecx, ecx ;If none, end
jz .end
.lup:
; Apply relocation
mov ebx, [es:eax]
add ebx, esi
add [es:ebx], esi
add eax, 4
dec ecx
jnz .lup
.end:
pop ebx
pop eax
pop ecx
pop esi
ret
bits 32
inPE:
mov [BOOT1_ADDR + TSS.ss0], dword 0x10
mov esp, [BOOT1_ADDR + TSS.esp0]
mov eax, 40
ltr ax
mov ax, [BOOT1_ADDR + print16.ind + 1]
mov [BOOT1_ADDR + print32.LAST], ax
mov esi, BOOT1_ADDR + MSG_STEP2s
mov eax, MSG_STEP2s.end - MSG_STEP2s
call print32
.KERNEL_START equ ($ + 1)
mov edi, strict dword 0
lea ebx, [edi + 14]
; Sym 0: ppm_Bitmap
mov eax, [ebx]
add eax, [edi] ;Base address that used to be magic header
push dword [BOOT1_ADDR + TSS.esp0] ;As said before, end of stack ~ beginning of mmap!
pop dword [eax]
add ebx, 6
; Sym 1: vpm_init
mov eax, [ebx]
add eax, [edi]
.IMPORTANT_MEM_END equ ($ + 1)
push strict dword 0
call eax
add esp, 4
add ebx, 6
; Sym 2: vpm_map
mov eax, [ebx]
add eax, [edi]
mov [BOOT1_ADDR + .VPM_MAP_ENTRY], eax
add ebx, 6
; Sym 3: canal_init
mov eax, [ebx]
add eax, [edi]
call eax
add ebx, 6
; Sym 4: pci_init
mov eax, [ebx]
add eax, [edi]
call eax
add ebx, 6
; Sym 5: scheduler_init
mov eax, [ebx]
add eax, [edi]
call eax
add ebx, 6
; Sym 6: scheduler_spawn
mov eax, [ebx]
add eax, [edi]
mov [BOOT1_ADDR + .SCHEDULER_SPAWN_ENTRY], eax
add ebx, 6
; Sym 7: scheduler_start
mov eax, [ebx]
add eax, [edi]
mov [BOOT1_ADDR + .SCHEDULER_START_ENTRY], eax
.MOD_COUNT equ ($ + 1)
mov ebp, strict dword 0
.modspawnlup:
; Find next module
movzx ecx, word [edi + 4]
lea eax, [edi + ecx * 4]
lea eax, [eax + ecx * 2]
movzx ecx, word [edi + 6]
lea eax, [eax + ecx * 4]
add eax, 15
and eax, ~15
mov ecx, [edi + 8]
lea eax, [eax + ecx]
add eax, 4096 + 4095
and eax, ~4095
mov edi, eax
mov ebx, 0x40000000
call rebase_module32
sub edx, edi
push strict dword edx
push strict dword edi
push strict dword 0x40000000
push strict dword 1 ;user
lea eax, [edi - 4096] ; cr3
push eax
.VPM_MAP_ENTRY equ ($ + 1)
mov eax, strict dword 0
call eax
add esp, 20
push strict dword 0
.SCHEDULER_SPAWN_ENTRY equ ($ + 1)
mov eax, strict dword 0
call eax
add esp, 4
lea ebx, [edi - 4096]
mov [eax + 46], ebx ;cr3
mov ebx, [edi + 14] ;get sym 0 (modentry)
add ebx, [edi] ;add data offset
mov [eax + 34], ebx ;eip
dec ebp
jnz .modspawnlup
.SCHEDULER_START_ENTRY equ ($ + 1)
mov eax, strict dword 0
jmp eax
print32:
push ebx
push eax
push ecx
movzx ebx, word [BOOT1_ADDR + .LAST]
mov ecx, eax
.lup:
mov al, [esi]
mov [0xB8000 + ebx], al
inc esi
inc ebx
inc ebx
dec ecx
jnz .lup
mov [BOOT1_ADDR + .LAST], bx
pop ecx
pop eax
pop ebx
ret
.LAST: dw 0
; In: EDI = module pointer, EBX = rebase address
; Out: EDX = physical module end
rebase_module32:
push ecx
push eax
push ebx
push ebp
movzx ecx, word [edi + 4]
lea eax, [edi + 12 + ecx * 2]
lea eax, [eax + ecx * 4] ; eax = relocations pointer
movzx ecx, word [edi + 6] ;ecx = amount of relocations
lea edx, [eax + ecx * 4] ; edx = data pointer
add edx, 15
and edx, ~15
mov [edi], edx ;Store offset of data buffer in
sub [edi], edi ;file in place of magic header
add [edi], ebx ;
mov ebx, [edi]
test ecx, ecx
jz .end
.lup:
mov ebp, [eax]
add [edx + ebp], ebx
add eax, 4
dec ecx
jnz .lup
.end:
add edx, [edi + 8]
pop ebp
pop ebx
pop eax
pop ecx
ret
MSG_FAIL: db "Failed to detect memory map. "
.end:
MSG_START: db "Starting eklernel. "
.end:
MSG_DETECTED_MEMORY_MAP: db "Found memory map. "
.end:
MSG_STEP2s: db "Entered protected mode. "
.end:
MSG_STEP3s: db "Generated paging structures. "
.end:
MSG_STEP4f: db "Boot filesystem must have block size of 1024. "
.end:
KERNEL_FILE: db "krnl.mod"
.end:
MSG_STEP5f: db "Kernel not found; aborting boot. "
.end:
GDT:
dw (GDT.END - GDT) - 1 ;GDT descriptor in the null entry
dd BOOT1_ADDR + GDT
dw 0
dw 0xFFFF, 0x0000
db 0x00, BIT_READWRITE | BIT_EXECUTABLE | BIT_ISCODEORDATA | BIT_PRESENT | (0 << 5)
.R0CSLIMFLAGS:
db (128 | 64) | 0xF
db 0
dw 0xFFFF, 0x0000
db 0x00, BIT_READWRITE | BIT_ISCODEORDATA | BIT_PRESENT | (0 << 5)
db (128 | 64) | 0xF
db 0
dw 0xFFFF, 0x0000
db 0x00, BIT_READWRITE | BIT_EXECUTABLE | BIT_ISCODEORDATA | BIT_PRESENT | (3 << 5)
db (128 | 64) | 0xF
db 0
dw 0xFFFF, 0x0000
db 0x00, BIT_READWRITE | BIT_ISCODEORDATA | BIT_PRESENT | (3 << 5)
db (128 | 64) | 0xF
db 0
dw (TSS.END - TSS) - 1
dw TSS
db BOOT1_ADDR / 0x10000, 1 | 8 | 128
db 0, 0
.END:
TSS:
dd 0
.esp0: dd 0
.ss0: dd 0
times 23 dd 0
.END:
end:
times (end - start + 511) / 512 * 512 - ($ - $$) db 0
endSector: