荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: jjksam (欢迎光临荔园晨风Linux版, InstallBBS版!), 信区: Linux
标 题: [转载] 我对linux启动的一点分析(一)
发信站: 荔园晨风BBS站 (Sun Dec 9 13:15:27 2001), 转信
bootsect.s
#include <asm/boot.h>
注:asm/boot.h
{
1: #ifndef _LINUX_BOOT_H
2: #define _LINUX_BOOT_H
3:
4: /* Don't touch these, unless you really know what you're doing. */
5: #define DEF_INITSEG 0x9000
6: #define DEF_SYSSEG 0x1000
7: #define DEF_SETUPSEG 0x9020
8: #define DEF_SYSSIZE 0x7F00 ;508K
9:
10: /* Internal svga startup constants */
11: #define NORMAL_VGA 0xffff /* 80x25 mode */
12: #define EXTENDED_VGA 0xfffe /* 80x50 mode */
13: #define ASK_VGA 0xfffd /* ask for it at bootup */
14:
15: #endif
16:
}
SETUPSECTS = 4 /* default nr of setup-sectors */
BOOTSEG = 0x07C0 /* original address of boot-sector */
INITSEG = DEF_INITSEG /* we move boot here - out of the way */
SETUPSEG = DEF_SETUPSEG /* setup starts here */
SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */
SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
/* to be loaded */
ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef ROOT_RDONLY
#define ROOT_RDONLY 1
#endif
.code16
.text
.global _start
_start:
# First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
movw $BOOTSEG, %ax ;BOOTSEG = 7c00h
movw %ax, %ds ;%ds = BOOTSEG
movw $INITSEG, %ax ;INITSEG = 9000h
movw %ax, %es ;%ax = %es = INITSEG
movw $256, %cx ;512 Bytes
subw %si, %si
subw %di, %di
cld
rep ;复制
movsw
ljmp $INITSEG, $go ;跳转到9000:go处执行
# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
# wouldn't have to worry about this if we checked the top of memory. Also
# my BIOS can be configured to put the wini drive tables in high memory
# instead of in the vector table. The old stack might have clobbered the
# drive table.
;将软盘参数表读入9000:3FF4,设置maximum sector count = 36,
;并将中断向量表中的软盘参数表始址指向地址9000:3FF4; ds=es=ss=cs = 90
00h;cx = 0
go: movw $0x4000-12, %di ;4000h-12 is an arbitrary value >=
;length of bootsect + length of
;setup + room for stack;
;12 is disk parm size.
movw %ax, %ds ;%ax and %es already contain INITSEG=9000h
movw %ax, %ss
movw %di, %sp ;put stack at 9000:4000h-12.
# Many BIOS's default disk parameter tables will not recognize
# multi-sector reads beyond the maximum sector number specified
# in the default diskette parameter tables - this may mean 7
# sectors in some cases.
#
# Since single sector reads are slow and out of the question,
# we must take care of this by creating new parameter tables
# (for the first disk) in RAM. We will set the maximum sector
# count to 36 - the most we will encounter on an ED 2.88.
#
# High doesn't hurt. Low does.
#
# Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0,
# and %gs is unused.
# and %gs is unused.
movw %cx, %fs ;%fs = 0
movw $0x78, %bx ;%fs:%bx是软盘参数表地址
;0x78 = 0x1E*4, 0x1E 是软盘参数表的在中断向
量表中的中断号
pushw %ds
ldsw %fs:(%bx), %si ;%ds:%si = fs:bx = 0:78h is source
movb $6, %cl ;copy 12 bytes
pushw %di ;%di = 0x4000-12. ES = 9000h
rep ;don't worry about cld(第一次移动代码段时)
movsw ;做完了
popw %di ;di = 4000h-12
popw %ds ;ds = 9000h
movb $36, 0x4(%di) ;每磁道最大扇区数为36
movw %di, %fs:(%bx) ;在中断向量表里设置软盘参数表地址:
movw %es, %fs:2(%bx) ;9000:4000h-12 = 9000:3ff4
# Get disk drive parameters, specifically number of sectors/track.
# It seems that there is no BIOS call to get the number of sectors.
# Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18
# can be read, 15 if sector 15 can be read. Otherwise guess 9.
# Note that %cx = 0 from rep movsw above.
;获取软盘驱动器的参数sectors/track(每磁道的扇区数)存入变量sectors;
;由于通常BIOS不支持此功能,所以在程序中用36、18、15、9来依次测试
movw $disksizes, %si ;table of sizes to try
probe_loop:
lodsb
cbtw ;extend to word
movw %ax, sectors ;每磁道扇区数放在sectors里
cmpw $disksizes+4, %si
jae got_sectors ;36,18,15都搞不定, 则默认为9
xchgw %cx, %ax ;%cx = 0(track):max sector number(36,18,15)
xorw %dx, %dx ;drive 0, head 0
movw $0x0200, %bx ;address = 512, in INITSEG (%es = %cs = 9000h)
movw $0x0201, %ax ;试读1个扇区
int $0x13 ;INT 1302h
jc probe_loop ;try next value
got_sectors:
movb $0x03, %ah ;read cursor pos
xorb %bh, %bh
int $0x10
movw $9, %cx ;9个字符
movb $0x07, %bl ;page 0, attribute 7 (normal)
;%bh is set above, int10 doesn't
;modify it
movw $msg1, %bp
movw $0x1301, %ax ;write string, move cursor
int $0x10 ;tell the user we're '\nLoading'
# Load the setup-sectors directly after the moved bootblock (at 0x90200).
# We should know the drive geometry to do it, as setup may exceed first
# cylinder (for 9-sector 360K and 720K floppies).
;将setup加载到9000h:0200h(0x90200,紧随其后)处
movw $0x0001, %ax ;set sread (sector-to-read) to 1 as
movw $sread, %si ;the boot sector has already been read
movw %ax, (%si)
xorw %ax, %ax ;软驱复位
xorb %dl, %dl
int $0x13
movw $0x0200, %bx ;address = 512, in INITSEG
next_step:
movb setup_sects, %al ;setup_sects为实际的setup.s占用扇区数,
;初始化为SETUPSECS(=4),但它在最后被build处
理时,
;根据setup.s实际的大小设置
movw sectors, %cx
subw (%si), %cx ;(%si) = sread, 第一次,cx--因为第一个扇区是引导扇区
cmpb %cl, %al
jbe no_cyl_crossing
movw sectors, %ax ;setup.s的位置超过了第一个柱面
subw (%si), %ax ;(%si) = sread, 还剩setup_sect-ax个扇区
no_cyl_crossing:
call read_track ;al中是当前要读的扇区数
pushw %ax ;save it
call set_next ;set %bx properly; it uses %ax,%cx,%dx
;?setup.s应该小于127个扇区才能保证set_next
;中的更新是对的(没有用ok1_read,因此要保证bx
不溢出的太多)
popw %ax ;restore
subb %al, setup_sects ;rest - for next step
jnz next_step
;setup.s加载完毕
pushw $SYSSEG ;准备加载压缩内核
popw %es ;%es = SYSSEG = 1000h
call read_it
call kill_motor
call print_nl
# 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, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8
)
# depending on the number of sectors we pretend to know we have.
# Segments are as follows: %cs = %ds = %ss = INITSEG,
# %es = SYSSEG, %fs = 0, %gs is unused.
movw root_dev, %ax
orw %ax, %ax
jne root_defined
movw sectors, %bx
movw $0x0208, %ax ;/dev/ps0 - 1.2Mb
cmpw $15, %bx
je root_defined
movb $0x1c, %al ;/dev/PS0 - 1.44Mb
cmpw $18, %bx
je root_defined
movb $0x20, %al ;/dev/fd0H2880 - 2.88Mb
cmpw $36, %bx
je root_defined
movb $0, %al ;/dev/fd0 - autodetect
root_defined:
movw %ax, root_dev
# After that (everything loaded), we jump to the setup-routine
# loaded directly after the bootblock:
ljmp $SETUPSEG, $0 ;setup.s
# These variables are addressed via %si register as it gives shorter code.
sread: .word 0 ;当前磁道已经读的扇区数
head: .word 0 ;current head
track: .word 0 ;current track
# This routine loads the system at address SYSSEG, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.
read_it: ;加载压缩内核是调用子过程read_it完成
的:
;每一次从磁盘上读内核代码都尽可能地一
次
;读完一个磁道的所有扇区,这也正是前面
设
;置软盘参数及获取软盘驱动器参数的用意
,
;同时,每读一次都在屏幕上的“Loading
”后面
;打印一个“.”。并且,保证以64KB为单
位存储,
;不能跨越64KB边界
movw %es, %ax ;%es = SYSSEG = 1000h when called
testw $0x0fff, %ax
die: jne die ;%es must be at 64kB boundary,死机
xorw %bx, %bx ;%bx=0 is starting address within segment
rp_read:
#ifdef __BIG_KERNEL__ ;look in setup.S for bootsect_kludge
bootsect_kludge = 0x220 ;0x200 + 0x20 which is the size of the
lcall bootsect_kludge ;bootsector + bootsect_kludge offset
#else
movw %es, %ax ;小内核
subw $SYSSEG, %ax ;当前段和SYSSEG间的偏移
movw %bx, %cx ;相对于当前段的偏移
shr $4, %cx
add %cx, %ax ;check offset,e.g. ax = 2020 (0)<--必然是0,
;因为是由段边界和扇区大小决定的
#endif
cmpw syssize, %ax ;have we loaded everything yet?
;syssize为实际的压缩内核vmlinux字节数,
;初始化为SYSSIZE(0x7F000=508KB),
;但它在最后被build处理时,根据压缩内核实际的
大小设置
jbe ok1_read ;?因为实际的syssize可能不正好以0结束,因此高32位
;相同并不一定读完了内核,故用jbe
ret
ok1_read:
movw sectors, %ax
subw (%si), %ax ;(%si) = sread
movw %ax, %cx ;al=当前磁道还剩多少扇区要读
shlw $9, %cx
addw %bx, %cx ;cx=若读完,偏移的位置
jnc ok2_read ;bx未溢出
je ok2_read ;cx = (1)0000h,使得set_next中的更新偏移是正确的
xorw %ax, %ax ;调整读取的扇区数,不然set_next中的更新将是错误
的,恰好是让bx填满64k剩下要读的扇区数
subw %bx, %ax ;0-bx -> bx 等价于 10000h – bx -> bx
shrw $9, %ax
ok2_read:
call read_track
call set_next
jmp rp_read
read_track:
pusha
pusha
movw $0xe2e, %ax ;loading... message 2e = .
movw $7, %bx ;每读一个磁道,打一个点
int $0x10
popa
# Accessing head, track, sread via %si gives shorter code.
movw 4(%si), %dx ;4(%si) = track
movw (%si), %cx ;(%si) = sread
incw %cx ;CL0-5 = sector number
movb %dl, %ch ;ch = track number
movw 2(%si), %dx ;2(%si) = head --> dl
movb %dl, %dh ;dh is head number
andw $0x0100, %dx
movb $2, %ah
pushw %dx ;save for error dump
pushw %cx
pushw %bx
pushw %ax
int $0x13 ;al has already been set
jc bad_rt
addw $8, %sp
popa
ret
set_next:
movw %ax, %cx ;ax = cx = 上次读的扇区数
addw (%si), %ax ;(%si) = sread
cmp sectors, %ax
jne ok3_set ;在一个磁道里读完了
movw $0x0001, %ax ;要转向下一个磁道
xorw %ax, 2(%si) ;change head
jne ok4_set ;=1
incw 4(%si) ;=0, next track
ok4_set:
xorw %ax, %ax
ok3_set:
movw %ax, (%si) ;set sread,旧磁道或新磁道内已读的扇区数,对新磁道而言是0。
shlw $9, %cx
addw %cx, %bx ;bx = bx + cx * 512,512 = bytes of a sector
jnc set_next_fin
movw %es, %ax ;bx溢出了
addb $0x10, %ah ;不能超过64K的边界,es,bx的更新在ok1_read的保证
下永远是正确的
movw %ax, %es ;更新段
xorw %bx, %bx ;偏移为0
set_next_fin:
ret
bad_rt:
pushw %ax ;save error code
call print_all ;%ah = error, %al = read
xorb %ah, %ah ;复位磁盘
xorb %dl, %dl
int $0x13
addw $10, %sp
popa
jmp read_track ;重读
# print_all is for debugging purposes.
#
# it will print out all of the registers. The assumption is that this is
# called from a routine, with a stack frame like
#
# %dx
# %cx
# %bx
# %ax
# (error)
# ret <- %sp
print_all:
movw $5, %cx ;error code + 4 registers
movw %sp, %bp
print_loop:
pushw %cx ;save count remaining
call print_nl ;<-- for readability
cmpb $5, %cl
jae no_reg ;see if register name is needed
movw $0xe05 + 'A' - 1, %ax
subb %cl, %al
int $0x10
movb $'X', %al
int $0x10
movb $':', %al
int $0x10
no_reg:
addw $2, %bp ;next register
call print_hex ;print it
popw %cx
loop print_loop
ret
print_nl: ;屏幕显示跳到下一行
movw $0xe0d, %ax ;CR
int $0x10
movb $0xa, %al ;LF
int $0x10
ret
# print_hex is for debugging purposes, and prints the word
# pointed to by %ss:%bp in hexadecimal.
print_hex:
movw $4, %cx ;4 hex digits
movw (%bp), %dx ;load word into %dx
print_digit:
rolw $4, %dx ;rotate to use low 4 bits
movw $0xe0f, %ax ;%ah = request
andb %dl, %al ;%al = mask for nybble
addb $0x90, %al ;convert %al to ascii hex
daa ;in only four instructions!
adc $0x40, %al
daa
int $0x10
loop print_digit
ret
# 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.
# NOTE: Doesn't save %ax or %dx; do it yourself if you need to.
kill_motor: ;关闭软驱动器
movw $0x3f2, %dx
xorb %al, %al
outb %al, %dx
ret
sectors: .word 0
disksizes: .byte 36, 18, 15, 9 ;一定要从大往小排列才能逐个试探
msg1: .byte 13, 10 ;\n
.ascii "Loading"
# XXX: This is a fairly snug fit.
.org 497
setup_sects: .byte SETUPSECTS
root_flags: .word ROOT_RDONLY
syssize: .word SYSSIZE
swap_dev: .word SWAP_DEV
ram_size: .word RAMDISK
vid_mode: .word SVGA_MODE
root_dev: .word ROOT_DEV ;检查出根目录设备,将其主/次设备号存入
;变量root_dev;根目录设备root_dev初始化
;为ROOT_DEV(=0)
;但它在最后被build处理时,是根据实际根设
;备的主/次设备号来设置的
;如果在build处理时也未设置,则根据软盘
驱
;动器参数sectors/track(即变量sectors)来
确
;定root_dev值
boot_flag: .word 0xAA55
启动内核 = arch/i386/boot/bootsect、arch/i386/boot/setup及压缩内核arch/i386/
boot/compressed/vmlinux
压缩内核vmlinux = arch/i386/boot/compressed/head.o位于压缩内核的起始位置,解
压算法模块arch/i386/boot/compressed/misc.o紧随其后,然后才是被压缩了的基本内
核模块arch/i386/boot/compressed/piggy.o
基本内核 = arch/i386/kernel/head.o位于基本内核vmlinux的起始位置。
--
※ 来源:·BBS 水木清华站 smth.org·[FROM: 137.194.8.113]
--
※ 修改:·jjksam 於 Dec 9 15:04:53 修改本文·[FROM: 192.168.0.146]
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.146]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店