2

i have been writting an os for the last weeks and its going fine i decided to try it on a real machine but it doesnt jump to kernel i have tried to use an hdd instead of an usb stick and it doesnt even get the bootloader as it gets on the usb stick

ive tried to search on the web and use other code but it just doesnt jump to kernel here is the boot code

[ORG 0x7c00]

[BITS 16]

CODE_SEG EQU gdt_code - gdt_start
DATA_SEG EQU gdt_data - gdt_start

jmp short biosBlock  
nop

biosBlock:
    jmp 0:start



start:
    ;umas checkagens 
    cli ;clear interrupts
    mov ax, 0x00
    mov ds, ax
    mov es, ax

    mov ss, ax
    mov sp, 0x7c00
    sti ;enables interrupts


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

;just using gdt so we can use all the memory :D 4GB
gdt_start:
gdt_nul:
    dd 0x0
    dd 0x0

;offset 0x8
gdt_code:  ;cs should point to this
    dw 0xffff
    dw 0
    db 0
    db 0x9a
    db 11001111b
    db 0
;offset 0x10
gdt_data:
    dw 0xffff
    dw 0
    db 0
    db 0x92
    db 11001111b
    db 0

gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

[BITS 32]
 load32:
    mov eax, 1
    mov ecx, 100
    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 hard disk controller
    shr eax, 24
    or eax, 0xE0 ; Select the  master drive
    mov dx, 0x1F6
    out dx, al
    ; Finished sending the highest 8 bits of the lba

    ; Send the total sectors to read
    mov eax, ecx
    mov dx, 0x1F2
    out dx, al
    ; Finished sending the total sectors to read

    ; Send more bits of the LBA
    mov eax, ebx ; Restore the backup LBA
    mov dx, 0x1F3
    out dx, al
    ; Finished sending more bits of the LBA

    ; Send more bits of the LBA
    mov dx, 0x1F4
    mov eax, ebx ; Restore the backup LBA
    shr eax, 8
    out dx, al
    ; Finished sending more bits of the LBA

    ; Send upper 16 bits of the LBA
    mov dx, 0x1F5
    mov eax, ebx ; Restore the backup LBA
    shr eax, 16
    out dx, al
    ; Finished sending upper 16 bits of the LBA

    mov dx, 0x1f7
    mov al, 0x20
    out dx, al 


.next_sector:
    push ecx


.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 sectors into memory
    ret





times 510 - ($- $$) db 0
dw 0xAA55


make file code:

        dd if=./bin/boot.bin >> ./bin/os.bin
    dd if=./bin/kernel.bin >> ./bin/os.bin
    dd if=/dev/zero bs=1048576 count=32 >> ./bin/os.bin

im using

dd if=os.bin of="the_usb"
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Rabyt
  • 61
  • 6
  • this ATA stuff only accesses hard disks. You should definitely be able to run the bootloader from a hard disk (even if the OS loading doesn't work) - did you install it properly? – user253751 Feb 15 '23 at 19:28
  • 1
    You say _"it just doesnt jump to kernel"_. I assume that means whatever is on the other side of `jmp CODE_SEG:0x0100000` doesn't get called. So it may very well be a problem with your `ata_lba_read`. Write something on the screen to see how far it gets. Also, using direct ata functions in your MBR is generally counter productive. Just load whatever you need prior to switching into protected mode via the usual BIOS disk interrupt services. That will work no matter how your boot device has been set up. – Jester Feb 15 '23 at 19:38
  • 1
    Did you enable A20? I'd add code to the boot loader to output either to the text console or a UART with a "progress" message/char. That is, if there are (e.g.) 8 stages until the kernel is running, output a single char at each step (e.g.) `1`, then `2`, then `3`, ... That way, you can see where/when things went off the rails – Craig Estey Feb 15 '23 at 19:38
  • yes the a20 is being enabled in the kernel, i will try to output some chars to see how far is gets. and if ata only works hard disks how am i suposed to read the kernel and run it in a usb stick?? – Rabyt Feb 15 '23 at 20:12
  • 1
    One `nop` isn't big enough to leave space for the BPB where some real BIOSes will overwrite part of your MBR. Likely duplicate: [Custom bootloader booted via USB drive produces incorrect output on some computers](https://stackoverflow.com/a/47320115). Also: [Boot loader doesn't jump to kernel code](https://stackoverflow.com/q/32701854) - go through that checklist if a BPB doesn't fix it. Also, [How to make the kernel for my bootloader?](https://stackoverflow.com/q/33603842) has a good example bootloader + C kernel you can compare against. – Peter Cordes Feb 15 '23 at 21:22

1 Answers1

4

The most likely problem is that (for real hardware but not emulators) the ATA controller got replaced by SATA/AHCI about 20 years ago (and then got replaced by NVMe about 10 years ago); and you have no sanity checks to determine if the ATA controller you think you're talking to exists (or is faulty, or has a hard drive plugged into it, or if that hard drive is faulty, or if the commands you sent worked and didn't just return a "read error") and no code to handle any of the many possible problems (not even displaying the absolute worst possible "Sorry, something went wrong" error message).

This is why it's a good idea to use the firmware to access devices (e.g. BIOS "int 0x13") during early boot, until the OS is able to detect which devices are present (e.g. starting with PCI bus enumeration) and set up the right device drivers for whatever devices were detected.

To be more general; (for both BIOS and UEFI) its best to split "boot" into 4 phases:

a) Early boot. During this phase the firmware owns all the hardware and you only use the firmware to do things. The purpose of early boot code is to gather information from firmware (e.g. get a memory map that you need later), setup temporary memory management (including disabling A20 using the relevant BIOS function), load "kernel plus drivers and whatever else will be needed later" (possibly kernel plus "initial RAM disk"), setup a default video mode, etc. This typically involves switching between real mode (to use BIOS functions) and either protected mode or long mode (to be able to access more memory); to avoid the "kernel and/or initial RAM disk is many MiB but real mode can only access about 640 KiB of RAM" problem (by loading into a temporary buffer in real mode and then moving data where you actually want it using protected/long mode). This phase ends when the OS either figuratively (for BIOS) or literally (for UEFI, via. its "ExitBootServices()") takes control of all hardware from firmware.

b) Kernel initialization. This is where you set up things like physical memory management, virtual memory management, scheduler, device driver interfaces, etc. Note that during this phase nothing needs to access any device. Also, some work can be done (e.g. decompressing kernel's file, setting up paging and mapping kernel into the right place in virtual memory) before passing control to kernel (not all of "kernel initialization" is done by kernel itself).

c) Setting up devices (detecting which devices are present, loading their device drivers from "initial RAM disk", letting device drivers test their device and see what is connected to them, etc). Note that this can be conceptually recursive and can end with "non-device services". E.g. PCI bus enumeration detects network card and starts network card driver, then network card driver initializes (and checks for faulty hardware) and detects network connection/s and starts TCPI/IP stack (if not already started); or PCI bus enumeration detects USB controller and starts relevant USB controller driver, USB controller driver detects sound device and starts relevant USB sound device driver; USB sound device driver detects speakers/microphones and starts some kind of sound service (if not already started). In the same way a hard disk controller could (after initialization, detecting attached disks, etc) start a RAID layer and/or start file systems.

d) Pass control to user-space for more initialization. This could be an "init script" (ancient Unix), or could be something a user can use to log in (followed by a GUI); and will probably include various other things (starting services/daemons).

Note that (especially in early phases where you can't just say "LOL, if something is wrong kernel or device drivers will handle it") maybe half of your code will exist to deal with things that should never happen. This means that it's impossible to have acceptable early boot code that fits in 512 bytes, even if you do none of the thing that are necessary (e.g. get a memory map).

Brendan
  • 35,656
  • 2
  • 39
  • 66