OS

OS lab3 保护模式

Posted by RogersLuo's Blog on April 14, 2024

image-20240412203853545

本科生实验报告

实验课程: 操作系统原理实验

任课教师: 刘宁

实验题目:保护模式

专业名称: 计算机科学与技术

学生姓名:罗弘杰

学生学号: 22336173

实验地点: 实验中心D503

实验时间: 2024/4/12

实验资料:lab3 · NelsonCheung/SYSU-2023-Spring-Operating-System - 码云 - 开源中国 (gitee.com)

实验要求

​ 学习从LBA和C/H/S的磁盘寻址方式,以及使用IO和中断实现的磁盘读取

​ 学习进入保护模式的方式

​ 学习gdb调试源码级程序

实验任务

​ 复现example1, 说说怎么做并截图

​ 在example1的基础上将LBA28的寻址方式改为CHS,同时给出逻辑扇区号向CHS的转换公式

​ 利用gdb进行实验资料例子2的debug分析

实验过程

任务一

​ 在实验资料的基础上编写Mbr和bootloader.asm,mbr负责加载bootloader,bootloader的任务是打印字符。

​ 我的理解是:操作系统为了性能需求,启动时只会自动加载512B的MBR, 其余磁盘操作由MBR内容管理和控制

​ 过程:

image-20240412204552002

​ 编写bootloader.asm和mbr.asm两个文件,然后编译为可执行文件

写入到虚拟机的磁盘上,mbr.asm写的位置时0号扇区,数量为1,bootloader为扇区1,数量为5.

​ 然后使用QEMU命令启动虚拟机

image-20240412204810631

任务二

img img

​ 先复习磁盘工作原理:

磁盘空间 \(Space = c *s *h *512(扇区容量)\) 先确定柱面,在柱面上确定磁头,在磁头确定的磁道上找到扇区

编号差异:LBA规则的扇区编号从0开始,但是C/H/S的编号从1开始,从CHS到LBA转换要-1.

CHS->LBA \(LBA = (c*HPC+h)*SPT+s-1\) HPC是每个柱面的磁头数目,SPT是每个磁道上的扇区总数

由此可推出反变换公式:

0
1
2
C = LBA //(HPC * SPT) #注意是整除
H = [(LBA +1)//每磁道扇区总数SPT]mod HPC
S = (LBA +1% 每磁道扇区总数SPT

​ 在例一中, 读取的LBA从1-5,HPC为18, SPT为63,所以柱面是0,磁道也是0, S从1递增到6;并根据中断读取的参数要求重新编写mbr_chs.asm,编译以后在qemu上运行

mov ax, 2                ; 物理扇区第2位
load_bootloader:
    call asm_read_hard_disk  ; 读取硬盘
    inc ax
    cmp ax, 5
    jle load_bootloader
jmp 0x0000:0x7e00        ; 跳转到bootloader

jmp $ ; 死循环

asm_read_hard_disk:                           
; 从硬盘读取一个逻辑扇区

; 参数列表
; ax=起始扇区号

; 返回值
; bx=bx+512
; ax=ax+1
    mov dl, 80h
    mov dh, 0; 磁头号
    mov ch, 0; 柱面号
    mov cl, al; 扇区号


    mov ah, 2; 功能号
    mov al, 1; 扇区数
    int 13h; 调用int 13h中断

    add bx, 512; bx=bx+512, 读取下一个扇区
    ret

​ 在这里逻辑扇区和物理扇区编号的差异是要注意的细节(逻辑从0开始,物理从1开始),然后按照任务1的方式,再编译并写入磁盘,启动qemu

image-20240412211222013

​ 实现相同的功能。

任务三

​ 复现实验资料中进入保护模式的程序,然后使用gdb调试。

​ 首先编写boot.inc的头文件

; 常量定义区
; _____________Loader_____________
; 加载器扇区数
LOADER_SECTOR_COUNT equ 5
; 加载器起始扇区
LOADER_START_SECTOR equ 1
; 加载器被加载地址
LOADER_START_ADDRESS equ 0x7e00
; _____________GDT_____________
; GDT起始位置
GDT_START_ADDRESS equ 0x8800  ; gdt表的起始位置

​ 然后重新编写bootloader.asm,在输出字符后进入保护模式

image-20240414233134488

%include "boot.inc"
[bits 16]
mov ax, 0xb800 ;显存段地址
mov gs, ax
mov ah, 0x03 ;青色
mov ecx, bootloader_tag_end - bootloader_tag
xor ebx, ebx
mov esi, bootloader_tag
output_bootloader_tag:
    mov al, [esi]
    mov word[gs:bx], ax
    inc esi
    add ebx,2
    loop output_bootloader_tag

;空描述符
mov dword [GDT_START_ADDRESS+0x00],0x00
mov dword [GDT_START_ADDRESS+0x04],0x00  

;创建描述符,这是一个数据段,对应0~4GB的线性地址空间
mov dword [GDT_START_ADDRESS+0x08],0x0000ffff    ; 基地址为0,段界限为0xFFFF
mov dword [GDT_START_ADDRESS+0x0c],0x00cf9200    ; 粒度为4KB,存储器段描述符 

;建立保护模式下的堆栈段描述符      
mov dword [GDT_START_ADDRESS+0x10],0x00000000    ; 基地址为0x00000000,界限0x0 
mov dword [GDT_START_ADDRESS+0x14],0x00409600    ; 粒度为1个字节

;建立保护模式下的显存描述符   
mov dword [GDT_START_ADDRESS+0x18],0x80007fff    ; 基地址为0x000B8000,界限0x07FF
mov dword [GDT_START_ADDRESS+0x1c],0x0040920b    ; 粒度为字节

;创建保护模式下平坦模式代码段描述符
mov dword [GDT_START_ADDRESS+0x20],0x0000ffff    ; 基地址为0,段界限为0xFFFF
mov dword [GDT_START_ADDRESS+0x24],0x00cf9800    ; 粒度为4kb,代码段描述符 


;初始化描述符表寄存器GDTR
mov word [pgdt], 39      ;描述符表的界限   
lgdt [pgdt]

; _____________Selector_____________
;平坦模式数据段选择子
DATA_SELECTOR equ 0x8
;平坦模式栈段选择子
STACK_SELECTOR equ 0x10
;平坦模式视频段选择子
VIDEO_SELECTOR equ 0x18
VIDEO_NUM equ 0x18
;平坦模式代码段选择子
CODE_SELECTOR equ 0x20

in al,0x92                         ;南桥芯片内的端口 
or al,0000_0010B
out 0x92,al                        ;打开A20

cli                                ;禁用中断
mov eax,cr0
or eax,1
mov cr0,eax                        ;设置PE位

jmp dword CODE_SELECTOR:protect_mode_begin

;16位的描述符选择子:32位偏移
;清流水线并串行化处理器
[bits 32]           
protect_mode_begin:                              

mov eax, DATA_SELECTOR                     ;加载数据段(0..4GB)选择子
mov ds, eax
mov es, eax
mov eax, STACK_SELECTOR
mov ss, eax
mov eax, VIDEO_SELECTOR
mov gs, eax

mov ecx, protect_mode_tag_end - protect_mode_tag
mov ebx, 80 * 2
mov esi, protect_mode_tag
mov ah, 0x3
output_protect_mode_tag:
    mov al, [esi]
    mov word[gs:ebx], ax
    add ebx, 2
    inc esi
    loop output_protect_mode_tag

jmp $ ; 死循环

pgdt dw 0    ; pgdt 将占据48位,前16位为界限,在这里是39,后32位是起始地址
    dd GDT_START_ADDRESS

bootloader_tag db 'bootloader'
bootloader_tag_end:

protect_mode_tag db 'enter protect mode'
protect_mode_tag_end:

​ 代码分析:

进入保护模式需要4个过程:

  1. 准备GDT,用lgdt指令加载GDTR信息

  2. 打开第21根地址线。//扩大内存访问空间是保护模式的出现背景

  3. 开启cr0的保护模式标志位

  4. 远跳转,进入保护模式

    怎么做:

    1. GDTR是一个x86架构专用寄存器,是48位存储全局描述符表的寄存器,在这里我们先把GDT信息存储在内存中,然后使用lgdt指令加载到该寄存器,修改全局描述符表信息。

      0
      1
      
      mov word [pgdt], 39      ;描述符表的界限   
      lgdt [pgdt]
      
    2. 南桥芯片0x92端口的第二位控制第二十条地址线的开关,将其置为1就能打开第二十条地址线;

      使用与方法可以将其置为一

      0
      1
      2
      
      in al,0x92                         ;南桥芯片内的端口 
      or al,0000_0010B					;将第二位置为1
      out 0x92,al                        ;打开A20
      
    3. cr0是专用寄存器,将其最低位(protection enable)置为1,就可以启用保护模式。

      禁用中断保证当前代码执行

      0
      1
      2
      3
      
      cli                                ;禁用中断
      mov eax,cr0
      or eax,1
      mov cr0,eax                        ;设置PE位
      
    4. jmp dword CODE_SELECTOR:protect_mode_begin 用于执行跳转到指定代码段的指定地址,进入保护模式下的代码执行。

      0
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      jmp dword CODE_SELECTOR:protect_mode_begin
             
      ;16位的描述符选择子:32位偏移
      ;清流水线并串行化处理器
      [bits 32]           
      protect_mode_begin:                              
             
      mov eax, DATA_SELECTOR                     ;加载数据段(0..4GB)选择子
      mov ds, eax
      mov es, eax
      mov eax, STACK_SELECTOR
      mov ss, eax
      mov eax, VIDEO_SELECTOR
      mov gs, eax
      

编写MBR.asm

​ 与之前的相似,更改了硬盘读取函数的传参方式

gdb调试:

​ 调用qemu后,没有显示内容

屏幕截图 2024-04-14 112440

断点1:准备GDT,用lgdt指令加载GDTR信息。

​ 根据实验资料;image-20240414180146319

​ 通过调试,先获取PGDT在内存的位置,然后查看里面的内容,确认是39

image-20240414151554751屏幕截图 2024-04-14 174000

断点2:打开第21根地址线。

​ 怎么打开:参照以下资料将0x92第二位置为1image-20240414181746933

image-20240414180537838

屏幕截图 2024-04-14 175732

断点3:开启cr0的保护模式标志位。

image-20240414180657188

​ 修改前是16,修改后是17,16和17相比只有最后一位不一样,17说明进入保护模式了

屏幕截图 2024-04-14 174248

断点4:远跳转,进入保护模式。

image-20240414182107580

此时,jmp指令将CODE_SELECTOR送入cs,将protect_mode_begin + LOADER_START_ADDRESS送入eip,进入保护模式。然后我们将选择子放入对应的段寄存器。image-20240414180819922

​ 可以查看跳转到保护模式时各个寄存器的状态

image-20240414175914702

Creative Commons License本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.