哈工大操作系统实验(一)系统引导


实验一内容:

  1. 阅读《Linux内核完全注释》的第6章,对计算机和Linux0.11的引导过程进行初步的了解;
  2. 按照下面的要求改写0.11的引导程序bootsect.s
  3. 修改build.c,以便可以使用 make BootImage命令

实验步骤:

  1. 修改bootsect.s中的提示信息及相关代码;
  2. 在目录linux-0.11\boot下,分别用命令as86 -0 -a -o bootsect.obootsect.s和ld86 -0 -s -o bootsect bootsect.o编译和链接bootsect.s,生成bootsect文件;
  3. 用命令dd bs=1 if=bootsect of=Image skip=32去掉bootsect的文件头生成Image文件,并复制Image到linux-0.11目录下;
  4. 运行run命令验证运行结果是否正确;
  5. 重新用make命令生成BootImage,结合提示信息和makefile文件修改build.c;
  6. 验证:用make是否能成功生成BootImage。

(一)改写bootsect.s主要完成的如下功能:

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

关键代码:

! 首先读入光标位置
    mov ah,#0x03        
    xor bh,bh
    int 0x10

    ! 显示字符串“LZJos is running...”
    mov cx,#25          ! 要显示的字符串长度
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg1
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

inf_loop:
    jmp inf_loop        ! 后面都不是正经代码了,得往回跳呀
    ! msg1处放置字符串

msg1:  //msg1 为要输出的字符串  ,13为换行,10为回车
    .byte 13,10         ! 换行+回车
    .ascii "LZJos is running..."
    .byte 13,10,13,10           ! 两对换行+回车
    !设置引导扇区标记0xAA55
    .org 510
boot_flag:
    .word 0xAA55            ! 必须有它,才能引导

我们需要改的就是msg1中的内容,以及mov cx,#25 这里的字符串长度
注意计算字符串的长度;
比如这样

 Print some inane message

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

    mov cx,#27                  !修改这里
    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 "Jinux OS is booting ..."
    .byte 13,10,13,10

然后修改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,忽略所有与system有关的工作,或者在该写system的位置都写上“0”。

要修改的部分在build.c文件尾端:
bulid.c 之前为:

    if ((id=open(argv[3],O_RDONLY,0))<0)  
            die("Unable to open 'system'");  
    //  if (read(id,buf,GCC_HEADER) != GCC_HEADER)  
    //      die("Unable to read header of 'system'");  
    //  if (((long *) buf)[5] != 0)  
    //      die("Non-GCC header of 'system'");  
        for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )  
            if (write(1,buf,c)!=c)  
                die("Write call failed");  
        close(id);  
        fprintf(stderr,"System is %d bytes.\n",i);  
        if (i > SYS_SIZE*16)  
            die("System is too big");  
        return(0);

修改为:

     if(strcmp("none",argv[3]) == 0)    //添加判断
          return 0;
    if ((id=open(argv[3],O_RDONLY,0))<0)
          die("Unable to open 'system'");
//    if (read(id,buf,GCC_HEADER) != GCC_HEADER)
//        die("Unable to read header of 'system'");
//    if (((long *) buf)[5] != 0)
//        die("Non-GCC header of 'system'");
    for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
        if (write(1,buf,c)!=c)
            die("Write call failed");
    close(id);
    fprintf(stderr,"System is %d bytes.\n",i);
    if (i > SYS_SIZE*16)
        die("System is too big");

    return(0);

现在,显示部分就做好了;

编译与运行:

cd ~/oslab/linux-0.11/boot/
as86 -0 -a -o bootsect.o bootsect.s
ld86 -0 -s -o bootsect bootsect.o

其中bootsect.o是中间文件。bootsect是编译、链接后的目标文件。
需要留意的文件是bootsect的文件大小是544字节,而引导程序必须要正好占用一个磁盘扇区,即512个字节。造成多了32个字节的原因是ld86产生的是Minix可执行文件格式,

struct exec {
    unsigned char a_magic[2];  //执行文件魔数
    unsigned char a_flags;
    unsigned char a_cpu;       //CPU标识号
    unsigned char a_hdrlen;    //头部长度,32字节或48字节
    unsigned char a_unused;
    unsigned short a_version;
    long a_text; long a_data; long a_bss; //代码段长度、数据段长度、堆长度
    long a_entry;    //执行入口地址
    long a_total;    //分配的内存总量
    long a_syms;     //符号表大小 
};

算一算:6 char(6字节)+1 short(2字节)+6 long(24字节)=32,正好是32个字节,去掉这32个字节后就可以放入引导扇区了(这是tools/build.c的用途之一)。

dd bs=1 if=bootsect of=Image skip=32
//该命令是删除bootsect那32个字节并保存为Image文件
cp Image ../Image
//复制到 linux-0.11/目录中

../run //运行oslab目录下的run命令

神奇画面出现了:

哈工大操作系统实验(一)系统引导_第1张图片
image.png

(二)改写bootsect.s 读入setup.s

我们需要改写bootsect.s 使输出 “XXX is booting..." 后跳转到setup.s 中,并在setup.s 中输出"Now we are in SETUP"

开撸:

既然输出"XXX is booting..."后跳转到setup.s 那 除了数据段 后面的代码就是多余的(当然 ,你留着也行)

.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

SETUPLEN = 4                ! setup程序代码占用扇区数
BOOTSEG  = 0x07c0           ! bootsect程序代码所在内存原始地址
INITSEG  = 0x9000           ! 将bootsect移动到0x9000处
SETUPSEG = 0x9020           ! setup程序开始的地址

entry _start
_start:

! 下面这段代码将自身复制到0x9000处
    mov ax,#BOOTSEG
    mov ds,ax
    mov ax,#INITSEG
    mov es,ax
    mov cx,#256
    sub si,si
    sub di,di
    rep
    movw
    
! 复制完成从0x9000的go标号处开始执行
    jmpi    go,INITSEG
go: mov ax,cs
    mov ds,ax  !设置ds=es=cs
    mov es,ax

! 加载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

ok_load_setup:

!输出一些信息

    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    
    mov cx,#15
    mov bx,#0x000c      ! page 0, attribute c 
    mov bp,#msg1        ! es:bp 指向待显示 字符串
    mov ax,#0x1301      ! write string, move cursor
    int 0x10
!输出后开始执行setup代码
    jmpi 0,SETUPSEG

msg1:
    .byte 13,10
    .ascii "hello os!"
    .byte 13,10,13,10

.org 510
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

BOOTSEG  = 0x07c0           ! original address of boot-sector
INITSEG  = 0x9000           ! we move boot here - out of the way
SETUPSEG = 0x9020           ! setup starts here

entry _start
_start:

!设置cs=ds=es
    mov ax,cs
    mov ds,ax
    mov es,ax

    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    
    mov cx,#28
    mov bx,#0x000c      ! page 0, attribute c 
    mov bp,#msg1
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

! ok, the read went well so we get current cursor position and save it for
! posterity.
! 获取光标位置 =>  0x9000:0
    mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov ah,#0x03    ! read cursor pos
    xor bh,bh
    int 0x10        ! save it in known place, con_init fetches
    mov [0],dx      ! it from 0x90000.

! Get memory size (extended mem, kB)
! 获取拓展内存大小 => 0x9000:2
    mov ah,#0x88
    int 0x15
    mov [2],ax

! Get hd0 data
! 获取硬盘参数 => 0x9000:80  大小:16B
    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0080
    mov cx,#0x10
    rep
    movsb

! 前面修改了ds寄存器,这里将其设置为0x9000
    mov ax,#INITSEG
    mov ds,ax
    mov ax,#SETUPSEG
    mov es,ax  

!显示 Cursor POS: 字符串
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    mov cx,#11
    mov bx,#0x0007      ! page 0, attribute c 
    mov bp,#cur
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

!调用 print_hex 显示具体信息
    mov ax,[0]
    call print_hex
    call print_nl

!显示 Memory SIZE: 字符串
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    mov cx,#12
    mov bx,#0x0007      ! page 0, attribute c 
    mov bp,#mem
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

!显示 具体信息
    mov ax,[2]
    call print_hex

!显示相应 提示信息
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    mov cx,#25
    mov bx,#0x0007      ! page 0, attribute c 
    mov bp,#cyl
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

!显示具体信息
    mov ax,[0x80]
    call print_hex
    call print_nl

!显示 提示信息
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    mov cx,#8
    mov bx,#0x0007      ! page 0, attribute c 
    mov bp,#head
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

!显示 具体信息
    mov ax,[0x80+0x02]
    call print_hex
    call print_nl

!显示 提示信息
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10
    mov cx,#8
    mov bx,#0x0007      ! page 0, attribute c 
    mov bp,#sect
    mov ax,#0x1301      ! write string, move cursor
    int 0x10

!显示 具体信息
    mov ax,[0x80+0x0e]
    call print_hex
    call print_nl

!死循环
l:  jmp l

!以16进制方式打印ax寄存器里的16位数
print_hex:
    mov cx,#4   ! 4个十六进制数字
    mov dx,ax   ! 将ax所指的值放入dx中,ax作为参数传递寄存器
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_nl:
    mov ax,#0xe0d
    int 0x10
    mov al,#0xa
    int 0x10
    ret

msg1:
    .byte 13,10
    .ascii "Now we are in setup..."
    .byte 13,10,13,10
cur:
    .ascii "Cursor POS:"
mem:
    .ascii "Memory SIZE:"
cyl:
    .ascii "KB"
    .byte 13,10,13,10
    .ascii "HD Info"
    .byte 13,10
    .ascii "Cylinders:"
head:
    .ascii "Headers:"
sect:
    .ascii "Secotrs:"

.text
endtext:
.data
enddata:
.bss
endbss:

修改后,进入linux-0.11目录重新一键编译

#编译
make BootImage

#运行
../run

好啦,惊喜出现:

哈工大操作系统实验(一)系统引导_第2张图片
image.png

你可能感兴趣的:(哈工大操作系统实验(一)系统引导)