从真实模式切换到保护模式后跳远

时间:2017-02-27 10:12:30

标签: c assembly operating-system intel bootloader

根据这个tutorial,创建一个简单的操作系统就足够了,切换到保护模式就像下面的代码一样简单,而不需要其他众所周知的操作,例如启用A20 ...

无论如何,我是这个领域的新手,我编写了以下代码,因为他们提到的修改完全受到SO的启发。

代码结构: 这个简单的操作系统应该按如下方式加载:

  1. 加载/读取15个扇区
  2. 启用GDT
  3. 切换到保护模式(并打印“成功登陆32位保护模式”)。
  4. 加载内核并打印“X”
  5. 然而,模拟器仍在重启。请附上整个代码。

    bootloader.asm

    [bits 16]
    [org 0x7C00]
    
    KERNEL_OFFSET equ 0x1000
    
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov [BOOT_DRIVE], dl
    mov ax, 0x07E0                  ; End of stack
    cli
    mov ss, ax
    mov sp, 0x1200                  ; Size of Stack. By this, we assume that stack starts at 9000h
                                ; of size 1200h and ends at 7E00h to avoid being overwritten.
    sti
    
    call    load_kernel
    call    switch_to_pm
    
    jmp $
    
    %include "src/functions/disk_load.asm"
    
    load_kernel:
        mov bx, KERNEL_OFFSET   
        mov dh, 15
        mov dl, [BOOT_DRIVE]
        call    disk_load
    
        ret
    
    ; Global variables
    BOOT_DRIVE  db 0
    SECTORS     db 0
    MSG_PROT_MODE   db "Successfully landed in 32-bit Protected Mode" , 0
    
    %include "src/functions/gdt.asm"
    %include "src/functions/switch_to_pm.asm"
    
    [ bits 32]
    ; This is where we arrive after switching to and initialising protected mode.
    BEGIN_PM:
        mov ebx , MSG_PROT_MODE
        call    print_string_pm     ; Use our 32 - bit print routine.
    
    
        ;call   KERNEL_OFFSET       ; Now jump to the address of our loaded
                        ; kernel code , assume the brace position ,
                        ; and cross your fingers. Here we go !
    
        jmp $           ; Hang.
    
    %include "src/functions/writing_video_mode.asm"
    
    ; Bootsector padding
    times 510-($-$$) db 0
    dw 0xAA55
    
    ; 15 sector padding
    times 15*256 dw 0xDADA
    

    disk_load.asm

    disk_load:
        mov [SECTORS], dh
        mov ch, 0x00            ;C=0
        mov dh, 0x00            ;H=0
        mov cl, 0x02            ;S=2
    
    next_group:
        mov di, 5                ;Max 5 tries
    
    again: 
        mov ah, 0x02            ;Read sectors
        mov al, [SECTORS]
        int 0x13
        jc   maybe_retry
        sub [SECTORS], al ;Remaining sectors
        jz  ready
        mov cl, 0x01            ;Always sector 1
        xor dh, 1                ;Next head on diskette!
        jnz next_group
        inc ch                      ;Next cylinder
        jmp next_group
    
    maybe_retry:
        mov ah, 0x00            ;Reset diskdrive
        int 0x13
        dec di
        jnz again
        jmp disk_error
    
    ready:
        ret
    
    disk_error:
        mov ah, 0x0e
        mov al, 'Y'
        int 0x10
        jmp $
    
    DISK_ERROR_MSG db "Disk read error!", 0
    

    gdt.asm

    gdt_start:
    
        gdt_null:
            dd 0x0 ; ’ dd ’ means define double word ( i.e. 4 bytes )
            dd 0x0
    
        gdt_code:
            dw 0xffff
            dw 0x0
            db 0x0
            db 10011010b ; 1 st flags , type flags
            db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
            db 0x0
    
        gdt_data:
            dw 0xffff
            dw 0x0
            db 0x0
            db 10010010b ; 1 st flags , type flags
            db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
            db 0x0
    
        gdt_end:
    
        gdt_descriptor:
            dw gdt_end - gdt_start - 1
            dd gdt_start
    
    CODE_SEG equ gdt_code - gdt_start
    DATA_SEG equ gdt_data - gdt_start
    

    switch_to_pm.asm

    [ bits 16 ]
    
    switch_to_pm:
        cli
        lgdt [ gdt_descriptor ]
        mov eax , cr0
        or eax , 0x1
        mov cr0 , eax
        jmp CODE_SEG:init_pm
    
    [ bits 32 ]
    
    init_pm:
        mov ax, DATA_SEG
        mov ds, ax
        mov ss, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        mov ebp , 0x90000
        mov esp , ebp
        call BEGIN_PM
    

    为了确保我们落入保护模式:

    writing_video_mode.asm

    [ bits 32]
    
    VIDEO_MEMORY equ 0xb8000
    WHITE_ON_BLACK equ 0x0f
    
    print_string_pm:
        push eax
        push ebx
        push edx
        mov edx , VIDEO_MEMORY ; Set edx to the start of vid mem.
    
        print_string_pm_loop:
            mov al, [ebx]
            mov ah, WHITE_ON_BLACK
            cmp al, 0
            je print_string_pm_done
            mov [edx], ax
            add ebx, 1
            add edx, 2
            jmp print_string_pm_loop
    
        print_string_pm_done:
            pop edx 
            pop ebx 
            pop eax 
            ret
    

    kernel.c

    void main () {
        char * video_memory = (char *) 0xb8000;
        *video_memory = 'X';
    }
    

    顺便说一下,我正在使用 Makefile

    all: bootloader.bin kernel.bin
    
    bootloader.bin: src/bootloader.asm
        nasm src/bootloader.asm -f bin -o output/bootloader.bin
    
    kernel.o: src/kernel/kernel.c
        gcc -ffreestanding -c src/kernel/kernel.c -o output/kernel.o -m32
    
    kernel.bin: kernel.o
        ld -o output/kernel.bin -Ttext 0x1000 --oformat binary output/kernel.o -melf_i386
    
    clean:
        rm -f output/*.* output/*
    

    为了将其移入闪存,我使用以下命令:

    cat output/bootloader.bin output/kernel.bin > os-image
    sudo dd if=os-image of=/dev/sdb bs=512 conv=notrunc && sync
    

    为了运行它,我使用qemu和这个命令:

    qemu-system-i386 -hda /dev/sdb
    

    注意/ dev / sdb是我的闪存驱动器。

    问题: 实际上,只有在call KERNEL_OFFSET中禁用/评论bootloader.asm时,代码才会进入受保护模式(即打印“成功登陆32位保护模式”)。但是,启用此行时,它将开始启动并重新启动。

    我希望我提供了所有必要的信息。对我来说,远程跳跃不应该这样做。任何评论都表示赞赏。

1 个答案:

答案 0 :(得分:0)

只需删除

  

次15 * 256 dw 0xDADA

(顺便问一下,为什么DADA?)
然后编译你的内核

  

cat output / bootloader.bin output / kernel.bin> OS-图像

以某种方式使你的os图像长8192字节(16个扇区,bootloader + 15)。我不是Linux / Unix粉丝(甚至不能使用它们),但我认为 dd 命令(类似 dd if = dev \ zero of = temp_file count = (8192 - 文件实际大小,然后 cat os-image temp-file> os-image )应该完成这项工作。我也不确定这个编译命令是否正确(只是不确定)。我会删除" -melf_i386"从链接器命令,但idk,我只在Windows上使用MinGW(它只与GCC类似)。

抱歉我的英语不好,我希望能帮到你。