forked from KolibriOS/kolibrios
922 lines
20 KiB
NASM
922 lines
20 KiB
NASM
; Copyright (c) 2008-2009, diamond
|
|
; All rights reserved.
|
|
;
|
|
; Redistribution and use in source and binary forms, with or without
|
|
; modification, are permitted provided that the following conditions are met:
|
|
; * Redistributions of source code must retain the above copyright
|
|
; notice, this list of conditions and the following disclaimer.
|
|
; * Redistributions in binary form must reproduce the above copyright
|
|
; notice, this list of conditions and the following disclaimer in the
|
|
; documentation and/or other materials provided with the distribution.
|
|
; * Neither the name of the <organization> nor the
|
|
; names of its contributors may be used to endorse or promote products
|
|
; derived from this software without specific prior written permission.
|
|
;
|
|
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka <Lrz> ''AS IS'' AND ANY
|
|
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
; DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
|
|
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
;*****************************************************************************
|
|
|
|
; KordOS bootloader, based on mtldr, KolibriOS bootloader, by diamond
|
|
; It is used when main bootloader is Windows loader.
|
|
|
|
; this code is loaded:
|
|
; NT/2k/XP: by ntldr to 0D00:0000
|
|
; 9x: by io.sys from config.sys to xxxx:0100
|
|
; Vista: by bootmgr to 0000:7C00
|
|
format binary
|
|
use16
|
|
|
|
; in any case, we relocate this code to 0000:0600
|
|
org 0x600
|
|
; entry point for 9x and Vista booting
|
|
call @f
|
|
db 'NTFS'
|
|
@@:
|
|
pop si
|
|
sub si, 3
|
|
cmp si, 100h
|
|
jnz boot_vista
|
|
mov si, load_question + 100h - 600h
|
|
call out_string
|
|
; mov si, answer + 100h - 0600h ; 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:
|
|
push bx
|
|
@@:
|
|
lodsb
|
|
test al, al
|
|
jz @f
|
|
mov ah, 0Eh
|
|
mov bx, 7
|
|
int 10h
|
|
jmp @b
|
|
@@:
|
|
pop bx
|
|
ret
|
|
xxz:
|
|
; boot KordOS
|
|
call out_string
|
|
; 9x bootloader has already hooked some interrupts; to correctly remove all DOS handlers,
|
|
; issue int 19h (reboot interrupt) and trace its DOS handler until original BIOS handler is reached
|
|
xor di, di
|
|
mov ds, di
|
|
mov word [di+4], new01handler + 100h - 600h
|
|
mov [di+6], cs
|
|
pushf
|
|
pop ax
|
|
or ah, 1
|
|
push ax
|
|
popf
|
|
; we cannot issue INT 19h directly, because INT command clears TF
|
|
; int 19h ; don't issue it directly, because INT command clears TF
|
|
; so instead we use direct call
|
|
; pushf ; there will be no IRET
|
|
call far [di + 19h*4]
|
|
xxt:
|
|
xor di, di
|
|
mov ds, di
|
|
cmp word [di + 8*4+2], 0F000h
|
|
jz @f
|
|
les bx, [di + 8*4]
|
|
mov eax, [es:bx+1]
|
|
mov [di + 8*4], eax
|
|
@@:
|
|
mov si, 100h
|
|
boot_vista:
|
|
; relocate cs:si -> 0000:0600
|
|
push cs
|
|
pop ds
|
|
xor ax, ax
|
|
mov es, ax
|
|
mov di, 0x600
|
|
mov cx, 2000h/2
|
|
rep movsw
|
|
jmp 0:real_entry
|
|
|
|
load_question db 'Load KordOS? [y/n]: ',0
|
|
answer db ?
|
|
db 13,10,0
|
|
|
|
new01handler:
|
|
; [sp]=ip, [sp+2]=cs, [sp+4]=flags
|
|
push bp
|
|
mov bp, sp
|
|
push ds
|
|
lds bp, [bp+2]
|
|
cmp word [ds:bp], 19cdh
|
|
jz xxt
|
|
pop ds
|
|
pop bp
|
|
iret
|
|
|
|
; read from hard disk
|
|
; in: eax = absolute sector
|
|
; cx = number of sectors
|
|
; es:bx -> buffer
|
|
; out: CF=1 if error
|
|
read:
|
|
pushad
|
|
add eax, [bp + partition_start - dat]
|
|
cmp [bp + use_lba - dat], 0
|
|
jz .chs
|
|
; LBA read
|
|
push ds
|
|
.lbado:
|
|
push ax
|
|
push cx
|
|
cmp cx, 0x7F
|
|
jbe @f
|
|
mov cx, 0x7F
|
|
@@:
|
|
; create disk address packet on the stack
|
|
; dq starting LBA
|
|
push 0
|
|
push 0
|
|
push eax
|
|
; dd buffer
|
|
push es
|
|
push bx
|
|
; dw number of blocks to transfer (no more than 0x7F)
|
|
push cx
|
|
; dw packet size in bytes
|
|
push 10h
|
|
; issue BIOS call
|
|
push ss
|
|
pop ds
|
|
mov si, sp
|
|
mov dl, [bp + boot_drive - dat]
|
|
mov ah, 42h
|
|
int 13h
|
|
jc .disk_error_lba
|
|
add sp, 10h ; restore stack
|
|
; increase current sector & buffer; decrease number of sectors
|
|
movzx esi, cx
|
|
mov ax, es
|
|
shl cx, 5
|
|
add ax, cx
|
|
mov es, ax
|
|
pop cx
|
|
pop ax
|
|
add eax, esi
|
|
sub cx, si
|
|
jnz .lbado
|
|
pop ds
|
|
popad
|
|
ret
|
|
.disk_error_lba:
|
|
add sp, 14h
|
|
pop ds
|
|
popad
|
|
stc
|
|
ret
|
|
|
|
.chs:
|
|
pusha
|
|
pop edi ; loword(edi) = di, hiword(edi) = si
|
|
push bx
|
|
|
|
; eax / (SectorsPerTrack) -> eax, remainder bx
|
|
movzx esi, [bp + sectors - dat]
|
|
xor edx, edx
|
|
div esi
|
|
mov bx, dx ; bx = sector-1
|
|
|
|
; eax -> dx:ax
|
|
push eax
|
|
pop ax
|
|
pop dx
|
|
; (dword in dx:ax) / (NumHeads) -> (word in ax), remainder dx
|
|
div [bp + heads - dat]
|
|
|
|
; number of sectors: read no more than to end of track
|
|
sub si, bx
|
|
cmp cx, si
|
|
jbe @f
|
|
mov cx, si
|
|
@@:
|
|
|
|
inc bx
|
|
; now ax=track, dl=head, dh=0, cl=number of sectors, ch=0, bl=sector
|
|
; convert to int13 format
|
|
movzx edi, cx
|
|
mov dh, dl
|
|
mov dl, [bp + boot_drive - dat]
|
|
shl ah, 6
|
|
mov ch, al
|
|
mov al, cl
|
|
mov cl, bl
|
|
or cl, ah
|
|
pop bx
|
|
mov si, 3
|
|
mov ah, 2
|
|
@@:
|
|
push ax
|
|
int 13h
|
|
jnc @f
|
|
xor ax, ax
|
|
int 13h ; reset drive
|
|
pop ax
|
|
dec si
|
|
jnz @b
|
|
add sp, 12
|
|
popad
|
|
stc
|
|
ret
|
|
@@:
|
|
pop ax
|
|
mov ax, es
|
|
mov cx, di
|
|
shl cx, 5
|
|
add ax, cx
|
|
mov es, ax
|
|
push edi
|
|
popa
|
|
add eax, edi
|
|
sub cx, di
|
|
jnz .chs
|
|
popad
|
|
ret
|
|
|
|
disk_error2 db 'Fatal: cannot read partitions info: '
|
|
disk_error_msg db 'disk read error',0
|
|
disk_params_msg db 'Fatal: cannot get drive parameters',0
|
|
start_msg db 2,' KordOS 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
|
|
fatxx_msg db 'FATxx'
|
|
newline db 13,10,0
|
|
ntfs_msg db 'NTFS',13,10,0
|
|
error_msg db 'Error'
|
|
colon db ': ',0
|
|
root_string db '\',0
|
|
nomem_msg db 'No memory',0
|
|
filesys_string db '(filesystem)',0
|
|
directory_string db 'is a directory',0
|
|
notdir_string db 'not a directory',0
|
|
|
|
; entry point for NT/2k/XP booting
|
|
; ntldr loads our code to 0D00:0000 and jumps to 0D00:0256
|
|
repeat 600h + 256h - $
|
|
db 1 ; any data can be here; 1 in ASCII is a nice face :)
|
|
end repeat
|
|
; cs=es=0D00, ds=07C0, ss=0
|
|
; esi=edi=ebp=0, esp=7C00
|
|
xor si, si
|
|
jmp boot_vista
|
|
|
|
real_entry:
|
|
; ax = 0
|
|
mov ds, ax
|
|
mov es, ax
|
|
; our stack is 4 Kb: memory range 2000-3000
|
|
mov ss, ax
|
|
mov sp, 3000h
|
|
mov bp, dat
|
|
sti ; just for case
|
|
; say hi to user
|
|
mov si, start_msg
|
|
call out_string
|
|
; we are booting from hard disk identified by [boot_drive]
|
|
mov dl, [bp + boot_drive - dat]
|
|
; is LBA supported?
|
|
mov [bp + use_lba - dat], 0
|
|
mov ah, 41h
|
|
mov bx, 55AAh
|
|
int 13h
|
|
jc .no_lba
|
|
cmp bx, 0AA55h
|
|
jnz .no_lba
|
|
test cl, 1
|
|
jz .no_lba
|
|
inc [bp + use_lba - dat]
|
|
jmp disk_params_ok
|
|
.no_lba:
|
|
; get drive geometry
|
|
mov ah, 8
|
|
mov dl, [bp + boot_drive - dat]
|
|
int 13h
|
|
jnc @f
|
|
mov si, disk_params_msg
|
|
call out_string
|
|
jmp $
|
|
@@:
|
|
movzx ax, dh
|
|
inc ax
|
|
mov [bp + heads - dat], ax
|
|
and cx, 3Fh
|
|
mov [bp + sectors - dat], cx
|
|
disk_params_ok:
|
|
; determine size of cache for folders
|
|
int 12h ; ax = size of available base memory in Kb
|
|
sub ax, 94000h / 1024
|
|
jc nomem
|
|
shr ax, 3
|
|
mov [bp + cachelimit - dat], ax ; size of cache - 1
|
|
; scan all partitions
|
|
new_partition_ex:
|
|
xor eax, eax ; read first sector of current disk area
|
|
mov [bp + extended_part_cur - dat], eax ; no extended partition yet
|
|
mov [bp + cur_partition_ofs - dat], 31BEh ; start from first partition
|
|
push es
|
|
mov cx, 1
|
|
mov bx, 3000h
|
|
call read
|
|
pop es
|
|
jnc new_partition
|
|
mov si, disk_error2
|
|
call out_string
|
|
jmp $
|
|
new_partition:
|
|
mov bx, [bp + cur_partition_ofs - dat]
|
|
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, [bp + extended_part_start - dat]
|
|
mov [bp + extended_part_cur - dat], eax
|
|
next_partition:
|
|
add [bp + cur_partition_ofs - dat], 10h
|
|
cmp [bp + cur_partition_ofs - dat], 31FEh
|
|
jb new_partition
|
|
mov eax, [bp + extended_part_cur - dat]
|
|
test eax, eax
|
|
jz partitions_done
|
|
cmp [bp + extended_part_start - dat], 0
|
|
jnz @f
|
|
mov [bp + extended_part_start - dat], eax
|
|
@@:
|
|
mov [bp + extended_parent - dat], eax
|
|
mov [bp + partition_start - dat], eax
|
|
jmp new_partition_ex
|
|
partitions_done:
|
|
mov si, total_kaput
|
|
call out_string
|
|
jmp $
|
|
not_extended:
|
|
mov eax, [bx+8]
|
|
add eax, [bp + extended_parent - dat]
|
|
mov [bp + partition_start - dat], eax
|
|
; try to load from current partition
|
|
; inform user
|
|
mov si, part_msg
|
|
inc [si + part_char - part_msg]
|
|
call out_string
|
|
; read bootsector
|
|
xor eax, eax
|
|
mov [bp + cur_obj - dat], filesys_string
|
|
push es
|
|
mov cx, 1
|
|
mov bx, 3200h
|
|
call read
|
|
pop es
|
|
mov si, disk_error_msg
|
|
jc find_error_si
|
|
movzx si, byte [bx+13]
|
|
mov word [bp + sect_per_clust - dat], si
|
|
test si, si
|
|
jz unknown_fs
|
|
lea ax, [si-1]
|
|
test si, ax
|
|
jnz unknown_fs
|
|
; determine file system
|
|
; Number of bytes per sector == 0x200 (this loader assumes that physical sector size is 200h)
|
|
cmp word [bx+11], 0x200
|
|
jnz unknown_fs
|
|
; is it NTFS?
|
|
cmp dword [bx+3], 'NTFS'
|
|
jnz not_ntfs
|
|
cmp byte [bx+16], bl
|
|
jz ntfs
|
|
not_ntfs:
|
|
; is it FAT? FAT12/FAT16/FAT32?
|
|
; get count of sectors to dword in cx:si
|
|
mov si, [bx+19]
|
|
xor cx, cx
|
|
test si, si
|
|
jnz @f
|
|
mov si, [bx+32]
|
|
mov cx, [bx+34]
|
|
@@:
|
|
xor eax, eax
|
|
; subtract size of system area
|
|
sub si, [bx+14] ; BPB_ResvdSecCnt
|
|
sbb cx, ax
|
|
mov ax, [bx+17] ; BPB_RootEntCnt
|
|
add ax, 0xF
|
|
rcr ax, 1
|
|
shr ax, 3
|
|
sub si, ax
|
|
sbb cx, 0
|
|
push cx
|
|
push si
|
|
mov ax, word [bx+22]
|
|
test ax, ax
|
|
jnz @f
|
|
mov eax, [bx+36]
|
|
@@:
|
|
movzx ecx, byte [bx+16]
|
|
imul ecx, eax
|
|
pop eax
|
|
sub eax, ecx
|
|
; now eax = count of sectors in the data region
|
|
xor edx, edx
|
|
div [bp + sect_per_clust - dat]
|
|
; now eax = count of clusters in the data region
|
|
mov si, fatxx_msg
|
|
cmp eax, 0xFFF5
|
|
jae test_fat32
|
|
; test magic value in FAT bootsector - FAT12/16 bootsector has it at the offset +38
|
|
cmp byte [bx+38], 0x29
|
|
jnz not_fat
|
|
cmp ax, 0xFF5
|
|
jae fat16
|
|
fat12:
|
|
mov [bp + get_next_cluster_ptr - dat], fat12_get_next_cluster
|
|
mov di, cx ; BPB_NumFATs
|
|
mov ax, '12'
|
|
push ax ; save for secondary loader
|
|
mov word [si+3], ax
|
|
call out_string
|
|
movzx ecx, word [bx+22] ; BPB_FATSz16
|
|
; FAT12: read entire FAT table (it is no more than 0x1000*3/2 = 0x1800 bytes)
|
|
.fatloop:
|
|
; if first copy is not readable, try to switch to other copies
|
|
push 0x6000
|
|
pop es
|
|
xor bx, bx
|
|
movzx eax, word [0x320E] ; BPB_RsvdSecCnt
|
|
push cx
|
|
cmp cx, 12
|
|
jb @f
|
|
mov cx, 12
|
|
@@:
|
|
call read
|
|
pop cx
|
|
jnc fat1x_common
|
|
add eax, ecx ; switch to next copy of FAT
|
|
dec di
|
|
jnz .fatloop
|
|
mov si, disk_error_msg
|
|
jmp find_error_si
|
|
fat16:
|
|
mov [bp + get_next_cluster_ptr - dat], fat16_get_next_cluster
|
|
mov ax, '16'
|
|
push ax ; save for secondary loader
|
|
mov word [si+3], ax
|
|
call out_string
|
|
; FAT16: init FAT cache - no sectors loaded
|
|
mov di, 0x3400
|
|
xor ax, ax
|
|
mov cx, 0x100/2
|
|
rep stosw
|
|
fat1x_common:
|
|
mov bx, 0x3200
|
|
movzx eax, word [bx+22] ; BPB_FATSz16
|
|
xor esi, esi ; no root cluster
|
|
jmp fat_common
|
|
test_fat32:
|
|
; FAT32 bootsector has it at the offset +66
|
|
cmp byte [bx+66], 0x29
|
|
jnz not_fat
|
|
mov [bp + get_next_cluster_ptr - dat], fat32_get_next_cluster
|
|
mov ax, '32'
|
|
push ax ; save for secondary loader
|
|
mov word [si+3], ax
|
|
call out_string
|
|
; FAT32 - init cache for FAT table: no sectors loaded
|
|
lea si, [bp + cache1head - dat]
|
|
mov [si], si ; no sectors in cache:
|
|
mov [si+2], si ; 'prev' & 'next' links point to self
|
|
mov [bp + cache1end - dat], 3400h ; first free item = 3400h
|
|
mov [bp + cache1limit - dat], 3C00h
|
|
mov eax, [bx+36] ; BPB_FATSz32
|
|
mov esi, [bx+44] ; BPB_RootClus
|
|
jmp fat_common
|
|
not_fat:
|
|
unknown_fs:
|
|
mov si, errfs_msg
|
|
call out_string
|
|
jmp next_partition
|
|
fat_common:
|
|
push ss
|
|
pop es
|
|
movzx edx, byte [bx+16] ; BPB_NumFATs
|
|
mul edx
|
|
mov [bp + root_start - dat], eax ; this is for FAT1x
|
|
; eax = total size of all FAT tables, in sectors
|
|
movzx ecx, word [bx+17] ; BPB_RootEntCnt
|
|
add ecx, 0xF
|
|
shr ecx, 4
|
|
add eax, ecx
|
|
mov cx, word [bx+14] ; BPB_RsvdSecCnt
|
|
add [bp + root_start - dat], ecx ; this is for FAT1x
|
|
add eax, ecx
|
|
; cluster 2 begins from sector eax
|
|
movzx ebx, byte [bx+13] ; BPB_SecPerClus
|
|
sub eax, ebx
|
|
sub eax, ebx
|
|
mov [bp + data_start - dat], eax
|
|
; no clusters in folders cache
|
|
mov di, foldcache_clus - 2
|
|
xor ax, ax
|
|
mov cx, 7*8/2 + 1
|
|
rep stosw
|
|
mov [bp + root_clus - dat], esi
|
|
; load secondary loader
|
|
mov [bp + load_file_ptr - dat], load_file_fat
|
|
load_secondary:
|
|
push 0x1000
|
|
pop es
|
|
xor bx, bx
|
|
mov si, kernel_name
|
|
mov cx, 0x30000 / 0x200
|
|
call [bp + load_file_ptr - dat]
|
|
; say error if needed
|
|
mov si, error_too_big
|
|
dec bx
|
|
js @f
|
|
jz find_error_si
|
|
mov si, disk_error_msg
|
|
jmp find_error_si
|
|
@@:
|
|
; fill loader information and jump to secondary loader
|
|
mov al, 'h' ; boot device: hard drive
|
|
mov ah, [bp + boot_drive - dat]
|
|
sub ah, 80h ; boot device: identifier
|
|
pop bx ; restore file system ID ('12'/'16'/'32'/'nt')
|
|
mov si, callback
|
|
jmp 1000h:0000h
|
|
|
|
nomem:
|
|
mov si, nomem_msg
|
|
call out_string
|
|
jmp $
|
|
|
|
ntfs:
|
|
push 'nt' ; save for secondary loader
|
|
mov si, ntfs_msg
|
|
call out_string
|
|
xor eax, eax
|
|
mov [bp + data_start - dat], eax
|
|
mov ecx, [bx+40h] ; frs_size
|
|
cmp cl, al
|
|
jg .1
|
|
neg cl
|
|
inc ax
|
|
shl eax, cl
|
|
jmp .2
|
|
.1:
|
|
mov eax, ecx
|
|
shl eax, 9
|
|
.2:
|
|
mov [bp + frs_size - dat], ax
|
|
; standard value for frs_size is 0x400 bytes = 1 Kb, and it cannot be set different
|
|
; (at least with standard tools)
|
|
; we allow extra size, but no more than 0x1000 bytes = 4 Kb
|
|
mov si, invalid_volume_msg
|
|
cmp eax, 0x1000
|
|
ja find_error_si
|
|
; must be multiple of sector size
|
|
test ax, 0x1FF
|
|
jnz find_error_si
|
|
shr ax, 9
|
|
xchg cx, ax
|
|
; initialize cache - no data loaded
|
|
lea si, [bp + cache1head - dat]
|
|
mov [si], si
|
|
mov [si+2], si
|
|
mov word [si+4], 3400h ; first free item = 3400h
|
|
mov word [si+6], 3400h + 8*8 ; 8 items in this cache
|
|
; read first MFT record - description of MFT itself
|
|
mov [bp + cur_obj - dat], mft_string
|
|
mov eax, [bx+30h] ; mft_cluster
|
|
mul [bp + sect_per_clust - dat]
|
|
push 0x8000
|
|
pop es
|
|
xor bx, bx
|
|
push es
|
|
call read
|
|
pop ds
|
|
call restore_usa
|
|
; scan for unnamed $DATA attribute
|
|
mov [bp + freeattr - dat], 4000h
|
|
mov ax, 80h
|
|
call load_attr
|
|
push ss
|
|
pop ds
|
|
mov si, nodata_string
|
|
jc find_error_si
|
|
; load secondary loader
|
|
mov [bp + load_file_ptr - dat], load_file_ntfs
|
|
jmp load_secondary
|
|
|
|
find_error_si:
|
|
push si
|
|
find_error_sp:
|
|
cmp [bp + in_callback - dat], 0
|
|
jnz error_in_callback
|
|
push ss
|
|
pop ds
|
|
push ss
|
|
pop es
|
|
mov si, error_msg
|
|
call out_string
|
|
mov si, [bp + cur_obj - dat]
|
|
@@:
|
|
lodsb
|
|
test al, al
|
|
jz @f
|
|
cmp al, '/'
|
|
jz @f
|
|
mov ah, 0Eh
|
|
mov bx, 7
|
|
int 10h
|
|
jmp @b
|
|
@@:
|
|
mov si, colon
|
|
call out_string
|
|
pop si
|
|
call out_string
|
|
mov si, newline
|
|
call out_string
|
|
mov sp, 0x3000
|
|
jmp next_partition
|
|
error_in_callback:
|
|
; return status: file not found, except for read errors
|
|
mov bx, 2
|
|
cmp si, disk_error_msg
|
|
jnz @f
|
|
inc bx
|
|
@@:
|
|
mov ax, 0xFFFF
|
|
mov dx, ax
|
|
mov sp, 3000h - 6
|
|
ret
|
|
|
|
callback:
|
|
; in: ax = function number; only functions 1 and 2 are defined for now
|
|
; save caller's stack
|
|
mov dx, ss
|
|
mov cx, sp
|
|
; set our stack (required because we need ss=0)
|
|
xor si, si
|
|
mov ss, si
|
|
mov sp, 3000h
|
|
mov bp, dat
|
|
mov [bp + in_callback - dat], 1
|
|
push dx
|
|
push cx
|
|
; set ds:si -> ASCIIZ name
|
|
lea si, [di+6]
|
|
; set cx = limit in sectors; 4Kb = 8 sectors
|
|
movzx ecx, word [di+4]
|
|
shl cx, 3
|
|
; set es:bx = pointer to buffer
|
|
les bx, [di]
|
|
; call our function
|
|
stc ; unsupported function
|
|
dec ax
|
|
jz callback_readfile
|
|
dec ax
|
|
jnz callback_ret
|
|
call continue_load_file
|
|
jmp callback_ret_succ
|
|
callback_readfile:
|
|
; function 1: read file
|
|
; in: ds:di -> information structure
|
|
; dw:dw address
|
|
; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
|
|
; ASCIIZ name
|
|
; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error
|
|
; out: dx:ax = file size (0xFFFFFFFF if file was not found)
|
|
call [bp + load_file_ptr - dat]
|
|
callback_ret_succ:
|
|
clc
|
|
callback_ret:
|
|
; restore caller's stack
|
|
pop cx
|
|
pop ss
|
|
mov sp, cx
|
|
; return to caller
|
|
retf
|
|
|
|
read_file_chunk.resident:
|
|
; auxiliary label for read_file_chunk procedure
|
|
mov di, bx
|
|
lodsw
|
|
read_file_chunk.resident.continue:
|
|
mov dx, ax
|
|
add dx, 0x1FF
|
|
shr dx, 9
|
|
cmp dx, cx
|
|
jbe @f
|
|
mov ax, cx
|
|
shl ax, 9
|
|
@@:
|
|
xchg ax, cx
|
|
rep movsb
|
|
xchg ax, cx
|
|
clc ; no disk error if no disk requests
|
|
mov word [bp + num_sectors - dat], ax
|
|
ret
|
|
|
|
read_file_chunk:
|
|
; in: ds:si -> file chunk
|
|
; in: es:bx -> buffer for output
|
|
; in: ecx = maximum number of sectors to read (high word must be 0)
|
|
; out: CF=1 <=> disk read error
|
|
lodsb
|
|
mov [bp + cur_chunk_resident - dat], al
|
|
test al, al
|
|
jz .resident
|
|
; normal case: load (non-resident) attribute from disk
|
|
.read_block:
|
|
lodsd
|
|
xchg eax, edx
|
|
test edx, edx
|
|
jz .ret
|
|
lodsd
|
|
; eax = start cluster, edx = number of clusters, cx = limit in sectors
|
|
imul eax, [bp + sect_per_clust - dat]
|
|
add eax, [bp + data_start - dat]
|
|
mov [bp + cur_cluster - dat], eax
|
|
imul edx, [bp + sect_per_clust - dat]
|
|
mov [bp + num_sectors - dat], edx
|
|
and [bp + cur_delta - dat], 0
|
|
.nonresident.continue:
|
|
cmp edx, ecx
|
|
jb @f
|
|
mov edx, ecx
|
|
@@:
|
|
test dx, dx
|
|
jz .read_block
|
|
add [bp + cur_delta - dat], edx
|
|
sub [bp + num_sectors - dat], edx
|
|
sub ecx, edx
|
|
push cx
|
|
mov cx, dx
|
|
call read
|
|
pop cx
|
|
jc .ret
|
|
test cx, cx
|
|
jnz .read_block
|
|
.ret:
|
|
ret
|
|
|
|
cache_lookup:
|
|
; in: eax = value to look, si = pointer to cache structure
|
|
; out: di->cache entry; CF=1 <=> the value was not found
|
|
push ds bx
|
|
push ss
|
|
pop ds
|
|
mov di, [si+2]
|
|
.look:
|
|
cmp di, si
|
|
jz .not_in_cache
|
|
cmp eax, [di+4]
|
|
jz .in_cache
|
|
mov di, [di+2]
|
|
jmp .look
|
|
.not_in_cache:
|
|
; cache miss
|
|
; cache is full?
|
|
mov di, [si+4]
|
|
cmp di, [si+6]
|
|
jnz .cache_not_full
|
|
; yes, delete the oldest entry
|
|
mov di, [si]
|
|
mov bx, [di]
|
|
mov [si], bx
|
|
push word [di+2]
|
|
pop word [bx+2]
|
|
jmp .cache_append
|
|
.cache_not_full:
|
|
; no, allocate new item
|
|
add word [si+4], 8
|
|
.cache_append:
|
|
mov [di+4], eax
|
|
stc
|
|
jmp @f
|
|
.in_cache:
|
|
; delete this sector from the list
|
|
push si
|
|
mov si, [di]
|
|
mov bx, [di+2]
|
|
mov [si+2], bx
|
|
mov [bx], si
|
|
pop si
|
|
@@:
|
|
; add new sector to the end of list
|
|
mov bx, di
|
|
xchg bx, [si+2]
|
|
push word [bx]
|
|
pop word [di]
|
|
mov [bx], di
|
|
mov [di+2], bx
|
|
pop bx ds
|
|
ret
|
|
|
|
include 'fat.inc'
|
|
include 'ntfs.inc'
|
|
|
|
total_kaput db 13,10,'Fatal error: cannot load the secondary loader',0
|
|
error_too_big db 'file is too big',0
|
|
nodata_string db '$DATA '
|
|
error_not_found db 'not found',0
|
|
noindex_string db '$INDEX_ROOT not found',0
|
|
badname_msg db 'bad name for FAT',0
|
|
invalid_volume_msg db 'invalid volume',0
|
|
mft_string db '$MFT',0
|
|
fragmented_string db 'too fragmented file',0
|
|
invalid_read_request_string db 'cannot read attribute',0
|
|
|
|
kernel_name db 'kernel.mnt',0
|
|
|
|
align 4
|
|
dat:
|
|
|
|
extended_part_start dd 0 ; start sector for main extended partition
|
|
extended_part_cur dd ? ; start sector for current extended child
|
|
extended_parent dd 0 ; start sector for current extended parent
|
|
partition_start dd 0 ; start sector for current logical disk
|
|
cur_partition_ofs dw ? ; offset in MBR data for current partition
|
|
sect_per_clust dd 0
|
|
; change this variable if you want to boot from other physical drive
|
|
boot_drive db 80h
|
|
in_callback db 0
|
|
|
|
; uninitialized data
|
|
use_lba db ?
|
|
cur_chunk_resident db ?
|
|
align 2
|
|
heads dw ?
|
|
sectors dw ?
|
|
cache1head rw 2
|
|
cache1end dw ?
|
|
cache1limit dw ?
|
|
data_start dd ?
|
|
cachelimit dw ?
|
|
load_file_ptr dw ?
|
|
cur_obj dw ?
|
|
missing_slash dw ?
|
|
root_clus dd ?
|
|
root_start dd ?
|
|
get_next_cluster_ptr dw ?
|
|
frs_size dw ?
|
|
freeattr dw ?
|
|
index_root dw ?
|
|
index_alloc dw ?
|
|
cur_index_seg dw ?
|
|
cur_index_cache dw ?
|
|
filesize dd ?
|
|
filesize_sectors dd ?
|
|
cur_cluster dd ?
|
|
cur_delta dd ?
|
|
num_sectors dd ?
|
|
sectors_read dd ?
|
|
cur_chunk_ptr dw ?
|
|
|
|
rootcache_size dw ? ; must be immediately before foldcache_clus
|
|
if $-dat >= 0x80
|
|
warning: unoptimal data displacement!
|
|
end if
|
|
foldcache_clus rd 7
|
|
foldcache_mark rw 7
|
|
foldcache_size rw 7
|
|
fat_filename rb 11
|
|
|
|
if $ > 2000h
|
|
error: file is too big
|
|
end if
|
|
|
|
; for NT/2k/XP, file must be 16 sectors = 0x2000 bytes long
|
|
repeat 0x2600 - $
|
|
db 2 ; any data can be here; 2 is another nice face in ASCII :)
|
|
end repeat
|