• OSLab:开启保护模式


    日期:2019/5/22

    关键词:操作系统;OS;保护模式;A20地址线激活;分页开启;二级页表的设置

    PS:OSLAB实验课的整理。

    本文主要内容是分析操作系统中一个简易的MBR。

    建议先阅读:https://www.cnblogs.com/sinkinben/p/10888599.html

    宏定义和数据定义

    %define CR0_PE      (1 << 0)

    %define CR0_MP      (1 << 1)

    %define CR0_EM      (1 << 2)

    %define CR0_TS      (1 << 3)

    %define CR0_WP      (1 << 16)

    %define CR0_PG      (1 << 31)

     

    %define PTE_PRESENT (1 << 0)

    %define PTE_WRITE (1 << 1)

    %define OUTTER_PGTBL 0x1000

    %define INNER_PGTBL 0x2000

     

    msg:db "Hello world from sinkinben", 0

    gdt:dw 0,0,0,0 ; dummy

     

        dw 0xFFFF ; limit=4GB

        dw 0x0000 ; base address=0

        dw 0x9A00 ; code read/exec

        dw 0x00CF ; granularity=4096,386

     

        dw 0xFFFF ; limit=4GB

        dw 0x0000 ; base address=0

        dw 0x9200 ; data read/write

        dw 0x00CF ; granularity=4096,386

     

        align 8

    gdt_desc:

        dw 23 ; gdt limit=sizeof(gdt) - 1

        dw gdt

     

        times 510-($-$$) db 0

        dw 0xAA55

    实模式:初始化

    设置段寄存器。

        org 0x7c00

        [bits 16]

        ; First, BIOS loads the bootsector into 0000:7C00.

        cli

        xor ax, ax

        mov ds, ax

        mov ss, ax

        mov sp, 0

    实模式:A20地址线激活

    参考:https://wenku.baidu.com/view/d6efe68fcc22bcd126ff0c00.html

    在8086/8088中,地址线20条(A0-A19),可寻址空间220bytes=1MB(可使用内存最大值)。但是寄存器是16位的(CPU可以"看到"216=64KB的内存),所以才引入了段寄存器,引入之后,寻址格式变为[ES:DI],计算方法是ES<<4+DI(这个值是20bit的)。

    在这种情况下,CPU可以"看到"的内存的最大地址是:[0xFFFF: 0xFFFF] = ‭0x10FFEF = 1087 KB‬ > 1024KB‬

    本来是为了让CPU能看到完整的1MB内存,但是现在却多出了[1024, 1087]这个区间的地址,如果程序访问了这个区间的地址,CPU该如何处理?

    答案是:当程序员给出超过1M(100000H-10FFEFH)的地址时,系统并不认为其访问越界而产生异常,而是自动从重新0开始计算。

    即:存在映射:1024=>0, 1025=>1以此类推。

    但是后来的80286,系统的地址总线发展为24根(A0-A23),这样能够访问的内存可以达到224=16M。

    这样使得80286芯片却存在一个问题:如果程序员访问100000H-10FFEFH之间的内存,系统将实际访问这块内存,而不是重新从0开始。

    这就导致一个问题:不能在80286上面运行8086/8088的程序。(不能向前兼容,当时80286性能再好也不会有人买)

    为了解决这个问题,IBM使用键盘控制器上剩余的一些输出线来管理第21根地址线,即A20 Gate。

    • 如果A20 Gate被打开,则当程序员给出100000H-10FFEFH之间的地址的时候,系统将真正访问这块内存区域;
    • 如果A20 Gate被禁止,则当程序员给出100000H-10FFEFH之间的地址的时候,系统仍然使用8086/8088的方式。

    从80286开始出现保护模式,那么A20 Gate就一定需要激活。

    如果A20 Gate被禁止(不论给出的地址中A20是什么,CPU将A20看作0处理):

    • 对于80286来说,其地址为24bit,其地址表示为EFFFFF;
    • 对于80386极其随后的32-bit芯片来说,其地址表示为FFEFFFFF。

    如果A20禁止,那么访问到的地址不是连续的。

    A20地址线的激活是通过一个叫8042的芯片完成的,其编程方法和8259中断控制器类似,但是比8259简单得多。

    主要步骤是:

    • 禁止中断(cli)
    • 等待输入缓冲(端口0x64)为空
    • 把0xd1写入端口0x64(0xd1表示下面要准备写数据到输出端口P2,IBM-PC使用P2的位2即P21来控制A20地址线)
    • 等待输入缓冲(端口0x64)为空
    • 把0xdf写入端口0x60。0xdf = 11011111, P21所在bit为1。(0bit是P25)

    汇编程序

        ; Enable A20

    wait_8042_1:

        in al, 0x64

        test al, 0x2

        jnz wait_8042_1

        mov al, 0xd1

        out 0x64, al

     

    wait_8042_2:

        in al, 0x64

        test al, 0x2

        jnz wait_8042_2

        mov al, 0xdf

        out 0x60, al

    实模式:保护模式开启

    开启保护模式的主要工作有:

    • 段寄存器初始化
    • GDT的初始化
    • CR0寄存器PE位的设置

    参考https://www.cnblogs.com/sinkinben/p/10888599.html

    开启保护模式代码

        ; Switch to protect mode

        lgdt [gdt_desc]

        mov eax, cr0

        or eax, CR0_PE

        mov cr0, eax

        jmp 0x08:start32

     

        [bits 32]

    start32:

        ; In protect mode

        cli

        mov ax, 0x10

        mov ds, ax

        mov es, ax

        mov ss, ax

        mov esp, 0x10000

    保护模式:开启分页

    开启分页的主要工作:

    • 二级页表初始化(包括PDE和PTE)
    • 保存页表基地址(CR3)
    • 设置CR0的PG位

    PTE表项的格式

    其中,31-12bit是该PTE项对应的物理页框号。

    其实PDE表项的格式与之类似,32-12bit是PDE项对应的PTE表的基地址。

    代码

        ; Initialize inner page table(页表)

        mov eax, PTE_WRITE|PTE_PRESENT ;PTE项的实际内容

        mov edi, INNER_PGTBL            ;PTE表在内存中的起始地址

        mov ecx, 1024

        cld

    init_pte:

        stosd ;eax => [edi]

        add eax, 4096 ;每个页面4KB大小,4096=1 0000 0000 0000

        loop init_pte ;loop 1024 times, 1024PTE

     

        ; Initialize outter page table

        ; 页目录项全部0,即PDE[i] = 0

        xor eax, eax

        mov edi, OUTTER_PGTBL

        mov ecx, 1024

        cld

        rep

        stosd

        ; 设置2PDE

        mov dword [OUTTER_PGTBL+0*4], INNER_PGTBL|PTE_WRITE|PTE_PRESENT ;PDE[0] -> PTE[0]

        mov dword [OUTTER_PGTBL+1*4], INNER_PGTBL|PTE_WRITE|PTE_PRESENT ;PDE[1] -> PTE[0]

        ; Load CR3

        mov eax, OUTTER_PGTBL ;页目录基地址

        mov cr3, eax

        ; Enable paging and write-protect

        mov eax, cr0

        and eax, ~(CR0_EM|CR0_TS)    ;EMTS位清零

        or eax, CR0_PG|CR0_WP|CR0_MP ;PGWPMP位置1

        mov cr0, eax

    保护模式:输出字符串

    完整代码

    %define CR0_PE      (1 << 0)

    %define CR0_MP      (1 << 1)

    %define CR0_EM      (1 << 2)

    %define CR0_TS      (1 << 3)

    %define CR0_WP      (1 << 16)

    %define CR0_PG      (1 << 31)

     

    %define PTE_PRESENT (1 << 0)

    %define PTE_WRITE (1 << 1)

    %define OUTTER_PGTBL 0x1000

    %define INNER_PGTBL 0x2000

     

        org 0x7c00

        [bits 16]

        ; First, BIOS loads the bootsector into 0000:7C00.

        cli

        xor ax, ax

        mov ds, ax

        mov ss, ax

        mov sp, 0

     

        ; Enable A20

    wait_8042_1:

        in al, 0x64

        test al, 0x2

        jnz wait_8042_1

        mov al, 0xd1

        out 0x64, al

     

    wait_8042_2:

        in al, 0x64

        test al, 0x2

        jnz wait_8042_2

        mov al, 0xdf

        out 0x60, al

     

        ; Switch to protect mode

        lgdt [gdt_desc]

        mov eax, cr0

        or eax, CR0_PE

        mov cr0, eax

        jmp 0x08:start32

     

        [bits 32]

    start32:

        ; In protect mode

        cli

        mov ax, 0x10

        mov ds, ax

        mov es, ax

        mov ss, ax

        mov esp, 0x10000

     

        ; Initialize inner page table

        mov eax, PTE_WRITE|PTE_PRESENT

        mov edi, INNER_PGTBL

        mov ecx, 1024

        cld

    init_pte:

        stosd

        add eax, 4096

        loop init_pte

     

        ; Initialize outter page table

        xor eax, eax

        mov edi, OUTTER_PGTBL

        mov ecx, 1024

        cld

        rep

        stosd

        mov dword [OUTTER_PGTBL+0*4], INNER_PGTBL|PTE_WRITE|PTE_PRESENT

        mov dword [OUTTER_PGTBL+1*4], INNER_PGTBL|PTE_WRITE|PTE_PRESENT

     

        ; Load CR3

        mov eax, OUTTER_PGTBL

        mov cr3, eax

     

        ; Enable paging and write-protect

        mov eax, cr0

        and eax, ~(CR0_EM|CR0_TS)

        or eax, CR0_PG|CR0_WP|CR0_MP

        mov cr0, eax

          

        mov esi, msg

        mov edi, 0xB8000

    label:

        mov al, [esi]

        mov ah, 0x0c

        mov [edi], ax

        add edi, 2

        inc esi

        cmp al, 0

        jne label

        jmp $

        align 8

    msg:db "Hello world from sinkinben", 0

    gdt:dw 0,0,0,0 ; dummy

     

        dw 0xFFFF ; limit=4GB

        dw 0x0000 ; base address=0

        dw 0x9A00 ; code read/exec

        dw 0x00CF ; granularity=4096,386

     

        dw 0xFFFF ; limit=4GB

        dw 0x0000 ; base address=0

        dw 0x9200 ; data read/write

        dw 0x00CF ; granularity=4096,386

     

        align 8

    gdt_desc:

        dw 23 ; gdt limit=sizeof(gdt) - 1

        dw gdt

     

        times 510-($-$$) db 0

        dw 0xAA55

    如何运行?

    QEMU硬盘启动:

    nasm -f bin -o paging-string.bin paging-string.asm

    dd if=paging-string.bin of=paging-string.img count=1 bs=512 conv=notrunc

    qemu-system-i386 paging-string.img

  • 相关阅读:
    数1的个数
    找水王2
    书店促销
    返回一个二维整数数组中最大联通子数组的和
    敏捷软件开发读书笔记(三)
    软件工程团队开发——第一次冲刺会议总结
    返回一个二维整数数组中最大联通子数组的和
    结对项目开发电梯调度
    《最后期限》——读书笔记03
    最后期限——阅读笔记2
  • 原文地址:https://www.cnblogs.com/sinkinben/p/10910790.html
Copyright © 2020-2023  润新知