实验内核版本:0.11
◆ 从开机到main函数的三步:
① 启动BIOS,准备实模式下的中断向量表和中断服务程序;
② 从启动盘加载OS程序到内存中,加载OS程序的工作就是利用第一步中的中断服务程序实现的;
③ 为执行保护模式下32位的main函数做过渡工作。
➩ Intel将所有80x86系列的CPU硬件都设计为加电即进入16位实模式状态运行;
➩ 将CPU硬件逻辑设计为在加电瞬间强行将CS置为0xFFFF,IP置为0x0000,即是CS:IP指向了0xFFFF0这个地址;
整个过程是一个纯硬件完成给你的,恰好ROM-BIOS的入口地址就是0xFFFF0,即是BIOS程序的第一条指令就设计在这个位置上。
➤ BIOS加载中断向量和终端服务程序到内存
BIOS通常被固化在ROM中,它通常会检查显卡、内存等自检操作,当然,在这里最值得一提的是BOIS在内存中建立中断向量表和中断服务程序。书中选择的BIOS的大小为8KB,地址为:0xFE000~0xFFFFF。
① BIOS在内存最开始的位置(0x00000)用1KB的内存空间(0x00000~0x003FFF)构建中断向量表。中断向量表由256个中断向量,每个中断向量占4 Byte,其中两个Byte为CS的值,两个Byte为IP的值。1K = 256 × 4B。
② 在紧接着中断向量表的256 Byte的内存空间构建BIOS数据区(0x00400~0x004FF)。
③ 在大约56KB以后的位置(0xE2CE)加载8KB左右的与中断向量相应的若干中断服务程序。0xE2CE = 14 + 12 × 16 + 2 × 162 + 14 × 163 = 14 + 12 × 24 + 2 × 28 + 14 × 212(14 × 4 × 210 = 56KB) = 56.52734375KB。
◆ 加载OS内核程序:
现在将开始执行boot操作了。此时,计算机将分三次将OS逐次加载到内存中。
① 由BIOS中断int0x19 把第1扇区bootsect的内容加载到内存中。
② 由bootsect将第1个扇区之后的4个扇区加载至内存。
③ 由bootsect将第5个扇区之后的240个扇区加载至内存。
图1 boot下的三个文件
➤ 加载引导程序bootsect(第一个扇区)
1. 计算机硬件与BIOS联手,通过CPU执行int 0x19中断将引导程序所在的第一个扇区加载至内存。int 0x19中断向量指向的中断服务程序的入口地址是0x0E6F2,该中断服务程序的功能是固定的,它会将软驱的0号磁头对应盘面的0磁道1扇区的内存拷贝至0x07C00处。引导程序代码在boot/bootsect.s文件中。
2. 当它被执行时,首先会将自己移动到0x90000(代码段最开始的地方就做了这个操作line 46)。
3. 然后,它设置堆栈,栈顶指定为0x9000:0xFF00。
4. 读第2~5共4个扇区的代码(setup.s)到内存0x90200处。
5. 将第5个扇区之后的240个扇区的代码(system模块)读入到以0x10000开始的内存。
6. 之后,bootsect.s将控制权交给setup.s。
图2 执行过程图
bootsect.s代码如下:
!
! SYS_SIZE is the number of clicks ( bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
SYSSIZE = 0x3000
!
! bootsect.s (C) 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 * bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 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 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
! put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512 ! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up. 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: ! Get disk drive parameters, specifically nr of sectors/track mov dl,#0x00
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax ! Print some inane message mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10 mov cx,#24
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) mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
call read_it
call kill_motor ! After that we check which root-device to use. If the device is
! defined (!= ), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (,) or /dev/at0 (,), depending
! on the number of sectors that the BIOS reports currently. seg cs
mov ax,root_dev
cmp ax,#
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - .2Mb
cmp bx,#
je root_defined
mov ax,#0x021c ! /dev/PS0 - .44Mb
cmp bx,#
je root_defined
undef_root:
jmp undef_root
root_defined:
seg cs
mov root_dev,ax ! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock: jmpi ,SETUPSEG ! 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)
!
sread: .word +SETUPLEN ! sectors read of current track
head: .word ! current head
track: .word ! current track read_it:
mov ax,es
test ax,#0x0fff
die: jne die ! es must be at 64kB boundary
xor bx,bx ! bx is starting address within segment
rp_read:
mov ax,es
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg cs
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,#
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#
ok2_read:
call read_track
mov cx,ax
add ax,sread
seg cs
cmp ax,sectors
jne ok3_read
mov ax,#
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#
add bx,cx
jnc rp_read
mov ax,es
add ax,#0x1000
mov es,ax
xor bx,bx
jmp rp_read read_track:
push ax
push bx
push cx
push dx
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
mov dl,#
and dx,#0x0100
mov ah,#
int 0x13
jc bad_rt
pop dx
pop cx
pop bx
pop ax
ret
bad_rt: mov ax,#
mov dx,#
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
mov dx,#0x3f2
mov al,#0
outb
pop dx
ret sectors:
.word 0 msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10 .org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55 .text
endtext:
.data
enddata:
.bss
endbss: