0,0 → 1,733 |
; 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 |