目录
(一)修改 bootsect.s 代码,实现向屏幕输出信息 “Hello OS world, my name is XXX”。
(二)修改 setup.s 代码,向屏幕输出一行信息 "Now we are in SETUP",并实现 bootsect.s 对 setup.s 的载入。
一、实验内容
1、阅读《Linux内核完全注释:基于0.11内核》第 6 章,对计算机和 Linux 0.11 的引导过程进行初步了解。
2、改写 bootsect.s 代码,实现:
在屏幕上打印一段自己设定的信息,如 “XXX is booting...” 。
3、改写 setup.s 代码,实现:
① bootsect.s 完成 setup.s 的载入,并跳转到 setup.s 开始地址执行。而 setup.s 向屏幕输出信息 "Now we are in SETUP" 。
② setup.s 能够获取至少一个基本的硬件参数(如 内存参数、显卡参数、硬盘参数 等),将其存放在内存的特定地址,并输出到屏幕上。
③ setup.s 不再加载 Linux 内核,保持上述信息显示在屏幕上即可。
二、实验准备
1、实验环境
蓝桥云课实验楼:操作系统原理与实践_Linux - 蓝桥云课
2、相关代码文件
Linux-0.11/boot 目录下的 bootsect.s、setup.s 和 Linux-0.11/tools 目录下 build.c 是本实验会涉及到的源文件,它们的功能详见《注释》的 6.2、6.3 节和 16 章。
3、引导程序的运行环境
引导程序由 BIOS 加载并运行。它活动时,操作系统还不存在,整台计算机的所有资源都由它掌控,此时能利用的功能只有 BIOS 中断调用。实验中主要使用 BIOS 0x10 和 0x13 中断。
三、正式实验
(一)修改 bootsect.s 代码,实现向屏幕输出信息 “Hello OS world, my name is XXX”。
1. 打开 oslab/linux-0.11/boot 目录下的 bootsect.s 源码文件。
2. 直接删除原来所有的代码,替换以下新代码并保存。
entry _start
_start:
!使用 0x10 号中断读取光标位置
mov ah,#0x03
xor bh,bh
int 0x10
!显示字符串 “Hello OS world, my name is LQN”
!cx 为要显示字符串的长度(除字符串外还有3个换行+1个回车)
mov cx,#36
mov bx,#0x0007
! es:bp 为显示字符串的地址
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 LQN"
!两对 “回车+换行”
.byte 13,10,13,10
!boot_flag 必须在最后两个字节
.org 510
!设置引导扇区标记 0xAA55(必须设置,否则无法成功引导)
boot_flag:
.word 0xAA55
3. 打开终端(Ctrl + Alt + T),输入以下命令 编译 新编写的 bootsect.s。
as86 -0 -a -o bootsect.o bootsect.s
- bootsect.o 为 bootsect.s 编译生成的中间文件
4. 输入以下命令 链接 生成的 bootsect.o 文件。
ld86 -0 -s -o bootsect bootsect.o
- bootsect 是编译、链接后的生成的目标文件
5. 输入 ls -l 列出文件信息,可以看出 boot 目录下多了一个中间文件 bootsect.o 和一个目标文件 bootsect。
6. 可以看出这里 bootsect 文件大小是 544 字节,而引导程序必须要正好占用一个磁盘扇区,即 512 个字节,这里多出了 32 个字节,所以输入以下指令去掉文件头的 32 个字节。
dd bs=1 if=bootsect of=Image skip=32
生成的 Image 就是去掉文件头的 bootsect,刚好512字节:
7. 确保上一步生成的 Image 文件位于 linux-0.11 目录下,如果没有就拷贝一下。
cp ./Image ../Image
8. 输入以下指令执行 oslab 目录下的 run 脚本。
../../run
9. 运行结果:
(二)修改 setup.s 代码,向屏幕输出一行信息 "Now we are in SETUP",并实现 bootsect.s 对 setup.s 的载入。
1. 这里 setup.s 的功能和前面 bootsect.s 的功能基本一致,所以可以直接拷贝前面的 bootsect.s 代码,将显示的信息改为 “Now we are in SETUP” 即可 ,完整 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
.ascii "Now we are in SETUP"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
2. 编写 bootsect.s 中 载入 setup.s 的代码,完整 bootsect.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 LQN"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
3. 再次编译。
现在有两个文件 bootsect.s 和 setup.s 需要编译、链接。如果还是使用之前的手动编译,效率较低(先 as86 再 ld86 )。所以可以借助 Makefile 提高效率。
进入 linux-0.11 目录,输入以下命令:
make BootImage
编译失败,显示以下结果:
Unable to open 'system'
make: *** [BootImage] Error 1
- 因为 make 指令根据 Makefile 的指引执行了 tools/build.c ,它是为生成整个内核的镜像文件而设计的,没考虑我们只需要 bootsect.s 和 setup.s 的情况。它在向我们要求 “系统” 的核心代码,所以为完成实验需要对 build.c 进行一点修改。
4. 修改 build.c 源码,打开 linux-0.11/tools/build.c ,将框中内容注释掉。
- build.c 从命令行参数得到 bootsect、setup 和 system 内核的文件名,将三者做简单的整理后一起写入 Image。其中 system 是第三个参数 - argv[3]
- 当 make all 或 makeall 时,该参数传过来的是正确的文件名,build.c 会打开它,将内容写入 Image。而 make BootImage 时,传过来的是字符串 "none"
所以,修改 build.c 的思路就是当 argv[3] 是 "none" 的时候,只将 bootsect 和 setup 写入 Image,忽略所有与 system 有关的工作,或者在该写 system 的位置都写上 “0”。
5. 回到 linux-0.11 目录,再次执行编译命令。
make BootImage
6. 编译成功后,再次执行 run 脚本,运行结果:
(三)修改 setup.s 代码获取基本硬件参数。
1. setup.s 参考代码。
INITSEG = 0x9000
entry _start
_start:
! Print "NOW we are in SETUP"
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 ax,cs
mov es,ax
! init ss:sp
mov ax,#INITSEG
mov ss,ax
mov sp,#0xFF00
! 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,cs
mov es,ax
mov ax,#INITSEG
mov ds,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 dx,[0]
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 dx,[2]
call print_hex
! Add KB
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#2
mov bx,#0x0007
mov bp,#msg_kb
mov ax,#0x1301
int 0x10
! Cyles
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#7
mov bx,#0x0007
mov bp,#msg_cyles
mov ax,#0x1301
int 0x10
mov dx,[4]
call print_hex
! Heads
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#8
mov bx,#0x0007
mov bp,#msg_heads
mov ax,#0x1301
int 0x10
mov dx,[6]
call print_hex
! Secotrs
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007
mov bp,#msg_sectors
mov ax,#0x1301
int 0x10
mov dx,[12]
call print_hex
inf_loop:
jmp inf_loop
print_hex:
mov cx,#4
print_digit:
rol dx,#4
mov ax,#0xe0f
and al,dl
add al,#0x30
cmp al,#0x3a
jl outp
add al,#0x07
outp:
int 0x10
loop print_digit
ret
print_nl:
mov ax,#0xe0d ! CR
int 0x10
mov al,#0xa ! LF
int 0x10
ret
msg2:
.byte 13,10
.ascii "NOW we are in SETUP"
.byte 13,10,13,10
msg_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 510
boot_flag:
.word 0xAA55
2. 运行结果: