为什么添加此行后我的引导加载程序会崩溃?
Why does my bootloader crash after adding this line?
我正在学习 Nick Blundell 的引导扇区编程教程 (https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf and https://www.youtube.com/watch?v=YvZhgRO7hL4)。我的代码在我的 qemu 模拟器中工作得很好,但是当我 运行 它在物理机器上时,只要我开始引用段寄存器,它就会崩溃。
我在学校的老师不熟悉低级编程,无法帮助我。这是我的引导加载程序,在这里我用字符串 CRASH 注释了导致它崩溃的行(注意:当我说崩溃时,它实际上只是继续从下一个磁盘加载我的 OS。我正在加载这个来自外部硬盘的代码):
[bits 16]
[org 0x7c00]
mov bp, 0xffff
mov sp, bp
mov ax, 0x0000
mov ds, ax
;; mov es, ax ;; CRASH
;; mov ss, ax ;; CRASH
mov si, BOOT_MSG
call print_string
call print_newline
mov si, INIT_SEG_MSG
call print_string
call print_newline
;; mov dx, ds ;; CRASH
;; call print_hex
;; call print_newline
;;mov dx, cs ;; CRASH
;;call print_hex
;;call print_newline
;;mov dx, es ;; CRASH
;;call print_hex
;;call print_newline
;;mov dx, ss ;; CRASH
;;call print_hex
;;call print_newline
;; mov dl, 0x80 ;; disk where kernel is
;; mov cl, 3 ;; start sect
;; mov al, 1 ;; num sect
;; mov bx, 0x7ef0 ;; RAM addr
;; call load_kernel
;; mov si, KERN_MSG
;; call print_string
;; call print_newline
;; call switch_to_pm
jmp $
%include "print.asm"
%include "print_hex.asm"
%include "disk.asm"
%include "pm.asm"
[bits 32]
pm :
mov esi, PM_MSG
call print_string_pm
jmp 0x7ef0
jmp $
[bits 16]
BOOT_MSG : db 'booted 16-bit to 0x7c00',0
KERN_MSG : db 'loaded kernel to es 0x7ef0',0
PM_MSG : db 'switched to 32-bit mode',0
INIT_SEG_MSG : db 'init segment registers',0
times 510-($-$$) db 0
dw 0xaa55 `
我确信我有一个根本性的误解,任何帮助将不胜感激。这是我的打印例程:
print_string :
push ax
_loop :
lodsb
cmp al, 0
je _end
mov ah, 0x0e
int 0x10
jmp _loop
_end :
pop ax
ret
print_hex :
mov si, HEX_TEMPLATE
mov bx, dx
shr bx, 12
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+2], bl
mov bx, dx ;; bx -> 0x1234
shr bx, 8 ;; bx -> 0x0012
and bx, 0x000f ;; bx -> 0x0002
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+3], bl
mov bx, dx
shr bx, 4
and bx, 0x00f
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+4], bl
mov bx, dx
and bx, 0x0f
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+5], bl
call print_string
ret
HEX_TEMPLATE : db '0x???? ',0
HEXABET : db '0123456789abcdef'
print_newline :
pusha
mov ah, 0x0e
mov al, 0x0d
int 0x10
mov al, 0x0a
int 0x10
popa
ret
BIOS 的一个特点是 CS 的状态是未知的。在 Bochs 中,可能 Qemu CS = 0,因此 origin 为 0x7C00 的代码将起作用。真实硬件可能会传递 CS = 0x7C0,因此如果在代码开始时没有适当的远跳转,对近绝对函数的调用将偏移 0x7C00 字节,原点设置为 0x7C00。
解决方案:
org 0x7C00
jmp 0:Begin ; Far jump so CS = 0
Begin:
mov ax, cs
mov ds, ax
mov es, ax
或
org 0
jmp 0x7C0:0 ; Far jump so CS = 0x7C0
这可能是正在发生的事情,崩溃即将来临 @call print_string 实际上正在寻找代码 @ 0xF8??。
所以在 多次 硬重置之后,我找到了解决方案。问题是我认为是问题,不是问题。所以我决定用我的闪存驱动器从我爸爸的电脑上启动。它工作得很好。所以我继续在我自己的电脑上写我的引导加载程序。我写了进入PM模式的代码,运行吧,完美,没问题。然后我做了另一个改变。使用 dd 将字节复制到闪存驱动器,并 运行 它在我爸爸的电脑上。可是等等。更改没有出现...所以我 运行 又 dd 几次,将驱动器归零,但仍然没有。所以我重新启动了我的计算机,运行 再次 DD,它工作了。 问题似乎是我的OS(Ubuntu16,不确定是否相关)发出dd命令的方式。 /dev/sdb 似乎是一种实际上并没有写入磁盘的缓冲区,至少在我第一次移除闪存驱动器之后是这样。也许这是 OS 中的错误。也许这是dd中的错误。也许我错过了什么。谁知道呢。 问题(如评论中所述)是我不知道 Linux 块设备是如何工作的。参见:sync。显然我所做的更改是缓存的而不是写入的。无论哪种方式,我将从现在开始使用模拟器。谢谢大家!
我正在学习 Nick Blundell 的引导扇区编程教程 (https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf and https://www.youtube.com/watch?v=YvZhgRO7hL4)。我的代码在我的 qemu 模拟器中工作得很好,但是当我 运行 它在物理机器上时,只要我开始引用段寄存器,它就会崩溃。 我在学校的老师不熟悉低级编程,无法帮助我。这是我的引导加载程序,在这里我用字符串 CRASH 注释了导致它崩溃的行(注意:当我说崩溃时,它实际上只是继续从下一个磁盘加载我的 OS。我正在加载这个来自外部硬盘的代码):
[bits 16]
[org 0x7c00]
mov bp, 0xffff
mov sp, bp
mov ax, 0x0000
mov ds, ax
;; mov es, ax ;; CRASH
;; mov ss, ax ;; CRASH
mov si, BOOT_MSG
call print_string
call print_newline
mov si, INIT_SEG_MSG
call print_string
call print_newline
;; mov dx, ds ;; CRASH
;; call print_hex
;; call print_newline
;;mov dx, cs ;; CRASH
;;call print_hex
;;call print_newline
;;mov dx, es ;; CRASH
;;call print_hex
;;call print_newline
;;mov dx, ss ;; CRASH
;;call print_hex
;;call print_newline
;; mov dl, 0x80 ;; disk where kernel is
;; mov cl, 3 ;; start sect
;; mov al, 1 ;; num sect
;; mov bx, 0x7ef0 ;; RAM addr
;; call load_kernel
;; mov si, KERN_MSG
;; call print_string
;; call print_newline
;; call switch_to_pm
jmp $
%include "print.asm"
%include "print_hex.asm"
%include "disk.asm"
%include "pm.asm"
[bits 32]
pm :
mov esi, PM_MSG
call print_string_pm
jmp 0x7ef0
jmp $
[bits 16]
BOOT_MSG : db 'booted 16-bit to 0x7c00',0
KERN_MSG : db 'loaded kernel to es 0x7ef0',0
PM_MSG : db 'switched to 32-bit mode',0
INIT_SEG_MSG : db 'init segment registers',0
times 510-($-$$) db 0
dw 0xaa55 `
我确信我有一个根本性的误解,任何帮助将不胜感激。这是我的打印例程:
print_string :
push ax
_loop :
lodsb
cmp al, 0
je _end
mov ah, 0x0e
int 0x10
jmp _loop
_end :
pop ax
ret
print_hex :
mov si, HEX_TEMPLATE
mov bx, dx
shr bx, 12
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+2], bl
mov bx, dx ;; bx -> 0x1234
shr bx, 8 ;; bx -> 0x0012
and bx, 0x000f ;; bx -> 0x0002
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+3], bl
mov bx, dx
shr bx, 4
and bx, 0x00f
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+4], bl
mov bx, dx
and bx, 0x0f
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+5], bl
call print_string
ret
HEX_TEMPLATE : db '0x???? ',0
HEXABET : db '0123456789abcdef'
print_newline :
pusha
mov ah, 0x0e
mov al, 0x0d
int 0x10
mov al, 0x0a
int 0x10
popa
ret
BIOS 的一个特点是 CS 的状态是未知的。在 Bochs 中,可能 Qemu CS = 0,因此 origin 为 0x7C00 的代码将起作用。真实硬件可能会传递 CS = 0x7C0,因此如果在代码开始时没有适当的远跳转,对近绝对函数的调用将偏移 0x7C00 字节,原点设置为 0x7C00。
解决方案:
org 0x7C00
jmp 0:Begin ; Far jump so CS = 0
Begin:
mov ax, cs
mov ds, ax
mov es, ax
或
org 0
jmp 0x7C0:0 ; Far jump so CS = 0x7C0
这可能是正在发生的事情,崩溃即将来临 @call print_string 实际上正在寻找代码 @ 0xF8??。
所以在 多次 硬重置之后,我找到了解决方案。问题是我认为是问题,不是问题。所以我决定用我的闪存驱动器从我爸爸的电脑上启动。它工作得很好。所以我继续在我自己的电脑上写我的引导加载程序。我写了进入PM模式的代码,运行吧,完美,没问题。然后我做了另一个改变。使用 dd 将字节复制到闪存驱动器,并 运行 它在我爸爸的电脑上。可是等等。更改没有出现...所以我 运行 又 dd 几次,将驱动器归零,但仍然没有。所以我重新启动了我的计算机,运行 再次 DD,它工作了。 问题似乎是我的OS(Ubuntu16,不确定是否相关)发出dd命令的方式。 /dev/sdb 似乎是一种实际上并没有写入磁盘的缓冲区,至少在我第一次移除闪存驱动器之后是这样。也许这是 OS 中的错误。也许这是dd中的错误。也许我错过了什么。谁知道呢。 问题(如评论中所述)是我不知道 Linux 块设备是如何工作的。参见:sync。显然我所做的更改是缓存的而不是写入的。无论哪种方式,我将从现在开始使用模拟器。谢谢大家!