保护模式主要是为了防止用户程序故意使坏.而且用户所引用的地址都是指向真实的物理地址.跟内核属于同特权级.不利于安全.另外保护模式将是32/64位.大家所说的实模式一般指的是32位的cpu在16位模式下的状态.不是指的是16位cpu.本来没有实模式这个说法.不过后来出现保护模式.于是就出现了实模式说法
显示效果如图
保护模式下的改变
通用寄存器扩展为32位
段寄存器保存的不再是段基址,而是段选择子(段描述符的索引)
段基址保存在段描述符中,而段描述符保存在全局描述符表(GDT)中. 而全局描述符表保存在专门的寄存器gdtr,加载时用lgdt指令进行加载
全局描述符表(本简单kernel没用局部描述符表)所存储的描述符格式
之所以如此..混乱.完全是由于历史兼容原因
再来看看段选择子(保存在段寄存器中)的定义
保护模式下的选址方式发生了改变
一开始从段寄存器获取段选择子.通过段选择子的索引位*8(乘以8是因为.一个段描述符是8个字节)+gdtr保存的全局描述符表基址. 得到段基址
然后加上偏移寄存器.得到实际地址
如何进入保护模式:
1.打开A20(历史遗留问题.第21根地址线)
2.加载dgt
3.将cr0的pe位定义为1(表示进入保护模式)
-------------------------配置文件boot.inc的定义----------------
- ;------------- LOADER和KERNEL ----------
- loader_base_addr equ 0X900 ;加载位置
- loader_stack_top equ loader_base_addr ;栈指向的位置
- loader_start_sector equ 0X2 ;loader存储到第二个扇区
- ;-------------- GDT描述符属性 -----------
- desc_g_4k equ 1_00000000000000000000000B ;描述符的g位为4k粒度
- desc_d_32 equ 1_0000000000000000000000B ;表示是32位还是16位
- desc_l equ 0_000000000000000000000B ; 64位代码标记,此处标记为0便可。
- desc_avl equ 0_00000000000000000000B ; CPU不用此位,暂置为0
- desc_limit_code2 equ 1111_0000000000000000B ;段的第二部分界限
- desc_limit_data2 equ desc_limit_code2
- desc_limit_video2 equ 0000_000000000000000B ;
- desc_p equ 1_000000000000000B
- desc_dpl_0 equ 00_0000000000000B ;段描述符特权级
- desc_dpl_1 equ 01_0000000000000B
- desc_dpl_2 equ 10_0000000000000B
- desc_dpl_3 equ 11_0000000000000B
- desc_s_code equ 1_000000000000B
- desc_s_data equ desc_s_code
- desc_s_SYS equ 0_000000000000B
- desc_type_code equ 1000_00000000B ;X=1,C=0,R=0,A=0 代码段是可执行的,非依从的,不可读的,已访问位A清0.
- desc_type_data equ 0010_00000000B ;X=0,E=0,W=1,A=0 数据段是不可执行的,向上扩展的,可写的,已访问位A清0.
- ;段的24位
- 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
- 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 + 0X0B
- ;-------------- 选择子属性 ---------------
- rpl0 equ 00B
- rpl1 equ 01B
- rpl2 equ 10B
- rpl3 equ 11B
- ti_gdt equ 000B
- ti_ldt equ 100B
- ;------------- PROGRAM TYPE 定义 --------------
- pt_null equ 0
- %include "boot.inc" ;保存有关加载程序位置.源跟目的
- section mbr vstart=0x7c00
- jmp near start
- message db 'ZYW_OS start'
- start:
- ;ds:si指向数据源.es:di指向显存.
- mov sp,0x7c00 ;栈顶,栈成长的方向向上
- mov ax,0xb800
- mov es,ax ;es指向显存位置
- ; 清屏(摘自百度)
- ;利用0x06号功能,上卷全部行,则可清屏。
- ; -----------------------------------------------------------
- ;INT 0x10 功能号:0x06 功能描述:上卷窗口
- ;------------------------------------------------------
- ;输入:
- ;AH 功能号= 0x06
- ;AL = 上卷的行数(如果为0,表示全部)
- ;BH = 上卷行属性
- ;(CL,CH) = 窗口左上角的(X,Y)位置
- ;(DL,DH) = 窗口右下角的(X,Y)位置
- mov ax,0x0600
- mov bx,0x0700
- mov cx,0 ; 左上角: (0, 0)
- mov dx,0x184f ; 右下角(80,25),
- int 10h
- mov si,message
- mov di,0
- mov cx,start-message
- zyw:
- movsb
- mov byte [es:di],0xA4 ;控制背景跟颜色
- inc di
- loop zyw
- mov eax,loader_start_sector ;eax保存加载程序的位置
- mov bx,loader_base_addr ;bx保存加载到哪个区域
- mov cx,4 ;cx保存加载几个扇区(从eax开始)
- call read_disk
- jmp loader_base_addr
- read_disk:
- push eax
- mov dx,0x1f2 ;硬盘端口0x1f2 ,设置要读取多少数据
- mov al,cl ;这里表示读取一个
- out dx,al
- pop eax
- ;将lba地址存入0x1f3-0x1f6处
- mov dx,0x1f3
- out dx,al
- push cx
- mov cl,8
- shr eax,cl
- mov dx,0x1f4
- out dx,al
- shr eax,cl
- mov dx,0x1f5
- out dx,al
- shr eax,cl
- and al,0x0f
- or al,0xe0
- mov dx,0x1f6
- out dx,al
- pop cx
- ;向0x1f7写入命令
- mov dx,0x1f7
- mov al,0x20
- out dx,al
- noready:
- nop
- in al,dx
- and al,0x88
- cmp al,0x08
- jnz noready
- ;开始从0x1f0端口读取数据
- mov ax,cx
- mov dx,256
- mul dx
- mov cx,ax
- mov dx,0x1f0
- readdata:
- in ax,dx
- mov [bx],ax
- add bx,2
- loop readdata
- ret
- times 510-($-$$) db 0
- db 0x55,0xaa
-----------------loader的定义-------------------------------
- %include "boot.inc"
- section loader vstart=loader_base_addr
- jmp start
- ;------------全局描述符表的定义
- gdt_base:
- dd 0x00000000 ;全局描述表.第一个描述符要为空
- dd 0x00000000
- code_base:
- dd 0x0000FFFF
- dd desc_code_high4
- data_stack_desc:
- dd 0x0000ffff
- dd desc_data_high4
- video_desc:
- dd 0x80000007 ;段界限 limit=0x7fff.基址位于0xb8000
- dd desc_video_high4
- ;---------GDT的属性
- gdt_size equ $-gdt_base ;GDT大小
- gdt_limit equ gdt_size ;GDT限制
- time 60 dq 0 ;预留60个描述符的空位置
- ;----------定义段选择子-------------
- selector_code equ (0x0001<<3)+ti_gdt+rpl0
- selector_data equ (0x0002<<3)+ti_gdt+rpl0
- selector_video eque(0x0003<<3)+ti_gdt+rpl0
- ;定义gdt的指针,前2个字节为gdt界限,后4个字节为gdt起始地址
- gdt_ptr dw gdt_limit
- dd gdt_base
- ;-----进入保护模式----------
- in al,0x92
- or al,0000_0010B
- out 0x92,al
- ;----------加载GDT----------
- lgdt [gdt_ptr]
- ;---------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 $
<bochs:3> creg
CR0=0x60000011: pg CD NW ac wp ne ET ts em mp PE
从上可以看出 PE位已经置为1了
最后loader.S定义了
- mov byte [gs:160],'P'
最终显示效果.如果成功进入了保护模式.就会出现P这个字符