欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

实验1:操作系统的引导

程序员文章站 2022-03-04 15:53:21
...

实验1:操作系统的引导

实验的准备工作操作

  • 解压源码用tar -zxvf hit-oslab-linux-20110823.tar.gz 可以使用-C来指定解压路径,tar -zxvf hit-oslab-linux-20110823.tar.gz -C [path]
  • 编译linux-0.11的源码,在linux-0.11的文件夹下运行make all或者make
  • 在oslab文件目录下运行./run运行bochs 中的linux-0.11
  • 访问linux-0.11里面的文件,使用sudo ./mount-hdc来装载硬盘,然后在hdc中访问,卸载硬盘sudo umount hdc

实验内容

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

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

  • bootsect.s 能在屏幕上打印一段提示信息“XXX is booting…”,其中 XXX 是你给自己的操作系统起的名字,也可以显示一个特色 logo,以表示自己操作系统的与众不同。

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

  1. bootsect.s 能完成 setup.s 的载入,并跳转到 setup.s 开始地址执行。而 setup.s 向屏幕输出一行"Now we are in SETUP"。
  2. setup.s 能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
  3. setup.s 不再加载 Linux 内核,保持上述信息显示在屏幕上即可。

bootsect.s的修改

bootsect.s的功能是将自己从0x7c00处移动到了0x90000处。显示字符loding system ...,然后将磁盘上的setup.ssystem模块加载到内存中来,最后跳转到setup.s所在的位置,执行setup.s

实验内容:

  1. bootsect.s 能在屏幕上打印一段提示信息“XXX is booting…”;

  2. bootsect.s 能完成 setup.s 的载入,并跳转到 setup.s 开始地址执行。

1. bootsect.s 打印字符

首先使用BIOS0x10号中断的子功能0x03获得光标的位置,然后再利用BIOS0x10号中断的子功能0x13打印字符到屏幕上。代码如下:

entry start
start:	
    mov	ah,#0x03	! read cursor pos
	  xor	bh,bh
	  int	0x10

    mov cx,#26
    mov bl,#07
    mov bp,#msg !寻址es:bp
    mov ax,#0x7c0 !因为bootsect的代码放在0x7c00处
    mov es,ax
    mov ax,#0x1301
    int 0x10 
msg:
    .byte 13,10 	!回车和换行
    .ascii "HaoOS is loading ..."
    .byte 13,10,13,10

.org 510
boot_flag:
    .word 0xAA55

BIOS中断指令int前面的代码都是为中断设置一些参数。最后的三行是因为bootsect的大小必须为512字节,所以添加到后面使编译后的文件大小为512字节,最后两个字节为0xAA55

2. 载入setup并跳转

代码如下:

entry start
start:	
    mov	ah,#0x03	! read cursor pos
	  xor	bh,bh
	  int	0x10

    mov cx,#26
    mov bl,#07
    mov bp,#msg !寻址es:bp
    mov ax,#0x7c0 !因为bootsect的代码放在0x7c00处
    mov es,ax
    mov ax,#0x1301
    int 0x10

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+4    ! service 2, nr of sectors
		int	0x13			! read it

  	jmpi 0,#0x07e0      ! jump to setup 0x07e0 = 0x07c0 + 0x0200
                        ! there is set cs to 0x07e0, in the setup the cs be used.

msg:
    .byte 13,10 	!回车和换行
    .ascii "HaoOS is loading ..."
    .byte 13,10,13,10

.org 510
boot_flag:
    .word 0xAA55

在第一部分的代码中添加了load_step的部分,这一部分利用BIOS0x13中断读入setup.s然后利用jmpi跳转到setup.s模块开始的地方。

注意

由于在bootsect中我们并没有移动bootsect的位置,所以在跳转的时候的地址是0x07c0 + 0x0200 = 0x07e0,而不是和linux-0.11中一样跳转到0x90200处。

setup的修改

setup的主要功能是使用BIOS中断读取系统参数,然后放到内存中的指定位置,同时将cpu从实模式转化到保护模式。

实验内容

  1. setup.s 向屏幕输出一行"Now we are in SETUP"。
  2. setup.s 能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
  3. setup.s 不再加载 Linux 内核,保持上述信息显示在屏幕上即可。

1. setup向屏幕输出字符

这一部分代码和bootsect部分类似。有的部分需要修改,比如es寄存器的指向,还有打印的字符的长度。

2. 获取硬件参数并打印

这里如何获取硬件参数,既可以参考linux-0.11的源码,也可以自己查看BIOS中断的手册。我在这里比较迷惑的是如何将获得的参数打印到屏幕上?看了老师的实现后,发现还是比较简单的,主要问题是自己对汇编语言还是不够熟悉。老师的代码如下(大体的思路是先读参数,然后获取光标,打印,获取光标,打印…):

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

这里如何使用的bootsect还是上个实验的代码的话,需要把前面的INITSEG改成0x07e0

疑问:

在上面代码的获取参数部分,不明白是如何把数据保存到特定位置的?如何循环的?

mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0004
mov cx,#0x10
rep
movsb

试着解释:在这里是从原地址到目的地址移动1字节的内容,这里设置cs=16,意思是移动16次,共16字节。