从实模式跳入保护模式0

编译器:nasm
虚拟机:bochs
操作系统:macosx


两个汇编程序boot.s和init.s

boot.s模拟引导程序,写入启动盘的0磁道,1扇区(512字节)中,在操作系统启动时,被bios加载进0x7c00处,并从这开始执行,它负责把init.s读入内存0x10000处,然后拷贝到0x0处,到这为止操作系统都运行在实模式下
(实模式的寻址方式是段地址<<4 + 偏移地址,如段基址0x7c0,偏移地址0x03,则实际地址是0x7c03。)然后准备进入保护模式,设置gdt、idt表,通过设置cr0寄存器的pe位,进入保护模式,然后通过jmp 段描述符:0跳入操作系统内核执行。

init.s就模拟内核程序,boot.s把它拷贝到0x0处,进入保护模式,然后从0x0开始执行。

这次的init.s就写一个不断在屏幕上打印字符’A’的程序。

其实从0x0开始运行时是内核态,这样写比较简单,没有涉及到任务,用户态。

init.s引导程序:

BOOTSEG equ 0x07c0
SYSSEG equ 0x1000
SYSLEN equ 3

section .text vstart=0

jmp BOOTSEG:start

start:
    mov ax, cs
    mov ss, ax
    mov ds, ax

    ;读磁盘
    mov ax, 0x0200+SYSLEN
        mov cx, 0x0002
        mov dx, 0x0000
        mov bx, SYSSEG
        mov es, bx
        xor bx, bx
        int 0x13

        jnc ok_load

ok_load:
    ;移动到内存0x0处(原地址ds:si,目的地址es:di)
    cli
    mov ax, SYSSEG
    mov ds, ax
    xor ax, ax
    mov es, ax
    mov cx, SYSLEN*128
    sub si, si
    sub di, di
    rep movsw

    ;加载idt和gdt基地址寄存器idtr和ldtr
    mov ax, BOOTSEG
    mov ds, ax
    lidt [idt_48]
    lgdt [gdt_48]

    ;设置cr0寄存器到pe位,进入保护模式
    mov ax, 0x0001
    lmsw ax
    jmp 8:0

gdt:
    dw 0,0,0,0

    dw 0x07ff ;代码段
    dw 0x0000
    dw 0x9a00
    dw 0x00c0

    dw 0x07ff ;数据段
    dw 0x0000
    dw 0x9200
    dw 0x00c0

idt_48:
    dw 0
    dw 0,0

gdt_48:
    dw 0x7ff
    dw 0x7c00+gdt,0

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

模拟内核代码init.s,重新设置了gdt表,新增了一个显示内存段,就是0xb8000开始的显存,通过在这里写入字符,直接显示在屏幕上:

[BITS 32]

SCRN_SEL equ 0x18

section .text vstart=0

;现在是保护模式
;
;重新定义gdt
;除了代码段、数据段再定义一个显示内存段(0xb8000开始的显存)
;
;在内核代码段中执行不断打印字母'A'的程序

start_up:
    mov eax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    lss esp, [init_stack]   

    lgdt [lgdt_opcode]  

f:  mov al, 'A'
    call print
    jmp f

print:
    ;打印字母al中的字母
    mov ebx, SCRN_SEL
    mov gs, bx
    mov ebx, [scr_loc]
    shl ebx, 1
    mov byte [gs:ebx], al
    mov byte [gs:ebx+1], 07
    shr ebx, 1
    inc ebx
    cmp ebx, 2000
    jb l
    mov ebx, 0
l:  mov [scr_loc], ebx
    ret

scr_loc dd 0 ; 如果dd大于2000就复位为0,因为屏幕是25*80=2000,一屏最多显示2000个字符

lgdt_opcode:
    dw (end_gdt-gdt)-1
    dw gdt, 0

;gdt:   dq 0x0000_0000_0000_0000 ;gdt表。第一项不用
;   dq 0x00c0_9a00_0000_07ff ;内核代码段。其选择符是0x08
;   dq 0x00c0_9200_0000_07ff ;内核数据段。其选择符是0x10
;   dq 0x00c0_920b_8000_0002 ;显示内存段。其选择符是0x18

gdt:    dw 0,0,0,0
    ;dq 0x0000000000000000 why not work?    

    dw 0x07ff ;代码段
    dw 0x0000
    dw 0x9a00
    dw 0x00c0

    dw 0x07ff ;数据段
    dw 0x0000
    dw 0x9200
    dw 0x00c0

    dd 0x80000002
    dd 0x00c0920b   

end_gdt:
    times 128 dd 0        ;内核堆栈段
init_stack:
    dd init_stack         ; esp
    dw 0x10           ; ss

这里写图片描述

github real2protect0源码

参考:赵炯《Linux内核完全剖析》

猜你喜欢

转载自blog.csdn.net/familyshizhouna/article/details/81120145