实验2 ——操作系统的启动
载入环境
cd /home/shiyanlou/oslab/
tar -zxvf hit-oslab-linux-20110823.tar.gz \
-C /home/shiyanlou/
ls -al
cd ./linux-0.11/
cd ./boot/
首先进入oslab文件夹,找到目标压缩包并解压,查看是否解压成功后进入系统。
改写bootsect.s并显示
mv bootsect.s bootsect-old.s
gedit bootsect.s
创建一个新的.s文件,用于显示新值并对bootsect.s文件进行写入
图1 编写bootsect.s文件
具体代码如下:
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
inf_loop:
jmp inf_loop
msg1:
.byte 13,10
.ascii "Hello OS world, my name is LZJ"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
在这段代码中的核心部分是修改参数,首先用int找到并读入光标位置,预先设定并写出要展示的字符串长度。mgs1是我们设定的字符串,除此之外还需要考虑三个换行与回车,一共六个字符。输出语句的长度为30,总计36,故修改为mov cx,#36。设置一个循环,然后修改输出的字符串,最后为了保证boot_flag在最后两个字节,修改.org为510,同时设置引导扇区标记0xAA55引导。
接下来,让编写完成的bootsect.s 代码在开发环境中编译,并将编译后的目标文件做成 Image 文件:
as86 -0 -a -o bootsect.o bootsect.s
ld86 -0 -s -o bootsect bootsect.o
dd bs=1 if=bootsect of=Image skip=32
首先生成与GNU as和ld部分兼容的代码,然后用ld886去除最后生成的可执行文件中的符号信息,最后把32个字节的文件头部去掉,便可以放入引导扇区。
最后将处理完成的文件重命名,并输出结果:
cp ./Image ../Image
../../run
将刚刚生成的 Image 复制到 linux-0.11 目录下,并执行oslab目录中的 run 脚本。
图3 改写bootsect.s显示图
bootsect.s 读入 setup
首先将原来的setup.s重命名,并编写一个新的setup.s用于输出信息。
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
.ascil "Now we are in SETUP"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
此处设立新的字符串msg2,并且修改es的值使其可以使用cs(以ax为中间变量)。最后就是修改此处需要输出的字符。
在对setup.s进行改写之后还需要对bootsect.s进行修改,载入setup.s的关键代码使其可以启动setup.s。
SETUPLEN=2
SETUPSEG=0x07e0
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:
mov dx,#0x0000
mov cx,#0x0002
mov bx,#0x0200
mov ax,#0x0200+SETUPLEN
int 0x13
jnc ok_load_setup
mov dx,#0x0000
mov ax,#0x0000
int 0x13
jmp load_setup
ok_load_setup:
jmpi 0,SETUPSEG
msg1:
.byte 13,10
.ascii "Hello OS world, my name is ZJC"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
在这里设置了驱动、磁盘以及读入的内存地址,由于我们需要两个扇区大小内存,设置SETUPLEN=2。我们没有将bootsect移动到0x9000,因此跳转后的段地址为0x07e0。为了跳到setup进行执行,设置SETUPSEG为0x07e0。
_start部分为原版bootsect.s中的代码,而我们需要在其中添加载入setup.s的关键代码:load_setup。分别设置扇区号、磁道、读入的内存地址、扇区个数(其中SETUPLEN扇区个数,由前可知设置为2),接着利用0x13号BIOS中断读入2个setup.s扇区。如果成功跳转则进入ok_load_setup,跳转到SETUPSEG位置处执行setup。后续程序与原版bootsect一致。
图4 改写bootsect.s截图
现在我们获得了setup.s和新的bootsect.s,需要对这两个文件进行编译.为了提高效率我们使用Makefile进行编译,在这之前需要对tools/build.c文件进行修改(正常情况下该文件生成整个系统内核镜像)。修改的目的是使argv[3]是“none"的时候,只写bootsect和setup,忽略所有与system有关的工作,或者在该写system的位置都写上0。把build.c的全局变量注释掉即可。
图5 改写build.c截图
之后再进行编译,输出结果如图:
图6 setup显示成功截图
实验成功。
setup获取硬件参数及显示硬件参数
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
setup.s 将获得硬件参数放在内存的 0x90000 处,用 ah=#0x03 调用 0x10 中断可以读出光标的位置,用 ah=#0x88 调用0x15 中断可以读出内存的大小。 int 0x41 的中断向量位置存放第一个硬盘的基本参数表。这部分就是显示硬件参数的关键代码。
setup正确显示硬件参数
INITSEG = 0x9000entry _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(...) mov dx,[12] call print_hexinf_loop: jmp inf_loopprint_hex: mov cx,#4print_digit: rol dx,#4 mov ax,#0xe0f and al,dl add al,#0x30 cmp al,#0x3a jl outp add al,#0x07outp: int 0x10 loop print_digit retprint_nl: mov ax,#0xe0d ! CR int 0x10 mov al,#0xa ! LF int 0x10 retmsg2: .byte 13,10 .ascii "NOW we are in SETUP" .byte 13,10,13,10msg_cursor: .byte 13,10 .ascii "Cursor position:"msg_memory: .byte 13,10 .ascii "Memory Size:"msg_cyles: .byte 13,10 .ascii "Cyls:"msg_heads: .byte 13,10 .ascii "Heads:"msg_sectors: .byte 13,10 .ascii "Sectors:"msg_kb: .ascii "KB".org 510boot_flag: .word 0xAA55
将原二进制数每 4 位划成一组,按组求对应的 ASCII 码并让显示器显示。调用BIOS的INT 0x10中断读出光标位置,即AH=0x0E,其中AL为要显示的字符的ASCII码,依次调用出需要显示的部件。接着调用 print_bx 和 print_nl将获得硬件参数打印到屏幕上。
结果截图如下:
图7 setup显示硬件参数结果截图