前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >编程开启保护模式

编程开启保护模式

作者头像
shysh95
发布2021-08-06 14:42:46
4890
发布2021-08-06 14:42:46
举报
文章被收录于专栏:shysh95shysh95

Hi~朋友,码字不易,点点关注呗

摘要

  1. 编写代码进入保护模式
  2. 程序实现
  3. 程序运行

如何进入保护模式

我们的计算机启动时,首先BIOS会进行自检操作,在自检通过以后就需要将控制权交给MBR程序,在MBR程序中我们跳转到我们的OBR(内核加载器)中。

程序实现

首先我们需要准备三个文件:

  • boot.inc:宏文件,定义了一些常用标识符
  • mbr.asm:MBR程序
  • loader.asm:内核加载程序

boot.inc

代码语言:javascript
复制
;配置信息文件(loader和kernel)

LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2

;----------------全局描述符表属性----------------
; 定义段界限的单位为4k
DESC_G_4K equ 10000000_00000000_00000000b
; 定义指令中有效地址和操作数为32位
DESC_D_32 equ 1000000_00000000_00000000b
; 定义32位代码段
DESC_L equ 000000_00000000_00000000b
; 对于操作系统,CPU不会使用此位
DESC_AVL equ 00000_00000000_00000000b
; 代码段界限第2部分(16~19位)
DESC_LIMIT_CODE2 equ 1111_00000000_00000000b
; 数据段界限第2部分(16~19位)
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
; 显存段
DESC_LIMIT_VIDEO2 equ 0000_00000000_00000000b
; 段存在于内存中
DESC_P equ 10000000_00000000b
; 0特权级
DESC_DPL_0 equ 0000000_00000000b
; 1特权级
DESC_DPL_1 equ 0100000_00000000b
; 2特权级
DESC_DPL_2 equ 1000000_00000000b
; 3特权级
DESC_DPL_3 equ 1100000_00000000b
; 代码段
DESC_S_CODE equ 10000_00000000b
; 数据段
DESC_S_DATA equ DESC_S_CODE
; 系统段
DESC_S_SYS equ 00000_00000000b
; 代码段可执行,非一致性,不可读,已访问为清0
DESC_TYPE_CODE equ 1000_00000000b
; 数据段不可执行,向上扩展,可写,已访问为清0
DESC_TYPE_DATA equ 0010_00000000b

;定义代码段的高位4个字节,0x00 << 24代表24~31位的段基址,0x00代表0~7的段基址
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 +\
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 +\
DESC_S_CODE + DESC_TYPE_CODE + 0x00

;定义数据段的高位4个字节,0x00 << 24代表24~31位的段基址,0x00代表0~7的段基址
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + \
DESC_S_DATA + DESC_TYPE_DATA + 0x00

; 显存段
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + \
DESC_S_DATA + DESC_TYPE_DATA + 0x00

;----------段选择子------------
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b

loader.asm

代码语言:javascript
复制
%include "boot.inc"
SECTION loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start

; 构建全局描述符表和内部段描述符

GDT_BASE: dd 0x00000000
          dd 0x00000000

CODE_DESC: dd 0x0000FFFF
           dd DESC_CODE_HIGH4

DATA_STACK_DESC: dd 0x0000FFFF
                 dd DESC_DATA_HIGH4

VIDEO_DESC: dd 0x80000007;
            dd DESC_VIDEO_HIGH4

GDT_SIZE equ $ - GDT_BASE
; 获取GDT界限
GDT_LIMIT equ GDT_SIZE - 1

times 60 db 0

SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002 << 3) + TI_GDT+ RPL0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0

gdt_ptr dw GDT_LIMIT ;写入GDT界限
        dd GDT_BASE ;写入GDT起始内存地址

loadermsg db '2 loader in real.'

loader_start:

    mov sp, LOADER_BASE_ADDR
    mov bp, loadermsg
    mov cx, 17
    mov ax, 0x1301
    mov bx, 0x001f
    mov dx, 0x1800
    int 0x10

    ;准备进入保护模式

    ;1. 打开A20
    in al, 0x92
    or al, 0000_0010b
    out 0x92, al

    ;2. 加载GDT
    lgdt [gdt_ptr]

    ;3. cr0第0位置1

    mov eax, cr0
    or eax, 0x00000001
    mov cr0, eax

    jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线

[bits 32]
p_mode_start:
    mov ax, SELECTOR_DATA
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov esp, LOADER_STACK_TOP
    mov ax, SELECTOR_VIDEO
    mov gs, ax

    mov byte [gs:160], 'P'

    jmp $

mbr.asm

代码语言:javascript
复制
%include "boot.inc"
;主引导程序
SECTION MBR vstart=0x7c00
    ; 初始化寄存器
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    mov sp, 0x7c00
    ;0xb800是实模式文本模式显示适配器起始地址
    mov ax, 0xb800
    mov gs, ax

    ; AH = 0x06,上卷全部行,AL = 上卷的行数,如果为0,表示全部
    mov ax,0600h
    ; BH = 上卷行属性
    mov bx,0700h
    ; (CL, CH) = 窗口左上角的位置(x,y)
    mov cx,0
    ; (DL, DH) = 窗口右下角的位置(x,f)
    mov dx,184fh
    ; 中断清屏
    int 10h

    ; 输出字符串'1 MBR'
    mov byte [gs:0x00], '1'
    mov byte [gs:0x01], 0xA4; A表示绿色背景闪烁,4代表前景为红色

    mov byte [gs:0x02], ' '
    mov byte [gs:0x03], 0xA4

    mov byte [gs:0x04], 'M'
    mov byte [gs:0x05], 0xA4

    mov byte [gs:0x06], 'B'
    mov byte [gs:0x07], 0xA4

    mov byte [gs:0x07], 'R'
    mov byte [gs:0x08], 0xA4

    ; loder的扇区位置
    mov eax, LOADER_START_SECTOR
    ; 设置loder被加载到内存以后的地址
    mov bx, LOADER_BASE_ADDR
    ; 读取的扇区数,由于我们自己编写的loader不会超过512字节,因此设置为1
    mov cx, 1
    call rd_disk_m_16

    jmp LOADER_BASE_ADDR

    ;读取硬盘第n个扇区
    rd_disk_m_16:
        mov esi, eax ;备份eax
        mov di, cx ;备份cx

        ; 第一步
        ; 设置要读取的扇区数
        mov dx, 0x1f2 ;0x1f2是端口号
        mov al, cl
        out dx, al

        mov eax, esi ;恢复eax寄存器

        ; 第二步
        ; LBA地址0~7位写入端口0x1f3
        mov dx, 0x1f3
        out dx, al

        ; LBA地址8~15位写入0x1f4
        mov cl, 8
        shr eax, cl
        mov dx, 0x1f4
        out dx, al

        ; LBA地址16~23位写入0x1f5
        shr eax, cl
        mov dx, 0x1f5
        out dx, al

        ; device端口
        shr eax, cl
        and al, 0x0f ; lba第24~27位
        or al, 0xe0 ; 设置device高4位为1110,表示LBA模式
        mov dx, 0x1f6
        out dx, al

        ; 第三步,向0x1f7端口写入读命令0x20
        mov dx, 0x1f7
        mov al, 0x20
        out dx, al

        ; 第4步 检测硬盘状态
        .not_ready:
            nop
            in al, dx
            and al, 0x88 ; 第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
            cmp al, 0x08
            jnz .not_ready ; 如果没有准备好,继续等

        mov ax, di ;di为要读取的扇区数
        mov dx, 256 ; 扇区有512字节,每次读入一个字(实模式下2个字节),要读取的扇区数为1,1*(512/2),所以是di*256
        mul dx
        mov cx, ax
        mov dx, 0x1f0

        ; 循环读取数据
        .go_on_read:
            in ax, dx
            mov [bx], ax
            add bx, 2
            loop .go_on_read
            ret

    times 510 - ($-$$) db 0
    db 0x55, 0xaa

程序运行

代码语言:javascript
复制
cd boot
# 编译mbr和loader
nasm -I include -o loader.bin loader.asm
nasm -I include -o mbr.bin mbr.asm

# 创建虚拟镜像
qemu-img create -f raw vm1.raw 1G

# 写入Mbr
dd if=mbr.bin of=vm1.raw bs=512 count=1 conv=notrunc
# 写入loader
dd if=loader.bin of=vm1.raw bs=512 count=1 seek=2 conv=notrunc

# 启动程序
qemu-system-x86_64 vm1.raw 
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员修炼笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档