写操作系统----3.进入保护模式

本文介绍了一个简单的保护模式实现过程,包括设置全局描述符表(GDT)、开启保护模式及进行简单输出。通过逐步调试和修改代码,实现了从实模式到保护模式的成功转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《操作系统真相还原》 与 《Orange'S:一个操作系统的实现》都参考了
在上一篇文章中mbr读取了硬盘上的一个sector到0x9000,然后跳到了0x9000运行
1. 第一次写的loader.S
  1. org 0x9000
  2. jmp begin

  3. TYPE_0 equ 0x0

  4. S_0 equ (0x0<<4) ;s=0-->gate
  5. S_1 equ (0x1<<4) ;s=1-->data/code 

  6. DPL_0 equ (0x0<<5) ;privilege level
  7. DPL_3 equ (0x3<<5)

  8. P_0 equ (0x00<<7) ;not in memory
  9. P_1 equ (0x01<<7) ;in memory

  10. AVL_0 equ (0x00<<8)

  11. DB_0 equ (0x00<<10) ;16bit data&code
  12. DB_1 equ (0x01<<10) ;32bit data&code

  13. G_0 equ (0x00<<11) ;g=0,byte
  14. G_1 equ (0x01<<11) ;g=1,4K

  15. TI_0 equ (0x0<<2) ;TI=0,gdt
  16. TI_1 equ (0x1<<2) ;TI=1,ldt

  17. RPL_0 equ (0x0)

  18. %macro DESC 3
  19.     dw %1 & 0xFFFF ;limit[0-15]
  20.     dw %2 & 0xFFFF ;base[0-15]
  21.     db (%2>>16)&0xFF ;base[16-23]
  22.     db (%3&0xFF) ;prop[0-7]
  23.     db (((%1>>8)&0xF) | (%3>>4)&0xF0) ;limit[16-19]prop[8-11]
  24.     db (%2>>24) & 0xFF
  25. %endmacro
  26. gdt_base: DESC 0, 0, 0
  27. gdt_code: DESC 0xFFFF, 0x0, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1)
  28. gdt_video: DESC 0xFFFF, 0xB8000, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1)

  29. gdt_ptr: 
  30.     dw 24 ;gdt_limit: 8*3=16, only 3 gdt item
  31.     dd gdt_base ;gdt_base

  32. select_code equ (0x01<<3)|TI_0|RPL_0
  33. select_video equ (0x02<<3)|TI_0|RPL_0
  34. begin:
  35.     ;----------------- 打开A20 ----------------
  36.     in al,0x92
  37.     or al,0000_0010B
  38.     out 0x92,al

  39.     ;----------------- 加载GDT ----------------
  40.     lgdt [gdt_ptr]

  41.     ;----------------- cr0第0位置1 ----------------
  42.     mov eax, cr0 
  43.     or eax, 0x00000001
  44.     mov cr0, eax

  45.     ;----------------- 跳到保护模式 ----------------
  46.     jmp select_code:protect_mode
  47. protect_mode:
  48.     mov ax,select_video
  49.     mov gs,ax
  50.     mov byte [gs:160], 'P'
  51.     jmp $
1.2 看需不需要改mbr
  1. cong@msi:/work/os/code/3pm$ ls -l loader.bin 
  2. -rw-rw-r-- 1 cong cong 86 Aug 17 14:24 loader.bin
才86个字节,mbr是读了512个字节,这儿不到512字节,所以不需要修改mbr.S
下面开始分析问题
2. 
2.1.a 第一次调试
  1. (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
  2. <bochs:1> b 0x9000                      -->因为把代码加载到了0x9000,所以这儿直接到0x9000执行
  3. <bochs:2> c
  4. (0) Breakpoint 1, 0x00009000 in ?? ()
  5. Next at t=156817505
  6. .....
  7. <bochs:11> 
  8. Next at t=156817513
  9. (0) [0x000000009035] 0000:00009035 (unk. ctxt): jmpf 0x0008:903a ; ea3a900800    -->向保护模式跳转
  10. <bochs:12> 
  11. (0).[156817513] [0x000000009035] 0000:00009035 (unk. ctxt): jmpf 0x0008:903a ; ea3a900800  
  12. Next at t=156817514
  13. (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0 -->这儿发现不对了,这儿是复位了
2.1.b 第一次修改
  1. cong@msi:/work/os/code/3pm$ diff ./loader.S loader.S_bad 
  2. 4,5c4
  3. < TYPE_8 equ 0x8 ;code -->exec
  4. < TYPE_2 equ 0x2 ;data -->r/w
  5. ---
  6. > TYPE_0 equ 0x0
  7. 38,39c37,38
  8. < gdt_code: DESC 0xFFFF, 0x0, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_8)   -->code段加上可执行权限
  9. < gdt_video: DESC 0xFFFF, 0xB8000, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_2)   
  10. ---
  11. > gdt_code: DESC 0xFFFF, 0x0, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1)          -->原先code段没有可执行权限
  12. > gdt_video: DESC 0xFFFF, 0xB8000, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1)
2.2.a 第二次调试
  1. (0) [0x000000009039] 0000:00009039 (unk. ctxt): jmpf 0x0008:903e ; ea3e900800
  2. <bochs:15> 
  3. Next at t=313385561
  4. (0) [0x00000000903e] 0008:0000903e (unk. ctxt): mov eax, 0xe88e0010 ; b810008ee8    -->这儿的确是跳到903e上面来了,但是还是乱的
  5. <bochs:16> 
  6. Next at t=313385562
  7. (0) [0x000000009043] 0008:00009043 (unk. ctxt): mov byte ptr gs:[esi], 0xa0 ; 65c606a0
  8. <bochs:17> 
  9. Next at t=313385563
  10. (0) [0x000000009047] 0008:00009047 (unk. ctxt): add byte ptr ds:[eax+101], dl ; 005065
  11. <bochs:18> 
  12. (0).[313385563] [0x000000009047] 0008:00009047 (unk. ctxt): add byte ptr ds:[eax+101], dl ; 005065
  13. Next at t=313385564
  14. (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0
  15. <bochs:19> 
  16. Next at t=313385565
  17. (0) [0x0000000fe05b] f000:e05b (unk. ctxt): xor ax, ax ; 31c0
2.2.b第二次修改
  1. 63 [bits 32]               -->加入这个标签,使得这儿编出来的是32位的代码
  2. 64 protect_mode:
2.2.c 对比一下两次生成的二进制代码
xxd ./loader.bin
  1. 0000000: eb1e 0000 0000 0000 0000 ffff 0000 0098 ................
  2. 0000010: 4f00 ffff 0080 0b92 4f00 1800 0290 0000 O.......O.......
  3. 0000020: e492 0c02 e692 0f01 161a 900f 20c0 6683 ............ .f.    -->没有加[bits 32]标签
  4. 0000030: c801 0f22 c0ea 3a90 0800 b810 008e e865 ..."..:........e
  5. 0000040: c606 a000 50eb fe ....P..


  6. 0000000: eb1e 0000 0000 0000 0000 ffff 0000 0098 ................
  7. 0000010: 4f00 ffff 0080 0b92 4f00 1800 0290 0000 O.......O.......
  8. 0000020: e492 0c02 e692 0f01 161a 900f 20c0 6683 ............ .f.    -->加了[bits 32]标签
  9. 0000030: c801 0f22 c0ea 3a90 0800 66b8 1000 8ee8 ..."..:...f.....
  10. 0000040: 65c6 05a0 0000 0050 ebfe e......P..
2.3 运行结果就对了


2.4 虽然结果上是对了,但还有点小问题
  1. cong@msi:/work/os/code/3pm$ diff ./loader.S loader.S_ok 
  2. 34c34
  3. < db (((%1>>8)&0xF) | (%3>>4)&0xF0) ;limit[16-19]prop[8-11]    -->arg1的前16位己填序,现在要填16-19位
  4. ---
  5. > db (((%1>>16)&0xF) | (%3>>4)&0xF0) ;limit[16-19]prop[8-11]   -->所以要右称16位
2.5 代码打包
3pm.rar  (下载后改名为3pm.tar.gz)
2.6 下面分析一下代码
  1. cong@msi:/work/os/code/3pm$ cat loader.S
  2. org 0x9000                            --> org是条伪指令不会生成二进制的代码
  3. jmp begin                             --> jmp下面是数据,cpu区分不了数据与代码,防止cpu把数据当成代码就加上jmp,人为的控制cpu去执行代码段中的代码
 -->下面定义了一堆段属性
  1. TYPE_8 equ 0x8 ;code -->exec
  2. TYPE_2 equ 0x2 ;data -->r/w

  3. S_0 equ (0x0<<4) ;s=0-->gate
  4. S_1 equ (0x1<<4) ;s=1-->data/code 

  5. DPL_0 equ (0x0<<5) ;privilege level
  6. DPL_3 equ (0x3<<5)

  7. P_0 equ (0x00<<7) ;not in memory
  8. P_1 equ (0x01<<7) ;in memory

  9. AVL_0 equ (0x00<<8)

  10. DB_0 equ (0x00<<10) ;16bit data&code
  11. DB_1 equ (0x01<<10) ;32bit data&code

  12. G_0 equ (0x00<<11) ;g=0,byte
  13. G_1 equ (0x01<<11) ;g=1,4K

  14. TI_0 equ (0x0<<2) ;TI=0,gdt
  15. TI_1 equ (0x1<<2) ;TI=1,ldt

  16. RPL_0 equ (0x0)
  17. -->宏中arg1=段界限,arg2=段基址, arg3=段属性
  18. %macro DESC 3                           -->这儿采用了《Orange'S:一个操作系统的实现》中的宏,用宏之后就比较直观
  19.     dw %1 & 0xFFFF ;limit[0-15]
  20.     dw %2 & 0xFFFF ;base[0-15]
  21.     db (%2>>16)&0xFF ;base[16-23]
  22.     db (%3&0xFF) ;prop[0-7]
  23.     db (((%1>>16)&0xF) | (%3>>4)&0xF0) ;limit[16-19]prop[8-11]
  24.     db (%2>>24) & 0xFF
  25. %endmacro
  26. gdt_base:  DESC 0, 0, 0                    -->第一个描述符为空
  27. gdt_code:  DESC 0xFFFF, 0x0, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_8)             -->代码段描述符
  28. gdt_video: DESC 0xFFFF, 0xB8000, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_2)         -->显示段描述符

  29. gdt_ptr: 
  30.     dw 24                ;gdt_limit: 8*3=24, 只预留了3个gdt描述符的段界限
  31.     dd gdt_base          ;gdt_base gdt的基地址就是gdt_base这个标签的地址

  32. select_code equ (0x01<<3)|TI_0|RPL_0
  33. select_video equ (0x02<<3)|TI_0|RPL_0

  34. begin:
  35.     ;----------------- 打开A20 ----------------
  36.     in al,0x92
  37.     or al,0000_0010B
  38.     out 0x92,al

  39.     ;----------------- 加载GDT ----------------
  40.     lgdt [gdt_ptr]

  41.     ;----------------- cr0第0位置1 ----------------
  42.     mov eax, cr0 
  43.     or eax, 0x00000001
  44.     mov cr0, eax

  45.     ;----------------- 跳到保护模式 ----------------
  46.     jmp select_code:protect_mode
  47. [bits 32]
  48. protect_mode:
  49.     mov ax,select_video
  50.     mov gs,ax
  51.     mov byte [gs:160], 'P'
  52.     mov byte [gs:161], 0xA4
  53.     jmp $

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值