1

In my previous question, I had an issue using ah=0x2 in which I could not read more than 65 sectors. I was recommended to use ah=42h, and after changing my code, I can confirm that it does read more than 65 sectors at a time, without any issue in reading and loading so far. However, qemu still will enter a reboot loop. Here is my code so far.

loader.asm

[bits 16]
[extern kernel]

section .boot
global boot

KERNEL_SIZE_SECTORS equ 255    ;Kernel size in sectors

jmp boot

dap:
    db 0x10
    db 0
    dw KERNEL_SIZE_SECTORS
    dw 0                       ;
    dw 0x07e0                  ;value recommended by a friend
    dq 0                       ;Start reading from the second sector

print:
    pusha
start:
    mov   al, [bx] 
    cmp   al, 0 
    je    done

    mov   ah, 0x0e
    int   0x10 

    add   bx, 1
    jmp   start

done:
    popa
    ret



print_nl:
    pusha
    
    mov   ah, 0x0e
    mov   al, 0x0a
    int   0x10
    mov   al, 0x0d
    int   0x10
    
    popa
    ret

print_hex:
    pusha

    mov   cx, 0

hex_loop:
    cmp   cx, 4
    je    end
    
    mov   ax, dx
    and   ax, 0x000f
    add   al, 0x30 
    cmp   al, 0x39
    jle   step2
    add   al, 7

step2:
    mov   bx, HEX_OUT + 5
    sub   bx, cx
    mov   [bx], al
    ror   dx, 4

    add   cx, 1
    jmp   hex_loop

end:
    mov   bx, HEX_OUT
    call  print

    popa
    ret

HEX_OUT:
    db    '0x0000',0


disk_read_error: db "Disk read error!", 0

disk_err:                      ;Label for any disk errors
    mov   bx, disk_read_error  ;Print the disk error
    call  print
    call  print_nl
    mov   dh, ah               ;Load error code in ah register
    call  print_hex
    jmp   $

boot:
    mov   ax, 0x2401           ;GDT preperation
    int   0x15

    mov   ax, 0x3              ;Set the VGA mode, unknown at boot
    int   0x10

    mov   [disk], dl           ;Load the disk

    mov   ah, 42h              ;Do modern disk reading (no fuckery)
    mov   dl, [disk]           ;Account for cosmic bit flips
    mov   si, dap              ;Load disk address packet
    int   0x13

    jc    disk_err             ;Jump if disk read error

    cli                        ;Clear the interrupts
    lgdt  [GDT_POINTER]        ;Load the GDT using a gdt pointer
    mov   eax, cr0
    or    eax, 0x1
    mov   cr0, eax
    jmp   CODE_SEG:boot2
boot2:
    mov   ax, DATA_SEG
    mov   ds, ax
    mov   es, ax
    mov   fs, ax
    mov   gs, ax
    mov   ss, ax
GDT_START:
    dq    0x0
GDT_CODE:
    dw    0xFFFF
    dw    0x0
    db    0x0
    db    10011010b
    db    11001111b
    db    0x0
GDT_DATA:
    dw    0xFFFF
    dw    0x0
    db    0x0
    db    10010010b
    db    11001111b
    db    0x0
GDT_END:
GDT_POINTER:
    dw    GDT_END - GDT_START
    dd    GDT_START
disk:
    db    0x0
CODE_SEG  equ GDT_CODE - GDT_START
DATA_SEG  equ GDT_DATA - GDT_START

times 510 - ($-$$) db 0
dw 0xaa55
copy_target:
[bits 32]
loaded_msg: db "Operating system loaded",0
                               ;Message here to verify disk read
    mov   esi, loaded_msg
    mov   ebx, 0xb8000
.loop:                         ;Print a message using a loop
    lodsb
    or    al, al
    jz    load_kernel          ;If the message is finished, load kern
    or    eax,0x0F00
    mov   word [ebx], ax
    add   ebx,2
    jmp   .loop
load_kernel:
    mov   esp, kernel_stack_top;Load the stack to call C functions
    call  kernel               ;Call the actual kernel
halt:    
    cli
    hlt

section .bss
align 4
kernel_stack_bottom: equ $     ;equ the current address for the stack
    resb 16384                 ;Use 16kb for stack size
kernel_stack_top:              ;Top of the stack

Linker script

ENTRY(boot)
OUTPUT_FORMAT("binary")
SECTIONS {
    . = 0x7c00;
    .text :
    {
        *(.boot)
        *(.text)
    }
    
    .rodata :
    {
        *(.rodata)
    }

    .data :
    {
        *(.data)
    }

    .bss :
    {
        *(.bss)
    }
}

Kernel.c file

void kernel() {
    const short color = 0x0F00;
    const char* hello = "Hello world!";
    short* vga = (short*)0xb8000;
    for (int i = 0; i<16;++i)
        vga[i+80] = color | hello[i];
}

Makefile

CC = i686-elf-gcc
LD = i686-elf-gcc
VM = qemu-system-x86_64
ASM = nasm


BOOTLOADER_FLAGS = -felf32
BOOTLOADER_INFILE = ./loader/loader.asm
BOOTLOADER_OUTFILE = ./bin/loader.o

KERNEL_FLAGS = -nostdlib -ffreestanding -nostdlib -Wall -Wextra -Werror -std=c99 $(KERNEL_STDLIB)
KERNEL_INFILE = ./kernel/kernel.c
KERNEL_OUTFILE = ./bin/kernel.o
KERNEL_STDLIB = -Istdlib/
KERNEL_SRCS = $(shell find . -name '.ccls-cache' -type d -prune -o -type f -name '*.c' -print | sed -e 's/ /\\ /g')

LINKER_FILE = ./build/linker.ld
LINKER_FLAGS = -ffreestanding -O2 -nostdlib $(BOOTLOADER_OUTFILE) $(KERNEL_OUTFILE) -lgcc 
OS_NAME = esf-pmc1

all:
    @$(ASM) $(BOOTLOADER_FLAGS) $(BOOTLOADER_INFILE) -o $(BOOTLOADER_OUTFILE)
    @$(CC) $(KERNEL_INFILE) $(BOOTLOADER_OUTFILE) -o $(OS_NAME).bin $(KERNEL_FLAGS) -T ./build/linker.ld
    dd if=/dev/zero of=$(OS_NAME).img bs=512 count=2880
    dd if=$(OS_NAME).bin of=$(OS_NAME).img conv=notrunc

run:
    @qemu-system-x86_64 -fda $(OS_NAME).img

clean:
    @rm -r iso/boot/*.bin
    @rm -r *.iso

When placing jmp $'s, I can trace that int 0x13 has no problems, however, when it executes jmp CODE_SEG:boot2, then it will enter a reboot loop. If I place a jmp $ before the long jump, qemu will not enter the loop.

Edit: After removing the disk reading code, and the moving boot2 inside the first sector, my code no longer enters a reboot loop. I am currently thinking that I may have not used ah=42h correctly

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 2
    Somehow too much space got into the code, making it hard to read. Can you edit it? I didn't see where you set the segment registers in your first stage bootloader. Make sure you actually read from the correct locations. If the assembler counts from 0 (I don't see an `ORG` directive) you have to fix the GDT address (which is linear). Also the GDT size must be one byte less than its actual size. The selectors are right (I realize only now that you can make them from the offsets of the descriptors, granted the RPL wanted is 0 and the table is the GDT). – Margaret Bloom Sep 26 '22 at 17:36
  • 1
    Just a suggestion that can help with troubleshooting, if you haven't already tried it: move `boot2` inside the first stage boot block and disable the disk loading. This will help isolate whether the problem is that entering protected mode isn't working, or if the load is overwriting something. – sj95126 Sep 26 '22 at 18:27
  • 1
    Not sure about qemu but bochsdbg (part of the bochs package) has the ability to single-step code. This can make it _much_ easier to determine what is going on. – SoronelHaetir Sep 26 '22 at 19:35
  • 1
    @SoronelHaetir: QEMU can as well (with gdb) but Bochs is a lot better for diagnosing issues when still in real mode. – sj95126 Sep 26 '22 at 19:39
  • 1
    I have moved boot2 inside the first stage boot block and disabled the disk loading, and it solves the reboot problem. Maybe the problem is I am not loading it into memory correctly? – Yomada Cheezit Sep 26 '22 at 22:42
  • 1
    If there is a linker script can you add it to your code along with the command you use to assemble,link and generate your kernel and build the disk. Everything to make this a minimal complete example would help. You don't seem to set up the SS:SP pointer so you still run the risk of overwriting whatever stack BIOS set. You say you are using int 13h/ah=42h since someone suggested it to you. Did you add error checking to see if there is an error being returned by the `int 13h` call? Maybe it totally failed. That could happen if you are booting as floppy media which usually doesn't support ah=42h – Michael Petch Sep 26 '22 at 23:10
  • I'd also recommend debugging in BOCHs. You can step through this kind of code very easily to see exactly where real mode code is failing. BOCHs may even throw an error hinting at what the problem might be. – Michael Petch Sep 26 '22 at 23:11
  • 1
    I have added the linker script, full bootloader, kernel files and makefile. I hope that this is enough to reproduce my error, and I have confirmed that ah=42h fails by using jc and jmp $. When calling a error label, it prints that it has a error code of 0x0100 – Yomada Cheezit Sep 27 '22 at 01:21
  • 1
    I've taken a quick look at the code again (the formatting with all the spaces makes it very difficult, you might consider reformatting it). Anyway, in the DAP you specify that the starting LBA (Logical block address) in the DQ field is 0. LBA sector numbering starts at 0, 0 is the first sector (boot sector), 1 would be the second sector. Maybe you meant to use 1 instead? – Michael Petch Sep 27 '22 at 06:50
  • 1
    I think part of issue is something I have said previously but floppy media usually doesn't support Int 13h/AH=42h. You should be aware that when your bootloader is run by the BIOS, `dl` contains the drive number. SO instead of hard coding the `disk1 number as 0 (first floppy) you should use the value passed to the bootloader in DL. You could do `mov [disk], dl` at the start. That way if you change the boot drive you don't need to change the code. This brings me back to floppy media. You boot with `-fda` which is floppy media. Your error 1 in AH is because drive 0 doesn't support Int13h/AH=42h – Michael Petch Sep 27 '22 at 06:57
  • 1
    If you want to use int 13h/ah=42 on QEMU (and BOCHS) you will need to boot as a hard disk like `-hda` (in QEMU) instead of `-fda`. Set DL as I suggested in the last comment when your bootloader first starts. First hard disk is numbered 0x80 and that is what should be passed in DL and you can store that in `disk`. – Michael Petch Sep 27 '22 at 06:58
  • I highly recommend you look at my Bootloader Tips in this answer: https://stackoverflow.com/a/32705076/3857942 . Your code makes assumptions about the values in the segment registers, the stack (SS:SP) etc. If you were to ever to try and boot on real hardware you may find the code won't work and do unexpected things. – Michael Petch Sep 27 '22 at 07:01
  • 1
    I've cleaned things up. Something I didn't mention is that although int 13h/ah=42h doesn't have DMA boundary issues the maximum number of sectors it can read is usually 128 sectors. Anything more than that will be an error. If you modify your `Makefile` so that when QEMU runs you use `-hda` instead of `-fda` and you used this cleaned up loader.asm https://pastebin.com/Zxhi8SQL it should work. I've set up the segment registers and stack appropriately, moved data out of the way of code and moved `boot` and `boot2` to places that they make sense. – Michael Petch Sep 27 '22 at 08:53
  • 1
    Lastly a good resource for all the interrupts is Ralph Brown's interrupt list: http://www.ctyme.com/intr/int.htm . It also includes the disk error codes and their description. – Michael Petch Sep 27 '22 at 08:58
  • @MichaelPetch That's Ralf with an F. – ecm Sep 27 '22 at 09:38
  • 2
    @MichaelPetch: Good point on LBA support, I don't think we knew it was floppy booting in the other thread when I made that suggestion. Recommend always using `int13` function `41h` to test for LBA support regardless of boot device type. – sj95126 Sep 27 '22 at 14:01
  • 1
    @sj95126 : Yeah, despite not knowing for certain I did actually point that issue out in the comments of the original question asked. In general I have noticed most people who have OSDev/Bootloader questions are usually booting from floppy or floppy emulated media. – Michael Petch Sep 28 '22 at 06:24

0 Answers0