《操作系统真象还原》学习——保护模式入门

理论

  • 保护模式:实模式下存在一些限制/缺陷,如用户程序和操作系统程序没有分离,地址总线有限等。

  • 平坦模式:无需段基址,一个寄存器就可以访问所有内存,这是因为一个寄存器的位数与地址总线位数相等,就不需要用段基址+偏移量方式寻找内存了。只在保护模式下使用。

  • 在16位的实模式下,CPU照样可以处理32位的数据,反之亦然,操作数反转前缀和寻址方式反转前缀可以帮忙做到这点。

  • GDT,选择子,段描述符之间的关系:

  • 段描述符:在分段内存管理中,描述符是一个数据结构,用于定义内存段的属性,如段的基地址、段的长度、访问权限等。

    假设我们有一个段描述符,其二进制表示如下:

    1
    2
    3
    4
    | 31-24   | 23-20 | 19-16 | 15-8    | 7-0     |
    |---------|-------|-------|---------|---------|
    | 基址高  | 标志  | 限制高| 访问权限| 限制低  |
    | 00000000| 1100  | 1111  | 10011010| 11111111|
    

    在这个例子中:

    • 基址高(Base Address High)00000000。这是段基址的高8位。
    • 标志位(Flags)1100。包含G、D/B、L、AVL位。比如,1 在 G 位表示段限制以4KB为单位。
    • 限制高(Segment Limit High)1111。这是段限制的高4位。
    • 访问权限(Access Rights)10011010。这包括段的类型、是否可读写、是否可执行等。
    • 限制低(Segment Limit Low)11111111。这是段限制的低8位。

    解释:

    • 段限制:20位长,由 1111 11111111 组成,表示段的大小或长度。
    • 基址:假设这里只展示了基址的高8位,实际上基址还包括更多位。
    • 标志位:G位(1表示4KB为单位)、D/B位、L位和AVL位。
    • 访问权限:包含了段的类型、DPL(Descriptor Privilege Level)、P位(Present bit)等。

    这个结构允许操作系统以灵活的方式管理内存,为不同类型的段提供不同的保护和特性。在实际的系统中,这些描述符会被加载到特定的硬件寄存器中,如全局描述符表寄存器(GDTR)或局部描述符表寄存器(LDTR),以供CPU在内存访问时参考。

1
2
3
4
5
6
7
8
9
10
31                      16 15              0
+-----------------------+-------------------+
| Segment Base Address  | Segment Limit     |
|         (High)        |      (Low)        |
+-----------------------+-------------------+
63                      48 47  45 44  40 39 32
+-----------------------+----+---+----+-----+
| Segment Base Address  | G  | D | S  | Type|
|         (Low)         | /B | /B |    |     |
+-----------------------+----+---+----+-----+
  • 选择子结构
1
2
3
4
15                    3           2               0
+---------------------+-----------+---------------+
|      描述符索引值     | TI 表指示位|  RPL 请求特权级 |
+---------------------+-----------+---------------+

实践

定义段描述符

# 配置信息
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
;-------- DESC_字段名_字段相关信息 --------
; 描述符中的一个位,用于指定段的界限(或大小)是以字节为单位还是以4K字节(也就是页面大小)为单位。如果G位设置为1,则表示段的大小以4K字节为单位计算;如果G位为0,则表示段大小以字节为单位。
DESC_G_4K equ 1_00000000000000000000000b ; 描述符G位为4K颗粒度,其值等于1_00000000000000000000000b
; 描述符中的一个位,用于指示该段的默认操作大小。在保护模式下,D位用于确定段内指令的默认长度和(或)操作数大小。D位为1:表示该段默认为32位操作。D位为0:表示该段默认为16位操作。
DESC_D_32 equ 1_0000000000000000000000b
; L位是描述符中的一个位,用于指示该段是否支持长模式(64位模式)。这种模式是64位处理的基础。
DESC_L equ 0_000000000000000000000b
; AVL位为0:表示这个位未被操作系统使用或者保留状态。
DESC_AVL equ 0_00000000000000000000b
; 用于指定段的大小或长度
DESC_LIMIT_CODE2  equ 1111_0000000000000000b
DESC_LIMIT_DATA2  equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0000_000000000000000b
; 表示段存在,即该段是有效的,并且可以被访问。
DESC_P equ 1_000000000000000b
; 在x86体系结构中,有四个特权级别,从0到3。0是最高的特权级别,通常用于操作系统内核,而3是最低的,通常用于应用程序。
DESC_DPL_0 equ 00_0000000000000b
DESC_DPL_1 equ 01_0000000000000b
DESC_DPL_2 equ 10_0000000000000b
DESC_DPL_3 equ 11_0000000000000b
; S位为1:表示该段是一个代码或数据段。
DESC_S_CODE equ 1_000000000000b
DESC_S_DATA equ DESC_S_CODE
; S位为0:表示该段是一个系统段,如门描述符(例如调用门、中断门)或其他类型的系统专用段。
DESC_S_sys equ 0_000000000000b
; x=1,c=0,r=0,a=0 代码段是可执行的,非一致性,不可读,已访问位a清0
; 非一致性代码段是关于代码段访问控制的一种机制,它强调执行代码必须严格符合段的特权级别要求
DESC_TYPE_CODE equ 1000_00000000b
;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写,已访问位a清0
DESC_TYPE_DATA equ 0010_00000000b
DESC_CODE_HIGH4 equ (0x00 << 24) + DBSC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P+DESC_DPL_0 + DESC_S_CODE +DESC_TYPB_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 + DBSC_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

进入保护模式

section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start

;构建gdt及其内部的描述符,一个8字节
;注意GDT中的第一个段描述符没用,因此全为0
GDT_BASE:  dd		0x00000000  ; 低
           dd   0x00000000  ; 高
CODE_DESC: dd   0x0000FFFF  ; 代码段描述符。低2字节的FFFF是段界限的第0~15位,高2字节的0000是段基址的第0~15位
           dd   DESC_CODE_HIGH4
DATA_STACK_DESC:  dd   0x0000FFFF  ; 数据段和栈段描述符
                  dd   DESC_DATA_HIGH4 ; 栈段也是数据段,栈段的段界限按照数据段的规则来检查了
                ; 用于文本模式显示适配器的内存地址是0xb8000~0xbffff
                ; 显存段描述符。低2字节的段界限:limit=(0xbffff-0xb8000)/4k(段颗粒)=0x7。高2字节的段基址:8000
VIDEO_DESC: dd  0x800000007  
      			dd  DESC_VIDEO_HIGH4
GDT_SIZE  equ  $ - GDT_BASE
GDT_LIMIT equ  GDT_SIZE - 1
times 60 dq 0  ; dq用来定义了8字节数据。 即 define quad-word. 预留60个描述符空位。
; 按描述符索引值, TI, RPL来构造选择子。其中代码段索引值是(CODE_DESC-GDT_BASE)/8
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的指针,前2字节是gdt界限,后4字节是gdt起始地址
gdt_ptr dw GDT_LIMIT ; define word, 16位
        dd GDT_BASE  ; define double word, 32位
loadermsg db '2 loader in real.'
loader_start:
;==================================================
; INT 0x10    功能号: 0x13   功能描述:打印字符串
;==================================================
	mov sp, LOADER_BASE_ADDR
	mov bp, loadermsg
	mov cx, 17
	mov ax, 0x1301
	mov bx, 0x001f
	mov dx, 0x1800
	int 0x10
  ; ===================准备进入保护模式================
  ;1 打开A20地址线
  ;2 在gdt寄存器中加载GDT的地址及偏移量 (界限值)
  ;3 将cr0寄存器的pe位置1

  ; ======================打开A20===================
  in al,0x92  ; 端口 0x92 通常是系统控制端口,用于访问多种系统级功能,其中包括控制A20地址线。
  or al,0000_0010B  ; 这个操作的目的是设置AL寄存器中对应于A20线的那一位(第二位)而不改变其他位。
  out 0x92,al

  ; ======================加载GDT===================
  ; 前2字节是gdt界限,后4字节是gdt起始地址。界限值是它来告诉CPU只显示(界限值+l/段描述符大小8字节)个描述符,所以段描述符在定义时,一定要保证它们在GDT中是连续的。
  lgdt [gdt_ptr]

  ; ======================cr0第0位置1===================
  ; 将PE位置l后, 从此便进入了保护模式的大门,此位相当于保护模式的开关。
  mov eax, cr0   ; CR0是一个32位控制寄存器,在x86架构的CPU中扮演着重要角色,控制着操作系统的重要特性,包括保护模式。
  or eax, 0x00000001  ; 这个操作的目的是设置 CR0 寄存器的最低位(也就是第0位)。在 CR0 寄存器中,第0位被称为PE(Protection Enable)位,用于控制CPU是否运行在保护模式下。将这一位设置为1即启用保护模式
  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 $