内核似乎已由引导加载程序加载,但没有进一步执行。看来我使用
qemu
运行图像的方式或内核加载的方式有问题。
makefile
bold=`tput bold`
black=`tput setaf 0`
red=`tput setaf 1`
green=`tput setaf 2`
yellow=`tput setaf 3`
blue=`tput setaf 4`
magenta=`tput setaf 5`
cyan=`tput setaf 6`
white=`tput setaf 7`
reset=`tput sgr0`
default: build run
build:
@echo "${bold}${green}[INFO]${reset} Compiling Bootloader"
@nasm -f bin src/bootloader.asm -o bin/boot.bin
@echo "${bold}${green}[INFO]${reset} Compiling Kernel"
@nasm -f bin src/kernel.asm -o bin/kernel.bin
@echo "${bold}${green}[INFO]${reset} Compiling the OS"
@cat bin/boot.bin bin/kernel.bin > bin/os.bin
@echo "${bold}${green}[INFO]${reset} Compiled."
run:
@echo "${bold}${green}[INFO]${reset} Launching the VM:"
@qemu-system-x86_64 -enable-kvm -drive format=raw,file=bin/os.bin -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on
clean:
@echo "${bold}${green}[INFO]${reset} Cleaning..."
@rm -rfv bin/*.bin
@echo "${bold}${green}[INFO]${reset} Cleaned"
辅助功能: src/utils/print.asm
print_string_buffer:
; The function prints a string from the buffer
; stored in the SI register, it expects a
; null symbol to be found in the buffer.
; Parameters:
; si - memory offset to the buffer
push si
push ax
.loop:
lodsb
or al, al
jz .done
; display character
mov ah, 0x0A
mov bh, 0x00
mov cx, 1
int 0x10
; move cursor
mov ah, 0x02
inc dl
int 0x10
jmp .loop
.done:
pop ax
pop si
ret
src/disk.asm
disk_load:
; The function reads DH number of sectors
; into ES:BX memory location from drive DL
; Parameters:
; es:bx - buffer memory address
push dx ; store dx on stack for error handling later
mov ah, 0x02 ; INT 13H 02H, BIOS read disk sectors into memory
mov al, dh ; number of sectors
mov ch, 0x00 ; cylinder
mov dh, 0x00 ; head
mov cl, 0x02 ; start reading sector (2 is the sector after the bootloader)
int 0x13 ; BIOS interrupt for disk functions
jc disk_error ; checks if CF (carry flag) set to 1
pop dx ; restore dx value from stack
cmp dh, al ; checks dh (number of read sectors) vs al (number of desired read sectors)
jne disk_error ; if not the desired amount of sectors were read, then error
call disk_success
ret ; return to caller
disk_error:
mov si, DISK_ERROR_MESSAGE
call print_string_buffer
hlt
disk_success:
mov si, DISK_SUCCESS_MESSAGE
call print_string_buffer
; move cursor
mov ah, 0x02
inc dh
mov dl, 0
int 0x10
ret
DISK_ERROR_MESSAGE: db "could not read from disk", 0
DISK_SUCCESS_MESSAGE: db "successfully loaded the disk", 0
引导加载程序 src/bootloader.asm
org 0x7C00
%define color_black 0
%define color_blue 1
%define color_green 2
%define color_cyan 3
%define color_red 4
%define color_magenta 5
%define color_orange 6
%define color_gray 7
%define color_yellow 14
%define color_white 15
;;;;;;;;
; INIT ;
;;;;;;;;
clear_screen:
; Int 0x10
; AH = 06h
; AL = number of lines by which to scroll up (00h = clear entire window)
; BH = attribute used to write blank lines at bottom of window
; CH, CL = row, column of window's upper left corner
; DH, DL = row, column of window's lower right corner
mov ax, 0x0600 ; AH = 6 = Scroll Window Up, AL = 0 = clear window
mov bh, color_black << 4 | color_magenta ; Attribute to clear screen with (White on Red)
xor cx, cx ; Clear window from 0, 0
mov dx, 25 << 8 | 80 ; Clear window to 24, 80
int 0x10 ; Clear the screen
mov ah, 0x02 ; Set cursor
mov bh, 0x00 ; Page 0
mov dx, 0x00 ; Row = 0, col = 0
int 0x10
set_custom_cursor:
; Int 0x10
; AH = 01h
; CH = start scan line of character matrix (0-1fH; 20H=no cursor)
; CL = end scan line of character matrix (0-1fH)
mov ax, 0x0100 ; AH = 1 = Set Cursor Shape & Size, AL = 0 = nothing
mov ch, 0x1 ; Sets the width of the cursor, the higher the thicker
mov cl, 0x10 ; Sets the height of the cursor, the less the higher
int 0x10
; bootloader start-up message
mov si, start_up_message
call print_string_buffer
; set up DX for disk loading
mov dh, 0x1 ; number of sectors that will be loaded into memory
mov dl, 0x0 ; drive number to load (0 = boot disk)
; set up ES:BX memory address to load sectors into
mov bx, 0x1000 ; load sector to memory address 0x1000
mov es, bx ; ES = 0x1000
mov bx, 0x0 ; ES:BX = 0x1000:0 (segment:offset)
; set up segment registers for RAM
mov ax, 0x1000
mov ds, ax ; data segment
mov es, ax ; extra segment
mov fs, ax
mov gs, ax
mov ss, ax ; stack segment
;;;;;;;;;;;;;;;;;;;;;;;;;;
; BOOTLOADER ENTRY POINT ;
;;;;;;;;;;;;;;;;;;;;;;;;;;
call disk_load ; loads kernel into memory
jmp 0x1000:0x0
;;;;;;;;;;;
; IMPORTS ;
;;;;;;;;;;;
%include "src/utils/print.asm"
%include "src/disk.asm"
;;;;;;;
; VAR ;
;;;;;;;
start_up_message: db "Bootloader bootstrap", 0
;;;;;;;;;;;;;;;;;
; MAGIC NUMBERS ;
;;;;;;;;;;;;;;;;;
times 510-($-$$) db 0
dw 0xAA55
内核 src/kernel.asm
;;;;;;;;;;;;;;;
; ENTRY POINT ;
;;;;;;;;;;;;;;;
start:
mov si, OS_VERSION
call print_string_buffer
hlt
;;;;;;;;;;;
; IMPORTS ;
;;;;;;;;;;;
%include "src/utils/print.asm"
OS_VERSION: db "OS v1.0", 0
; sector padding
times 512-($-$$) db 0
运行
make
后,可以看到如下项目结构:
.
├── bin
│ ├── boot.bin
│ ├── kernel.bin
│ └── os.bin
├── makefile
└── src
├── bootloader.asm
├── disk.asm
├── kernel.asm
└── utils
└── print.asm
我尝试通过将
ES:BX
寄存器设置为值(如 0x0:0x1000
)来从引导加载程序跳转到不同位置的内核。另外,我将虚拟机的内存增加到 4GB RAM。以下均无帮助。
任何理论或想法都会有帮助。谢谢。
制作一个软映像并在 VirtualBox 中运行它似乎工作得很好。而相同的软盘映像在执行引导加载程序后仍然冻结,无法到达内核。因此源代码应该是正确的,问题在于
qemu
参数。
makefile 的更改:
...
build:
@echo "${bold}${green}[INFO]${reset} Compiling Bootloader"
@nasm -f bin src/bootloader.asm -o bin/boot.bin
@echo "${bold}${green}[INFO]${reset} Compiling Kernel"
@nasm -f bin src/kernel.asm -o bin/kernel.bin
@echo "${bold}${green}[INFO]${reset} Compiling the OS"
@cat bin/boot.bin bin/kernel.bin > bin/os.bin
@echo "${bold}${green}[INFO]${reset} Compiling the Floopy image"
@dd if=/dev/zero of=bin/floopy.img bs=1024 count=1440
@dd conv=notrunc if=bin/os.bin of=bin/floopy.img
@echo "${bold}${green}[INFO]${reset} Compiled."
run:
@echo "${bold}${green}[INFO]${reset} Launching the VM:"
@qemu-system-x86_64 -enable-kvm -drive format=raw,file=bin/floopy.img -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on
...
在将控制权交给引导加载程序之前,保存由 BIOS 设置的
DL
寄存器的初始值,解决了问题。
固定引导加载程序
org 0x7C00
%define color_black 0
%define color_blue 1
%define color_green 2
%define color_cyan 3
%define color_red 4
%define color_magenta 5
%define color_orange 6
%define color_gray 7
%define color_yellow 14
%define color_white 15
;;;;;;;;
; INIT ;
;;;;;;;;
; save DL (drive number) value from the BIOS
mov [drive_number], dl
clear_screen:
; Int 0x10
; AH = 06h
; AL = number of lines by which to scroll up (00h = clear entire window)
; BH = attribute used to write blank lines at bottom of window
; CH, CL = row, column of window's upper left corner
; DH, DL = row, column of window's lower right corner
mov ax, 0x0600 ; AH = 6 = Scroll Window Up, AL = 0 = clear window
mov bh, color_black << 4 | color_magenta ; Attribute to clear screen with (White on Red)
xor cx, cx ; Clear window from 0, 0
mov dx, 25 << 8 | 80 ; Clear window to 24, 80
int 0x10 ; Clear the screen
mov ah, 0x02 ; Set cursor
mov bh, 0x00 ; Page 0
mov dx, 0x00 ; Row = 0, col = 0
int 0x10
set_custom_cursor:
; Int 0x10
; AH = 01h
; CH = start scan line of character matrix (0-1fH; 20H=no cursor)
; CL = end scan line of character matrix (0-1fH)
mov ax, 0x0100 ; AH = 1 = Set Cursor Shape & Size, AL = 0 = nothing
mov ch, 0x1 ; Sets the width of the cursor, the higher the thicker
mov cl, 0x10 ; Sets the height of the cursor, the less the higher
int 0x10
; bootloader start-up message
print_start_up_message:
mov si, start_up_message
call print_string_buffer
; move cursor
mov ah, 0x02
inc dh
mov dl, 0
int 0x10
; set up DX for disk loading
mov dh, 0x1 ; number of sectors that will be loaded into memory
mov dl, [drive_number] ; drive number to load (0 = boot disk)
; set up ES:BX memory address to load sectors into
mov bx, 0x1000 ; load sector to memory address 0x1000
mov es, bx ; ES = 0x1000
mov bx, 0x0 ; ES:BX = 0x1000:0 (segment:offset)
; set up segment registers for RAM
mov ax, 0x1000
mov ds, ax ; data segment
mov es, ax ; extra segment
mov fs, ax
mov gs, ax
mov ss, ax ; stack segment
;;;;;;;;;;;;;;;;;;;;;;;;;;
; BOOTLOADER ENTRY POINT ;
;;;;;;;;;;;;;;;;;;;;;;;;;;
call disk_load ; loads kernel into memory
jmp 0x1000:0x0
;;;;;;;;;;;
; IMPORTS ;
;;;;;;;;;;;
%include "src/utils/print.asm"
%include "src/disk.asm"
;;;;;;;
; VAR ;
;;;;;;;
start_up_message: db "Bootloader bootstrap", 0
drive_number: resb 8
;;;;;;;;;;;;;;;;;
; MAGIC NUMBERS ;
;;;;;;;;;;;;;;;;;
times 510-($-$$) db 0
dw 0xAA55
mov dl, 0x0 ; drive number to load (0 = boot disk)
您已经在@Jester 和@MichaelPetch 的帮助下解决了主要问题。
仍然存在一些问题:
问题1
org 0x7C00 %define color_black 0 ... mov [drive_number], dl
您可能习惯于模拟器 (qemu) 使用清零的段寄存器来启动引导加载程序。在许多系统上情况并非如此!如果您关心开发强大的软件,那么至少在引导加载程序旁边添加:
org 0x7C00
%define color_black 0
...
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax ; Keep these two together
mov sp, 0x7C00 ; and in this order
cld ; So LODSB functions allright
mov [drive_number], dl ; Depends on DS=0
...
问题2
mov ax, 0x1000 mov ds, ax ; data segment mov es, ax ; extra segment mov fs, ax mov gs, ax mov ss, ax ; stack segment call disk_load ; loads kernel into memory jmp 0x1000:0x0
因为您甚至在通过 disk_load 过程加载内核之前就设置了这些段寄存器,所以 disk_load 过程无法显示其消息,无论是 disk_error 或 disk_success。
更糟糕的是,您更改了 SS 段寄存器,但没有分配合适的 SP 寄存器。目前,这不会造成任何问题,因为您只加载一个扇区。但是想象一下,一旦您的项目增长并加载例如,会发生什么。 100 个部门。那么 SP 中发生的任何值(与新的 SS 结合)都会在内核中造成严重破坏,并且很难检测到该错误......
总之,更改内核第一行中的这些段寄存器和 SP(为了提高可读性,也可以使用顶部的
org 0
来实现,即使该设置碰巧是默认设置)。
问题3
print_string_buffer过程依赖于DL和DH持有合适的列和行指示,但是当从disk_load中调用时(显示错误或成功消息),情况并非如此!不知道这些消息是否会出现或在哪里出现。
问题4
mov dx, 25 << 8 | 80 ; Clear window to 24, 80
80x25 屏幕的右下角位于 (79,24)。该说明应为:
mov dx, 24 << 8 | 79
。