哈工大 操作系统 lab1解答
实验目的
- 熟悉hit-oslab实验环境;
- 建立对操作系统引导过程的深入认识;
- 掌握操作系统的基本开发过程;
- 能对操作系统代码进行简单的控制,揭开操作系统的神秘面纱。
实验内容
此次实验的基本内容是:
- 阅读《Linux内核完全注释》的第6章,对计算机和Linux 0.11的引导过程进行初步的了解;
- 按照下面的要求改写0.11的引导程序bootsect.s
- 有兴趣同学可以做做进入保护模式前的设置程序setup.s。
改写bootsect.s主要完成如下功能:
- bootsect.s能在屏幕上打印一段提示信息“XXX is booting...”,其中XXX是你给自己的操作系统起的名字,例如LZJos、Sunix等
改写setup.s主要完成如下功能:
- bootsect.s能完成setup.s的载入,并跳转到setup.s开始地址执行。而setup.s向屏幕输出一行"Now we are in SETUP"。
- setup.s能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
- setup.s不再加载Linux内核,保持上述信息显示在屏幕上即可。
实验过程
1 bootsect.s在屏幕上打印一段提示信息
linux0.11源码中便有,改改即可。
核心代码如下:
! Print some inane message
ok_load_setup:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#23
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
msg1:
.byte 13,10
.ascii "COS is booting..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
2 bootsect.s完成setup.s的载入,并跳转到setup.s开始地址执行
首先是需要知道内存中的几个模块,比如boot、setup、system;
系统上电后各模块之间的执行顺序:
- 系统上电后,会将bootsec.s加载入0x7C00位置
- bootsec.s将自己移动到0x90000
- 加载setup.s入内存,加载system入0x10000处,然后跳转至setup.s中执行
- setup.s加载一些硬件参数
- 将系统移动至0x0000处,跳转至system中的head.s中执行
需要在bootsect.s中实现跳转到setup.s中执行:
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = 0x9020 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc
ROOT_DEV = 0x306
! move BOOTSEG -> INITSEG
entry _start
_start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG
go: mov ax,cs
mov ds,ax
mov es,ax
! load setup.s
load_setup:
mov dx,#0x0000 ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette
int 0x13
j load_setup
! jump to setup.s
jmpi 0,SETUPSEG
然后在setup.s中显示已经跳转到setup.s中了,如下:(一定要记得重设ds数据段的寄存器,因为msg是在0x90200开始的数据段上的)
entry _start
_start:
init_ds_es:
mov ax,cs
mov ds,ax
mov es,ax
! Print in begining of setup.s
print_cur:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#28
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
msg1:
.ascii "Now we are in SETUP..."
.byte 13,10,13,10
3 setup.s获取硬件参数,将其存放在内存的特定地址,并输出到屏幕上
3.1 获取光标位置
使用bios的0x10号中断实现,ah是功能选择,结果放在dx中,最后将结果放入0x90000。
! get cursor pos
mov ax,#INITSEG
mov ds,ax ! set ds=0x9000
mov ah,#0x03 ! get cursor pos
xor bh,bh
int 0x10 ! interrupt
mov [0],dx ! write cursor pos to 0x90000.
打印:
print_cursor_pos:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#cursor
mov ax,#0x1301 ! write string, move cursor
int 0x10
mov ax,[0]
call print_hex
call print_nl
3.2 获取内存大小
同上,利用bios的0x15号中断:
! get memory size
mov ah,#0x88
int 0x15
mov [2],ax
打印:
print_memmory_size:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#14
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#memory_size
mov ax,#0x1301 ! write string, move cursor
int 0x10
mov ax,[2]
call print_hex
3.3 获取硬盘参数
! 从0x41处拷贝16个字节(磁盘参数表)
mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0004
mov cx,#0x10
rep ! for i in 16:
movsb
打印:
print_hd_info:
! Cylinders
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#16
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#hdinfo
mov ax,#0x1301 ! write string, move cursor
int 0x10
mov ax,[4]
call print_hex
call print_nl
! head
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#head
mov ax,#0x1301 ! write string, move cursor
int 0x10
xor ax, ax
mov al,[4+2]
call print_hex
call print_nl
! sect
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#sect
mov ax,#0x1301 ! write string, move cursor
int 0x10
xor ax, ax
mov al,[0x4+0x0E]
call print_hex
call print_nl
4 setup.s不再加载Linux内核,保持上述信息显示在屏幕上即可
上述工作完成后,直接死循环即可:
dead_loop:
jmp dead_loop
5 完整代码
bootsec.s:
!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
SYSSIZE = 0x3000
!
! bootsect.s (C) 1991 Linus Torvalds
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts.
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 512 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole sectors at a time whenever possible.
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = 0x9020 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc
ROOT_DEV = 0x306
! move BOOTSEG -> INITSEG
entry _start
_start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG
go: mov ax,cs
mov ds,ax
mov es,ax
! load setup.s
load_setup:
mov dx,#0x0000 ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette
int 0x13
j load_setup
! Print some inane message
ok_load_setup:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#23
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! jump to setup.s
jmpi 0,SETUPSEG
sectors:
.word 0
msg1:
.byte 13,10
.ascii "COS is booting..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
setup.s:
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = 0x9020 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc
ROOT_DEV = 0x306
entry _start
_start:
init_ds_es:
mov ax,cs
mov ds,ax
mov es,ax
! Print in begining of setup.s
print_cur:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#28
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! get cursor pos
mov ax,#INITSEG
mov ds,ax ! set ds=0x9000
mov ah,#0x03 ! get cursor pos
xor bh,bh
int 0x10 ! interrupt
mov [0],dx ! write cursor pos to 0x90000.
! get memory size
mov ah,#0x88
int 0x15
mov [2],ax
! 从0x41处拷贝16个字节(磁盘参数表)
mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0004
mov cx,#0x10
rep ! for i in 16:
movsb
reset_ds_es:
mov ax,#INITSEG
mov ds,ax
mov ax,#SETUPSEG
mov es,ax
print_cursor_pos:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#cursor
mov ax,#0x1301 ! write string, move cursor
int 0x10
mov ax,[0]
call print_hex
call print_nl
print_memmory_size:
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#14
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#memory_size
mov ax,#0x1301 ! write string, move cursor
int 0x10
mov ax,[2]
call print_hex
print_hd_info:
! Cylinders
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#16
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#hdinfo
mov ax,#0x1301 ! write string, move cursor
int 0x10
mov ax,[4]
call print_hex
call print_nl
! head
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#head
mov ax,#0x1301 ! write string, move cursor
int 0x10
xor ax, ax
mov al,[4+2]
call print_hex
call print_nl
! sect
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#sect
mov ax,#0x1301 ! write string, move cursor
int 0x10
xor ax, ax
mov al,[0x4+0x0E]
call print_hex
call print_nl
dead_loop:
jmp dead_loop
! 以16进制方式打印栈顶的16位数, AX
print_hex:
mov cx,#4 ! 4个十六进制数字
mov dx,ax ! 将ax所指的值放入dx中
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
ret
! print \n
print_nl:
mov ax,#0xe0d ! CR
int 0x10
mov al,#0xa ! LF
int 0x10
ret
msg1:
.ascii "Now we are in SETUP..."
.byte 13,10,13,10
cursor:
.ascii "cursor: 0x"
memory_size:
.ascii "memory_size:0x "
hdinfo:
.ascii "KB"
.byte 13,10
.ascii "Cylinders:0x"
head:
.ascii "Headers:0x"
sect:
.ascii "Secotrs:0x"
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
使用make BootImage
编译不过去,是要改一下tools/build.c:
判断system模块有没有,没有直接跳过后边读取即可:
if (strcmp(argv[3], "none") == 0) {
return 0;
}
加入位置如下:
实验结果
编译:
运行结果:
查看bochs配置文件:
romimage: file=$OSLAB_PATH/bochs/BIOS-bochs-latest
megs: 16
vgaromimage: file=$OSLAB_PATH/bochs/vgabios.bin
floppya: 1_44="$OSLAB_PATH/linux-0.11/Image", status=inserted
ata0-master: type=disk, path="$OSLAB_PATH/hdc-0.11.img", mode=flat, cylinders=204, heads=16, spt=38
boot: a
log: $OSLAB_PATH/bochsout.txt
keyboard: type=mf, serial_delay=200, paste_delay=100000
cpu: count=1, ips=4000000
mouse: enabled=0
private_colormap: enabled=0
fullscreen: enabled=0
screenmode: name="sample"
其中有cylinders=204, heads=16, spt=38
,和系统输出的获取到的参数是一样的,因此本次实验是成功的!
reference
[1] 实验指导书