哈工大 操作系统 lab1 操作系统的引导解答

实验目的

  • 熟悉hit-oslab实验环境;
  • 建立对操作系统引导过程的深入认识;
  • 掌握操作系统的基本开发过程;
  • 能对操作系统代码进行简单的控制,揭开操作系统的神秘面纱。

实验内容

此次实验的基本内容是:

  1. 阅读《Linux内核完全注释》的第6章,对计算机和Linux 0.11的引导过程进行初步的了解;
  2. 按照下面的要求改写0.11的引导程序bootsect.s
  3. 有兴趣同学可以做做进入保护模式前的设置程序setup.s。

改写bootsect.s主要完成如下功能:

  1. bootsect.s能在屏幕上打印一段提示信息“XXX is booting...”,其中XXX是你给自己的操作系统起的名字,例如LZJos、Sunix等

改写setup.s主要完成如下功能:

  1. bootsect.s能完成setup.s的载入,并跳转到setup.s开始地址执行。而setup.s向屏幕输出一行"Now we are in SETUP"。
  2. setup.s能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
  3. 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;

image-20211205110121853

系统上电后各模块之间的执行顺序:

image-20211205110302115

  1. 系统上电后,会将bootsec.s加载入0x7C00位置
  2. bootsec.s将自己移动到0x90000
  3. 加载setup.s入内存,加载system入0x10000处,然后跳转至setup.s中执行
  4. setup.s加载一些硬件参数
  5. 将系统移动至0x0000处,跳转至system中的head.s中执行

image-20211205110339803

需要在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;
}

加入位置如下:

image-20211205112753847

实验结果

编译:

image-20211205113026925

运行结果:

image-20211205113131567

查看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] 实验指导书

posted @ 2021-12-05 11:48  zju_cxl  阅读(181)  评论(0编辑  收藏  举报