0%

SYSU-2024操作系统lab3实验

实验课程: 操作系统

实验名称: Lab3 从实模式到保护模式

1. 实验要求

任务 1

1.1

复现Example 1,说说你是怎么做的并提供结果截图,也可以参考Ucore、Xv6等系统源码,实现自己的LBA方式的磁盘访问。
提示:部分需要的文件存放在src/example-1下,请根据需要将其放置于自己创建的lab3文件夹下。

1.2

在Example1中,我们使用了LBA28的方式来读取硬盘。此时,我们只要给出逻辑扇区号即可,但需要手动去读取I/O端口。然而,BIOS提供了实模式下读取硬盘的中断,其不需要关心具体的I/O端口,只需要给出逻辑扇区号对应的磁头(Heads)、扇区(Sectors)和柱面(Cylinder)即可,又被称为CHS模式。现在,同学们需要将LBA28读取硬盘的方式换成CHS读取,同时给出逻辑扇区号向CHS的转换公式。最后说说你是怎么做的并提供结果截图。

LBA向CHS模式的转换
int 13h中断

参数 数值
驱动器号(DL寄存器) 80h
每磁道扇区数 63
每柱面磁头数(每柱面总的磁道数) 18

任务 2

复现Example 2,使用gdb或其他debug工具在进入保护模式的4个重要步骤上设置断点,并结合代码、寄存器的内容等来分析这4个步骤,最后附上结果截图。gdb的使用可以参考appendix的“debug with gdb and qemu”部份。

提示:部分需要的文件存放在src/example-2下,请根据需要将其放置于自己创建的lab3文件夹下。

任务 3

改造“Lab2-Assignment 4”为32位代码,即在保护模式后执行自定义的汇编程序。

2. 实验过程

任务 1

1.1

编写bootloader.asm和mbr.asm,在 mbr.asm处放入使用LBA模式读取硬盘的代码,并加载bootloader到地址0x7e00,输出Hello World部份的代码放入到bootloader.asm中,具体代码内容见附件

然后我们编译 bootloader.asm,写入硬盘起始编号为1的扇区,共有5个扇区。

1
2
nasm -f bin bootloader.asm -o bootloader.bin
dd if=bootloader.bin of=hd.img bs=512 count=5 seek=1 conv=notrunc

mbr.asm也要重新编译和写入硬盘起始编号为0的扇区。

1
2
nasm -f bin mbr.asm -o mbr.bin
dd if=mbr.bin of=hd.img bs=512 count=1 seek=0 conv=notrunc

使用qemu运行即可,

1
qemu-system-i386 -hda hd.img -serial null -parallel stdio

1.2

CHS和LBA的转换

磁头数为硬盘磁头的总数,扇区数为每磁道的扇区数

1
2
3
4
5
6
7
逻辑编号(即LBA地址)=(柱面编号×磁头数+磁头编号)×扇区数+扇区编号-1

C = LBA//每磁道的扇区数//磁头号 即 C=(LBA div NS)div NH;

H = (LBA//每磁道扇区数) mod 磁头号 即 H=(LBA div NS)mod NH;

S = (LBA mod 每磁道扇区数) + 1 即 S=(LBA mod NS)+1

例如 LBA = 0 则 CHS = 0/0/1

经过计算,S = 2, C =0 , H = 0
在这里我们改写load_bootloader:和asm_read_hard_disk:

接下来的步骤与1.1相同

具体代码见关键代码展示部分

任务 2

创建img:

1
qemu-img create hd.img 10m

首先我们编译bootloader.asm,写入硬盘起始编号为1的扇区,共有5个扇区。

1
2
nasm -f bin bootloader.asm -o bootloader.bin
dd if=bootloader.bin of=hd.img bs=512 count=5 seek=1 conv=notrunc

mbr.asm也要重新编译和写入硬盘起始编号为0的扇区。

1
2
nasm -f bin mbr.asm -o mbr.bin
dd if=mbr.bin of=hd.img bs=512 count=1 seek=0 conv=notrunc

使用qemu运行即可,观察结果

1
qemu-system-i386 -hda hd.img -serial null -parallel stdio

下面进行调试

生成符号表

我们首先删除mbr.asm和bootloader.asm的org语句,因为我们会在链接的过程中指定他们代码和数据的起始地址,其效果和org指令完全相同。

我们编译mbr.asm,生成可重定位文件mbr.o。其中,-g参数是为了加上debug信息。

1
nasm -o mbr.o -g -f elf32 mbr.asm 

然后我们为可重定位文件mbr.o指定起始地址0x7c00,分别链接生成可执行文件mbr.symbol和mbr.bin

1
2
ld -o mbr.symbol -melf_i386 -N mbr.o -Ttext 0x7c00
ld -o mbr.bin -melf_i386 -N mbr.o -Ttext 0x7c00 --oformat binary

对于bootloader.asm,我们执行上述类似的操作。

1
2
3
nasm -o bootloader.o -g -f elf32 bootloader.asm 
ld -o bootloader.symbol -melf_i386 -N bootloader.o -Ttext 0x7e00
ld -o bootloader.bin -melf_i386 -N bootloader.o -Ttext 0x7e00 --oformat binary

然后将mbr.bin和bootloader.bin分别写入hd.img,写入的位置是lab2-Example 2中指定的位置。

1
2
dd if=mbr.bin of=hd.img bs=512 count=1 seek=0 conv=notrunc
dd if=bootloader.bin of=hd.img bs=512 count=5 seek=1 conv=notrunc

debug基本流程

写好makefile和gdbinit文件后

直接在命令行输入

1
2
3
make build

sudo make debug

在下面的位置设置第一个断点:
我们在内存中使用一个48位的变量pgdt来表示GDTR的内容。
然后把GDT的信息写入变量pgdt,把pgdt的内容加载进GDTR。即这两句

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

设置断点 b bootloader.asm:35(即在mov word[pgdt], 39)一行
加载GDTR信息后查看

汇编查看内存

如x/3uh 0x54320从地址0x54320开始,读取3个双字节(h),以无符号十六进制方式显示(u)

第二个断点位置:

当我们想进入保护模式时,首先需要打开第 21 根地址线。第21根地址线的开关位于南桥芯片的端口A20,使用 in,out 指令可以对主板端口进行读、写操作。代码如下。

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

在or语句设置断点,查看ax的第二位是否变为1

第三个断点位置

保护模式的真正开关——CR0。CR0 是 32 位的寄存器,包含了一系列用于控制处理器操作模式和运行状态的标志位,其第0位是保护模式的开关位,称为PE(protect mode enable)位。 PE置1,CPU 进入保护模式

1
2
3
4
cli        ; 保护模式下中断机制尚未建立,应禁止中断
mov eax, cr0
or eax, 1
mov cr0, eax ; 设置 PE 位

在or语句处设置断点观察第0位变化

第四个断点位置

1
jmp dword CODE_SELECTOR:protect_mode_begin

解决“dbus-launch“ (No such file or directory)的问题

任务 3

对lab2assignment4中的代码进行修改。使之能在32位模式保护模式下运行

因为保护模式下禁止了中断,不能通过int 10h方式输出字符,我们需要按照lab2任务1的方式进行输出。

在bootloader进入保护模式部分后加入代码

3. 关键代码

任务1.2中读取磁盘代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
load_bootloader:
call asm_read_hard_disk ; 读取硬盘
;inc ax
;cmp ax, 5
;jle load_bootloader
jmp 0x0000:0x7e00 ; 跳转到bootloader
asm_read_hard_disk:
; 从硬盘读取一个逻辑扇区

; 参数列表
; ax=逻辑扇区号0~15位
; cx=逻辑扇区号16~28位
; ds:bx=读取出的数据放入地址
;pushad

; 返回值
; bx=bx+512

mov ah, 2
mov al, 5;读5个扇区
mov ch, 0;CH=柱面
mov cl, 2;CL=扇区
mov dh, 0;DH=磁头
mov dl, 80h;DL=驱动器
int 13h

ret

任务2中gdbinit内容

1
2
3
4
5
6
7
target remote:1234
set disassembly-flavor intel
# 设置反汇编的格式为Intel风格
add-symbol-file mbr.symbol 0x7c00
# 添加符号文件mbr.symbol,并指定它的加载地址为0x7c00。
add-symbol-file bootloader.symbol 0x7e00
layout src

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
run:
@qemu-system-i386 -hda hd.img -serial null -parallel stdio
# 运行命令,使用qemu-system-i386虚拟机执行hd.img镜像文件,禁用串行输出和并行输出。

debug:
@qemu-system-i386 -s -S -hda hd.img -serial null -parallel stdio &
@sleep 1
@gnome-terminal -e "gdb -q -x gdbinit"
# 调试命令,使用qemu-system-i386虚拟机以调试模式运行hd.img镜像文件,
# 禁用串行输出和并行输出,同时在后台运行。
# 等待1秒钟,然后打开一个新的终端窗口并运行gdb调试器。

build:
@nasm -g -f elf32 mbr.asm -o mbr.o
@ld -o mbr.symbol -melf_i386 -N mbr.o -Ttext 0x7c00
@ld -o mbr.bin -melf_i386 -N mbr.o -Ttext 0x7c00 --oformat binary
@nasm -g -f elf32 bootloader.asm -o bootloader.o
@ld -o bootloader.symbol -melf_i386 -N bootloader.o -Ttext 0x7e00
@ld -o bootloader.bin -melf_i386 -N bootloader.o -Ttext 0x7e00 --oformat binary
@dd if=mbr.bin of=hd.img bs=512 count=1 seek=0 conv=notrunc
@dd if=bootloader.bin of=hd.img bs=512 count=5 seek=1 conv=notrunc
# 构建命令,使用nasm汇编器将mbr.asm文件编译为mbr.o目标文件,
# 使用ld链接器将mbr.o目标文件链接为mbr.symbol符号文件,
# 使用ld链接器将mbr.o目标文件链接为mbr.bin二进制文件,
# 设置链接地址为0x7c00,输出格式为二进制文件。
# 使用nasm汇编器将bootloader.asm文件编译为bootloader.o目标文件,
# 使用ld链接器将bootloader.o目标文件链接为bootloader.symbol符号文件,
# 使用ld链接器将bootloader.o目标文件链接为bootloader.bin二进制文件,
# 设置链接地址为0x7e00,输出格式为二进制文件。
# 使用dd命令将mbr.bin文件的内容复制到hd.img镜像文件的偏移量为0的位置,
# 复制512字节,覆盖原有内容,不进行截断。
# 使用dd命令将bootloader.bin文件的内容复制到hd.img镜像文件的偏移量为512的位置,
# 复制5个512字节,覆盖原有内容,不进行截断。

clean:
@rm -fr *.bin *.o
# 清理命令,删除所有的.bin和.o文件。

4. 实验结果

任务 1

1.1

img

1.2

使用CHS读取硬盘并加上了姓名学号
img

任务2

  1. 准备GDT,用lgdt指令加载GDTR信息 。(pgdt为39)

img
2. 打开第21根地址线 。(al第2位为1)

img
3. 开启cr0的保护模式标志位 。(ax第0位变为1)

img
4. 远跳转,进入保护模式
img
5.最后进入保护模式
img

任务3

d

5. 总结

遇到的问题及改进意见

在任务2中gdbinit文档中bootloader.symbol的地址应为0x7e00而不是0x7c00

写好makefile文件后在命令行make debug报错

“Failed to execute child process “dbus-launch”

Error constructing proxy for :1.288:/org/gnome/Terminal/Factory0:

连接已关闭Failed to use specified server:

连接已关闭Falling back to default server.Error constructing proxy for org.gnome.Terminal:/org/gnome/Terminal/Factory0: 连接已关闭

make: [makefile:6:debug] 错误 1

分别使用

sudo apt install dbus-x11

sudo make debug后解决了问题

实验总结

在这次实验中,我学习了LBA,CHS两种读写硬盘的方法,在实验中我还学习了实模式和保护模式的区别,从实模式跳转进入到保护模式、gdb的使用等内容。最后实现了移植大于512字节的程序。