学习《操作系统真相还原》三

本文介绍了从实模式进入保护模式的过程,包括开启A20地址线以允许访问更多内存,设置CR0寄存器的PE位以启用保护模式,以及加载全局描述符表GDT来实现更安全的内存访问。通过这些步骤,loader成功启动并在屏幕上打印出2LOADER,展示了基本的系统初始化技术。
摘要由CSDN通过智能技术生成

前言

我们开始实现了loader,从mbr跳转到loader。并执行的loader的代码。在屏幕上打印了"2 LOADER"。

1.进入保护模式

保护模式,顾名思义,就是为了让操作系统变的更加安全。相比实模式,在保护模式下也可以充分利用计算机资源。

1.打开A20地址线

IBM为了兼容以前的cpu,必须兼容实模式。在兼容实模式下情况,又要有保护模式。为了解决这个问题,IBM在键盘控制器上的一些输出线来控制第21根地址线。

  1. 如果A20Gate被打开,当访问到0x100000~0x10FFFEF之间的地址时,CPU将真正访问这块内存地址。

  2. 如果A20Gate被打开,当访问到0x100000~0x10FFFEF之间的地址时,CPU采用地址回绕

    in al,0x92
    or al,0000_0010B
    out 0x92,al

打开A20地址只有三行代码

2.打开CR0寄存器的PE位

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

代码比较简单,就只有三行。

3.加载全局描述符表GDT

这个是进入保护模式最麻烦的部分。前面说道,保护模式对于实模式来说更加安全。那么如何来保证一些关键的内存不能被用户随便访问了。那这个时候就需要GDT了。将内存映射到这个表里面。并对每一块内存进行说明。保护模式也不是直接影cs:ip来执行代码。而是有个新的玩意,叫选择子,顾名思义,选择子就是在GDT里面选择内存进行访问。这样有个好处,就是比直接用cs:ip进行访问更加安全。

   %include "boot.inc"
   section loader vstart=LOADER_BASE_ADDR
   LOADER_STACK_TOP equ LOADER_BASE_ADDR
   jmp loader_start                 ; 此处的物理地址是:
   
;构建gdt及其内部的描述符
   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        ;limit=(0xbffff-0xb8000)/4k=0x7
           dd    DESC_VIDEO_HIGH4  ; 此时dpl已改为0
​
   GDT_SIZE   equ   $ - GDT_BASE
   GDT_LIMIT   equ   GDT_SIZE - 1 
   times 60 dq 0                     ; 此处预留60个描述符的slot
   SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0         ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0
   SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0     ; 同上
   SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0    ; 同上 
​
   ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址
​
   gdt_ptr  dw  GDT_LIMIT 
        dd  GDT_BASE
   loadermsg db '2 loader in real.'
​
   loader_start:
​
;------------------------------------------------------------
;INT 0x10    功能号:0x13    功能描述:打印字符串
;------------------------------------------------------------
;输入:
;AH 子功能号=13H
;BH = 页码
;BL = 属性(若AL=00H或01H)
;CX=字符串长度
;(DH、DL)=坐标(行、列)
;ES:BP=字符串地址 
;AL=显示输出方式
;   0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
;   1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
;   2——字符串中含显示字符和显示属性。显示后,光标位置不变
;   3——字符串中含显示字符和显示属性。显示后,光标位置改变
;无返回值
   mov   sp, LOADER_BASE_ADDR
   mov   bp, loadermsg           ; ES:BP = 字符串地址
   mov   cx, 17          ; CX = 字符串长度
   mov   ax, 0x1301      ; AH = 13,  AL = 01h
   mov   bx, 0x001f      ; 页号为0(BH = 0) 蓝底粉红字(BL = 1fh)
   mov   dx, 0x1800      ;
   int   0x10                    ; 10h 号中断
​
;----------------------------------------   准备进入保护模式   ------------------------------------------
                                    ;1 打开A20
                                    ;2 加载gdt
                                    ;3 将cr0的pe位置1
​
​
   ;-----------------  打开A20  ----------------
   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         ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,
   jmp  SELECTOR_CODE:p_mode_start       ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,
                         ; 这将导致之前做的预测失效,从而起到了刷新的作用。
​
[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 $

这里的loader.s进行了修改。最后用远跳转刷新流水线,清空段描述符寄存器。

2.编译运行

调用info gdt查看是否建立成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值