OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 2:16 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: [Solved] BIOS INT 13 throws error after functioning properly
PostPosted: Thu Apr 21, 2022 5:55 pm 
Offline
User avatar

Joined: Thu Apr 21, 2022 5:31 pm
Posts: 3
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):

Code:
%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
....


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:

Code:
%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:
%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


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!


Last edited by SimpleTarpShelter on Fri Apr 22, 2022 2:21 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: BIOS INT 13 throws error after functioning properly
PostPosted: Fri Apr 22, 2022 8:55 am 
Offline
Member
Member

Joined: Sat Mar 10, 2018 10:16 am
Posts: 296
One thing that immediately jump on me is that you are not jumping over DAPACK, so after call print16 processor is going to execute DAPACK, what can cause errors.

_________________
https://github.com/VendelinSlezak/BleskOS


Top
 Profile  
 
 Post subject: Re: BIOS INT 13 throws error after functioning properly
PostPosted: Fri Apr 22, 2022 9:08 am 
Offline
User avatar

Joined: Thu Apr 21, 2022 5:31 pm
Posts: 3
Klakap wrote:
One thing that immediately jump on me is that you are not jumping over DAPACK, so after call print16 processor is going to execute DAPACK, what can cause errors.


Ah indeed.....I fixed that but it didn't fix the error. Nor did I expect it to though, since the same error occurs when I use DiskAddressPacket as defined in hdd.s. Thanks though!


Top
 Profile  
 
 Post subject: Re: BIOS INT 13 throws error after functioning properly
PostPosted: Fri Apr 22, 2022 9:39 am 
Offline
Member
Member

Joined: Sat Jul 02, 2016 7:02 am
Posts: 207
If you can reproduce the error on qemu, then there are debugging statements for both qemu's emulation (assuming ATA, though other types of block devices may also support debugging) and seabios's BIOS that can be enabled.

https://www.seabios.org/Debugging

qemu needs an additional command line param: -trace enable=ide_*
In its monitor console, you can check the supported traces: help trace

Edit: With qemu, there's gdb debugging possible too.


Top
 Profile  
 
 Post subject: Re: BIOS INT 13 throws error after functioning properly
PostPosted: Fri Apr 22, 2022 11:27 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
SimpleTarpShelter wrote:
The error occurs right as I try to read the FAT-formatted drive's root directory table into memory at 0x500

The BDA may occupy a handful of bytes at that address. You should use 0x600 instead.

Code:
   mov ss, ax
   mov sp, STAGE2_ADDR

You have 512 bytes of stack before you start to overwrite stage 1. Aren't you still using data from stage 1 in stage 2?

Code:
   mov si, DebugMsg
   call print16
   hlt

error:
   mov [0x7000], ah
errl:
   mov si, errmsg
   call print16
   jmp errl

If interrupts are enabled (by INT 0x13), this code will always print the error message.

Code:
   mov ax, [NumberOfFATs]
   mul word [SectorsPerFAT]

The BPB field for the number of FATs is a byte. You're loading some extra garbage into AX and then doing calculations that can be affected by it.


Code:
   xor ax, ax
   mov ax, 1
   mul word [SectorsPerFAT]

But why?

Code:
   mul dword [BytesPerSector]
   shr eax, 4

The BPB field for the bytes per sector is a word. You're multiplying by some extra garbage and then doing calculations that can be affected by it.

Code:
; ax = ((ax - 2) * SectorsPerCluster) + DataSector
cluster2lba:
   sub ax, 2
   mul byte [SectorsPerCluster]

The comment is incorrect. MUL with a byte operand uses AL as its implied input operand, not AX.


Top
 Profile  
 
 Post subject: Re: BIOS INT 13 throws error after functioning properly
PostPosted: Fri Apr 22, 2022 11:34 am 
Offline
User avatar

Joined: Thu Apr 21, 2022 5:31 pm
Posts: 3
Octocontrabass wrote:
SimpleTarpShelter wrote:
The error occurs right as I try to read the FAT-formatted drive's root directory table into memory at 0x500

The BDA may occupy a handful of bytes at that address. You should use 0x600 instead.

Code:
   mov ss, ax
   mov sp, STAGE2_ADDR

You have 512 bytes of stack before you start to overwrite stage 1. Aren't you still using data from stage 1 in stage 2?

Code:
   mov ax, [NumberOfFATs]
   mul word [SectorsPerFAT]

The BPB field for the number of FATs is a byte. You're loading some extra garbage into AX and then doing calculations that can be affected by it.


Code:
   xor ax, ax
   mov ax, 1
   mul word [SectorsPerFAT]

But why?

Code:
   mul dword [BytesPerSector]
   shr eax, 4

The BPB field for the bytes per sector is a word. You're multiplying by some extra garbage and then doing calculations that can be affected by it.

Code:
; ax = ((ax - 2) * SectorsPerCluster) + DataSector
cluster2lba:
   sub ax, 2
   mul byte [SectorsPerCluster]

The comment is incorrect. MUL with a byte operand uses AL as its implied input operand, not AX.


Thanks for these notes. I decided to rewrite everything from scratch and noticed recognized a lot of what you had pointed out while doing it, and it's working fine now. Not sure where exactly it went wrong though but I consider the issue resolved.

Code:
   xor ax, ax
   mov ax, 1
   mul word [SectorsPerFAT]


Not sure how that slipped in there, ha...


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot] and 53 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group