操作系统实验三
3.1. 实验目的
- 熟悉实验环境;
- 建立对操作系统引导过程的深入认识;
- 掌握操作系统的基本开发过程;
- 能对操作系统代码进行简单的控制,揭开操作系统的神秘面纱。
3.2. 实验内容
3.2.1. 改写 bootsect.s 主要完成如下功能:
bootsect.s 可以在屏幕上输出提示信息
输出语句“BDSOS is booting…”
检查bootsect.s,以下代码用于打印信息
ljmp $BOOTSEG, $_start
_start:
mov $0x03,%ah
xor %bh,%bh
int $0x10
mov $24, %cx
mov $0x0007, %bx # page 0, attribute 7 (normal)
#lea msg1, %bp
mov $msg1, %bp
mov $0x1301, %ax # write string, move cursor
int $0x10
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
因此,对bootsect.s的内容进行修改,使其输出所需的信息,然后删除其余代码,最终,bootsect.s的完整内容如下:
.code16
.global _start
.equ SETUPLEN, 4 # nr of setup-sectors
.equ BOOTSEG, 0x07c0 # original address of boot-sector
.equ INITSEG, 0x9000 # we move boot here - out of the way
.equ SETUPSEG, 0x9020 # setup starts here
.equ ROOT_DEV, 0x301
ljmp $BOOTSEG, $_start
_start:
mov $0x03,%ah
xor %bh,%bh
int $0x10
mov $31,%cx
mov $0x0007,%bx
mov $msg1,%bp
mov $0x07c0,%ax
mov %ax,%es
mov $0x1301,%ax
int $0x10
msg1:
.byte 13,10
.ascii "WangWeiOS is booting..."
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
最后,编译文件并执行,结果如下图,显示bootsect.s的修改是正确的,成功输出语句“BDSOS is booting…”
3.2.2. 改写 setup.s 主要完成如下功能:
首先,确保setup.s能够向屏幕输出信息,这个功能已经在前一个问题中在bootsect.s中完成,因此,将相应代码复制到setup.s中,然后删除多余代码,完整的setup.s代码如下:
.code16
.equ SETUPSEG, 0x9020
.global _start
ljmp $SETUPSEG, $_start
_start:
mov $0x03,%ah
xor %bh,%bh
int $0x10
mov $25,%cx
mov $0x0007,%bx
mov $msg2,%bp
mov $0x07c0,%ax
mov %cs,%ax
mov %ax,%es
mov $0x1301,%ax
int $0x10
inf_loop:
jmp inf_loop
msg2:
.byte 13,10
.ascii "Now we are in SETUP"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
接下来,对bootsect.s进行修改,以使其能够加载并跳转到setup.s的起始地址执行。检查原bootsect.s的内容,找到以下代码实现了这个功能:
load_setup:
mov $0x0000, %dx # drive 0, head 0
mov $0x0002, %cx # sector 2, track 0
mov $0x0200, %bx # address = 512, in INITSEG
.equ AX, 0x0200+SETUPLEN
mov $AX, %ax # service 2, nr of sectors
int $0x13 # read it
jnc ok_load_setup # ok - continue
mov $0x0000, %dx
mov $0x0000, %ax # reset the diskette
int $0x13
jmp load_setup
ok_load_setup:
ljmp $SETUPSEG, $0
因此,对bootsect.s的代码进行修改,首先加载setup.s,然后显示语句“WangWeiOS is booting…”,最后跳转至setup.s。完成这些功能并删除多余代码后,bootsect.s的完整代码如下:
.code16
.equ SETUPLEN, 2 # nr of setup-sectors
.equ BOOTSEG, 0x07c0 # original address of boot-sector
.equ INITSEG, 0x9000 # we move boot here - out of the way
.equ SETUPSEG, 0x9020 # setup starts here
.equ ROOT_DEV, 0x301
ljmp $BOOTSEG, $_start
_start:
mov $BOOTSEG, %ax
mov %ax, %ds
mov $INITSEG, %ax
mov %ax, %es
mov $256, %cx
sub %si, %si
sub %di, %di
rep
movsw
load_setup:
mov $0x0000, %dx # drive 0, head 0
mov $0x0002, %cx # sector 2, track 0
mov $0x0200, %bx # address = 512, in INITSEG
.equ AX, 0x0200+SETUPLEN
mov $AX, %ax # service 2, nr of sectors
int $0x13 # read it
jnc ok_load_setup # ok - continue
mov $0x0000, %dx
mov $0x0000, %ax # reset the diskette
int $0x13
jmp load_setup
ok_load_setup:
mov $0x03, %ah # read cursor pos
xor %bh, %bh
int $0x10
mov $31, %cx
mov $0x0007, %bx # page 0, attribute 7 (normal)
mov $msg1, %bp
mov $0x1301, %ax # write string, move cursor
int $0x10
ljmp $SETUPSEG, $0
msg1:
.byte 13,10
.ascii "WangWeiOS is booting..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
最后,编译文件并执行,结果如下图,bootsect.s和setup.s的修改是正确的,成功显示语句“Now we are in SETUP”
setup.s 可以获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存储在内存的特定地址,并在屏幕上输出。
在保持setup.s原有功能不变的同时,使其能够读取硬件参数。首先,在原setup.s文件中,有以下代码可以读取各项硬件信息:
mov $INITSEG, %ax # get Cursor Poser
mov %ax, %ds
mov $0x03, %ah
xor %bh, %bh
int $0x10
mov %dx, %ds:0 # get Memory Size
mov $0x88, %ah
int $0x15
mov %ax, %ds:2
mov $0x0000, %ax # get hd0 data
mov %ax, %ds
lds %ds:4*0x41, %si
mov $INITSEG, %ax
mov %ax, %es
mov $0x0004, %di
mov $0x10, %cx
rep
movsb
但是,读取出的数以无符号整数形式存在,需要对这些数进行处理,以十六进制形式显示。可以通过以下函数完成这个操作:
print_hex:
mov $4,%cx
print_digit:
rol $4,%dx
mov $0xe0f,%ax
and %dl,%al
add $0x30,%al
cmp $0x3a,%al
jl outp
add $0x07,%al
outp:
int $0x10
loop print_digit
ret
print_nl:
mov $0xe0d,%ax
int $0x10
mov $0xa,%al
int $0x10
ret
然后,结合以上两点信息,使setup.s在显示"Now we are in SETUP"之后,进一步打印"Memory Size:"、内存大小、“KB”、"Cursor Poser:"等信息,以满足要求。完整的setup.s代码如下:
.code16
.equ SETUPSEG, 0x9020 # this is the current segment
.equ INITSEG, 0x9000 # we move boot here - out of the way
.global _start
ljmp $SETUPSEG, $_start
_start:
mov $0x03,%ah # print "Now we are in SETUP"
xor %bh,%bh
int $0x10
mov $25,%cx
mov $0x0007,%bx
mov $msg2,%bp
mov $0x07c0,%ax
mov %cs,%ax
mov %ax,%es
mov $0x1301,%ax
int $0x10
mov $INITSEG, %ax # get Cursor Poser
mov %ax, %ds
mov $0x03, %ah
xor %bh, %bh
int $0x10
mov %dx, %ds:0
mov $0x88, %ah # get memory size
int $0x15
mov %ax, %ds:2
mov $0x03,%ah # print "Memory Size:"
xor %bh,%bh
int $0x10
mov $12,%cx
mov $0x0007,%bx
mov $msg3,%bp
mov $0x1301,%ax
int $0x10
mov %ds:2 , %dx # print memory size
call print_hex
mov $0x03,%ah # print "KB"
xor %bh,%bh
int $0x10
mov $2,%cx
mov $0x0007,%bx
mov $msg4,%bp
mov $0x1301,%ax
int $0x10
call print_nl
mov $0x03,%ah # print "Memory Size:"
xor %bh,%bh
int $0x10
mov $12,%cx
mov $0x0007,%bx
mov $msg8,%bp
mov $0x1301,%ax
int $0x10
mov %ds:0 , %dx # print memory size
call print_hex
call print_nl
mov $0x03,%ah # print "Cylinders:"
xor %bh,%bh
int $0x10
mov $10,%cx
mov $0x0007,%bx
mov $msg5,%bp
mov $0x1301,%ax
int $0x10
mov $0x0000, %ax # get hd0 data
mov %ax, %ds
lds %ds:4*0x41, %si
mov $INITSEG, %ax
mov %ax, %es
mov $0x0004, %di
mov $0x10, %cx
rep
movsb
mov $INITSEG, %ax # print Cylinders
mov %ax, %ds
mov %ds:4 , %dx
call print_hex
call print_nl
mov $0x03,%ah # print "Headers:"
xor %bh,%bh
int $0x10
mov $8,%cx
mov $0x0007,%bx
mov $msg6,%bp
mov $0x07c0,%ax
mov %cs,%ax
mov %ax,%es
mov $0x1301,%ax
int $0x10
mov $INITSEG, %ax # print Headers
mov %ax, %ds
mov %ds:6 , %dx
call print_hex
call print_nl
mov $0x03,%ah # print "Sectors Per Track:"
xor %bh,%bh
int $0x10
mov $18,%cx
mov $0x0007,%bx
mov $msg7,%bp
mov $0x1301,%ax
int $0x10
mov $INITSEG, %ax # print Sectors Per Track
mov %ax, %ds
mov %ds:18 , %dx
call print_hex
call print_nl
inf_loop:
jmp inf_loop
print_hex:
mov $4,%cx
print_digit:
rol $4,%dx
mov $0xe0f,%ax
and %dl,%al
add $0x30,%al
cmp $0x3a,%al
jl outp
add $0x07,%al
outp:
int $0x10
loop print_digit
ret
print_nl:
mov $0xe0d,%ax
int $0x10
mov $0xa,%al
int $0x10
ret
msg2:
.byte 13,10
.ascii "Now we are in SETUP"
.byte 13,10,13,10
msg3:
.ascii "Memory Size:"
msg4:
.ascii "KB"
msg5:
.ascii "Cylinders:"
msg6:
.ascii "Headers:"
msg7:
.ascii "Sectors Per Track:"
msg8:
.ascii "Cursor Pose:"
.org 510
boot_flag:
.word 0xAA55
最后,编译文件并执行,结果如下图。
- 成功显示信息“Memory Size:3C00KB”。表明3C00KB即为15MB,又有15MB+1MB=16MB。查看bochs配置文件bochs/bochsrc.bxrc,发现有"megs: 16",则配置的bochs为16MB内存,所以这项信息正确。
- 成功显示信息“Cursor Poser:1800”,光标位置为1800,也是正确的。
- 成功显示信息“Cylinders:00cc”、“Headers:0010”、“Sectors Per Track:0026”。查看bochs配置文件bochs/bochsrc.bxrc,发现有“ata0-master: type=disk, path=“$OSLAB_PATH/hdc-0.11.img”, mode=flat, cylinders=204, heads=16, spt=38”,设定的cylinders=204=0x00CC,heads=16=0x0010,spt(Sectors Per Track)=38=0x0026,与所显示的信息相符,因此这几项信息也是正确的。
3.3. 实验报告
有时,继承传统意味着别手蹩脚。 x86 计算机为了向下兼容,导致启动过程比较复杂。 请找出 x86 计算机启动过程中,被硬件强制,软件必须遵守的两个“多此一举”的步骤(多找几个也无妨),说说它们为什么多此一举,并设计更简洁的替代方案。
实模式和保护模式切换:
- 初始时,x86处理器以实模式运行,这是旧版处理器的工作模式。为了支持现代操作系统,处理器在启动后需要切换到保护模式。这一切换包括加载新的段描述符、设置新的全局描述符表(GDT)和禁用一些过时的实模式特性。
- 实模式是为老软件和硬件设计的,而保护模式提供更强大和安全的特性。然而,切换过程增加了启动的复杂性,而在现代系统中,实模式的许多特性是多余的。
- 直接从启动时开始在保护模式下运行,省略实模式和保护模式的切换,以简化启动过程。
BIOS中断调用:
- 在启动过程中,操作系统通常需要与硬件交互。在x86中,传统方式是通过调用BIOS中断请求服务,例如,读取键盘输入、显示字符等。这导致了对中断向量表的依赖,增加了复杂性。
- 随着计算机硬件和操作系统的发展,直接调用硬件驱动程序或使用更现代的接口方式可以更高效地进行硬件交互。
- 直接使用现代的硬件抽象层和驱动程序模型,避免对BIOS中断的依赖。通过在内核中嵌入适当的驱动程序,而不是依赖BIOS中断调用,可以更为简洁地实现相同的功能。