内核加载后未执行

问题描述 投票:0回答:1

问题

内核似乎已由引导加载程序加载,但没有进一步执行。看来我使用

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
assembly kernel nasm qemu bootloader
1个回答
0
投票
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_errordisk_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

© www.soinside.com 2019 - 2024. All rights reserved.