1

I am building a hobby operating system, but at some point it didn't work (Got a black screen not showing anything), and while trying to know why I tried to make the smallest kernel possible that worked inappropriately, which led instead to a guru medidation error, which I suspect have something to do with int 13h

This is the code that produces guru meditation error:

VGA_MEMORY equ 0xB8000

org 0x7C00
bits 16

mov ax, VGA_MEMORY >> 4
mov es, ax
mov ax, 0x07E0
mov ss, ax
mov esp, 0xFFF0
mov ax, 0
mov ds, ax

; Disable the annoying cursor
mov ah, 0x01
mov ch, 001_11111b
int 0x10

reset_disk_hdd:
    mov ah, 0x00  ; reset function
    int 0x13      ; disk int
    jc reset_disk_hdd

mov ax, 00h
mov ds, ax
mov si, disk_address_packet

read_disk_hdd:
    mov ah, 42h   ; read function
    int 0x13      ; disk int
    jc read_disk_hdd

jmp $

disk_address_packet:
db 10h          ; size of DAP (set this to 10h)
db 00h          ; unused, should be zero
dw 44h          ; number of sectors to be read
dd 0x0000_7E00  ; segment:offset pointer to the memory buffer to which sectors will be transferred
dq 01h          ; absolute number of the start of the sectors to be read

times 510-($-$$) db 0

dw 0xAA55
dw 'H'

The original code I started working with read a double word located in the second sector of the hard disk, which was basically a color for the foreground, a color for the background and a character to print in screen, and then moved that double word to 0xB8000 to have it printed on the screen. But when I got this black screen showing nothing I started to reduce the code as much as possible to keep getting this error, but instead got a Guru Meditation error. Notice, for instance, that I am reading exactly 44h sectors from disk. This is the lowest number that keep giving me the error.

Those are the commands I use to turn on the virtual machine with this code:

mkdir vbox ; \
VBoxManage controlvm "X" poweroff ; \
sleep 1 ; \
VBoxManage unregistervm "e0b08add-d834-4af5-89e8-05abec11aa78" ; \
rm -r vbox/X ; \
rm kernel  ; \
rm kernel.raw ; \
rm kernel.vdi ; \
VBoxManage createvm \
     --name "X" \
     --ostype "Other" \
     --register \
     --basefolder "$(pwd)/vbox" \
     --uuid "e0b08add-d834-4af5-89e8-05abec11aa78" \
     --default ; \
VBoxManage modifyvm "e0b08add-d834-4af5-89e8-05abec11aa78" \
     --usbxhci on \
     --memory 8 ; \
nasm -l kernel.lst kernel.asm ; \
dd if=/dev/zero of=kernel.raw bs=1024 count=2048 ; \
dd if=kernel of=kernel.raw conv=notrunc ; \
VBoxManage convertfromraw kernel.raw kernel.vdi --format VDI ; \
VBoxManage storageattach "X" \
     --storagectl "IDE" \
     --port 0 \
     --device 0 \
     --type hdd \
     --medium "$(pwd)"/kernel.vdi ; \
VBoxManage startvm "X"; \

I am using Virtualbox 6.1.0

These are my operating system specs, which I add just in case because I don't know if you might find it useful:

What I really want to know is whether I am missing something (and if so, what) or it's an actual bug of the virtual machine.

EDIT

Michael Petch commented:

There is a rule for BIOS that you should never cross a 64KiB boundary because of DMA (direct memory address). I don't know if virtualbox enforces it but I could see that if the address you load at (0x7e00) + (the number of sectors * bytes per sector)> 0x10000 (0x10000=64KiB) a problem may occur . In theory though if that were the problem I'd expect potential problems to be with anything > 0x41 sectors (0x42 * 512 + 0x7e00) = 0x10200. If you load 0x44 sectors starting at 0x1000:0x0000 rather than 0x0000:0x7e00 does it fail?

I changed the assembly code to set the buffer address at 0x10000, but the program still doesn't work. However, the problem now is different: instead of getting a guru meditation, I only get a black screen not printing anything. This happens when I load as little as 3Fh sectors, but when I load any number of sectors below 3Fh the program correctly prints the 'H' character:

VGA_MEMORY equ 0xB8000

GREEN         equ 0x2
RED           equ 0x4

org 0x7C00
bits 16

mov ax, VGA_MEMORY >> 4
mov es, ax
mov ax, 0x07E0
mov ss, ax
mov esp, 0xFFF0
mov ax, 0
mov ds, ax

; Disable the annoying cursor
mov ah, 0x01
mov ch, 001_11111b
int 0x10

reset_disk_hdd:
    mov ah, 0x00  ; reset function
    int 0x13      ; disk int
    jc reset_disk_hdd

mov ax, 00h
mov ds, ax
mov si, disk_address_packet

read_disk_hdd:
    mov ah, 42h   ; read function
    int 0x13      ; disk int
    jc read_disk_hdd

mov ax, VGA_MEMORY >> 4
mov es, ax
mov ax, 0x1000
mov ds, ax
mov ax, [ds:0x0000]
mov [es:0x00], ax

jmp $

disk_address_packet:
db 10h          ; size of DAP (set this to 10h)
db 00h          ; unused, should be zero
dw 3Fh          ; number of sectors to be read
dd 0x1000_0000  ; segment:offset pointer to the memory buffer to which sectors will be transferred
dq 01h          ; absolute number of the start of the sectors to be read

times 510-($-$$) db 0

dw 0xAA55
dw (RED << 4 | GREEN) << 8 | 'H'
Adrian
  • 1,558
  • 1
  • 13
  • 31
  • In the best case this code should print an 'H' with background and foreground black, but that's on purpose because originally ir printed the H using other visible colors but I let them to black while trying to understand why the guru meditation error was showing up. – Adrian Mar 14 '20 at 15:41
  • I tried to debug the virtual machine, but I have this problem: https://stackoverflow.com/questions/60553100/virtualbox-debugging-g-error-the-vm-is-already-running – Adrian Mar 14 '20 at 15:43
  • Note that you overwrite your stack with the newly loaded sectors. Try to put the stack below your boot sector (e.g. starting at `0x7c00`) to fix this problem. – fuz Mar 14 '20 at 15:50
  • @fuz Now that you mention it, I have a theory: When I call int 13h the instruction pointer register's value is pushed onto the stack, but since the stack is in the memory location that gets rewritten by the int 13h instruction, the value of the instruction pointer is rewritten, so when the interrupt routine ends and the iret instruction is executed, a different random value is stored in the instruction pointer register, which makes the processor to execute other instructions. – Adrian Mar 14 '20 at 16:03
  • When I change the number of sectors to be read the guru meditation error is not produced anymore, which should mean that the data read from the disk overlaps the stack i.e. the 0x43 sectors multiplied by 512 bytes should be smaller than 0xFFF0 (the value stored in esp register at the start), whereas if instead of 0x43 it were 0x44, the result should be greater. – Adrian Mar 14 '20 at 16:09
  • 0x43 * 512 = 0x8600 ; 0x44 * 512 = 0x8800 ; Nope, it doesn't.The values aren't even close to what I expected – Adrian Mar 14 '20 at 16:13
  • 1
    There is a rule for BIOS that you should never cross a 64KiB boundary because of DMA (direct memory address). I don't know if virtualbox enforces it but I could see that if the address you load at (0x7e00) + (the number of sectors * bytes per sector)> 0x10000 (0x10000=64KiB) a problem may occur . In theory though if that were the problem I'd expect potential problems to be with anything > 0x41 sectors (0x42 * 512 + 0x7e00) = 0x10200. If you load 0x44 sectors starting at 0x1000:0x0000 rather than 0x0000:0x7e00 does it fail? – Michael Petch Mar 14 '20 at 22:10
  • There is this SO question/answer that I am wondering if it is related (and it too is VirtualBox): https://stackoverflow.com/questions/58564895/problem-with-bios-int-13h-read-sectors-from-drive – Michael Petch Mar 14 '20 at 22:20
  • @MichaelPetch The Guru Meditation error disappeared, but now I get a black screen instead when I load, at least, 3Fh sectors. See the edit I made to the question. – Adrian Mar 15 '20 at 13:06
  • Did you move the stack out of the way when you changed to loading at 0x1000:0x0000? Might be easier to simply place the stack at 0x0000:0x7c00 (below the bootloader). Although I asked you to test by loading at 0x1000:0x0000 that was an experiment. In theory if you want to to load a kernel at 0x0000:0x7e00 that was more than 0x41 sectors you'd have to to the read in multiple parts to avoid a read crossing a DMA boundary. – Michael Petch Mar 15 '20 at 13:10
  • I suspect that since your stack is at 0x07e0:0xfff0 that reading 0x3f sectors at 0x1000:0x0000 or more sectors will start clobbering the stack and the `int 13h` will never return properly because the stack is corrupted while doing the disk read. 0x07e0:0xfff0 is phys address 0x17df0. 0x1000:0x0000 is phys address 0x10000. If you read 0x3f sectors to 0x10000 you would overwrite phys memory from 0x10000 to 0x17e00 which includes the working part of the stack. – Michael Petch Mar 15 '20 at 13:13
  • 1
    Basically when you do a disk read with int 13h/ah=42h don't read across a DMA boundary (across physical address 0x00000, 0x10000, 0x20000, 0x30000, 0x40000, 0x50000, 0x60000, 0x70000, 0x80000, 0x90000, 0xA0000, 0xB0000, 0xC0000, 0xD0000, 0xE0000, 0xF0000). Also ensure that you don't read on top of your active stack. DOn't read on top of the interrupt vector table and the BDA between 0x000000 and 0x00500, don't read on top of the EBDA just below (0xA0000), don't read on top of the BIOS itself (in areas above 0xA0000), and don't read on top of the code that does the disk read. – Michael Petch Mar 15 '20 at 13:28
  • 2
    For maximum BIOS compatibility with Int 13h/ah=42h don't read more than 0x7f sectors as some ancient BIOSes do not support reading a number of sectors >= 0x80. – Michael Petch Mar 15 '20 at 13:34
  • @MichaelPetch I changed the stack location to before 0x7c00 and it worked. I didn't know that memory from 0x0500 to 0x7c00 was free for use until you suggested that. Thanks. Please post it as answer so I can accept it. – Adrian Mar 15 '20 at 13:39

1 Answers1

3

To make sure int 13h ah=02h or ah=42h works as it should check the following:

  • Wherever you load the bytes from disk to memory, make sure it doesn't overlap the stack
  • Don't load more than 0x7f sectors as some ancient BIOSes do not support reading a number of sectors >= 0x80
  • The buffer shouldn't cross the 64KiB boundary
  • You are not touching the interrupt vector table, BDA, EBDA, the BIOS or any other memory location forbidden or not free for use. See Memory map and Upper memory map.
Adrian
  • 1,558
  • 1
  • 13
  • 31