Linux x86引导程序

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

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

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

我已经在sda上创build了主引导logging的代码。 我可能是最好的,如果我只附加整个事情。 我想知道为什么它挂在远跳转指令后。

[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”取决于最终用户在安装操作系统时如何分区磁盘)。 您需要从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看看正在执行什么等)。

评论不符合代码!

xor bx, bx ; Will also set ss = dx

我严重怀疑这是否是你的问题…

免责声明:我没有这样做! 我是“鸡”,一直用软盘启动我的bootin。

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

我想我已经过了头了。 我会给我外套…