Kernel message not displaying in custom OS: Bootloader or Kernel issue?

huangapple go评论125阅读模式
英文:

Kernel message not displaying in custom OS: Bootloader or Kernel issue?

问题

我正在构建一个操作系统,并遇到一个问题,即引导加载程序成功打印其消息,但内核的消息未显示出来。以下是一个简要的说明:

设置:

汇编器和环境:

  • 使用NASM,使用-f bin选项
  • 在qemu-system-i386上进行测试
  • 环境:Windows Subsystem for Linux(WSL)与Xlaunch(尽管我怀疑这与问题无关)

快速注释

push ax
push bx
mov bl, dl 					; 这是为了保存dl
push bx

这段代码片段是引导加载程序的一部分,最初可能看起来有些混乱。

这里的要求是在整个函数中保留dl寄存器的值。你可能会问:

  1. 为什么不直接将dl压入堆栈?
    • 原因是堆栈只适用于16位值。dl是一个8位寄存器,因此不能直接将其推入堆栈。
  2. 为什么不使用push dx
    • 这是因为在这个上下文中,dh充当返回值。

为了解决这些限制,我采用了以下方法:

  • 将bx推入堆栈。
  • dl的值移动到bx的低位(即bl)。
  • 再次将bx(现在包含dl的值)推入堆栈。
  • 稍后,为了恢复这个值,我只需弹出bx,然后mov dl, bl

文件:

bootloader.asm

[org 0x7c00]
[bits 16]

jmp main

_message_read_err:  db 'Error in reading floppy!', 0
_message_boot:      db 'Booting SSoS...', 0
_sectors_per_track: dw 18
_head_count:        dw 2
_kernel_start_LBA:  dw 1
_kernel_size:       dw 1
_stack_ptr_addr:    dw 0x7c00
_es_start:          dw 0x7c0

; 参数:
;     无
; 返回值:
;     无
main:

    mov ax, 0 ; 不能直接写入ds和ss
    mov ds, ax 
    mov ss, ax 
    mov sp, [_stack_ptr_addr] ; 堆栈指针

    mov ax, [_es_start] ; 地址 * 16 = 实际地址 -> 0x7c0 * 16(十进制)= 0x7c00
    mov es, ax 

    mov si, _message_boot
    call puts

    ; 将LBA转换为CHS
    ;; 参数
    mov si, 1
    call LBA_to_CHS ; 调用后,CH、CL和DH将具有柱面、扇区和磁头的值
    
    mov bx, 0x200
    mov al, [_kernel_size] ; 要读取的扇区数
    call read_disk

    jmp 0:7e00h

; 参数:
;     si - 指向要打印的字符串
; 返回值:
;     无
puts:
    push ax
.begin:
    
    mov ah, 0x0E
    lodsb
    
    cmp al, 0
    je .done
    
    int 10h
    jmp .begin
.done:

    pop ax
    
    ret

; 参数:
;     si - LBA地址
; 返回值:
;     ch、dh、cl - CHS地址
;; dx和cx必须进行修改
LBA_to_CHS:

    push ax
    push bx

    mov bl, dl 					; 这是为了保存dl

    push bx
    
    mov ax, [_head_count]
    mul word [_sectors_per_track]

    mov bx, ax
    mov ax, si
    mul word bx
    mov ch, al 					; 将ax的低位作为柱面地址
    
    mov ax, si
    div word [_sectors_per_track] ; 结果在dx:ax中
    div word [_head_count]		  ; 结果还在dx:ax中(dx是余数)
    mov dh, dl					  ; 由于dx由dh(高位)和dl(低位)组成,我们希望将低位移到dh中

    push dx
    
    mov ax, si
    div word [_sectors_per_track] ; 余数在dx中
    inc dx

    mov cl, dl

    pop dx

    pop bx
    mov dl, bl
    pop bx
    pop ax
    
    ret

; 参数:
;     bx - 要加载数据的地址
;     ch、cl、dh - CHS值
; 返回值:
;     无

read_disk:
    push ax
    push si
    
    mov si, 4
    
.retry:
    dec si

    cmp si, 0
    je .read_error
    
    mov ah, 02h
    mov al, [_kernel_size]

    int 13h

    jc .retry

    pop si
    pop ax
    
    ret 

.read_error:
    
    mov si, _message_read_err
    call puts

    cli
    hlt

times 510-($-$$) db 0

dw 0aa55h

kernel.asm

[org 0x7E00]
[bits 16]

jmp main

_message: db 'Hello from kernel!', 0

main:
    mov si, _message
    call puts

    cli
    hlt

puts:
    mov ah, 0x0E
.begin:
    lodsb 
    cmp al, 0
    je .done
    int 10h
    jmp .begin
.done:
    ret

Makefile

BUILD_DIR = build
SRC_DIR = src
IMG_NAME = dev

dev: image
	qemu-system-i386 -fda $(BUILD_DIR)/$(IMG_NAME).img

image: $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin
	@dd if=$(BUILD_DIR)/bootloader.bin of=$(BUILD_DIR)/$(IMG_NAME).img bs=512
	@dd if=$(BUILD_DIR)/kernel.bin of=$(BUILD_DIR)/$(IMG_NAME).img bs=512 seek=1
	@truncate -s 1440k $(BUILD_DIR)/$(IMG_NAME).img
	@hexdump -C $(BUILD_DIR)/$(IMG_NAME).img
	@objdump -b binary -m

<details>
<summary>英文:</summary>

I am building an operating system and am experiencing a problem where the bootloader successfully prints its message, but the kernel&#39;s message isn&#39;t displayed. Here&#39;s a breakdown:

# Setup:

Assembler and Environment:
- NASM with -f bin option
- Tested on qemu-system-i386
- Environment: Windows Subsystem for Linux (WSL) with Xlaunch (though I suspect this isn&#39;t relevant)

# Quick Note

push ax
push bx
mov bl, dl ; This is to preserve dl
push bx

This code snippet is part of a bootloader, and it might seem confusing initially.

The requirement here is to preserve the dl register throughout the function. You might ask:

 1. Why not just `push dl` onto the stack?
    - The reason is the stack only works with 16-bit values. dl is an 8-bit register, so pushing it directly isn&#39;t possible.
 2. Why not `push dx`? 
    - This is because `dh` serves as a return value in this context. 

To work around these constraints, I took this approach:
- Pushed bx onto the stack.
- Moved the value of `dl into the lower bits of bx` (i.e., bl).
- Pushed bx (`now holding the value of dl`) onto the stack again.
- Later, to restore the value, I simply popped bx and then `mov dl, bl`.

# Files:

*bootloader.asm*

[org 0x7c00]
[bits 16]

jmp main

_message_read_err: db 'Error in reading floppy!', 0
_message_boot: db 'Booting SSoS...', 0
_sectors_per_track: dw 18
_head_count: dw 2
_kernel_start_LBA: dw 1
_kernel_size: dw 1
_stack_ptr_addr: dw 0x7c00
_es_start: dw 0x7c0

; Arguments:
; None
; Returns:
; None
main:

mov ax, 0 ; can&#39;t write to ds and ss directly
mov ds, ax 
mov ss, ax 
mov sp, [_stack_ptr_addr] ; stack pointer

mov ax, [_es_start] ; address * 16 = real address -&gt; 0x7c0 * 16 (dec) = 0x7c00
mov es, ax 

mov si, _message_boot
call puts

; Converting LBA to CHS
;; Parameters
mov si, 1
call LBA_to_CHS ; after this call, CH, CL, and DH will have cylinder, sector, and head values

mov bx, 0x200
mov al, [_kernel_size] ; number of sectors to read
call read_disk

jmp 0:7e00h

; Arguments:
; si - Points to the string to be printed
; Returns:
; None
puts:
push ax
.begin:

mov ah, 0x0E
lodsb

cmp al, 0
je .done

int 10h
jmp .begin

.done:

pop ax

ret

; Arguments:
; si - LBA adress
; Returns:
; ch, dh, cl - CHS adress
;; dx and cx will have to be modified
LBA_to_CHS:

push ax
push bx

mov bl, dl 					; This is to preserve dl

push bx

mov ax, [_head_count]
mul word [_sectors_per_track]

mov bx, ax
mov ax, si
mul word bx
mov ch, al 					; Put lower bits of ax as the cylinder address

mov ax, si
div word [_sectors_per_track] ; result in dx:ax
div word [_head_count]		  ; result also in dx:ax (dx is remainder)
mov dh, dl					  ; since dx is composed of dh (higher bits) and dl (lower bits) we want to move the lower bits into dh

push dx

mov ax, si
div word [_sectors_per_track] ; remainder in dx
inc dx

mov cl, dl

pop dx

pop bx
mov dl, bl
pop bx
pop ax

ret

; Arguments:
; bx - Address to load the data
; ch, cl, dh - CHS values
; Returns:
; None

read_disk:
push ax
push si

mov si, 4

.retry:
dec si

cmp si, 0
je .read_error

mov ah, 02h
mov al, [_kernel_size]

int 13h

jc .retry

pop si
pop ax

ret 

.read_error:

mov si, _message_read_err
call puts

cli
hlt

times 510-($-$$) db 0

dw 0aa55h


*kernel.asm*

[org 0x7E00]
[bits 16]

jmp main

_message: db 'Hello from kernel!', 0

main:
mov si, _message
call puts

cli
hlt

puts:
mov ah, 0x0E
.begin:
lodsb
cmp al, 0
je .done
int 10h
jmp .begin
.done:
ret

*Makefile*

BUILD_DIR = build
SRC_DIR = src
IMG_NAME = dev

dev: image
qemu-system-i386 -fda $(BUILD_DIR)/$(IMG_NAME).img

image: $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin
@dd if=$(BUILD_DIR)/bootloader.bin of=$(BUILD_DIR)/$(IMG_NAME).img bs=512
@dd if=$(BUILD_DIR)/kernel.bin of=$(BUILD_DIR)/$(IMG_NAME).img bs=512 seek=1
@truncate -s 1440k $(BUILD_DIR)/$(IMG_NAME).img
@hexdump -C $(BUILD_DIR)/$(IMG_NAME).img
@objdump -b binary -m i8086 -M intel -D build/dev.img

clean:
rm -f $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin

$(BUILD_DIR)/%.bin: $(SRC_DIR)/%.asm
nasm $^ -f bin -o $@


# Specifics on Constants in bootloader.asm:

- _stack_ptr_addr: Set to 0x7b00 because it&#39;s 256 bytes under the bootloader. I believe this is a safe location since the stack grows downwards.
- _es_start: Set to 0x7c0 so the actual physical address (in real mode) equals 0x7c00 — the bootloader&#39;s load address. I offset by 0x200 when jumping to the kernel, which matches the kernel&#39;s load address.
[![Disassembly of the image](https://i.stack.imgur.com/xaNai.png)](https://i.stack.imgur.com/xaNai.png) for clarity shows that the first instruction appears at byte 0x200.

# The issue:
Upon booting, the message &quot;Booting SSoS...&quot; is displayed, but the kernel&#39;s &quot;Hello from kernel!&quot; message isn&#39;t.

# Observations &amp; Assumptions:

Someone also had the same problem [here](https://stackoverflow.com/questions/26567810/trouble-with-custom-oss-bootloader-not-jumping-to-kernel). I tried removing the `mov dl, 0` in the read_disk function but it still didin&#39;t work.
I suspect the issue might be in the read_disk function within the bootloader.
Even though no error message displays, the qemu-system-i386 command seems unusual.
Constants like _sectors_per_track and drive number (register dl in read_disk function) seem accurate. However, even when I attempted different disk-reading methods, the problem was still occurring.

# Seeking Help:
I know this is a complex and low-level topic which requires a deep understanding of operating systems. That&#39;s why I thank everyone who will try to help me. Thanks a lot!

I tried different methods of reading the floppy disk (like reading directly with CHS adressing) however none of them worked. I also double-checked my constants and looked on the disassebly of the image but everything seems to be right. I looked into others code but mine seems (almost except it&#39;s not that perfect) identical.

</details>


# 答案1
**得分**: 1

下面是您的代码的翻译部分:

1. 我在这里和那里添加了一些 `xor dx, dx`。
2. `mul word bx` 应该改成 `div word bx`。
3. 在 kernel.asm 中,将第一行更改回旧值 `0x7e00`。
4. `push` 和 `pop` 指令没有特殊作用。您可以将它们删除。
5. 我不确定我是否正确理解了 LBA 到 CHS 的转换。应该考虑什么?ax 或 dx 中的值?

Bootloader.asm

```assembly
[org 0x7c00]
[bits 16]

jmp main

_message_read_err:  db 'Error in reading floppy!', 0
_message_boot:      db 'Booting SSoS...', 0
_sectors_per_track: dw 18
_head_count:        dw 2
_kernel_start_LBA:  dw 1
_kernel_size:       dw 1
_stack_ptr_addr:    dw 0x7c00                                   ;prev value 0x7b00
_es_start:          dw 0x7c0

; 参数:
;     无
; 返回:
;     无
main:
    mov ax, 0 ; 不能直接写入 ds 和 ss
    mov ds, ax 
    mov ss, ax 
    mov sp, [_stack_ptr_addr] ; 栈指针

    mov ax, [_es_start] ; 地址 * 16 = 真实地址 - 0x7c0 * 16(十进制) = 0x7c00
    mov es, ax 

    mov si, _message_boot
    call puts

    ; 将 LBA 转换为 CHS
    ;; 参数

;(* 1 *)
    ;; 在调用 LBA_to_CHS 之前
    ;; ax = 0x07c0
    ;; bx = 未知值,可以是 16 位范围内的任何值(0x0000 - 0xFFFF)
    ;; cx = 同样的 cx,未初始化
    ;; dx = 同样的情况

    ;; 未知的值可能会导致意外的结果;)

    ;; 在 LBA_to_CHS 内部,您 push ax / push bx / mov bl, dl / push bx
    ;; 在从 LBA_to_CHS 返回之前,您弹出这些值
    ;; 所以在过程之后:

    ;; ax = 弹出的值 0x07c0,然后 al = _kernel_size = 1,ah = 0x07 在 read_disk 内部更改为 0x02
    ;; bx = 未知值,但后来设置为 0x0200
    ;; cx = ch - 柱面,cl - 扇区
    ;; dx = dh - 磁头,dl - 弹出的值,您确定这是驱动器号(0 = A :) 吗?

    mov si, 1               ;LBA = si = 1
    call LBA_to_CHS ; 调用后,CH、CL 和 DH 将具有柱面、扇区和磁头值

    mov bx, 0x200
    mov al, [_kernel_size] ; 要读取的扇区数
    call read_disk

    jmp 0:7e00h

; 参数:
;     si - 指向要打印的字符串
; 返回:
;     无
puts:
    push ax
.begin:

    mov ah, 0x0E
    lodsb

    cmp al, 0
    je .done

    int 10h
    jmp .begin
.done:

    pop ax

    ret

; 参数:
;     si - LBA 地址
; 返回:
;     ch, dh, cl - CHS 地址
;; dx 和 cx 将需要修改
LBA_to_CHS:

    push ax                           ; push ax 和 push bx 不会破坏任何必要的数据
    push bx

    mov bl, dl                        ; dl = 垃圾值

    push bx                           ; 删除这个

    ; C = LBA ÷ (HPC × SPT)
    mov ax, [_head_count]               ; ax = 2
    mul word [_sectors_per_track]         ; ax = 2 * 18 = 36

    mov bx, ax                        ; bx = 36
    mov ax, si                        ; ax = 1
    xor dx,dx                         ; dx = 0
;(* 2 *)
; 在这里,您进行了 mul word bx,所以我将其更改为 div word bx
; 我们除以 LBA mod(磁头数×扇区数),而不是相乘
    ;mul word bx
    ;应该是 div word bx
    div word bx                       ; ax = 0 dx = 1
    mov ch, al                        ; 将 ax 的低位作为柱面地址

;(* 3 *)
    ; 在 div 指令之前,我使用 xor dx, dx,因为在 16 位除法中,被除数在 dx:ax 中
    ; 所以我们必须清除 dx,否则我们的结果可能是错误的值
    ; H = (LBA ÷ SPT) mod HPC
    xor dx, dx                        ; dx = 0
    mov ax, si                        ; ax = 1
    div word [_sectors_per_track]     ; 18;结果在 dx:ax 中,ax = 0 dx = 1
    xor dx, dx
    div word [_head_count]            ; 2 ;结果也在 dx:ax 中(dx 是余数) ax = 0 dx = 0
    mov dh, dl                        ; 由于 dx 由 dh(高位)和 dl(低位)组成,我们要将低位移入 dh

    push dx                           ; 保存 dx
    ; S = (LBA mod SPT) + 1
    xor dx, dx                        ; dx = 0
    mov ax, si                        ; ax = 1
    div word [_sectors_per_track]     ; 18;余数在 dx 中,ax = 0 dx = 1
    inc dx                            ; dx = 2

    mov cl, dl

    pop dx

    pop bx                            ; 删除
    mov dl, bl                         ; dl = 垃圾
    pop bx                            ; 删除
    pop ax                            ; 删除

    ret

; 参数:
;    

<details>
<summary>英文:</summary>

Update:

Below is Your code:

1. I added few `xor dx,dx` here and there
2. `mul word bx` should be `div word bx`
3. Changed in kernel.asm first line to old value `0x7e00`. 
4. `push` and `pop` instructions do nothing special. You can remove them.
5. I don&#39;t know if I understand conversion LBA -&gt; CHS correctly.
   What take into account? Values from ax or dx?

  Source:&lt;br&gt;
  https://datacadamia.com/io/drive/lba &lt;br&gt;
  https://en.wikipedia.org/wiki/Logical_block_addressing

But code seems to work fine!

Result is:

[![enter image description here][1]][1]

Bootloader.asm

[org 0x7c00]
[bits 16]

jmp main

_message_read_err: db 'Error in reading floppy!', 0
_message_boot: db 'Booting SSoS...', 0
_sectors_per_track: dw 18
_head_count: dw 2
_kernel_start_LBA: dw 1
_kernel_size: dw 1
_stack_ptr_addr: dw 0x7c00 ;prev value 0x7b00
_es_start: dw 0x7c0

; Arguments:
; None
; Returns:
; None
main:
mov ax, 0 ; can't write to ds and ss directly
mov ds, ax
mov ss, ax
mov sp, [_stack_ptr_addr] ; stack pointer

mov ax, [_es_start] ; address * 16 = real address -&gt; 0x7c0 * 16 (dec) = 0x7c00
mov es, ax 

mov si, _message_boot
call puts

; Converting LBA to CHS
;; Parameters

;(* 1 *)
;; before call to LBA_to_CHS
;; ax = 0x07c0
;; bx = unknown value, could be anything from 16-bit range (0x0000 - 0xFFFF)
;; cx = same cx, not initialized
;; dx = same here

 ;; unknown values could cause unexpected results ;)
 
 ;; Inside LBA_to_CHS You push ax / push bx / mov bl, dl / push bx
 ;; and before return from LBA_to_CHS You pop those values
 ;; So after procedure:
 
 ;; ax = popped value 0x07c0, then al = _kernel_size = 1, ah = 0x07 changed inside read_disk to 0x02
 ;; bx = unknown value but set later to 0x0200
 ;; cx = ch - cylinder, cl - sector
 ;; dx = dh - head, dl - popped value, are you sure this is drive number (0 = A:)
 
mov si, 1               ;LBA = si = 1
call LBA_to_CHS ; after this call, CH, CL, and DH will have cylinder, sector, and head values

mov bx, 0x200
mov al, [_kernel_size] ; number of sectors to read
call read_disk

jmp 0:7e00h

; Arguments:
; si - Points to the string to be printed
; Returns:
; None
puts:
push ax
.begin:

mov ah, 0x0E
lodsb

cmp al, 0
je .done

int 10h
jmp .begin

.done:

pop ax

ret

; Arguments:
; si - LBA adress
; Returns:
; ch, dh, cl - CHS adress
;; dx and cx will have to be modified
LBA_to_CHS:

push ax                           ; push ax and push bx won&#39;t destroy any necessary data 
push bx                             

mov bl, dl                        ; dl = trash values

push bx                           ; delete this too

; C = LBA &#247; (HPC &#215; SPT)
mov ax, [_head_count]               ; ax = 2  
mul word [_sectors_per_track]         ; ax = 2 * 18 = 36

mov bx, ax                        ; bx = 36 
mov ax, si                        ; ax = 1  
xor dx,dx                         ; dx = 0

;(* 2 )
; here You have mul word bx so I changed this to div word bx
; we divide LBA mod (Heads * Sectors) not mul
;mul word bx
;should be div word bx
div word bx ; ax = 0 dx = 1
mov ch, al ; Put lower bits of ax as the cylinder address
;(
3 *)
; I used xor dx, dx before div instructions because in 16-bit division dividend is in dx:ax
; so we have to clear dx or our result maybe wrong value
; H = (LBA ÷ SPT) mod HPC
xor dx, dx ; dx = 0
mov ax, si ; ax = 1
div word [_sectors_per_track] ; 18 ; result in dx:ax, ax = 0 dx = 1
xor dx, dx
div word [_head_count] ; 2 ; result also in dx:ax (dx is remainder) ax = 0 dx = 0
mov dh, dl ; since dx is composed of dh (higher bits) and dl (lower bits) we want to move the lower bits into dh

push dx                           ; save dx
; S = (LBA mod SPT) + 1
xor dx, dx                        ; dx = 0   
mov ax, si                        ; ax = 1
div word [_sectors_per_track]     ; 18 ; remainder in dx, ax = 0 dx = 1 
inc dx                            ; dx = 2

mov cl, dl      

pop dx  

pop bx                            ; delete
mov dl, bl                         ; dl = trash
pop bx                            ; delete
pop ax                            ; delete

ret

; Arguments:
; bx - Address to load the data
; ch, cl, dh - CHS values
; Returns:
; None

read_disk:
push ax
push si

mov si, 4

.retry:
dec si

cmp si, 0
je .read_error

mov ah, 02h
mov al, [_kernel_size]

int 13h

jc .retry

pop si
pop ax

ret 

.read_error:

mov si, _message_read_err
call puts

cli
hlt

times 510-($-$$) db 0

dw 0aa55h


Kernel.asm

[org 0x7e00]
[bits 16]

jmp main

_message: db 'Hello from kernel!', 0

main:
mov si, _message
call puts

cli
hlt

puts:
mov ah, 0x0E
.begin:
lodsb
cmp al, 0
je .done
int 10h
jmp .begin
.done:
ret



  [1]: https://i.stack.imgur.com/q1p2S.png
  [2]: https://i.stack.imgur.com/D9AWu.png
  [3]: https://i.stack.imgur.com/4ME1N.png
  [4]: https://i.stack.imgur.com/2NQdK.png

</details>



huangapple
  • 本文由 发表于 2023年8月10日 22:33:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76876746.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定