5

我正在尝试在 nasm 中构建一个简单的 x86 Linux 引导加载程序。

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

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

我已经为 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
4

2 回答 2

1

注释与代码不匹配!

xor bx, bx ; Will also set ss = dx

我严重怀疑这是不是你的问题...

免责声明:我没有这样做!我是“鸡”,总是从软盘上做我的引导。

我“期望”在 MBR 中看到的是让它自己移开,然后将活动分区上的第一个扇区再次加载到 7C00h,然后跳转到那里。这个“真正的引导加载程序”会加载其余部分。我不熟悉 bzImage 的布局 - 也许它会在 7E00h 加载...

我想我在我的头上。我去拿外套...

于 2013-02-02T15:17:41.717 回答
1

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

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

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

对于您的代码为何挂起,这并不是很重要,因为无论如何都需要重写所有代码。如果您很好奇,我强烈建议您在带有调试器(例如 Bochs)的模拟器中运行它,以便您可以准确检查发生的情况(例如,在 0x00007E00 处转储内存以查看其中包含的内容,单步执行 JMP 到查看正在执行的操作等)。

于 2013-02-02T21:24:39.747 回答