Linux x86引导加载程序
我正在尝试在nasm中构建一个简单的x86Linux引导加载程序 Linux bzImage存储在磁盘分区sda1上,从第一个扇区开始 我从bzImage(15个扇区)将实模式代码从0x7E00读入内存。 然而,当我跳入代码时,它只是挂起,什么也没发生 我已经在sda上为主引导记录创建了代码。我最好还是跟你联系一下 整件事。我想知道为什么它只是挂在跳远指令之后Linux x86引导加载程序,linux,assembly,kernel,nasm,16-bit,Linux,Assembly,Kernel,Nasm,16 Bit,我正在尝试在nasm中构建一个简单的x86Linux引导加载程序 Linux bzImage存储在磁盘分区sda1上,从第一个扇区开始 我从bzImage(15个扇区)将实模式代码从0x7E00读入内存。 然而,当我跳入代码时,它只是挂起,什么也没发生 我已经在sda上为主引导记录创建了代码。我最好还是跟你联系一下 整件事。我想知道为什么它只是挂在跳远指令之后 [BITS 16] %define BOOTSEG 0x7C0 %define BOOTADDR (BOOTSEG * 0x10)
[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
注释与代码不匹配
异或bx,bx;还将设置ss=dx
我很怀疑这是否是你的问题
免责声明:我没有这样做!我是“胆小鬼”,总是从软盘上启动
我“期望”在MBR中看到的是,它将自己移开,然后将活动分区上的第一个扇区再次加载到7C00h,然后跳到那里。这个“真正的引导加载程序”加载其余的。我不熟悉bzImage的布局-也许它可以在7E00h加载
我想我是疯了。假设您的代码是引导加载程序(因此不是MBR),我会给我弄到coat…:
- 除非必须,否则不要禁用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个字节应加载引导加载程序的其余部分(剩余的每个备用字节用于改进错误处理-例如
,而不仅仅是put(“尝试加载引导加载程序时读取错误”)
)。其他所有内容(加载内核片段、设置视频模式、在“real mode kernel header”字段中设置正确的值等)都应该在附加扇区中,而不是在第一个扇区中putc('B')
对于代码挂起的原因,考虑到所有代码都需要重写,这并不重要。如果您感到好奇,我强烈建议您在带有调试器(例如Bochs)的仿真器中运行它,这样您就可以准确地检查发生了什么(例如,在0x00007E00转储内存以查看它包含什么,单步执行JMP以查看执行了什么,等等).您研究过GRUB和LILO的实现吗?我已经研究过LILO的实现,它们似乎以同样的方式跳入Linux代码。但是阅读他们的代码有点困难。你是否使用了调试器来逐步检查代码并查看挂起的原因?除了不匹配注释外,
ax
和bx
是否应该是内核例如,而不是零?我想问题是在开始时清除了段寄存器。另一个问题是在跳转之前没有正确设置段寄存器。它们应该=KERNHDR,但cs应该=KERNSEG。谢谢你的回答,现在一切正常。