综述
启动代码位于/boot/,包含三个文件,均为汇编编写
/boot/bootsect.s
启动扇区的代码,位于启动扇区,即磁盘的0磁道,0磁头,第一扇区,bios运行之后会跳转到0x7c00处执行代码,这一段的代码则是将位于0x7c00处等待执行的代码。
!
! 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 为要加载的系统大小,对于当前版本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.
!
! bootsect.s (C) 1991 Linus Torvalds版权所有
!
! bootsect.s在bios启动过程当中被加载到0x7c00处,然后把它自己移动到0x90000处,再跳转到0x90000处执行
!
! 需要注意的是当前system有8×65536字节大小,在没有像minix中包含的缓冲区高速缓存的情况下
! 这512KB的内核大小是足够的,即使在之后的版本当中也应该是足够的。
!
! 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 !text,data,bss段放在一起,没有分区
SETUPLEN = 4 ! setup占扇区数量
BOOTSEG = 0x07c0 ! bootsect的最初地址
INITSEG = 0x9000 ! bootsect将会移动到整个地址
SETUPSEG = 0x9020 ! setup的开始位置
SYSSEG = 0x1000 !system加载的位置
ENDSEG = SYSSEG + SYSSIZE ! system加载位置结束
!ROOT_DEV:0x000 - 作为启动的软驱类型
! 0x301 - 第一个驱动器上的第一个分区
ROOT_DEV = 0x306
entry start ! 入口地址为start
start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep ! 将0x7c00:0x0000位置处256字节复制到0x9000:0x0000处,一次两个字节
movw
jmpi go,INITSEG ! 跳转至INITSEG段的go处,即复制之后位于0x90000处的下一条语句位置
go: mov ax,cs
mov ds,ax
mov es,ax
! put stack at 0x9ff00.
! cs, ds, es, ss指向0x90000处
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
! 栈指针sp指向0x9FF00
! load the setup-sectors directly after the bootblock.
! 在boot块后直接加载setup模块
! Note that 'es' is already set up.
! 此时es已经设置过了
load_setup:
! setup 模块加载
mov dx,#0x0000 ! drive 0, head 0
! dh为磁头号,dl为驱动器号,磁盘则位7置位
mov cx,#0x0002 ! sector 2, track 0
! ch为磁道号的低8位,cl为开始扇区(位0—5)与磁道号高二位(位6—7)
mov bx,#0x0200 ! address = 512, in INITSEG
! es:bx为目的地址,即0x9000:0x0200,0x92000位置
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
! ah中为0x13号中断的功能号,即2号,al中为需要执行的扇区数量
! 0x13号中断2号功能为将指定扇区的内容加载到内存里
int 0x13 ! read it
! 执行中断0x13
jnc ok_load_setup ! ok - continue
! 执行成功,跳转至ok_load_setup
mov dx,#0x0000
! 中断失败,复位驱动器,此时ah功能号为0x00
mov ax,#0x0000 ! reset the diskette
int 0x13
! 执行0x13号中断,0号功能,复位驱动器
j load_setup
! 回到load_setup重新加载
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
! 获取磁盘参数,尤其是扇区和磁道的数量
mov dl,#0x00
! 通过dl指定磁盘
mov ax,#0x0800 ! AH=8 is get drive parameters ! ah(功能号)为0x08,0x13号中断0x08号功能,执行读取磁盘信息,并且返回信息放在cx中
int 0x13
mov ch,#0x00
! 将ch设置为0,其实是将磁道号清空
seg cs
mov sectors,cx
! 之前提到了扇区数不会超过256,所以al的低高两位为0,cx此时为扇区数
! 将扇区数赋值给变量sectors
mov ax,#INITSEG
mov es,ax
! es已改变,重新指向INITSEG
! Print some inane message
mov ah,#0x03 ! read cursor pos
! 通过中断0x10,功能号0x03,读取光标位置
xor bh,bh
int 0x10
mov cx,#24
! 通过中断0x10打印消息
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! ok, we've written the message, now
! we want to load the system (at 0x10000)
! 接下来将system加载到0x10000
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
! es指向 0x010000
call read_it
! 调用子程序read_it,进行加载
call kill_motor
! 调用子程序kill_motor,关闭软驱motor
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
! 然后我们检查要使用的root设备,如果设备已经被定义了,那么就直接使用给出的设备
! 否则就根据bios目前给出的扇区数量决定使用/dev/PS0 (2, 28)(1.44m软驱)
! 或者
! /dev/at0 (2,8) (1.2m软驱)
seg cs
mov ax,root_dev ! 是否已经指定了root设备?
cmp ax,#0
jne root_defined ! root设备指定了,即不为0,则跳到root_defined子程序
seg cs
mov bx,sectors ! 扇区数量放入bx中,准备进行比较
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb ! 1.2Mb软驱的扇区数量
cmp bx,#15
je root_defined ! 如果是1.2Mb软驱,跳转
mov ax,#0x021c ! /dev/PS0 - 1.44Mb ! 判断是否为1.4Mb软驱
cmp bx,#18
je root_defined ! 跳转
undef_root:
jmp undef_root
! undef_root,死循环(未指定root_device也不为1.2m软驱和1.4m软驱则死循环)
root_defined:
! root设备已经指定
seg cs
mov root_dev,ax
! 保存root设备号
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
! 所有的都加载完毕之后进入setup
jmpi 0,SETUPSEG
! 跳转到setup
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
!
!
! 输入: es - 开始地址的内存段(一般为 0x1000)
!
sread: .word 1+SETUPLEN ! sectors read of current track
!已读扇区
head: .word 0 ! current head
!当前的磁头
track: .word 0 ! current track
!当前磁道
read_it:
mov ax,es
! 检测es是否在0x1000处,此时es,ax为0x1000
test ax,#0x0fff
die: jne die ! es must be at 64kB boundary
! es必须为0x1000,如果不是就死机
xor bx,bx ! bx is starting address within segment
!检测通过,清空bx bx = 0
rp_read:
!循环读取
mov ax,es
! 根据cs位置判断是否已经加载完毕,第一次的时候es和ax为0x1000
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
! 没有加载完毕,跳转至ok1_read
ret
ok1_read:
seg cs
mov ax,sectors
! 扇区数量放入ax中,(sectors为15或18)
sub ax,sread
! 减去已读扇区数量 sread为5,ax减法后为10
mov cx,ax
! 将未读扇区数量放入cx中
shl cx,#9
! 计算需要读取的字节数 cx * 512 = 10h * 512 = 8 * 1K = 8K
add cx,bx
! 计算段内偏移
jnc ok2_read
! cf位无进位跳转
je ok2_read
! zf位置位则跳转
xor ax,ax
! 清空ax
sub ax,bx
! 求补
shr ax,#9
! 计算最多可读长度
ok2_read:
! 读取64k以内数据
call read_track
! 读取磁道
mov cx,ax
! 保存ax
add ax,sread
! 当前所读取的扇区数
seg cs
cmp ax,sectors
! 对比需要读取扇区数
jne ok3_read
! 如果当前磁道还未读取扇区,则跳转至ok3_read继续读取
mov ax,#1
! 如果当前磁头面已经读取完毕,则读取下一磁头面
sub ax,head
! 如果是0磁头,则跳转至ok4_read读1磁头面
jne ok4_read
! 如果不是,读下一磁道
inc track
ok4_read:
mov head,ax
! 保存当前磁头号
xor ax,ax
! 清空ax,准备保存已读扇区
ok3_read:
mov sread,ax
! 保存已读取扇区数
shl cx,#9
! 计算读取的长度
add bx,cx
! 调整当前段内数据开始位置
jnc rp_read
! 如果小于64k边界值,跳转至rp_read继续读取
mov ax,es
! 已经读取64k数据,调整es位置
add ax,#0x1000
mov es,ax
xor bx,bx
! 清除段内偏移值
jmp rp_read
! 跳转至rp_read继续读取
read_track:
! 读取磁道函数
push ax
! ax、bx、cx、dx入栈保存
push bx
push cx
push dx
mov dx,track
! 当前磁道号
mov cx,sread
! 当前已读扇区数
inc cx
! 读取扇区数
mov ch,dl
! ch中为当前磁头号
mov dx,head
! 取磁头号
mov dh,dl
! dh为磁头号
mov dl,#0
! 当前驱动器号
and dx,#0x0100
! 确保磁头号不大于1
mov ah,#2
! int 0x13功能号2读磁盘到内存
int 0x13
! 执行中断,进行读取
jc bad_rt
! 判断是否出错,出错则跳转至bad_rt处理
pop dx
! 如果读取成功则弹出入栈寄存器,并跳出此函数
pop cx
pop bx
pop ax
ret
bad_rt: mov ax,#0
! ah为零
mov dx,#0
! 指定驱动器
int 0x13
! 重置磁盘驱动器
pop dx
! 弹出寄存器
pop cx
pop bx
pop ax
jmp read_track
! 跳转至前面重新读取
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
/*
* 这个过程是关闭软驱马达, 这样我们进入内核状态我们就能知道它的状态
* 之后就不用关心了
*/
kill_motor:
push dx
! dx入栈保存
mov dx,#0x3f2
! 软驱控制器端口
mov al,#0
! 命令字
outb
! 写入命令
pop dx
! dx出栈
ret
! 返回
sectors:
!之前用于存储扇区数量的变量
.word 0
msg1:
!要显示的信息
.byte 13,10
!回车换行的ASII码
.ascii "Loading system ..."
.byte 13,10,13,10
.org 508
!从地址508处开始
root_dev:
!定义根设备变量
.word ROOT_DEV
boot_flag:
!启动扇区的标志
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss: