[Solved] BIOS INT 13 throws error after functioning properly
Posted: Thu Apr 21, 2022 5:55 pm
Edit: marking this as solved. The solution was to rewrite the code while giving much more attention to register and operand sizes. Who would have thought
Hi all, I'm writing a custom bootloader and was wondering if I could get some help with an error that the BIOS is giving me, as described by the title of this post. I've successfully implemented stage 1 and loaded stage 2 into memory, and the next step is to load the kernel image into memory so I can begin parsing its ELF header. Here's the code I have so far for stage 2. The error occurs right as I try to read the FAT-formatted drive's root directory table into memory at 0x500 (the hard-coded values in DAPACK are 100% correct, I assure you):
You may be thinking to yourself "hey, that's the x86 example code that's in the wiki page about memory access using INT 13!", and you would be correct. I have a separate routine load_image that works perfectly fine when loading stage 2, but as I'm taking it apart step-by-step, the interrupt no longer works even as I try to read in the root directory table into memory. The error code is 1, which is defined as "invalid function in AH or invalid parameter", but I just don't see how that's possible.
Here's my source code for stage 1:
and for hdd.s (which contains load_image):
Any help would be greatly appreciated! I've done my best to make sure there's nothing wrong with my segment registers or stack--there were a couple issues I found but they didn't solve the bug!
Hi all, I'm writing a custom bootloader and was wondering if I could get some help with an error that the BIOS is giving me, as described by the title of this post. I've successfully implemented stage 1 and loaded stage 2 into memory, and the next step is to load the kernel image into memory so I can begin parsing its ELF header. Here's the code I have so far for stage 2. The error occurs right as I try to read the FAT-formatted drive's root directory table into memory at 0x500 (the hard-coded values in DAPACK are 100% correct, I assure you):
Code: Select all
%define USRMEM 0x500
%define KERNEL_ADDR 0x10000
%define STAGE2_ADDR 0x8000
org STAGE2_ADDR
jmp main
%include "io.s"
%include "hdd.s"
main:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, STAGE2_ADDR
mov si, LoadingMsg
call print16
;mov ebx, KERNEL_ADDR
;mov si, KernelImage
;call load_image
; DEBUG - Load root dir table at 0x500
DAPACK:
db 0x10
db 0
cnt: dw 0x20 ; 32 entries in root dir
add: dw 0x500 ; load at 0x500
dw 0
lba: dd 0x44 ; 4 reserved + 2 FATs of size 32 each
dd 0
mov si, DAPACK
mov ah, 0x42
mov dl, [DriveNumber] ; 0x80, as defined by the BPB
int 0x13
jc error
mov si, DebugMsg
call print16
hlt
error:
mov [0x7000], ah
errl:
mov si, errmsg
call print16
jmp errl
....
Here's my source code for stage 1:
Code: Select all
%define STAGE2_ADDR 0x8000
bits 16
org 0x7c00
jmp end_bpb
nop
times 0x3b db 0x00 ; skip over BIOS parameter block
; BPB macros defined in hdd.s
end_bpb:
cli
cld
jmp 0x00:start ; some BIOSes try and set cs because they think
; they're smarter than us
%include "hdd.s"
%include "io.s"
start:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
; Load stage 2
mov ebx, STAGE2_ADDR
mov si, Stage2Img
call load_image
jnc .stage2_init
mov si, FileNotFoundMsg
call print16
.die: hlt ; load_image failed
jmp .die
.stage2_init:
jmp 0x00:STAGE2_ADDR
; Shouldn't ever get here
jmp .die
; Data
Stage2Img: db "STAGE2 SYS"
times 510 - ($-$$) db 0x00
dw 0xaa55 ; boot sig at end of sector
and for hdd.s (which contains load_image):
Code: Select all
%define BPB_ADDR 0x7c00
%define BytesPerSector BPB_ADDR+0x0b
%define SectorsPerCluster BPB_ADDR+0x0d
%define ReservedSectors BPB_ADDR+0x0e
%define NumberOfFATs BPB_ADDR+0x10
%define RootEntries BPB_ADDR+0x11
%define SectorsPerFAT BPB_ADDR+0x16
%define DriveNumber BPB_ADDR+0x24
; ebx - buffer to write to
; si - name of binary file to load
; Sets carry on fail
load_image:
clc
shr ebx, 4 ; we will be loading into segments, not offsets.
push ebx
push si
; Load Root
xor cx, cx
mov ax, 0x20
mul word [RootEntries]
div word [BytesPerSector] ; root size = (entries*32) / sector size
mov [DataSector], ax
mov [DiskAddressPacket.Count], ax
xchg ax, cx
mov ax, [NumberOfFATs]
mul word [SectorsPerFAT]
add ax, [ReservedSectors] ; root loc = reserved + (num FAT * FAT size)
add [DataSector], ax
mov [DiskAddressPacket.LBA], ax
mov ax, USRMEMSEG
mov [DiskAddressPacket.Segment], ax
call read_sectors
; Find File -- loop through root directory
mov cx, [RootEntries]
mov di, USRMEMSEG
shl di, 4
pop si
.root_loop: ; ha
push cx
push di
push si
mov cx, 0xb
rep cmpsb
pop si
pop di
pop cx
je .load_fat
add di, 0x20
loop .root_loop
stc
pop ebx
ret
.load_fat:
mov dx, [di+0x1a] ; save starting cluster
push dx
; Load FAT table (only 1, since the second is a copy)
xor ax, ax
mov ax, 1
mul word [SectorsPerFAT]
mov [DiskAddressPacket.Count], ax
mov ax, [ReservedSectors]
mov [DiskAddressPacket.LBA], ax
mov ax, USRMEMSEG
mov [DiskAddressPacket.Segment], ax
call read_sectors
; Load Image
.img_loop:
xor ax, ax
mov al, [SectorsPerCluster]
mov [DiskAddressPacket.Count], ax
pop cx
mov ax, cx
call cluster2lba
mov [DiskAddressPacket.LBA], ax
pop ebx
mov [DiskAddressPacket.Segment], bx
call read_sectors
; Compute next segment
xor eax, eax
mov ax, [DiskAddressPacket.Count]
mul dword [BytesPerSector]
shr eax, 4
add ebx, eax
push ebx
; Compute next cluster
shl cx, 1
mov bx, USRMEMSEG
shl bx, 4
add bx, cx
mov dx, [bx]
push dx
shr dx, 3
cmp dx, 1111111111111b ; high 13 bits set -> EOF
jb .img_loop
pop dx
pop ebx
shl ebx, 4
ret
; Read sectors from disk
; DiskAddressPacket must be properly filled out
; Sets carry on fail
read_sectors:
mov si, DiskAddressPacket
mov dl, [DriveNumber]
mov ah, 0x42
int 0x13
ret
; Convert a cluster into a linear block address
; ax = ((ax - 2) * SectorsPerCluster) + DataSector
cluster2lba:
sub ax, 2
mul byte [SectorsPerCluster]
add ax, [DataSector]
ret
; Data
FileNotFoundMsg: db "ERROR: file not found", 0xd, 0xa, 0x0
DataSector: dw 0x00
DiskAddressPacket:
.Size: db 0x10
.Reserved: db 0x00
.Count: dw 0x0000
.Offset: dw 0x0000 ; heuristic: we will never touch this ever
.Segment: dw 0x0000
.LBA: dd 0x00000000
.LBAHigh: dd 0x00000000