操作系统实验1:引导
实验基本内容:
- 阅读《Linux 内核完全注释》的第 6 章,对计算机和 Linux 0.11 的引导过程进行初步的了解;
- 更改
bootsect.s
引导程序的开机显示文字,并能完成setup.s
的载入- 更改
setup.s
接管硬件程序的开机显示文字,并将基本硬件参数显示在硬盘上- 有时,继承传统意味着别手蹩脚。x86 计算机为了向下兼容,导致启动过程比较复杂。请找出 x86 计算机启动过程中,被硬件强制,软件必须遵守的两个“多此一举”的步骤(多找几个也无妨),说说它们为什么多此一举,并设计更简洁的替代方案。
一.阅读linux
内核完全注释:了解引导 P205
- 引导启动程序:boot/目录
- 启动引导时内核在内存中的位置和移动后的位置情况。(
bootsect.s
移位后引导其他扇区,setup.s
:探测+os
移位+临时建表进入保护模式) ROOT_DEV
:根文件系统所在的默认块-设备号。交换设备号SWAP_DEV
:用作虚拟存储交换空间的外部设备号
- 启动引导时内核在内存中的位置和移动后的位置情况。(
bootsect.S
程序- 该程序作用:
- 首先把从磁盘第 2 个扇区开始的 4 个扇区的 setup 模块(由
setup.s
编译而成)加载到内存紧接着bootsect
后面位置处(0x90200) - 然后利用BIOS 中断0x13 取磁盘参数表中当前启动引导盘的参数,接着在屏幕上显示“Loading system…”字符串。
- 把磁盘上 setup 模块后面的system 模块加载到内存0x10000 开始的地方。随后确定根文件系统的设备号。若没有指定,则根据所保存的引导盘的每磁道扇区数判别出盘的类型和种类(是 1.44M A 盘吗?),并保存其设备号于 root_dev (引导块的 508 地址处)中。
- 长跳转到setup 程序开始处(0x90200)去执行
- 首先把从磁盘第 2 个扇区开始的 4 个扇区的 setup 模块(由
- 该程序作用:
setup.S
程序- 该程序作用:
- 利用ROM BIOS 中断读取机器系统数据,并将这些数据保存到 0x90000 开始的位置(覆盖掉了
bootsect
程序所在的地方)。所取得的参数和保留的内存位置见表所示。这些参数将被内核中相关程序使用,例如字符设备驱动程序集中的console.c
和tty_io.c
程序等。
- 然后setup 程序将system 模块从0x10000-0x8ffff 整块向下移动到内存绝对地址 0x00000 处(当时认为内核系统模块 system 的长度不会超过此值:512KB)。接下来加载中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR),开启 A20 地址线,重新设置两个中断控制芯片 8259A,将硬件中断号重新设置为 0x20 - 0x2f。
- 设置CPU 的控制寄存器CR0(也称机器状态字),进入32 位保护模式运行,并跳转到位于 system 模块最前面部分的
head.s
程序继续运行。
- 利用ROM BIOS 中断读取机器系统数据,并将这些数据保存到 0x90000 开始的位置(覆盖掉了
- 该程序作用:
head.s
程序
二、实验
1.bootsect显示正确。es:bp
是显示字符串的地址:ES设#SETUPSEG
是内存中存放setup.s
开始的地方,BP表示存放#MSG2的偏移地址。
我没设es,没加inf_loop死循环。没改.org(! 表示下面语句从地址 506(0x1FA)开始,所以 root_dev 在启动扇区的第 508 开始的 2 个字节中。)
! 首先读入光标位置
mov ah,#0x03
xor bh,bh
int 0x10
! 显示字符串 “Hello OS world, my name is LZJ”
! 要显示的字符串长度
mov cx,#36 !大一些没关系
mov bx,#0x0007
mov bp,#msg1
! es:bp 是显示字符串的地址
mov ax,#0x1301
int 0x10
- 实验结果如图:(偷偷暗示我名字是YYF)
2.setup.s
显示文字,并载入bootsect.s
! setup.s (从bootsect.s复制而来,换掉了msg1和start,并加上了死循环)
entry _start
_start:
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#25
mov bx,#0x0007
mov bp,#msg2
mov ax,cs
mov es,ax
mov ax,#0x1301
int 0x10
inf_loop:
jmp inf_loop
msg2:
.byte 13,10
.ascii "NOW we are in SETUP"
.byte 13,10,13,10
boot_flag:
.word 0xAA55
! bootsect.s (仅仅改了SETUPLEN而已)
SETUPLEN=2
entry _start
_start:
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#36
mov bx,#0x0007
mov bp,#msg1
mov ax,#0x07c0
mov es,ax
mov ax,#0x1301
int 0x10
load_setup:
! 设置驱动器和磁头(drive 0, head 0): 软盘 0 磁头
mov dx,#0x0000
! 设置扇区号和磁道(sector 2, track 0): 0 磁头、0 磁道、2 扇区
mov cx,#0x0002
! 设置读入的内存地址:BOOTSEG+address = 512,偏移512字节
mov bx,#0x0200
! 设置读入的扇区个数(service 2, nr of sectors),
! SETUPLEN是读入的扇区个数,Linux 0.11 设置的是 4,
! 我们不需要那么多,我们设置为 2(因此还需要添加变量 SETUPLEN=2)
mov ax,#0x0200+SETUPLEN
! 应用 0x13 号 BIOS 中断读入 2 个 setup.s扇区
int 0x13
! 读入成功,跳转到 ok_load_setup: ok - continue
jnc ok_load_setup
! 软驱、软盘有问题才会执行到这里。我们的镜像文件比它们可靠多了
mov dx,#0x0000
! 否则复位软驱 reset the diskette
mov ax,#0x0000
int 0x13
! 重新循环,再次尝试读取
jmp load_setup
ok_load_setup:
! 接下来要干什么?当然是跳到 setup 执行。
! 要注意:我们没有将 bootsect 移到 0x9000,因此跳转后的段地址应该是 0x7ce0
! 即我们要设置 SETUPSEG=0x07e0
jmpi 0,SETUPSEG
! 注意将上一个实验的jmp inf_loop代码删掉!!
实验结果:
3.修改setup.s
- 显示数字的代码要看懂。其中用到的BIOS中断为INT 0x10,功能号0x0E(显示一个字符),即AH=0x0E,AL=要显示字符的ASCII码。但AL实际是由栈
bp
指向的字符赋值来的,那么其实显示的是ss:bp
的字符 - 注意与上面区别。
MOV AX,#0X1301 + INT 0x10
是显示es:bp下的
字符串 - 因此本题将
ss
设为#0x9000,es
设为setup代码段基址#0x9020。再不断改变bp
显示字符串即可
!以16进制方式打印栈顶的16位数
print_hex:
mov cx,#4 ! 4个十六进制数字
mov dx,(bp) ! 将(bp)所指的值放入dx中,如果bp是指向栈顶的话
print_digit:
rol dx,#4 ! 循环以使低4比特用上 !! 取dx的高4比特移到低4比特处。
mov ax,#0xe0f ! ah = 请求的功能值,al = 半字节(4个比特)掩码。
and al,dl ! 取dl的低4比特值。
add al,#0x30 ! 给al数字加上十六进制0x30
cmp al,#0x3a
jl outp !是一个不大于十的数字
add al,#0x07 !是a~f,要多加7
outp:
int 0x10
loop print_digit !cx--
ret
!setup.s关键代码
! Be Ready to Print
mov ax,#SETUPSEG ! init es = 0x9020
mov es,ax
mov ax,#INITSEG ! init ss:sp.put stack at 0x90000.
mov ss,ax
sub sp,sp
! Print "Now we are in SETUP"
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#25
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg2
mov ax,#0x1301 ! write string, move cursor
int 0x10
! Get Params
mov ax,#INITSEG
mov ds,ax
mov ah,#0x03
xor bh,bh
int 0x10
mov [0],dx
mov ah,#0x88
int 0x15
mov [2],ax
mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0004
mov cx,#0x10
rep
movsb
! Be Ready to Print
mov ax,#SETUPSEG ! init es = 0x9020
mov es,ax
! Cursor Position
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#18
mov bx,#0x0007
mov bp,#msg_cursor
mov ax,#0x1301
int 0x10
mov bp,#0x00
call print_hex
! Memory Size
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#14
mov bx,#0x0007
mov bp,#msg_memory
mov ax,#0x1301
int 0x10
mov bp,#0x02
call print_hex
(后续代码如磁盘信息或许同上)
实验结果如图:
最后补番外链接:
1.实验楼(操作系统原理与实践)
https://www.shiyanlou.com/courses/115
2.网易云课堂:哈尔滨工业大学,国家级精品课程,操作系统
https://mooc.study.163.com/course/1000002004#/info
3.推荐markdown神器,本文由此写成
https://typora.io/
(完)