; KolibriOS bootloader ; this code has been written by diamond in 2005,2006,2008 specially for KolibriOS ; this code is loaded by ntldr to 0D00:0000 ; and by io.sys from config.sys to xxxx:0100 ; and by bootmgr in vista to 0000:7C00 format binary use16 org 0xD000 ; entry point for 9x and Vista booting call @f ; db 'd' xor 'i' xor 'a' xor 'm' xor 'o' xor 'n' xor 'd' db 'NTFS' @@: pop si sub si, 3 cmp si, 7C00h jz boot_vista mov si, load_question + 100h - 0D000h call out_string ; mov si, answer + 100h - 0D000h ; already is xxy: mov ah, 0 int 16h or al, 20h mov [si], al cmp al, 'y' jz xxz cmp al, 'n' jnz xxy ; continue load Windows ; call out_string ; ret out_string: lodsb test al, al jz .xxx mov ah, 0Eh mov bx, 7 int 10h jmp out_string .xxx: ret xxz: ; boot KolibriOS call out_string push 0 pop ds mov word [4], new01handler + 100h - 0D000h mov [6], cs pushf pop ax or ah, 1 push ax popf ; int 19h ; pushf ; there will be no iret call far [19h*4] xxt: ; TF has been cleared when entered new01handler ; pushf ; pop ax ; and ah, not 1 ; push ax ; popf xor di, di mov ds, di cmp word [8*4+2], 0F000h jz @f les bx, [8*4] mov eax, [es:bx+1] mov [8*4], eax @@: mov si, 100h boot_vista: push cs pop ds push 0D00h pop es mov cx, 2000h/2 rep movsw jmp 0D00h:0256h load_question db 'Load KolibriOS? [y/n]: ',0 answer db ? db 13,10,0 new01handler: ; [sp]=ip, [sp+2]=cs, [sp+4]=flags push bp mov bp, sp push bx push ds lds bx, [bp+2] cmp word [bx], 19cdh jz xxt pop ds pop bx pop bp iret 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 dx 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',13,10,0 part_msg db 'looking at partition ' part_char db '0' ; will be incremented before writing message db ' ... ',0 errfs_msg db 'unknown filesystem',13,10,0 fat16_msg db 'FAT12/FAT16 - unsupported',13,10,0 fat32_msg db 'FAT32' newline db 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 ; init procedure - ntldr jmps here repeat 0D256h - $ db 1 end repeat start: ; cs=es=0D00, ds=07C0, ss=0 ; esi=edi=ebp=0, esp=7C00 xor ax, ax mov ds, ax mov es, ax ; our stack is 4Kb-512b-2b!!! (0xDFE) mov ss, ax mov esp, 0FFFEh ; we are booting from hard disk identified by [boot_drive] mov dl, [boot_drive] cld sti ; calculate drive size 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 ; now read first sector to determine file system type ; first sector of disk is MBR sector xor eax, eax new_partition_ex: mov cx, 1 mov bx, 0F000h call read new_partition: mov bx, [cur_partition_ofs] mov al, [bx+4] ; partition type test al, al jz next_partition cmp al, 5 jz @f cmp al, 0xF jnz not_extended @@: ; extended partition mov eax, [bx+8] ; partition start add eax, [extended_part_start] mov [extended_part_cur], eax next_partition: add [cur_partition_ofs], 10h cmp [cur_partition_ofs], 0xF1FE jb new_partition mov eax, [extended_part_cur] test eax, eax jz partitions_done cmp [extended_part_start], 0 jnz @f mov [extended_part_start], eax @@: mov [extended_parent], eax and [extended_part_cur], 0 mov [cur_partition_ofs], 0xF1BE jmp new_partition_ex partitions_done: mov si, total_kaput call out_string jmp $ not_extended: mov eax, [bx+8] add eax, [extended_parent] mov [partition_start], eax push ax mov si, part_msg inc [si+part_char-part_msg] call out_string pop ax 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 call out_string jmp next_partition fat1x: mov si, fat16_msg call out_string jmp next_partition 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 mov eax, [52Ch] ; root_cluster mov [cur_obj], root_string .parsedir: push ax mov si, [imgnameofs] push si @@: lodsb cmp al, 0 jz @f cmp al, '\' jnz @b dec si mov [missing_slash], si inc si @@: xchg ax, [esp+2] mov byte [si-1], 0 mov [imgnameofs], si call fat32_parse_dir call restore_slash 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 kolibri.img ; parse image name mov eax, 5 ; root cluster mov [cur_obj], root_string .parsedir: push ax mov si, [imgnameofs] push si @@: lodsb cmp al, 0 jz @f cmp al, '\' jnz @b dec si mov [missing_slash], si inc si @@: xchg ax, [esp+2] mov byte [si-1], 0 mov [imgnameofs], si call ntfs_parse_dir call restore_slash 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: ; kolibri.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 kolibri.img push ax dec ax dec ax movzx cx, byte [50Dh] mul cx add ax, [img_data_start] ; now ax is sector in kolibri.img mov [kernel_mnt_in_img], ax div [sect_per_clust] ; now ax is cluster in kolibri.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 mov si, newline call out_string or [fat_cur_sector], -1 mov [imgnameofs], kolibri_img_name call restore_slash mov sp, 0xFFFE jmp next_partition file_not_found: mov si, [esp+2] mov [cur_obj], si push notfound_string jmp find_error_sp restore_slash: mov si, [missing_slash] test si, si jz @f and [missing_slash], 0 mov byte [si], '\' @@: ret 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 imgnameofs dw kolibri_img_name ; ----------------------------------------------- ; ------------------ Settings ------------------- ; ----------------------------------------------- ; must be in lowercase, see ntfs_parse_dir.scan, fat32_parse_dir.scan kernel_mnt_name db 'kernel.mnt',0 kolibri_img_name db 'kolibri.img',0 ; change next variable if you want to boot from other physical drive boot_drive db 80h total_kaput db 13,10,'Fatal: image is not found.',13,10,0 missing_slash dw 0 align 2 cur_partition_ofs dw 0xF1BE extended_part_start dd 0 extended_part_cur dd 0 extended_parent dd 0 ; uninitialized data follows drive_size dd ? ; in sectors heads dw ? sectors dw ? cyls dw ? partition_start dd ? 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 ? ; file must be 16 sectors long repeat 0F000h - $ db 2 end repeat