【PM复习】进入保护模式

本文介绍了如何将CPU从16位实模式切换至32位保护模式,并通过实例展示了设置GDT(全局描述符表),填充段描述符,设置段寄存器等关键步骤。

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

     我们知道,现在大部分的CPU都是32位的,我们编写的OS当然也想在运行在32位的模式下,因为32位的CPU功能自然比16位的强大许多,32位的CPU在硬件给予了OS很大的支持。但是当机器刚刚启动时,CPU是运行在16位模式下的,所以我们必须学习怎么样进入32位模式,也即保护模式。

     16位模式也就是实模式,是我们最开始学习汇编时CPU运行的模式,是用段寄存器左移4位+偏移地址来寻址的;而在保护模式下则大不一样,是先分段再分页寻址的,分页是可选的,分段是必须的。所以想进入保护模式的第一步就是建立一张段表,也就是GDT,这个表中的每一个项就是一个段描述符Descriptor,有8个字节,记录了一个段的起始地址,段界限,以及段属性,从而对每一个段进行保护。

Code:
  1. [section .gdt]   
  2. LABEL_GDT:   
  3. LABEL_DESC_DUMMY:   
  4.     times 8 db 0   
  5. LABEL_DESC_CODE32:   
  6.     dw 0ffffh   
  7.     times 3 db 0   
  8.     db 9ch   
  9.     db 4fh   
  10.     db 0   
  11. LABEL_DESC_VIDEO:   
  12.     dw 0ffffh   
  13.     dw 8000h   
  14.     db 0bh   
  15.     db 92h   
  16.     db 4fh   
  17.     db 0  

     如上面的代码所示,GDT中有3个描述符,第1个是哑描述符,没什么意义;第2个表示1个32位的可执行非一致代码段,段界限为0xfffff,段基址没有填写,日后会填充;第3个是1个可读写的数据段,段界限位0xfffff,段基址为0xb8000,此段指示了显存。描述符的结构在这里就不细说了,查查书就好了。

     有了GDT,我们寻址的时候只要把想访问的段的相对于GDT的索引送到段寄存器就OK了,那么怎么知道GDT在内存中的位置呢?在CPU有一个48位的寄存器,就存放着GDT在内存的地址和GDT的长度。所以在进入保护模式必须给这个寄存器填上正确的地址和长度。如下代码所示:

Code:
  1. GDT_Len equ $ - LABEL_GDT   
  2. GDT_Ptr:   
  3.     dw GDT_Len - 1   
  4.     dd 0  

     我们看到,GDT的长度已填上,GDT实际的物理地址还不知道,因为我们的测试程序是在DOS下动态加载的,所以在日后再填写,这个以后填充段描述符的段基地址是一样的道理。

     还有一个概念是选择子selector,是一个16位的结构,它的高13位是相应段在GDT中的索引,为什么只用高13位表示呢?简单啊,因为一个描述符是8个字节,所以索引肯定是8个倍数,低3位就用不着,可以拿来做别的事,至于是什么事呢,日后再说。下面就是定义选择子的代码:

Code:
  1. Selector_Code32 equ LABEL_DESC_CODE32 - LABEL_DESC_DUMMY    
  2. Selector_Video  equ LABEL_DESC_VIDEO - LABEL_DESC_DUMMY  

     准备好了这些,还不够啊,还有一些东东没有填充好呢。先不理它,先明确我们进入保护模式后要干嘛,那就在屏幕上打印个字符吧,这样可以测试下我们的GDT有没有设置正确,下面就是这个32位的代码段:

Code:
  1. [section .s32]   
  2. [bits 32]   
  3. LABEL_CODE32:   
  4.     mov ax,Selector_Video   
  5.     mov gs,ax   
  6.     mov ah,0ch   
  7.     mov al,'x'  
  8.     mov [gs:80 * 10],ax   
  9.     jmp $  

     为了简单起见,打印完后进入死循环。

     最后只剩16位代码了,这段代码当然是做与进入保护模式有关的事情了,下面是代码:

Code:
  1. [section .s16]   
  2. [bits 16]   
  3. LABEL_BEGIN:   
  4.     mov ax,cs   
  5.     mov ds,ax   
  6.     mov es,ax   
  7.        
  8.     xor eax,eax   
  9.     mov ax,cs   
  10.     shl eax,4   
  11.     add eax,LABEL_CODE32   
  12.     mov word [LABEL_DESC_CODE32 + 2],ax   
  13.     shr eax,16   
  14.     mov byte [LABEL_DESC_CODE32 + 4],al   
  15.     mov byte [LABEL_DESC_CODE32 + 7],ah  
  16.   
  17.     xor eax,eax   
  18.     mov ax,ds   
  19.     shl eax,4   
  20.     add eax,LABEL_GDT   
  21.     mov dword [GDT_Ptr + 2],eax   
  22.        
  23.     lgdt [GDT_Ptr]   
  24.        
  25.     cli   
  26.        
  27.     in  al,92h   
  28.     or  al,00000010b   
  29.     out 92h,al   
  30.        
  31.     mov eax,cr0   
  32.     or  eax,1   
  33.     mov cr0,eax   
  34.        
  35.     jmp dword Selector_Code32:0  

     8到15行是填充32位代码段的段基址,17到21行是填充GDT的地址,接着加载到GDTR寄存器中,然后是关掉中断,因为在保护模式下中断的机制跟实模式不太一样,先关掉日后再说。接着打开A20地址线使得能够寻址到更大的范围,接着打开cr0寄存器的最低位,值得让CPU进入保护模式,最后是历史性的跳转正式跳转到保护模式。

     下面是完整的代码:

Code:
  1. org 0100h   
  2. jmp LABEL_BEGIN   
  3.   
  4. [section .gdt]   
  5. LABEL_DESC_DUMMY:   
  6.     times 8 db 0   
  7. LABEL_DESC_CODE32:   
  8.     dw 0ffffh   
  9.     times 3 db 0   
  10.     db 9ch   
  11.     db 4fh   
  12.     db 0   
  13. LABEL_DESC_VIDEO:   
  14.     dw 0ffffh   
  15.     dw 8000h   
  16.     db 0bh   
  17.     db 92h   
  18.     db 4fh   
  19.     db 0   
  20.        
  21. GDT_Len equ $ - LABEL_DESC_DUMMY   
  22. GDT_Ptr:   
  23.     dw  GDT_Len - 1   
  24.     dd  0   
  25.   
  26. Selector_Code32 equ LABEL_DESC_CODE32 - LABEL_DESC_DUMMY   
  27. Selector_Video  equ LABEL_DESC_VIDEO - LABEL_DESC_DUMMY   
  28.   
  29. [section .s16]   
  30. [bits 16]   
  31. LABEL_BEGIN:   
  32.     mov ax,cs   
  33.     mov ds,ax   
  34.     mov es,ax   
  35.        
  36.     xor eax,eax   
  37.     mov ax,cs   
  38.     shl eax,4   
  39.     add eax,LABEL_CODE32   
  40.     mov word [LABEL_DESC_CODE32 + 2],ax   
  41.     shr eax,16   
  42.     mov byte [LABEL_DESC_CODE32 + 4],al   
  43.     mov byte [LABEL_DESC_CODE32 + 7],ah   
  44.        
  45.     xor eax,eax   
  46.     mov ax,ds   
  47.     shl eax,4   
  48.     add eax,LABEL_DESC_DUMMY   
  49.     mov dword [GDT_Ptr + 2],eax   
  50.        
  51.     lgdt    [GDT_Ptr]   
  52.        
  53.     cli   
  54.        
  55.     in  al,92h   
  56.     or  al,00000010b   
  57.     out 92h,al   
  58.        
  59.     mov eax,cr0   
  60.     or  al,1   
  61.     mov cr0,eax   
  62.        
  63.     jmp Selector_Code32:0   
  64.   
  65. [section .s32]   
  66. [bits 32]   
  67. LABEL_CODE32:   
  68.     mov ax,Selector_Video   
  69.     mov gs,ax   
  70.     mov ah,0ch   
  71.     mov al,'x'  
  72.     mov [gs:80 * 10],ax   
  73.     jmp $   

        运行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值