0

I switched form CBS to LBA in my bootloader, that loads a kernel after ata_lba_read in 32-bit mode Before this moment, I always tested my system with QEMU, and it worked (and works) perfectly there. Now I decided to try booting a system on a real hardware. It prints a boot message and restarts forever (kernel isn't loading) I searched answers for a while, but there were only those, that are already implemented in my bootloader.

Here's the bootloader code:

ORG 7C00h
[BITS 16]

CODE_SEG equ GDT_CODE - GDT_START
DATA_SEG equ GDT_DATA - GDT_START

jmp short start
nop

;FAT16 Header
OEM_Identifier db 'FAT16OEM'
BytesPerSector dw 0x200
SectorsPerCluster db 0x80
ReservedSectors dw 200
FATCopies db 0x02
RootDirEntries dw 0x40
NumberOfSectors dw 0
MediaType db 0xF8
SectorsPerFAT dw 0x100
SectorsPerTrack dw 0x20
NumberOfHeads dw 0x40
HiddenSectors dd 0x00
SectorsBig dd 0x773594

;Extended BPB
DriveNumber db 0x80
WinNTBit db 0x00
Signature db 0x29
VolumeID dd 0xD105
VolumeIDString db 'MYSTOS_BOOT'
SystemIDString db 'MOSFAT16'

start:
    jmp 0x0:load

load:
    cli
    mov ax, 0x00
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x00
    sti

    mov cx, bootmsg
    call print
    mov cx, newline
    call print
    mov ax, 0xFFFF
    mov cx, 0x2000
    call sleep

.load_protected:
    cli
    lgdt[GDT_DESCRIPTOR]
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax
    jmp CODE_SEG:load32

print:
    push si
    push bx
    push ax
    mov si, cx
    mov bx, 0
    print_loop:
        lodsb
        cmp al, 0
        jz finish_print
        call PrintChar
        jmp print_loop
    finish_print:
        pop ax
        pop bx
        pop si
        ret

PrintChar:
    mov ah, 0Eh
    int 10h
    ret

sleep:
    push ax
    push cx
    sleep_loop:
        cmp cx, 0
        jz dec_ax
        dec cx
        jmp sleep_loop
    dec_ax:
        cmp ax, 0
        jz finish_sleep
        dec ax
        pop cx
        push cx
        jmp sleep_loop
    finish_sleep:
        pop cx
        pop ax
        ret


GDT_START:

GDT_NULL: ;64 bits of zeros
    dd 0x0 
    dd 0x0

; offset 0x8
GDT_CODE:     ;CS SHOULD POINT TO THIS
    dw 0xFFFF ;Segment limit first 0-15 bits
    dw 0      ;Base first 0-15 bits
    db 0      ;Base 16-23 bits
    db 0x9a   ;Access byte
    db 11001111b ;High and low 4 bit flags
    db 0      ;Base 24-31 bits

; offset 0x10
GDT_DATA:     ;DS, SS, ES, FS, GS
    dw 0xFFFF ;Segment limit first 0-15 bits
    dw 0      ;Base first 0-15 bits
    db 0      ;Base 16-23 bits
    db 0x92   ;Access byte
    db 11001111b ;High and low 4 bit flags
    db 0      ;Base 24-31 bits

GDT_END:

GDT_DESCRIPTOR:
    dw GDT_END - GDT_START - 1
    dd GDT_START


[BITS 32]

load32:
    mov eax, 1
    mov ecx, 128
    mov edi, 0x0100000
    call ata_lba_read
    jmp CODE_SEG:0x0100000

ata_lba_read:
    mov ebx, eax ;Backup the LBA
    ;Send the highest 8 bits of the LBA to harddisk controller
    shr eax, 24 ;Shift 24 bits so eax now contain only the highest 8 bits of the LBA
    or eax, 0xE0 ;Select the master drive
    mov dx, 0x1F6
    out dx, al ;Send
    ;Send the total sectors to read
    mov eax, ecx
    mov dx, 0x1F2
    out dx, al
    ;Send more bits of the LBA
    mov eax, ebx ;Restore LBA
    mov dx, 0x1F3
    out dx, al
    ;Send more bits of the LBA
    mov eax, ebx ;Restore LBA
    mov dx, 0x1F4
    shr eax, 8 ;Shift 8 bits that were sent
    out dx, al
    ;Send upper 16 bits of the LBA
    mov eax, ebx ;Restore LBA
    mov dx, 0x1F5
    shr eax, 16 ;Shift 16 bits
    out dx, al
    ;Finished sending
    mov dx, 0x1F7
    mov al, 0x20
    out dx, al
    ;Read all sectors
    .next_sector:
        push ecx
    ;Checking the need to read
    .try_again:
        mov dx, 0x1F7
        in al, dx
        test al, 8
        jz .try_again
    ;We need to read 256 words at a time
        mov ecx, 256
        mov dx, 0x1F0
        rep insw
        pop ecx
        loop .next_sector
    ;End of reading
        ret


bootmsg db 'www.https://stackoverflow.com/', 0
newline db 0Ah, 0Dh, 0
times 510-($-$$) db 0
dw 0xAA55

UPD: 32-bit print routine code (VGA Video Memory based on 0xB8000) Converted my working C print function to assembly one. The bootloader stops and prevents loading kernel in QEMU

makechar32: ;eax - chr, edx - color
    shl edx, 8
    or edx, eax
    mov eax, edx
    ret

putchar32: ;eax - chr, edx - color, ebx - x, ecx - y
    call makechar32
    mov esi, 0xB8000
    ;video_mem_saddr + y*80 + x
    add esi, ebx
    mov ebx, eax
    mov eax, 80
    mul ecx
    mov ecx, eax
    add esi, ecx
    mov [esi], ebx
    ret

printchar32: ;eax - chr, edx - color
    pushad
    mov ebx, [col]
    mov ecx, [row]
    call putchar32
    inc ebx
    cmp ebx, 80
    jge newrow
    newcol:
        inc ebx
        mov [col], ebx
        popad
        ret
    newrow:
        xor ebx, ebx
        mov [col], ebx
        inc ecx
        mov [row], ecx
        popad
        ret

print32: ;ecx - *str
    pushad
    mov esi, ecx
    print32_loop:
        lodsb
        cmp eax, 0
        jz finish_print32
        mov edx, 7
        call printchar32
        jmp print32_loop
    finish_print32:
        popad
        ret
NotMyCode
  • 11
  • 3
  • What is your real hardware? Is it a classical IDE drive? If not, is it set to IDE mode in the bios if available? – Jester Nov 12 '21 at 12:19
  • @Jester SATA Controller on IDE mode – NotMyCode Nov 12 '21 at 13:47
  • 1
    Sounds good. So, does it get to the `jmp CODE_SEG:0x0100000` and is that what is faulting? Maybe add a print there? Could also print the first/last byte of the loaded kernel to verify it's read correctly. – Jester Nov 12 '21 at 14:03
  • I'm afraid that I cannot add a print routine down here, because it works only in real mode – NotMyCode Nov 12 '21 at 14:52
  • 4
    You can "print" characters from protected mode by poking them directly into video memory at linear address `0xb8000`. Or, since the symptom is unexpected rebooting, you could insert an infinite loop at a strategic point in the code; if the machine then hangs instead of rebooting, you know you successfully reached that point. – Nate Eldredge Nov 12 '21 at 16:08
  • I tried to write a protected mode print routine, but system stops on booting (see question update). I converted my working C print routine to assembly one (Using VGA video memory on 0xB8000) – NotMyCode Nov 12 '21 at 18:02
  • Thanks for the infinite loop advice, I'll try it – NotMyCode Nov 12 '21 at 18:10
  • What do you mean the bootloader "stops"? Machine code can't just stop - *something* is being executed. – Nate Eldredge Nov 12 '21 at 18:29
  • I mean it just doesn't doing anything else. If I don't call this routine, the kernel loads (in QEMU). But when it's called, there's nothing more. I tried to interrupt the boot process through gdb, it shows only infinite "add eax, al". By the way, I just encountered the new problem. The thing is I again copied the bootloader on a USB stick via the DD command (```sudo dd if=./bin/os.bin of=/dev/sdb```), as I did it on the first test, and computer doesn't even see the USB as bootable and ignores it. What could be the problem here? – NotMyCode Nov 12 '21 at 20:36
  • You said you were using a SATA disk in IDE mode ... and now you say USB? I am confused. Anyway, one thing I don't see is you loading the segment registers in protected mode. – Jester Nov 12 '21 at 20:52
  • I'm trying to do both disk and USB stick, sorry for confusing. Can the segment registers be a cause on real hardware? And if they are, why does the system actually boots on QEMU? – NotMyCode Nov 12 '21 at 21:04
  • 1
    Accidental if it works. You need to reload `DS` and `SS` at the very least. While the original real mode `DS` descriptor may be cached, that certainly won't work for addressing `b8000h` due to the 64k limit. – Jester Nov 12 '21 at 21:16
  • This looks like a floppy boot record, not the [MBR](https://wiki.osdev.org/MBR_(x86)) that one would expect on a hard disk; in particular there's no partition table. This could be why your hardware's BIOS won't boot it from a hard disk or a USB device that emulates one. QEMU's BIOS is much more forgiving that many real ones. – Nate Eldredge Nov 12 '21 at 23:28
  • 1
    And adding to @Jester's point, along with reloading the segment registers, you also need to load the stack pointer to point to some usable memory. In real mode you had it at 0, so it wraps around the segment and grows down from linear address `0xffff`. That's fine, but in protected mode it would be trying to grow down from linear address `0xffffffff` which is not fine. Also, I don't think you can rely on the upper half of `esp` being zero or non-garbage. Better reload it with the stack address you really want. – Nate Eldredge Nov 12 '21 at 23:34

0 Answers0