我正在尝试在nasm中构建一个简单的x86 Linux bootstrap 。

Linux bzImage从第一个扇区开始存储在磁盘分区sda1上。

我将实模式代码从bzImage(15个扇区)读取到从0x7E00开始的内存中。
但是,当我跳入代码时,它只是挂起,没有任何 react 。

我已经为sda上的主引导记录创建了代码。如果我只是附上我可能是最好的
整个东西。我想知道为什么它在跳远指令后才挂起。

[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

最佳答案

假设您的代码是引导加载程序(因此不是MBR):

  • 除非必须,否则不要禁用IRQ。 BIOS需要它们正确运行,并且无论如何都将在某些BIOS功能中启用它们(例如,等待磁盘功能中的“扇区转移” IRQ)。因为您的代码只是加载并将控制权传递给更实模式的代码(例如,并且不切换到保护模式或涉及任何内容),所以您没有理由在整个引导加载程序的任何位置禁用IRQ。
  • 对于实模式寻址,通常使用0x0000:0x7C00而不是0x07C0:0x0000更加干净/容易。您似乎正在尝试将两者混合使用(例如,为前者设置段寄存器,但为后者定义BOOTSEG和HDRSEG)。
  • 分区表包含“扩展分区”而不是“主分区”,因此您的分区表是错误的(可能为空/空)。
  • 引导加载程序不应采用任何特定的/硬编码的“启动LBA”(分区的“启动LBA”取决于最终用户在安装OS时感觉如何对其磁盘进行分区)。您需要从MBR的主分区表中确定分区的“启动LBA”,这通常是通过希望MBR离开DS:SI指向分区的分区表条目来完成的。
  • 您不应假定自己是从“BIOS设备0x80”启动。 MBR应该将DL设置为正确的设备编号,并且如果(例如)将OS安装在第二个硬盘驱动器或其他某个设备上,则没有理由不使您的代码不起作用。
  • (在DAP中)您硬编码的“开始读取LBA”是错误的。由于历史原因,每个磁道可能有63个扇区,并且分区从第64个扇区开始。这意味着LBA扇区0x3F是分区中的第一个扇区(这是您的引导加载程序),而不是内核的第一个扇区。我假设内核的第一个扇区可能是LBA扇区0x40(分区的第二个扇区)。
  • “扇区数”也不应该硬编码。您需要加载内核的开头并进行检查,然后确定从该位置加载多少个扇区。
  • 通常512字节(实际上更像446字节)对于一个不错的引导加载程序来说太少了。引导加载程序的前512个字节应加载其余的引导加载程序(剩余的每个备用字节都用于改善错误处理-例如puts("Read error while trying to load boot loader")而不是putc('B'))。其他所有内容(加载内核片段,设置视频模式,在“实模式内核 header ”字段中设置正确的值等)应位于其他扇区中,而不应位于第一个扇区中。

  • 请注意,计算机的启动方式已经过精心设计,因此任何MBR都可以在任何磁盘的任何分区上链式加载任何OS。而MBR可能是允许安装多个OS的较大组件(例如,启动管理器)的一部分(例如,用户可以在其中使用漂亮的菜单或某些东西来选择应将MBR的代码链式加载到哪个分区)。这种设计允许用户在不影响任何已安装操作系统(或导致其所有已安装操作系统需要修复)的情况下,随时用其他任何方式替换MBR(或引导管理器)。举一个简单的例子,用户应该能够拥有12个不同的分区,每个分区都包含您的引导加载程序和一个单独的/独立的Linux版本,然后安装他们所需要的任何引导管理器(例如GRUB,Plop,GAG,MasterBooter等)随时都想要。

    对于为什么您的代码会挂起,考虑到所有代码都需要重写,这并不是很重要。如果您很好奇,我强烈建议您在带有调试器(例如Bochs)的仿真器中运行它,以便您可以准确地检查发生了什么(例如,在0x00007E00处转储内存以查看其中包含的内容,将JMP单步执行到看看正在执行什么,等等)。

    08-26 10:13