目录

哈工大操作系统 实验楼实验二 操作系统的引导

实验楼实验二 操作系统的引导

原文地址

bootsec.s文件

编写bootsec.s文件

oslab/linux-0.11/boot目录下将原来的bootsect.s改名为bootsect.s.bak,即执行命令

$ mv bootsect.s bootsect.s.bak

并新建一个并编写bootsect.s,命令如下:

$ vim bootsect.s

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
entry _start
_start:
    !读入光标位置
    mov ah,#0x03
    xor bh,bh
    int 0x10

    !字符串msg1长度
    mov cx,#378 
    !第 0 页,属性 7(正常)
    mov bx,#0x0007
    !字符串msg1
    mov bp,#msg1 
    !es:bp 是显示字符串的地址
    !相比与 linux-0.11 中的代码,需要增加对 es 的处理,因为原代码中在输出之前已经处理了 es
    mov ax,#0x07c0
    mov es,ax 
    !写入字符串,移动光标
    mov ax,#0x1301
    int 0x10!BIOS中断
! 设置一个无限循环
inf_loop:
    jmp inf_loop
msg1:
    .byte   13,10
    .ascii "#     #"
    .byte   13,10
    .ascii "##   ##   #   #   ####    #####     #     ####     ##    #"
    .byte   13,10
    .ascii "# # # #    # #   #          #       #    #    #   #  #   #"
    .byte   13,10
    .ascii "#  #  #     #     ####      #       #    #       #    #  #"
    .byte   13,10
    .ascii "#     #     #         #     #       #    #       ######  #"
    .byte   13,10
    .ascii "#     #     #    #    #     #       #    #    #  #    #  #"
    .byte   13,10
    .ascii "#     #     #     ####      #       #     ####   #    #  ######"
    .byte   13,10,13,10
! boot_flag 必须在最后两个字节
.org 510
! 设置引导扇区标记 0xAA55
! 必须有它,才能引导
boot_flag:
    .word   0xAA55

编译

boot目录下,执行下面的命令:

  • 编译

$ as86 -0 -a -o bootsect.o bootsect.s

-0(是数字'0’)以16位代码段运行,当使用了高于8086指令集的指令时警告

-a 使汇编程序部分兼容于Minix asld.交换了[]与()的用法,并且改变了一些16位跳转与调用的语法(“jmp @(bx)” 就成了一个合法的指令),生成与 GNU as 和 ld 部分兼容的代码

  • 链接

$ ld86 -0 -s -o bootsect bootsect.o

-0(是数字零)产生具有16bit魔数的头结构,并且对-lx选项使用i86子目录

-s 告诉链接器 ld86 去除最后生成的可执行文件中的符号信息

  • 生成文件
1
2
3
-rwxrwxr-x  1 root root   544 Aug  5 18:09 bootsect*
-rw-rw-r--  1 root root   511 Aug  5 18:08 bootsect.o
-rw-rw-r--  1 root root   862 Aug  5 18:08 bootsect.s

其中 bootsect.o 是中间文件;bootsect 是编译、链接后的目标文件,也就是可执行文件

运行

需要注意的文件是 bootsect 文件大小是 544 字节,而引导程序必须要正好占用一个磁盘扇区,即 512 个字节。多出来的32字节是Minix可执行文件头部,去掉这 32 个字节后就可以放入引导扇区了。可以使用dd命令进行处理

$ dd -bs=1 if=bootsect of=Image skip=32

之后复制该文件到linux-0.11目录下,并运行系统查看显示结果

$ cp ./Image ../Image

运行
!注意目前还位于/oslab/linux-0.11/boot

$ ../../run

运行截图:

https://jsdelivr.codeqihan.com/gh/MysticalDream/images/assets/202311122258404.png

setup.s文件

编写setup.s文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
entry _start
_start:
    !读入光标位置
    mov ah,#0x03
    xor bh,bh
    int 0x10

    !字符串msg2长度
    mov cx,#25 
    !第 0 页,属性 7(正常)
    mov bx,#0x0007
    !字符串msg2
    mov bp,#msg2 
    !es:bp 是显示字符串的地址
    !使用cs的值修改es的值
    mov ax,cs
    mov es,ax 
    !写入字符串,移动光标
    mov ax,#0x1301
    !BIOS中断
    int 0x10
! 设置一个无限循环
inf_loop:
    jmp inf_loop
msg2:
    .byte   13,10
    .ascii "Now we are in SETUP"
    .byte   13,10,13,10
! boot_flag 必须在最后两个字节
.org 510
! 设置引导扇区标记 0xAA55
! 必须有它,才能引导
boot_flag:
    .word   0xAA55

修改前面写的 bootsect.s

我们需要编写 bootsect.s 中载入 setup.s 的关键代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
load_setup:
! 设置驱动器和磁头(drive 0, head 0): 软盘 0 磁头 0
    mov dx,#0x0000
! 设置扇区号和磁道(sector 2, track 0): 0 磁道、2 扇区
    mov cx,#0x0002
! 设置读入的内存地址:BOOTSEG+address = 512,偏移512字节
    mov bx,#0x0200
! 设置读入的扇区个数(service 2, nr of sectors)
! SETUPLEN是读入的扇区个数Linux 0.11 设置的是 4
! 我们不需要那么多,我们设置为 2(因此还需要添加变量 SETUPLEN=2
    mov ax,#0x0200+SETUPLEN
! 应用 0x13  BIOS 中断读入 2  setup.s扇区
    int 0x13
! 读入成功,跳转到 ok_load_setup: ok - continue
    jnc ok_load_setup
! 软驱、软盘有问题才会执行到这里。我们的镜像文件比它们可靠多了
    mov dx,#0x0000
! 否则复位软驱 reset the diskette
    mov ax,#0x0000
    int 0x13
! 重新循环,再次尝试读取
    jmp load_setup
ok_load_setup:
! 接下来要干什么?当然是跳到 setup 执行。
! 要注意:我们没有将 bootsect 移到 0x9000,因此跳转后的段地址应该是 0x7e0
! 即我们要设置 SETUPSEG=0x07e0

个人理解: load_setup主要是将指定位置(0磁道2扇区)和数量(这里是SETUPLEN=2)的扇区读入内存(es:bx=07c0H:0200H即地址(07c0H✖10)+200H=07e00H),再将(cs:ip指向07e0:0000)执行setup部分的代码

补充: 入口参数: AH=02H(功能号:02H 表示读扇区)

AL=扇区数

CH=柱面

CL=扇区

DH=磁头

DL=驱动器,00H ~ 7FH:软盘;80H ~ 0FFH:硬盘

ES:BX=缓冲区的地址(读出数据的缓冲区地址) 出口参数: 若出错则CF示志置位 CF=0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码,参见下图:

https://jsdelivr.codeqihan.com/gh/MysticalDream/images/assets/202311122258224.png

编写完成后的完整代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
SETUPLEN=2
SETUPSEG=0x07e0
entry _start
_start:
    !读入光标位置
    mov ah,#0x03
    xor bh,bh
    int 0x10

    !字符串msg1长度
    mov cx,#378 
    ! 0 页,属性 7(正常)
    mov bx,#0x0007
    !字符串msg1
    mov bp,#msg1 
    !es:bp 是显示字符串的地址
    !相比与 linux-0.11 中的代码,需要增加对 es 的处理,因为原代码中在输出之前已经处理了 es
    mov ax,#0x07c0
    mov es,ax 
    !写入字符串,移动光标
    mov ax,#0x1301
    int 0x10!BIOS中断
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 "#     #"
    .byte   13,10
    .ascii "##   ##   #   #   ####    #####     #     ####     ##    #"
    .byte   13,10
    .ascii "# # # #    # #   #          #       #    #    #   #  #   #"
    .byte   13,10
    .ascii "#  #  #     #     ####      #       #    #       #    #  #"
    .byte   13,10
    .ascii "#     #     #         #     #       #    #       ######  #"
    .byte   13,10
    .ascii "#     #     #    #    #     #       #    #    #  #    #  #"
    .byte   13,10
    .ascii "#     #     #     ####      #       #     ####   #    #  ######"
    .byte   13,10,13,10
! boot_flag 必须在最后两个字节
.org 510
! 设置引导扇区标记 0xAA55
! 必须有它,才能引导
boot_flag:
    .word   0xAA55

再次编译

借助 Makefile 编译、链接bootsect.s和setup.s

在此之前我们需要修改一下tools/build.c,因为这个是生成整个系统镜像的,我们只需要bootsect.ssetup.s

注释掉build.c后面几段代码,如下图所示

https://jsdelivr.codeqihan.com/gh/MysticalDream/images/assets/202311122258400.png

最后切换到linux-0.11目录下,执行以下命令:

$ make BootImage

输入以下命令运行:

$ ../run

运行截图

https://jsdelivr.codeqihan.com/gh/MysticalDream/images/assets/202311122259334.png

setup.s 获取基本硬件参数

取出硬盘各参数放在了 0x90000 处,并通过十六进制打印在屏幕上

setup.s完整代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

INITSEG  = 0x9000
entry _start
_start:
! 打印 "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
! 初始化栈 ss:sp
    mov ax,#INITSEG
    mov ss,ax
    mov sp,#0xFF00

! 获取参数
    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
    !磁盘参数表,复制ds:si->es:di
    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0004
    mov cx,#0x10
    !重复16次
    rep
    movsb

! 准备打印
    mov ax,cs
    mov es,ax
    mov ax,#INITSEG
    mov ds,ax 

! 光标位置
    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
! 内存大小
    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
! 添加 KB
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#2
    mov bx,#0x0007
    mov bp,#msg_kb
    mov ax,#0x1301
    int 0x10
! 柱面
    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
! 磁头
    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
! 扇区
    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

回到linux-0.11目录下,执行命令:

$ make clean

$ make BootImage

$ ../run

运行截图

https://jsdelivr.codeqihan.com/gh/MysticalDream/images/assets/202311122259000.png

补充: 硬盘基本参数表:

https://jsdelivr.codeqihan.com/gh/MysticalDream/images/assets/202311122259190.png

文章参考:

INT 10H中断功能详解
BIOS int 13H中断介绍
操作系统原理与实践