向操作系统添加 32 位保护

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

最近在做操作系统,在制作GDT时遇到了一个大问题

内核代码(这就是我拼写内核的方式):

[org 0x0]
[bits 16]

jmp short start

%define ENDL 0x0D, 0x0A
kernal_LOAD_SEGMENT equ 0x2000

gdt_start:
    dq 0x0000000000000000

    dq 0x00CF9A000000FFFF
    dq 0x00CF92000000FFFF

    dq 0x00CF92000000FFFF

gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start + kernal_LOAD_SEGMENT * 16

print:
    push si
    push ax
    push bx

.loop:
    lodsb
    or al, al
    jz .done

    mov ah, 0x0E
    mov bh, 0
    int 0x10

    jmp .loop

.done:
    pop bx
    pop ax
    pop si    
    ret

start:
    mov si, start_sixteen
    call print

    in al, 0x64
.wait_ibe:
    test al, 0x02
    jnz .wait_ibe
    mov al, 0xD1
    out 0x64, al
.wait_ibe2:
    in al, 0x64
    test al, 0x02
    jnz .wait_ibe2
    mov al, 0xDF
    out 0x60, al

first_sixteen:
    cli
    lgdt [gdt_descriptor]

    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp dword 0x08:.afterstart

.afterstart:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    
[bits 32]
    VIDEO_MEMORY equ 0xb8000
    WHITE_ON_BLACK equ 0x0f

    mov ebx, start_thirty_two
    call print32

    jmp short start_prog

print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret

start_prog:
    mov al, 2
    mov eax, 1004
    mov [Xval], eax
    mov eax, 50
    mov [Yval], eax
    mov eax, 10
    mov [Wval], eax
    mov eax, 20
    mov [Hval], eax
    lea esi, [PySpr]
    
    call draw_spr

.halt:
    cli
    hlt

draw_spr:
    xor edx, edx
    mov ecx, [Hval]
draw_sprite:
    mov eax, [Yval]
    mov ebx, 1024
    mul ebx
    add eax, [Xval]
    mov edi, eax
    push ecx
    mov ecx, [Wval]
draw_pixels:
    mov al, [esi]
    cmp al, 5
    je skip_pixel_2
    stosd
    jmp skip_pixel
skip_pixel_2:
    inc edi
skip_pixel:
    inc esi
    loop draw_pixels
    pop ecx
    mov eax, [Yval]
    inc eax
    mov [Yval], eax
    loop draw_sprite
    ret

start_sixteen: db 'STAGE ONE START', ENDL, 0
start_thirty_two: db 'STAGE TWO START', ENDL, 0

Xval dd 0
Yval dd 0
Wval dd 0
Hval dd 0

PySpr db 5,5,5,2,2,2,2,5,5,5
      db 5,5,2,2,2,2,2,2,5,5
      db 5,2,2,2,2,2,2,2,2,5
      db 2,2,2,2,2,2,2,2,2,2
      db 5,8,8,8,8,8,8,8,8,5
      db 5,8,15,0,8,8,0,15,8,5
      db 5,8,8,8,8,8,8,8,8,5
      db 5,8,8,0,0,0,0,8,8,5
      db 5,5,8,8,8,8,8,8,5,5
      db 2,2,2,2,2,2,2,2,2,2
      db 2,2,2,2,2,2,2,2,2,2
      db 2,5,2,2,2,2,2,2,5,2
      db 2,5,2,2,2,2,2,2,5,2
      db 2,5,2,2,2,2,2,2,5,2
      db 8,5,2,2,2,2,2,2,5,8
      db 5,5,6,6,6,6,6,6,5,5
      db 5,5,6,6,5,5,6,6,5,5
      db 5,5,6,6,5,5,6,6,5,5
      db 5,5,6,6,5,5,6,6,5,5
      db 5,7,7,7,5,5,7,7,7,5

引导加载程序代码(基本上是第 3 部分的 Nanobyte 操作系统引导加载程序,但略有更改):

org 0x7C00
bits 16

%define ENDL 0x0D, 0x0A

jmp short start
nop

bdb_oem:                    db 'POVESK..'
bdb_bytes_per_sector:       dw 512
bdb_sectors_per_cluster:    db 1
bdb_reserved_sectors:       dw 1
bdb_fat_count:              db 2
bdb_dir_entries_count:      dw 0E0h
bdb_total_sectors:          dw 2880
bdb_media_descriptor_type:  db 0F0h
bdb_sectors_per_fat:        dw 9
bdb_sectors_per_track:      dw 18
bdb_heads:                  dw 2
bdb_hidden_sectors:         dd 0
bdb_large_sector_count:     dd 0

ebr_drive_number:           db 0
                            db 0
ebr_signature:              db 29h
ebr_volume_id:              db 12h, 34h, 56h, 78h
ebr_volume_label:           db 'POVESK.. OS'
ebr_system_id:              db 'FAT12   '

start:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    push es
    push word .after
    retf

.after:
    mov [ebr_drive_number], dl
    mov si, msg_loading
    call puts
    push es
    mov ah, 08h
    int 13h
    jc floppy_error
    pop es
    and cl, 0x3F
    xor ch, ch
    mov [bdb_sectors_per_track], cx
    inc dh
    mov [bdb_heads], dh
    mov ax, [bdb_sectors_per_fat]
    mov bl, [bdb_fat_count]
    xor bh, bh
    mul bx
    add ax, [bdb_reserved_sectors]
    push ax
    mov ax, [bdb_dir_entries_count]
    shl ax, 5
    xor dx, dx
    div word [bdb_bytes_per_sector]
    test dx, dx
    jz .root_dir_after
    inc ax

.root_dir_after:
    mov cl, al
    pop ax
    mov dl, [ebr_drive_number]
    mov bx, buffer
    call disk_read
    xor bx, bx
    mov di, buffer

.search_kernal:
    mov si, file_kernal_bin
    mov cx, 11
    push di
    repe cmpsb
    pop di
    je .found_kernal
    add di, 32
    inc bx
    cmp bx, [bdb_dir_entries_count]
    jl .search_kernal
    jmp kernal_not_found_error

.found_kernal:
    mov ax, [di + 26]
    mov [kernal_cluster], ax
    mov ax, [bdb_reserved_sectors]
    mov bx, buffer
    mov cl, [bdb_sectors_per_fat]
    mov dl, [ebr_drive_number]
    call disk_read
    mov bx, kernal_LOAD_SEGMENT
    mov es, bx
    mov bx, kernal_LOAD_OFFSET

.load_kernal_loop:
    mov ax, [kernal_cluster]
    add ax, 31
    mov cl, 1
    mov dl, [ebr_drive_number]
    call disk_read
    add bx, [bdb_bytes_per_sector]
    mov ax, [kernal_cluster]
    mov cx, 3
    mul cx
    mov cx, 2
    div cx
    mov si, buffer
    add si, ax
    mov ax, [ds:si]
    or dx, dx
    jz .even

.odd:
    shr ax, 4
    jmp .next_cluster_after

.even:
    and ax, 0x0FFF

.next_cluster_after:
    cmp ax, 0x0FF8
    jae .read_finish
    mov [kernal_cluster], ax
    jmp .load_kernal_loop

.read_finish:
    mov dl, [ebr_drive_number]
    mov ax, kernal_LOAD_SEGMENT
    mov ds, ax
    mov es, ax
    jmp kernal_LOAD_SEGMENT:kernal_LOAD_OFFSET
    jmp wait_key_and_reboot
    cli
    hlt

floppy_error:
    mov si, msg_read_failed
    call puts
    jmp wait_key_and_reboot

kernal_not_found_error:
    mov si, msg_kernal_not_found
    call puts
    jmp wait_key_and_reboot

wait_key_and_reboot:
    xor ah, ah
    int 16h
    jmp 0FFFFh:0

.halt:
    cli
    hlt

puts:
    push si
    push ax
    push bx

.loop:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    xor bh, bh
    int 0x10
    jmp .loop

.done:
    pop bx
    pop ax
    pop si    
    ret

lba_to_chs:
    push ax
    push dx
    xor dx, dx
    div word [bdb_sectors_per_track]
    inc dx
    mov cx, dx
    xor dx, dx
    div word [bdb_heads]
    mov dh, dl
    mov ch, al
    shl ah, 6
    or cl, ah
    pop ax
    mov dl, al
    pop ax
    ret

disk_read:
    push ax
    push bx
    push cx
    push dx
    push di
    push cx
    call lba_to_chs
    pop ax
    mov ah, 02h
    mov di, 3

.retry:
    pusha
    stc
    int 13h
    jnc .done
    popa
    call disk_reset
    dec di
    test di, di
    jnz .retry

.fail:
    jmp floppy_error

.done:
    popa
    pop di
    pop dx
    pop cx
    pop bx
    pop ax
    ret

disk_reset:
    pusha
    xor ah, ah
    stc
    int 13h
    jc floppy_error
    popa
    ret

msg_loading:            db 'BOOT START', ENDL, 0
msg_read_failed:        db 'FAIL READ DISK', ENDL, 0
msg_kernal_not_found:   db 'KERNAL NOT FOUND', ENDL, 0
file_kernal_bin:        db 'KERNAL  BIN'
kernal_cluster:         dw 0

kernal_LOAD_SEGMENT     equ 0x2000
kernal_LOAD_OFFSET      equ 0

times 510-($-$$) db 0
dw 0AA55h

buffer:

问题出在内核代码上。我做了一些调试,并确定这可能是因为三重错误(chatGPT 得出的结论),并且它通常发生在 jmp dword 0x08:.afterstart 中,但奇怪的是,在多次尝试解决此问题后(找到许多教程和项目以及将它们修复到我的代码中)三重错误出现在许多不同的代码行中。我在这个话题上非常缺乏经验,所以我可能真的很愚蠢并且错过了一些明显的东西。

如果有人有解决方案请告诉我。预先感谢!

jmp dword 0x08:.afterstart

 中的三重故障,并已开始出现在许多其他线路中。

构建命令:

--WinCMD NASM nasm scr/boot.asm -f bin -o build/boot.bin nasm scr/kernal.asm -f bin -o build/kernal.bin --WSL dd if=/dev/zero of=v_004.img bs=512 count=2880 mkfs.fat -F 12 -n NBOS v_004.img dd if=boot.bin of=v_004.img conv=notrunc mcopy -i v_004.img kernal.bin ::kernal.bin --QEMUCMD qemu-system-x86_64 v_004.img
    
assembly x86 operating-system nasm
1个回答
0
投票
ecm 指出了

gdt_start

 需要 
+ kernal_LOAD_SEGMENT * 16
 的错误,但这同样适用于 
kernal.asm
 中在 32 位模式下使用的每个标签。

从 16 位引导加载程序中,您在线性地址 0x20000 处加载内核并远跳转到 0x2000:0x0000,因此在进入内核时 CS = 0x2000。 通过文件顶部的

org 0

,汇编器计算从地址 0 开始的所有标签的地址(偏移量)。当 CS = 0x2000 时,这在 16 位模式下是正确的,但一旦处于 32 位模式下,就会出现错误。位模式,CS 是基数 = 0 的段。

所以第一个问题出现在

jmp dword 0x08:.afterstart

。  汇编器给
.afterstart
的地址为0x6E,相对于文件开头和16位段0x2000来说,它是正确的,但相对于基地址为0的32位段0x08来说,肯定不正确。实际代码at 
.afterstart
 位于线性地址 0x2006E,但您正在跳转到线性地址0x0006E,它位于 16 位中断向量表中的某个位置,其字节肯定不是任何有用的代码。  之后的某个时候,当这些字节解码为出错的指令时,您会崩溃(导致三重错误,因为您没有安装异常处理程序)。

您可以像以前一样通过将

.afterstart

 替换为 
.afterstart + kernal_LOAD_SEGMENT * 16
 来修复此问题,但您必须对 32 位模式下使用的所有其他标签执行相同的操作:
start_thirty_two
Xval
Yval
 等等等等

因此,更好的解决方案可能是将内核加载到低 64 KB 内存中,并使用 0 段远跳转到它。例如,将其加载到 0x0000:0x8000(线性地址 0x8000),进行远跳转从引导加载程序到 0x0000:0x8000(以便 CS = 0),然后将

org 0x8000

 放在文件的顶部。  然后,文件中的每个标签都具有正确的地址,无论是解释为 16 位段 0x0000 的偏移量,还是基地址为 0 的 32 位段的偏移量。只要您的内核适合 32 KB,就可以了;一旦超过这个范围,您可能在任何情况下都需要一个多阶段引导加载程序。

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