[SOLVED] Odd behavior with AH=0x03/INT=0x13

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

[SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Hello, I'm writing a small program to dump the BIOS ROM into sectors 3-131. To do this, I loop from SI=0 to SI=0xFFFF (65535), and DI=0 through DI=512. Once DI hits 512, I reset DI, write the buffer to sector 3+DH (DH is sector number) and increase DH.

However, this wasn't working, so I just tried writing one sector:

Code: Select all

xor ax, ax
mov ax, 0xF000
mov es, ax
xor di, di
WriteToDisk:
  cmp di, 512
  je Exit2

  mov ax, [es:di]
  mov [buffer+di], al

  inc di
  jmp WriteToDisk
  ret

Exit2:
  mov dh, 3   ;; Sector number
  mov bx, buffer  ;; Buuff
  call writeSector
My sector writing code looks like this:

Code: Select all

writeSector:
  push es
  xor ax, ax
  mov es, ax
  ;; BX already buffer
  
    mov ah, 0x03    ;; Hey, we want to write!
    mov al, 0x01    ;; Only one sector
    mov cl, dh
    mov dh, 0
    ;; DL already set by BIOS
    ;; ES:BX set above
    int 0x13
    jc .Failure
    ret

  pop es
  ret

  .Failure:
    mov ah, 0x01
    ;; DL set
    int 0x13
    hcf
Note that hcf is a macro for

Code: Select all

%macro hcf 0
	cli
	hlt
	jmp $
%endmacro
"buffer" is defined as just

Code: Select all

buffer: times 512 db 0
This fails immediately. I check EAX:

Code: Select all

(qemu) x/1i $eax
0x00002000: 00 00
Huh. AL is 0. That corresponds to a successful operation. But it shouldn't have been successful, since the CF was set. What's going on here? Why is it failing?
Last edited by avcado on Sat Sep 07, 2024 11:11 am, edited 1 time in total.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Turns out I needed to make a floppy disk to actually see the changes.
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by Octocontrabass »

avcado wrote: Sat Sep 07, 2024 9:35 amI'm writing a small program to dump the BIOS ROM
What are you trying to accomplish by dumping the BIOS ROM? On just about every PC, the "ROM" is actually RAM, and the data you dump might not be what you want.

Also, depending on the chipset and BIOS, specifying an INT 0x13 buffer address outside of normal RAM might not work, either failing with an error or silently misbehaving.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Octocontrabass wrote: Sat Sep 07, 2024 6:52 pm
avcado wrote: Sat Sep 07, 2024 9:35 amI'm writing a small program to dump the BIOS ROM
What are you trying to accomplish by dumping the BIOS ROM?
Honestly? I just want to take a peek at it. Eventually, I may try and get it running on real hardware.
I read this SO question, https://stackoverflow.com/a/41531355, and I decided it might be fun to try doing it.
Octocontrabass wrote: Sat Sep 07, 2024 6:52 pm On just about every PC, the "ROM" is actually RAM, and the data you dump might not be what you want.
From what I've been able to dump, the first few sectors (I'd say 1kb) is just format strings. What I'm trying to do is get the raw machine code and do stuff with that (i.e. disassemble). And this goes back to my point of wanting to run it on real hardware, since I know SeaBIOS is open source, I'd like to see (if possible) to reverse engineer some BIOS firmware, if that makes sense.
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by Octocontrabass »

avcado wrote: Sun Sep 08, 2024 7:38 amHonestly? I just want to take a peek at it.
I'm glad I'm not the only one.
avcado wrote: Sun Sep 08, 2024 7:38 amWhat I'm trying to do is get the raw machine code and do stuff with that (i.e. disassemble). And this goes back to my point of wanting to run it on real hardware, since I know SeaBIOS is open source, I'd like to see (if possible) to reverse engineer some BIOS firmware, if that makes sense.
Sure, you can do that, but which part of the BIOS code do you want to reverse-engineer? Right now you're only looking at the runtime portion of it, which is great if you want to figure out how the BIOS expects you to call its various interrupts, but not helpful if you want to see how it initializes the chipset.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Octocontrabass wrote: Sun Sep 08, 2024 3:43 pm Sure, you can do that, but which part of the BIOS code do you want to reverse-engineer?
I mean that's mainly what I'm after -- to see the code that's loaded into RAM/ROM when you start your computer / the code that's flashed onto the physical BIOS chip. Obviously I could go out and compile SeaBIOS and run objdump on the compiled binary, but what's the fun in that? Besides, one of the main goals of this project is to get it on running on real hardware and be able to easily examine the firmware.
Octocontrabass wrote: Sun Sep 08, 2024 3:43 pm Right now you're only looking at the runtime portion of it, which is great if you want to figure out how the BIOS expects you to call its various interrupts, but not helpful if you want to see how it initializes the chipset.
What should I be doing instead?
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by Octocontrabass »

avcado wrote: Sun Sep 08, 2024 7:28 pmWhat should I be doing instead?
Ideally, pull the ROM chip out of its socket and dump it with an EEPROM programmer.

But if you need a software solution, try reading the ROM from just below 4GB. Chipsets usually provide direct access to the ROM contents that way. You can use INT 0x15 AH=0x87 to avoid problems with the A20 gate. (In fact, there is a program to do this under DOS.)
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Octocontrabass wrote: Sun Sep 08, 2024 7:58 pm But if you need a software solution, try reading the ROM from just below 4GB. Chipsets usually provide direct access to the ROM contents that way.
Wouldn't I need to be in protected mode for that, since I can only access 1MB in real mode?
Octocontrabass wrote: Sun Sep 08, 2024 7:58 pm You can use INT 0x15 AH=0x87 to avoid problems with the A20 gate. (In fact, there is a program to do this under DOS.)
How does that work? (the using BIOS interrupts [in what I assume to be real mode] to read the data)
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by Octocontrabass »

avcado wrote: Mon Sep 09, 2024 7:06 amWouldn't I need to be in protected mode for that, since I can only access 1MB in real mode?
Accessing memory above 1MB involves switching the CPU to protected mode, but all of your code can stay in real mode if you use the BIOS.
Octocontrabass wrote: Sun Sep 08, 2024 7:58 pmHow does that work?
When you call that interrupt, the BIOS switches the CPU to protected mode, copies data with REP MOVSW, and switches back to real mode to return to your program.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Octocontrabass wrote: Sun Sep 08, 2024 7:58 pmHow does that work?
When you call that interrupt, the BIOS switches the CPU to protected mode, copies data with REP MOVSW, and switches back to real mode to return to your program.
[/quote]

I see. Though I am still a bit confused about what to put in the GDT. RBIL gives me a guide,

Code: Select all

Format of global descriptor table:

Offset  Size    Description     (Table 00499)
00h 16 BYTEs   zeros (used by BIOS)
10h    WORD    source segment length in bytes (2*CX-1 or greater)
12h  3 BYTEs   24-bit linear source address, low byte first
15h    BYTE    source segment access rights (93h)
16h    WORD    (286) zero
(386+) extended access rights and high byte of source address
18h    WORD    destination segment length in bytes (2*CX-1 or greater)
1Ah  3 BYTEs   24-bit linear destination address, low byte first
1Dh    BYTE    destination segment access rights (93h)
1Eh    WORD    (286) zero
(386+) extended access rights and high byte of destin. address
20h 16 BYTEs   zeros (used by BIOS to build CS and SS descriptors)
The DOS program you gave me did help with implementing it up to byte 0x16,

Code: Select all

gdt:
  dd 0, 0, 0, 0 ;; 16 bytes, 0
  dw 0xFFFF     ;; Source segment length
  db 0    ;; Linear source address, low bits
  db 0xF0 ;; High bits
  db 0xFF ;; 386 high bits
  db 0x93 ;; "Rights"
  dw 0      ;; 286 zero
  
Though the code gets confusing, as there's no mention of what FP_SEG, FP_OFF is, and I'm not sure how I'd implement the rest in assembly. Maybe I'm doing it wrong.
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by Octocontrabass »

avcado wrote: Mon Sep 09, 2024 5:37 pmThough I am still a bit confused about what to put in the GDT.
The BIOS copies a block of data from DS:0 to ES:0. You're providing the segment descriptors for DS and ES.
avcado wrote: Mon Sep 09, 2024 5:37 pmThough the code gets confusing, as there's no mention of what FP_SEG, FP_OFF is,
Those are macros to extract the segment and offset from a far pointer. The code doesn't explain them because they're a standard part of using C with 16-bit x86.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Octocontrabass wrote: Mon Sep 09, 2024 6:58 pm
avcado wrote: Mon Sep 09, 2024 5:37 pmThough the code gets confusing, as there's no mention of what FP_SEG, FP_OFF is,
Those are macros to extract the segment and offset from a far pointer. The code doesn't explain them because they're a standard part of using C with 16-bit x86.
I see. I've been messing around with it for a bit. Decided to see if I could port the DOS program to a bootsector with C. I got IA-16 to build me an ELF, that I objcopy to a binary and load into memory at 0x7e00.

I thought to myself, before I tried using INT 0x15/AH 0x87, to see if I could do something like I did before -- reading directly from memory.

Code: Select all

#define BIOS_START_386 0xfff0000
#define BIOS_SIZE      0xFFFF    // 65kb

		...
  uint16_t byte = *(uint16_t*)(BIOS_START_386);
  putw(byte);
  		...
This compiles fine. No warnings (I compile with -Wall -Wextra). However, when running in qemu, I get the output 0xFF03.

Note that my putw(uint16_t) implementation prints the high byte then low byte, so FF is the high byte. I then grab a copy of SeaBIOS from the git repository, compiling it and checking if byte 0-1 is 0xFF03.

It isn't!

Code: Select all

$ hexdump out/bios.bin
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0011ee0 0344 0000 039c 0000 03a4 0000 03d4 0000
Then I boot up a hex editor (okteta) to see what the first occurrence of the 0xFF03 word is. It's at
byte 0001:936E (or offset 0x1936D i think?). I'm probably doing it wrong, but I'd like to know why before I rewrite it using the approach used in the DOS app.

I'll happily give more code/other files if needed.
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by Octocontrabass »

avcado wrote: Sat Sep 14, 2024 6:12 pmI thought to myself, before I tried using INT 0x15/AH 0x87, to see if I could do something like I did before -- reading directly from memory.
How? You're still in real mode, which means you can't directly read from memory addresses above 1MB. Writing your code in C instead of assembly isn't going to change that.
avcado wrote: Sat Sep 14, 2024 6:12 pm

Code: Select all

#define BIOS_START_386 0xfff0000
Did you mean 0xfff00000? Also, don't forget the start address depends on the size of the BIOS ROM.
avcado wrote: Sat Sep 14, 2024 6:12 pmI then grab a copy of SeaBIOS from the git repository, compiling it and checking if byte 0-1 is 0xFF03.
Why go through all the trouble of building a second SeaBIOS binary? You already have the one that QEMU is using. Plus, the one you built probably won't match the one QEMU is using.
avcado
Member
Member
Posts: 29
Joined: Wed Jan 20, 2021 11:32 am
Contact:

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by avcado »

Octocontrabass wrote: Sat Sep 14, 2024 6:56 pm
avcado wrote: Sat Sep 14, 2024 6:12 pmI thought to myself, before I tried using INT 0x15/AH 0x87, to see if I could do something like I did before -- reading directly from memory.
How? You're still in real mode, which means you can't directly read from memory addresses above 1MB. Writing your code in C instead of assembly isn't going to change that.
Right. Completely forgot that INT 0x15/AH 0x87 swapped into protected mode, and I wasn't doing that, I guess you're right.
Octocontrabass wrote: Sat Sep 14, 2024 6:56 pm
avcado wrote: Sat Sep 14, 2024 6:12 pm

Code: Select all

#define BIOS_START_386 0xfff0000
Did you mean 0xfff00000? Also, don't forget the start address depends on the size of the BIOS ROM.
How would you know how big the BIOS ROM is?
MichaelPetch
Member
Member
Posts: 797
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: [SOLVED] Odd behavior with AH=0x03/INT=0x13

Post by MichaelPetch »

Since you are in real mode and already using BIOS interrupts. You can use Int 0x15/ax=0xe820 to get a memory map. Look for all the Type 2 memory entries and those will include BIOS memory areas (plus some other reserved regions). If you are on *really ancient* hardware without 0x15/ax=0xe820 then it is possible to scan memory between 0xc0000 and 0xfffff to find where the BIOS ROM and option ROMS are. See https://www.ctyme.com/intr/rb-1741.htm
Post Reply