forked from KolibriOS/kolibrios
812bae4a84
git-svn-id: svn://kolibrios.org@2783 a494cfbc-eb01-0410-851d-a64ba20cac60
734 lines
14 KiB
NASM
734 lines
14 KiB
NASM
; KolibriOS bootloader
|
|
; this code has been written by diamond in 2005,2006 specially for KolibriOS
|
|
|
|
format binary
|
|
use16
|
|
|
|
org 0xD000
|
|
|
|
; may be changed from ldklbr.vxd
|
|
partition_start dd -1
|
|
boot_drive db 80h
|
|
imgnameofs dw menuet_img_name - 0xD000
|
|
|
|
macro out_delay port
|
|
{
|
|
out port, al
|
|
jcxz $+2
|
|
}
|
|
|
|
cli
|
|
; reprogram IRQs
|
|
mov al, 11h
|
|
out_delay 20h
|
|
out_delay 0A0h
|
|
mov al, 8
|
|
out_delay 21h
|
|
mov al, 70h
|
|
out_delay 0A1h
|
|
mov al, 4
|
|
out_delay 21h
|
|
mov al, 2
|
|
out_delay 0A1h
|
|
mov al, 1
|
|
out_delay 21h
|
|
out_delay 0A1h
|
|
|
|
mov al, 0
|
|
out_delay 21h
|
|
out_delay 0A1h
|
|
|
|
; set videomode
|
|
mov ax, 3
|
|
int 10h
|
|
; reprogram timer
|
|
mov al, 00110100b
|
|
out_delay 43h
|
|
mov al, 0FFh
|
|
out_delay 40h
|
|
out_delay 40h
|
|
|
|
; reset mouse
|
|
mov ax, 0C201h
|
|
int 15h
|
|
|
|
jmp start
|
|
|
|
out_string:
|
|
lodsb
|
|
test al, al
|
|
jz .xxx
|
|
mov ah, 0Eh
|
|
mov bx, 7
|
|
int 10h
|
|
jmp out_string
|
|
.xxx: ret
|
|
|
|
relative_read:
|
|
add eax, [partition_start]
|
|
|
|
; read from hard disk
|
|
; drive_size must be already initialized
|
|
; in: eax = absolute sector
|
|
; cx = number of sectors
|
|
; es:bx -> buffer
|
|
read:
|
|
pushad
|
|
cmp eax, [drive_size]
|
|
jb .old_style
|
|
; new style - LBA, function 42
|
|
cmp [has_lba], 0
|
|
jz disk_error
|
|
; allocate disk address packet on the stack
|
|
; qword +8: absolute block number
|
|
push dword 0 ; dword +C is high dword
|
|
push eax ; dword +8 is low dword
|
|
; dword +4: buffer address
|
|
push es ; word +6 is segment
|
|
push bx ; word +4 is offset
|
|
; word +2: number of blocks = 1
|
|
; word +0: size of packet = 10h
|
|
push dword 10010h
|
|
; now pair ss:sp contain address of disk address packet
|
|
.patch1:
|
|
mov ax, 4200h
|
|
mov dl, [boot_drive]
|
|
mov si, sp
|
|
push ds
|
|
push ss
|
|
pop ds
|
|
int 13h
|
|
pop ds
|
|
add sp, 10h
|
|
.end:
|
|
popad
|
|
jc disk_error
|
|
add bx, 200h
|
|
inc eax
|
|
dec cx
|
|
jnz read
|
|
ret
|
|
.old_style:
|
|
; old style - CHS, function 2
|
|
; convert absolute sector in eax to cylinder-head-sector coordinates
|
|
; calculate sector
|
|
xor edx, edx
|
|
movzx ecx, [sectors]
|
|
div ecx
|
|
; sectors are counted from 1
|
|
inc dl
|
|
mov cl, dl ; low 6 bits of cl = sector number
|
|
; calculate head number
|
|
shld edx, eax, 10h ; convert eax to dx:ax
|
|
div [heads]
|
|
mov dh, dl ; dh = head
|
|
mov ch, al ; ch = low 8 bits of cylinder
|
|
shl ah, 6
|
|
or cl, ah ; high 2 bits of cl = high 2 bits of cylinder
|
|
.patch2:
|
|
mov ax, 201h ; function 2, al=1 - number of sectors
|
|
mov dl, [boot_drive]
|
|
int 13h
|
|
jmp .end
|
|
|
|
disk_error:
|
|
mov si, disk_error_msg
|
|
call out_string
|
|
jmp $
|
|
|
|
has_lba db 0
|
|
|
|
disk_error_msg db 'Disk read error!',0
|
|
start_msg db 2,' KolibriOS bootloader, running on ',0
|
|
errfs_msg db 'unknown filesystem, cannot continue',0
|
|
fat16_msg db 'FAT12/FAT16 - unsupported',13,10,0
|
|
fat32_msg db 'FAT32',13,10,0
|
|
ntfs_msg db 'NTFS',13,10,0
|
|
error_msg db 'Error'
|
|
colon db ': ',0
|
|
mft_string db 'MFT',0
|
|
root_string db '\',0
|
|
noindex_string db '$INDEX_ROOT not found',0
|
|
invalid_read_request_string db 'cannot read attribute',0
|
|
nodata_string db '$DATA '
|
|
notfound_string db 'not found',0
|
|
directory_string db 'is a directory',0
|
|
notdir_string db 'not a directory',0
|
|
fragmented_string db 'too fragmented file',0
|
|
exmem_string db 'extended memory error',0
|
|
bad_cluster_string db 'bad cluster',0
|
|
data_error_msg db 'data error',0
|
|
|
|
start:
|
|
xor ax, ax
|
|
mov ds, ax
|
|
mov es, ax
|
|
; our stack is 4Kb-2b!!! (0xFFE)
|
|
mov ss, ax
|
|
mov esp, 0FFFEh
|
|
cld
|
|
sti
|
|
; calculate drive size
|
|
mov dl, [boot_drive]
|
|
mov ah, 8 ; 8 = get drive parameters
|
|
int 13h
|
|
; now: CF is set on error;
|
|
; ch = low 8 bits of maximum cylinder number
|
|
; cl : low 6 bits makes maximum sector number, high 2 bits are high 2 bits of maximum cylinder number
|
|
; dh = maximum head number
|
|
jnc @f
|
|
mov cx, -1
|
|
mov dh, cl
|
|
@@:
|
|
movzx ax, dh
|
|
inc ax
|
|
; ax = number of heads
|
|
mov [heads], ax
|
|
mov dl, cl
|
|
and dx, 3Fh
|
|
; dx = number of sectors
|
|
; (note that sectors are counted from 1, and maximum sector number = number of sectors)
|
|
mov [sectors], dx
|
|
mul dx
|
|
xchg cl, ch
|
|
shr ch, 6
|
|
inc cx
|
|
; cx = number of cylinders
|
|
mov [cyls], cx
|
|
mul cx
|
|
mov word [drive_size], ax
|
|
mov word [drive_size+2], dx
|
|
; this drive supports LBA?
|
|
mov dl, [boot_drive]
|
|
mov ah, 41h
|
|
mov bx, 55AAh
|
|
int 13h
|
|
jc .no_lba
|
|
cmp bx, 0AA55h
|
|
jnz .no_lba
|
|
test cl, 1
|
|
jz .no_lba
|
|
inc [has_lba]
|
|
.no_lba:
|
|
; say hi to user
|
|
mov si, start_msg
|
|
call out_string
|
|
mov eax, [partition_start]
|
|
cmp eax, -1
|
|
jnz @f
|
|
; now read first sector to determine file system type
|
|
; first sector of disk is MBR sector
|
|
xor eax, eax
|
|
mov cx, 1
|
|
mov bx, 500h
|
|
call read
|
|
mov eax, [6C6h] ; first disk
|
|
mov [partition_start], eax
|
|
@@:
|
|
mov cx, 1
|
|
mov bx, 500h
|
|
call read
|
|
movzx ax, byte [50Dh]
|
|
mov [sect_per_clust], ax
|
|
; determine file system
|
|
cmp dword [536h], 'FAT1'
|
|
jz fat1x
|
|
cmp dword [552h], 'FAT3'
|
|
jz fat32
|
|
cmp dword [503h], 'NTFS'
|
|
jz ntfs
|
|
; mov si, errfs_msg ; already is
|
|
call out_string
|
|
jmp $
|
|
fat1x:
|
|
mov si, fat16_msg
|
|
call out_string
|
|
jmp $
|
|
fat32:
|
|
mov si, fat32_msg
|
|
call out_string
|
|
movzx eax, word [50Bh] ; bytes_per_sect
|
|
movzx ebx, byte [50Dh] ; sects_per_clust
|
|
mul ebx
|
|
mov [cluster_size], eax
|
|
movzx ebx, word [50Eh] ; reserved_sect
|
|
mov [fat_start], ebx
|
|
movzx eax, byte [510h] ; num_fats
|
|
mul dword [524h] ; sect_fat
|
|
add eax, ebx
|
|
; cluster 2 begins from sector eax
|
|
movzx ebx, byte [50Dh] ; sects_per_clust
|
|
sub eax, ebx
|
|
sub eax, ebx
|
|
mov [data_start], eax
|
|
; parse image name
|
|
add [imgnameofs], 0xD000
|
|
mov eax, [52Ch] ; root_cluster
|
|
mov [cur_obj], root_string
|
|
.parsedir:
|
|
push ax
|
|
mov si, [imgnameofs]
|
|
push si
|
|
@@:
|
|
lodsb
|
|
cmp al, '\'
|
|
jz @f
|
|
cmp al, 0
|
|
jnz @b
|
|
@@:
|
|
xchg ax, [esp+2]
|
|
mov byte [si-1], 0
|
|
mov [imgnameofs], si
|
|
call fat32_parse_dir
|
|
pop cx
|
|
test cl, cl
|
|
jz .end
|
|
test byte [di+0Bh], 10h
|
|
mov si, notdir_string
|
|
jz find_error_si
|
|
jmp .parsedir
|
|
.end:
|
|
test byte [di+0Bh], 10h
|
|
mov si, directory_string
|
|
jnz find_error_si
|
|
; parse FAT chunk
|
|
; runlist at 2000:0000
|
|
mov di, 5
|
|
push 2000h
|
|
pop es
|
|
mov byte [es:di-5], 1 ; of course, non-resident
|
|
mov dword [es:di-4], 1
|
|
stosd
|
|
.parsefat:
|
|
push es
|
|
push ds
|
|
pop es
|
|
call next_cluster
|
|
pop es
|
|
jnc .done
|
|
mov ecx, [es:di-8]
|
|
add ecx, [es:di-4]
|
|
cmp eax, ecx
|
|
jz .contc
|
|
mov dword [es:di], 1
|
|
scasd
|
|
stosd
|
|
jmp .parsefat
|
|
.contc:
|
|
inc dword [es:di-8]
|
|
jmp .parsefat
|
|
.done:
|
|
xor eax, eax
|
|
stosd
|
|
jmp read_img_file
|
|
|
|
ntfs:
|
|
mov si, ntfs_msg
|
|
call out_string
|
|
movzx eax, word [50Bh] ; bpb_bytes_per_sect
|
|
push eax
|
|
movzx ebx, byte [50Dh] ; bpb_sects_per_clust
|
|
mul ebx
|
|
mov [cluster_size], eax
|
|
mov [data_start], 0
|
|
mov ecx, [540h] ; frs_size
|
|
cmp cl, 0
|
|
jg .1
|
|
neg cl
|
|
xor eax, eax
|
|
inc eax
|
|
shl eax, cl
|
|
jmp .2
|
|
.1:
|
|
mul ecx
|
|
.2:
|
|
mov [frs_size], eax
|
|
pop ebx
|
|
xor edx, edx
|
|
div ebx
|
|
mov [frs_sectors], ax
|
|
; read first MFT record - description of MFT itself
|
|
mov [cur_obj], mft_string
|
|
movzx eax, byte [50Dh] ; bpb_sects_per_clust
|
|
mul dword [530h] ; mft_cluster
|
|
mov cx, [frs_sectors]
|
|
mov bx, 4000h
|
|
mov di, bx
|
|
push bx
|
|
call relative_read
|
|
call restore_usa
|
|
; scan for unnamed $DATA attribute
|
|
pop di
|
|
mov ax, 80h ; $DATA
|
|
mov bx, 700h
|
|
call load_attr
|
|
mov si, nodata_string
|
|
jc find_error_si
|
|
mov [free], bx
|
|
; load menuet.img
|
|
; parse image name
|
|
add [imgnameofs], 0xD000
|
|
mov eax, 5 ; root cluster
|
|
mov [cur_obj], root_string
|
|
.parsedir:
|
|
push ax
|
|
mov si, [imgnameofs]
|
|
push si
|
|
@@:
|
|
lodsb
|
|
cmp al, '\'
|
|
jz @f
|
|
cmp al, 0
|
|
jnz @b
|
|
@@:
|
|
xchg ax, [esp+2]
|
|
mov byte [si-1], 0
|
|
mov [imgnameofs], si
|
|
call ntfs_parse_dir
|
|
pop cx
|
|
test cl, cl
|
|
jnz .parsedir
|
|
read_img_file:
|
|
xor si, si
|
|
push es
|
|
pop fs
|
|
; yes! Now read file to 0x100000
|
|
lods byte [fs:si]
|
|
cmp al, 0 ; assume nonresident attr
|
|
mov si, invalid_read_request_string
|
|
jz find_error_si
|
|
mov si, 1
|
|
xor edi, edi
|
|
; read buffer to 1000:0000 and move it to extended memory
|
|
push 1000h
|
|
pop es
|
|
xor bx, bx
|
|
.img_read_block:
|
|
lods dword [fs:si] ; eax=length
|
|
xchg eax, ecx
|
|
jecxz .img_read_done
|
|
lods dword [fs:si] ; eax=disk cluster
|
|
.img_read_cluster:
|
|
pushad
|
|
; read part of file
|
|
movzx ecx, byte [50Dh]
|
|
mul ecx
|
|
add eax, [data_start]
|
|
call relative_read
|
|
; move it to extended memory
|
|
mov ah, 87h
|
|
mov ecx, [cluster_size]
|
|
push ecx
|
|
shr cx, 1
|
|
mov si, movedesc
|
|
push es
|
|
push ds
|
|
pop es
|
|
int 15h
|
|
pop es
|
|
test ah, ah
|
|
mov si, exmem_string
|
|
jnz find_error_si
|
|
pop ecx
|
|
add [dest_addr], ecx
|
|
popad
|
|
inc eax
|
|
loop .img_read_cluster
|
|
jmp .img_read_block
|
|
.img_read_done:
|
|
; menuet.img loaded; now load kernel.mnt
|
|
load_kernel:
|
|
push ds
|
|
pop es
|
|
mov [cur_obj], kernel_mnt_name
|
|
; read boot sector
|
|
xor eax, eax
|
|
mov bx, 500h
|
|
mov cx, 1
|
|
call read_img
|
|
; init vars
|
|
mov ax, [50Eh] ; reserved_sect
|
|
add ax, [51Ch] ; hidden
|
|
mov word [fat_start], ax
|
|
xchg ax, bx
|
|
movzx ax, byte [510h] ; num_fats
|
|
mul word [516h] ; fat_length
|
|
add ax, bx
|
|
; read root dir
|
|
mov bx, 700h
|
|
mov cx, [511h] ; dir_entries
|
|
add cx, 0Fh
|
|
shr cx, 4
|
|
call read_img
|
|
add ax, cx
|
|
mov [img_data_start], ax
|
|
shl cx, 9
|
|
mov di, bx
|
|
add bx, cx
|
|
mov byte [bx], 0
|
|
.scan_loop:
|
|
cmp byte [di], 0
|
|
mov si, notfound_string
|
|
jz find_error_si
|
|
mov si, kernel_mnt_name
|
|
call fat_compare_name
|
|
jz .found
|
|
and di, not 1Fh
|
|
add di, 20h
|
|
jmp .scan_loop
|
|
.found:
|
|
and di, not 1Fh
|
|
mov si, directory_string
|
|
test byte [di+0Bh], 10h
|
|
jnz find_error_si
|
|
; found, now load it to 1000h:0000h
|
|
mov ax, [di+1Ah]
|
|
; first cluster of kernel.mnt in ax
|
|
; translate it to sector on disk in menuet.img
|
|
push ax
|
|
dec ax
|
|
dec ax
|
|
movzx cx, byte [50Dh]
|
|
mul cx
|
|
add ax, [img_data_start]
|
|
; now ax is sector in menuet.img
|
|
mov [kernel_mnt_in_img], ax
|
|
div [sect_per_clust]
|
|
; now ax is cluster in menuet.img and
|
|
; dx is offset from the beginning of cluster
|
|
movzx eax, ax
|
|
push 2000h
|
|
pop ds
|
|
mov si, 1
|
|
.scani:
|
|
sub eax, [si]
|
|
jb .scanidone
|
|
; sanity check
|
|
cmp dword [si], 0
|
|
push invalid_read_request_string
|
|
jz find_error_sp
|
|
pop cx
|
|
; next chunk
|
|
add si, 8
|
|
jmp .scani
|
|
.scanidone:
|
|
add eax, [si] ; undo last subtract
|
|
add eax, [si+4] ; get cluster
|
|
push 0
|
|
pop ds
|
|
movzx ecx, [sect_per_clust]
|
|
push dx
|
|
mul ecx ; get sector
|
|
pop dx
|
|
movzx edx, dx
|
|
add eax, edx
|
|
add eax, [data_start]
|
|
mov [kernel_mnt_1st], eax
|
|
pop ax
|
|
push 1000h
|
|
pop es
|
|
.read_loop:
|
|
push ax
|
|
xor bx, bx
|
|
call img_read_cluster
|
|
shl cx, 9-4
|
|
mov ax, es
|
|
add ax, cx
|
|
mov es, ax
|
|
pop ax
|
|
call img_next_cluster
|
|
jc .read_loop
|
|
mov ax, 'KL'
|
|
mov si, loader_block
|
|
jmp 1000h:0000h
|
|
|
|
img_next_cluster:
|
|
mov bx, 700h
|
|
push ax
|
|
shr ax, 1
|
|
add ax, [esp]
|
|
mov dx, ax
|
|
shr ax, 9
|
|
add ax, word [fat_start]
|
|
mov cx, 2
|
|
push es
|
|
push ds
|
|
pop es
|
|
call read_img
|
|
pop es
|
|
and dx, 1FFh
|
|
add bx, dx
|
|
mov ax, [bx]
|
|
pop cx
|
|
test cx, 1
|
|
jz .1
|
|
shr ax, 4
|
|
.1:
|
|
and ax, 0FFFh
|
|
mov si, bad_cluster_string
|
|
cmp ax, 0FF7h
|
|
jz find_error_si
|
|
ret
|
|
img_read_cluster:
|
|
dec ax
|
|
dec ax
|
|
movzx cx, byte [50Dh] ; sects_per_clust
|
|
mul cx
|
|
add ax, [img_data_start]
|
|
movzx eax, ax
|
|
; call read_img
|
|
; ret
|
|
read_img:
|
|
; in: ax = sector, es:bx->buffer, cx=length in sectors
|
|
pushad
|
|
movzx ebx, bx
|
|
mov si, movedesc
|
|
shl eax, 9
|
|
add eax, 93100000h
|
|
mov dword [si+sou_addr-movedesc], eax
|
|
mov eax, 9300000h
|
|
mov ax, es
|
|
shl eax, 4
|
|
add eax, ebx
|
|
mov [si+dest_addr-movedesc], eax
|
|
mov ah, 87h
|
|
shl cx, 8 ; mul 200h/2
|
|
push es
|
|
push 0
|
|
pop es
|
|
int 15h
|
|
pop es
|
|
cmp ah, 0
|
|
mov si, exmem_string
|
|
jnz find_error_si
|
|
popad
|
|
ret
|
|
|
|
movedesc:
|
|
times 16 db 0
|
|
; source
|
|
dw 0xFFFF ; segment length
|
|
sou_addr dw 0000h ; linear address
|
|
db 1 ; linear address
|
|
db 93h ; access rights
|
|
dw 0
|
|
; destination
|
|
dw 0xFFFF ; segment length
|
|
dest_addr dd 93100000h ; high byte contains access rights
|
|
; three low bytes contains linear address (updated when reading)
|
|
dw 0
|
|
times 32 db 0
|
|
|
|
find_error_si:
|
|
push si
|
|
find_error_sp:
|
|
mov si, error_msg
|
|
call out_string
|
|
mov si, [cur_obj]
|
|
call out_string
|
|
mov si, colon
|
|
call out_string
|
|
pop si
|
|
call out_string
|
|
jmp $
|
|
|
|
file_not_found:
|
|
mov si, [esp+2]
|
|
mov [cur_obj], si
|
|
push notfound_string
|
|
jmp find_error_sp
|
|
|
|
include 'fat32.inc'
|
|
include 'ntfs.inc'
|
|
|
|
write1st:
|
|
; callback from kernel.mnt
|
|
; write first sector of kernel.mnt from 1000:0000 back to disk
|
|
push cs
|
|
pop ds
|
|
push cs
|
|
pop es
|
|
; sanity check
|
|
mov bx, 500h
|
|
mov si, bx
|
|
mov cx, 1
|
|
push cx
|
|
mov eax, [kernel_mnt_1st]
|
|
push eax
|
|
call relative_read
|
|
push 1000h
|
|
pop es
|
|
xor di, di
|
|
mov cx, 8
|
|
repz cmpsw
|
|
mov si, data_error_msg
|
|
jnz find_error_si
|
|
; ok, now write back to disk
|
|
or byte [read.patch1+2], 1
|
|
or byte [read.patch2+2], 1
|
|
xor bx, bx
|
|
pop eax
|
|
pop cx
|
|
call relative_read
|
|
and byte [read.patch1+1], not 1
|
|
and byte [read.patch2+2], not 2
|
|
; and to image in memory (probably this may be done by kernel.mnt itself?)
|
|
mov dword [sou_addr], 93010000h
|
|
movzx eax, [kernel_mnt_in_img]
|
|
shl eax, 9
|
|
add eax, 93100000h
|
|
mov dword [dest_addr], eax
|
|
mov si, movedesc
|
|
push ds
|
|
pop es
|
|
mov ah, 87h
|
|
mov cx, 100h
|
|
int 15h
|
|
cmp ah, 0
|
|
mov si, exmem_string
|
|
jnz find_error_si
|
|
retf
|
|
|
|
loader_block:
|
|
db 1 ; version
|
|
dw 1 ; flags - image is loaded
|
|
dw write1st ; offset
|
|
dw 0 ; segment
|
|
|
|
fat_cur_sector dd -1
|
|
|
|
; -----------------------------------------------
|
|
; ------------------ Settings -------------------
|
|
; -----------------------------------------------
|
|
|
|
; must be in lowercase, see ntfs_parse_dir.scan, fat32_parse_dir.scan
|
|
kernel_mnt_name db 'kernel.mnt',0
|
|
|
|
; uninitialized data follows
|
|
drive_size dd ? ; in sectors
|
|
heads dw ?
|
|
sectors dw ?
|
|
cyls dw ?
|
|
free dw ?
|
|
cur_obj dw ?
|
|
data_start dd ?
|
|
img_data_start dw ?
|
|
sect_per_clust dw ?
|
|
kernel_mnt_in_img dw ?
|
|
kernel_mnt_1st dd ?
|
|
; NTFS data
|
|
cluster_size dd ? ; in bytes
|
|
frs_size dd ? ; in bytes
|
|
frs_sectors dw ? ; in sectors
|
|
mft_data_attr dw ?
|
|
index_root dw ?
|
|
index_alloc dw ?
|
|
ofs dw ?
|
|
dir dw ?
|
|
; FAT32 data
|
|
fat_start dd ?
|
|
cur_cluster dd ?
|
|
|
|
; will be initialized by ldklbr.vxd
|
|
menuet_img_name rb 300
|