Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux x86 bootloader

I am trying to build a simple x86 Linux bootloader in nasm.

The Linux bzImage is stored on disk partition sda1 starting from the first sector.

I read the real mode code from the bzImage (15 sectors) into memory starting from 0x7E00. However when i jump into the code it just hangs, nothing happens.

I have created code for the master boot record on sda. I's probably best if i just attach the whole thing. I would like to know why it just hangs after the far jump instruction.

[BITS 16]

%define BOOTSEG 0x7C0 
%define BOOTADDR (BOOTSEG * 0x10)

%define HDRSEG (BOOTSEG + 0x20)
%define HDRADDR (HDRSEG * 0x10)

%define KERNSEG (HDRSEG + 0x20)

[ORG BOOTADDR]
entry_section:
    cli
    jmp     start
start:
    ; Clear segments
    xor     ax, ax
    mov     ds, ax  
    mov     es, ax
    mov     gs, ax
    mov     fs, ax
    mov     ss, ax
    mov     sp, BOOTADDR    ; Lots of room for it to grow down from here

    ; Read all 15 sectors of realmode code in the kernel
    mov     ah, 0x42
    mov     si, dap
    mov     dl, 0x80
    int     0x13
    jc  bad

    ; Test magic number of kernel header
    mov     eax, dword [HDRADDR + 0x202]
    cmp     eax, 'HdrS'
    jne     bad

    ; Test jump instruction is there
    mov     al, byte [KERNSEG * 16]
    cmp     al, 0xEB
    jne     bad

    xor     ax, ax      ; Kernel entry code will set ds = ax
    xor     bx, bx      ; Will also set ss = dx
    jmp     dword KERNSEG:0

; Simple function to report an error and halt
bad:
    mov     al, "B"
    call    putc
    jmp     halt

; Param: char in al 
putc:
    mov     ah, 0X0E    
    mov     bh, 0x0F
    xor     bl, bl  
    int     0x10
    ret

halt:
    hlt
    jmp     halt

; Begin data section
dap:                ; Disk address packet
    db  0x10            ; Size of dap in bytes
    db  0               ; Unused
    dw  15              ; Number of sectors to read
    dw  0               ; Offset where to place data
    dw  HDRSEG          ; Segment where to place data
    dd  0x3F            ; Low order of start addres in sectors
    dd  0               ; High order of start address in sectors

; End data section

times 446-($-$$) db 0   ; Padding to make the MBR 512 bytes

; Hardcoded partition entries
part_boot:
    dw 0x0180, 0x0001, 0xFE83, 0x3c3f, 0x003F, 0x0000, 0xF3BE, 0x000E
part_sda2:
    dw 0x0000, 0x3D01, 0xFE83, 0xFFFF, 0xF3FD, 0x000E, 0x5AF0, 0x01B3
part_sda3:
    dw 0xFE00, 0xFFFF, 0xFE83, 0xFFFF, 0x4EED, 0x01C2, 0xb113, 0x001D
part_sda4:
    dw 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000

dw 0xAA55   ; Magic number at relative address 510
mbrend:     ; Relative address 512
like image 732
Druesukker Avatar asked Nov 03 '22 05:11

Druesukker


1 Answers

Assuming your code is a boot loader (and therefore is not an MBR):

  • Never disable IRQs unless you have to. The BIOS needs them to function correctly, and will enable them inside some BIOS functions anyway (e.g. waiting for a "sectors transferred" IRQ inside disk functions). Because your code is only loading and passing control to more real mode code (e.g. and no switch to protected mode or anything is involved) you have no reason to disable IRQs anywhere in your entire boot loader.
  • For real mode addressing, it's typically cleaner/easier to use 0x0000:0x7C00 rather than 0x07C0:0x0000. You seem to be attempting to mix both (e.g. set segment registers for the former but define BOOTSEG and HDRSEG for the latter).
  • The partition table contains "extended partitions" and not "primary partitions" and your partition table is therefore wrong (and should probably be blank/empty).
  • The boot loader should not assume any specific/hard-coded "starting LBA" (the "starting LBA" for the partition depends on how the end user felt like partitioning their disk when the OS is installed). You need to determine the partition's "starting LBA" from the MBR's primary partition table, which is typically done by hoping that the MBR left DS:SI pointing to your partition's partition table entry.
  • You should not assume that your are booting from "BIOS device 0x80". The MBR should leave DL set to the correct device number, and there should be no reason why your code shouldn't work if (e.g.) the OS is installed on the second hard drive or something else.
  • Your hard-coded "starting LBA to read" (in the DAP) is wrong. For historical reasons there's probably 63 sectors per track and your partition starts on the 64th sector. This means that LBA sector 0x3F is the first sector in the partition (which is your boot loader) and is not the first sector of the kernel. I'm assuming the first sector of the kernel might be LBA sector 0x40 (the second sector of the partition).
  • The "number of sectors" shouldn't be hard-coded either. You'd want to load the beginning of the kernel and examine it, and determine how many sectors to load where from that.
  • Typically 512 bytes (actually more like 446 bytes) is far too little for a decent boot loader. The first 512 bytes of a boot loader should load the rest of the boot loader (with every spare byte left over used to improve error handling - e.g. puts("Read error while trying to load boot loader") rather than just putc('B')). Everything else (loading the pieces of the kernel, setting up a video mode, setting correct values in the "real mode kernel header" fields, etc) should be in the additional sectors and not be in the first sector.

Note that the way the computer boots has been carefully designed such that any MBR can chainload any OS on any partition of any disk; and the MBR may be part of something larger (e.g. a boot manager) that allows multiple OSs to be installed (e.g. where the user can use a pretty menu or something to choose which partition the MBR's code should chain-load). This design allows the user to replace the MBR (or boot manager) with anything else at any time without effecting any installed OS (or causing all of their installed OSs to need fixing). For a simple example, a user should be able to have 12 different partitions that all contain your boot loader and a separate/independent version of Linux, and then install any boot manager (e.g. GRUB, Plop, GAG, MasterBooter, etc) that they want at any time.

For why your code hangs, it's not very important given that all of the code needs to be rewritten anyway . If you're curious, I'd strongly recommend running it inside an emulator with a debugger (e.g. Bochs) so that you can examine exactly what has happened (e.g. dump memory at 0x00007E00 to see what it contains, single-step the JMP to see what is being executed, etc).

like image 176
Brendan Avatar answered Nov 09 '22 12:11

Brendan