荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: 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软件 网络书店