linux 0.11 内核学习 -- bootsect.s, 万里长征第一步

呵呵,终于将linux 0.11 下面的boot文件夹下的三个文件读完,下面是相关注释,没有汇编基础的人也是可以读的。废话少说,下面就是linux的源码了。

参考资料 Linux内核完全注释.pdf

            网上相关资料

!时间  : 2010-1-14
!工作 : 阅读linux 0.11 源码中的bootsect.s

!总体linux启动过程如下:
!
!当PC得电源打开之后,80x86结构的CPU将自动进入实时模式,并且从0xFFFF0开始自动执行程序代码,这个地址通常是
!ROM-BIOS的地址。PC机的BIOS将执行系统的检测,并且在物理地址的0处开始初始化中断向量。此后,它将可启动设备的第一
!扇区(512字节)读入内存的绝对地址0x7c00处,并且跳转到这个地方。启动设备通常是软盘或者是硬盘。这里的叙述是很简单
!的,但是这已经足够理解内核的初始化的工作过程。
!
!linux的0x9000由BIOS读入到内存的绝对地址0x7c00(31k)处,当它被
!执行时就会把自己移动到绝对地址0x90000处,并把启动设备中后2kb字节代码(boot/setup.s)读入到内存0x90200处,而内核的
!其他部分则被读入到从地址0x10000的开始处。在系统的加载期间显示信息?Loading...",然后将控制权传递给boot/setup.s中
!的代码.这是另一个实时模式汇编程序。
!
!系统启动部分识别主机的某些特性以及vga卡的类型。如果需要,它会要求用户为控制台选择显示模式。然后整个系统从地址
!0x10000移至0x0000处,进入保护模式病跳转至系统的余下部分。此时所有的32位运行方式的设置启动被完成:idt,gdt,ldt被
!加载,处理器和协处理器也确认,分页的工作也设置好了。最终将调用init/main.c中的main程序。上述的操作的源代码是在
!boot/head.s中的。这可能是整个内核中最有诀窍的代码了。注意如果在上述任何一步中出现了一步错误。计算机就会死锁。在
!操作系统还没有完全运转之前是处理不了错误的。
!
!
!bootsec.s文件说明如下:
!bootsec.s代码是磁盘的引导块程序,驻留在磁盘的第一扇区。在PC机加电rom bios自检之后,引导扇区由bios加载到内存0x7c00
!处,然后将自己移动到内存0x90000处。该程序的主要作用是首先将setup模块从磁盘加载到内存中,紧接着bootsect的后面位置
!(0x90200),然后利用bios中断0x13中断去磁盘参数表中当前引导盘的参数,然后在屏幕上显示"Loading system..."字符串。再者
!将system模块从磁盘上加载到内存0x10000开始的地方。随后确定根文件系统的设备号,如果没有指定,则根据所保存的引导盘的每
!类型和种类,并保存设备号与boot_dev,最后长跳转到 setup程序开始处0x90200执行setup程序。
!
!
!注释如下:
!
! 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 = 0x3000
!
!以下是这一段代码的翻译。
! bootsect.s
!bootsect.s被bios启动程序加载至0x7c00 31k处,并将自己移动到地址0x90000 576k处,并跳转到那里。
!
!它然后利用bios中断将setup直接加载到自己后面0x90200 576.5k,并将system加载到地址0x10000处。
!
!注意 : 目前的内核系统最大的长度限制为8*65536 512k字节,即使是在将来这也应该没有问题的。我想让他保持简单明了,
!这样512k的最大内核长度应该足够了,尤其是这里没有向minix中一样包含缓冲区高速缓冲。
!
!加载程序已经做的足够简单了,所以持续的独处错误将导致死循环。只能手工重启。只要可能,通过一次取出所有的扇区,加载的
!过程可以做的很快.
!
!
!
! 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) 256b, 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.

!六个全局标示符
.globl begtext, begdata, begbss, endtext, enddata, endbss
!文本段
.text
begtext:
!数据段
.data
begdata:
!堆栈段
.bss
begbss:

!文本段
.text
SETUPLEN = 4    ! nr of setup-sectors setup程序的扇区数(setup-sectors)值
BOOTSEG  = 0x07c0   ! original address of boot-sector bootsect的原始地址
INITSEG  = 0x9000   ! we move boot here - out of the way 将bootsect移动到这里
SETUPSEG = 0x9020   ! setup starts here setup程序开始地址
SYSSEG   = 0x1000   ! system loaded at 0x10000 (65536). 将system模块加载到的地址
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 !根文件系统设备是第二硬盘的第一个分区
   !0x300 -- /dev/hd0
   !0x301 -- /dev/hd1
   !...
   !0x309 -- /dev/hd9

entry start 告知连接程序,程序充start标号开始。
start:
////////////////////////////////////////////////////////////////////////////////////////////////////////////
!下面的代码是将自身bootsect从目前位置0x07c0 31k移动到0x9000 576k处,然后跳转到本程序的下一条语句处。
 !
 !此时(在实时模式下)内存使用如下的分布 :
 !0      0x7c00(bootsect.s)
 !--------++++++++++---------------------------
 !ds = 0x7c00
 !         <-
 mov ax,#BOOTSEG
 mov ds,ax

 !es = 0x9000
 mov ax,#INITSEG
 mov es,ax

 !移动计数的值 256
 !启动代码是512kb
 mov cx,#256

 !源地址 ds : si = 0x07co : 0x0000
 sub si,si
 !目的地址 es : di = 0x9000 : 0x0000
 sub di,di
 !重复执行知道cx = 0。循环程序实现的另一种方法是利用串操作处理的重复指令rep。rep指令以cx为重复次数,
 !当指令被重复执行完一次,那么cx的值会自动减一。rep指令和串操作指令movs,stos配合使用,它将这两条指令
 !重复执行cx次。
 rep
 !移动一个字
 movw

 !此时的内存使用情况如下 :
 !0         0x7c00            0x9000
 !----------+++++++++---------+++++++++---------
 !两个使用中的内存是相同的

 !在汇编中的段的使用
 !
 !间接跳转。这里INITSEG指出跳转到的地址。
 !其格式为:jmpi offset(标号), segment selector
 jmpi go,INITSEG -- 0x9000
//////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////
!在将自己成功移动之后,下面的几步是为下面加载setup程序做准备。
!
!在执行jmpi指令时,cs段会被自动更新。注意的是cs寄存器在call或者是jmp指令时会自动更新。将 ds es ss都设置成代码所
!在的段。
go: mov ax,cs
 mov ds,ax
 mov es,ax
!将堆栈指针sp指向0x9ff00 (0x9000 : 0xff00)
! put stack at 0x9ff00.
 !SS被成为堆栈段寄存器,用于存放堆栈段的基值.
 mov ss,ax

 !代码段的移动,需要重新设置堆栈段的位置。sp只要指向远大于512便宜处都可以。
 mov sp,#0xFF00  ! arbitrary value >>512
!//////////////////////////////////////////////////////////////////////////////////////////////////////////

!在bootsect程序块后紧随着加载setup模块。注意es已经设置好了。es是指附加段寄存器。附加段寄存器是es,它的作用是很大的.
!因为我们在处理数据的时候,往往需要用到两个数据段,特别是在字符串的处理方面,使用两个数据段简便了许多的操作.
!
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
!
!下面的这段代码的主要作用是使用int 0x13把磁盘上的setup模块加载到内存中,位置在bootsect.s (0x90000 + 512字节)之后,
!真个过程主要是操作寄存器ax,bx,cx,dx等四个寄存器。
!
!
!///////////////////////////////////////////////////////////////////////////////////////////////////////
!设置load_setup标号,是为了执行j load_setup语句。
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 调用中断信号,开始读取。
     !进位操作标示符cf = 0表示操作成功。

 jnc ok_load_setup  ! ok - continue·如果成功就跳转到下面的ok_load_setup
     !否则执行下面的代码,复位磁盘再次执行这段代码
 mov dx,#0x0000
 mov ax,#0x0000  ! reset the diskette 磁盘复位
 int 0x13   !ld86中就有j这条指令,等价于jmp。这条语句的含义是 :
     !跳转回去继续执行,如果总是失败系统,将总执行这段代码
 j load_setup
!//////////////////////////////////////////////////////////////////////////////////////////////////////

!时间 : 2010-1-15
!工作量  : 继续阅读linux 0.11 源码
!时间 : 2010-1-17
!工作量 : 继续阅读linux源码。
!
!如果上面讲setup模块顺利读入到内存中,那么执行下面的ok_load_setup代码。
!
ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track
!取得磁盘驱动器的参数,特别是没道的扇区数量。
!取得磁盘的参数使用的是int 0x13中断来实现,其调用格式如下 :
!ah = 0x08 dl = 启动器号
!返回信息如下 :
!如果出错的话cf置位,并且ah = 状态码
!ah = 0, al = 0 bl = 驱动器类型(at/ps2)
!ch = 最大磁盘号的低8位, cl = 每磁盘的最大扇区数(位0-5),最大磁道号高2位(位6-7)
!dh = 最大磁道数 dl = 驱动器数量
!es : di = 软驱磁盘参数表
!
!调用中断0x13
 mov dl,#0x00  !清空dl,以获得驱动器号
 mov ax,#0x0800  ! AH=8 is get drive parameters
 int 0x13
 
 mov ch,#0x00
 ///////////////////////////////////////////////////////////
 !先讲一下寄存器的默认组合问题,比如指令mov [si], ax表示将ax中的内容存入ds:si指向的内存单元,也就是说在寄存器间
 !接寻址的情况下,以si间接寻址时总是默认以ds为相应的段地址寄存器。同样di是以es为默认的段地址寄存器。
 !第二个要了解的是“段超越”的问题,就是在某些时候你不想使用默认的段地址寄存器,那
 !么你可以强制指定一个段地址寄存器(当然这种强制是在允许的情况下,建议看一下汇编
 !教材上的说明),同上例mov [si],ax表示存入ds:si中,但如果你想存入cs指向的段中可
 !以这样mov cs:[si],ax, 这样就强制指定将ax中的内容存入cs:si的内存单元。
 !第三个要明白的是seg cs这样的语句只影响到它下一条指令,比如在linux启动代码中的一段:
      !seg cs
      !mov sectors,ax
      !mov ax,#INITSEG
 !要说明两点:
     !第一,seg cs 只影响到mov sectors,ax而不影响mov ax,#INITSEG
     !第二,如果以Masm语法写,seg cs和mov sectors,ax两句合起来等
        !  价于mov cs:[sectors],ax,这里使用了间接寻址方式。
        !  重复一下前面的解释,mov [sectors],ax表示将ax中的内容
        !  存入ds:sectors内存单元,而mov cs:[sectors],ax强制以
        !  cs作为段地址寄存器,因此是将ax的内容存入cs:sectors内存
        !  单元,一般来说cs与ds的值是不同的,如果cs和ds的值一样,
        !  那两条指令的运行结果会是一样的。(编译后的指令后者比前
        !  者一般长一个字节,多了一个前缀。)
     !结论,seg cs只是表明紧跟它的下一条语句将使用段超越,因为在编
        !  译后的代码中可以清楚的看出段超越本质上就是加了一个字节
        !  的指令前缀,因此as86把它单独作为一条指令来写也是合理的。
        !
        !mov cs:[sectors],ax
 !
 !下面的代码在linux 2.6.x的内核中可能改变。没有查证。网上有关于其的讨论,认为其中含有错误。
 seg cs
 mov sectors,cx  !sectors在下面定义,保存每个磁道扇区数
 ///////////////////////////////////////////////////////////
 
 mov ax,#INITSEG
 mov es,ax   !INITSEG = 0x90000 恢复es值

! Print some inane message 在显示一些信息("Loading system...\n"回车换行。共24个字符)
 !//////////////////////////////////////////////////
 !读取光标位置
 mov ah,#0x03  ! read cursor pos
 xor bh,bh   ! bh = 0,使用xor指令将bh清0,但是速度比赋值快
 int 0x10
 !//////////////////////////////////////////////////

 !//////////////////////////////////////////////////
 !利用int 0x10来实现将移动光标,并写字符创
 mov cx,#24   ! 共24个字符
 mov bx,#0x0007  ! page 0, attribute 7 (normal)
 mov bp,#msg1  ! msgl下面定义,指向要显示的字符串
 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(64k)处。
 !/////////////////////////////////////////////////
 mov ax,#SYSSEG
 mov es,ax  ! segment of 0x010000,现在es就是存放system的段地址
 !/////////////////////////////////////////////////

 !//////////////////////////////////////////////////
 ! 调用邋read_it来实现读取磁盘上的system模块,其中es为输入参数。
 call read_it
 !//////////////////////////////////////////////////
 !关闭驱动器马达,这样就知道驱动器的状态了。
 call kill_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.
! 此后,我们检查使用哪个根文件系统设备。如果已经指定了设备,就直接使用给定的设备,
! 否则就需要根据bios的报告的每磁道扇区数来确定到底是使用/dev/ps0还是/dev/at0
 ! 上面一行中的两个设备文件的含义 :
 ! 在linux中软驱的主设备号是2,次设备号 = type * 4 + nr,其中nr 为0-3分别
 ! 对应软驱的abcd;type是软驱的类型(2->1.2m 7->1.44m等)。因为7*4+0=28,所
 ! 以/dev/ps0指的是1.44m a驱动器,起设备号是0x021c,同理/dev/at0(2,8)指的是1.2
 ! m a的驱动器,其设备号是0x0208

 seg cs
 mov ax,root_dev  ! 根设备号
 !////////////////////////////////////////////////////
 ! 使用cmp和jne指令来实现条件转移。在汇编语句中的跳转指令分为有条件跳转和
 ! 无条件跳转,jne可解释为如下:jump not equal
 !
 cmp ax,#0
 jne root_defined
 !////////////////////////////////////////////////////
 
 seg cs
 mov bx,sectors  ! 取上面保存的sectors。如果sectors=15,则说明是
     ! 1.2mb的驱动器;如果sectors=18,则说明是1.44mb
     ! 软驱。因为是可引导的驱动器,所以肯定是a驱。
     
 !////////////////////////////////////////////////////
 ! 如果跳转到root_defined,则使用ax作为参数来传递。
 mov ax,#0x0208  ! /dev/ps0 - 1.2Mb
 cmp bx,#15   ! 判断每磁道扇区数是否是15
 je root_defined  ! 如果等于,则ax中就是引导驱动器的设备号
 !///////////////////////////////////////////////////

 !//////////////////////////////////////////////////////
 ! 如果跳转到root_defined,使用ax来作为参数传递。
 mov ax,#0x021c  ! /dev/PS0 - 1.44Mb
 !/////////////////////////////////////////////////////
 ! 使用cmp和je指令来实现,助记符je的含义是jump equal
 cmp bx,#18
 je root_defined
 !/////////////////////////////////////////////////////

////////////////////////////////////////////////////////
undef_root:    ! 死循环 - 死机
 jmp undef_root
////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
root_defined:
 seg cs
 mov root_dev,ax  ! ax = 0x0208,将检查的设备号保存。
///////////////////////////////////////////////////////

! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
! 到此,所有的程序都加在完毕,我们就跳转到被加载在bootsect后面的setup程序去。
!
 jmpi 0,SETUPSEG  ! 跳转到0x9020 : 0000(setup.s的开始处)。
     ! 本程序到此结束。呵呵终于结束了。

! 下面是两个子程序。
! 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)
!
! 该子程序将系统模块加载到内存地址0x10000处,并确定没有跨越64kb内存边界,我们试图尽快的
! 进行加载,只要可能,就每次加载整条磁道的数据。
! 输入es : -- 开始内存地址段值 (通常是0x1000)
!
sread: .word 1+SETUPLEN ! sectors read of current track
    ! 定义变量sread,表示当前磁道中以读的扇区数。开始时已经读
    ! 进1扇区的引导扇区,bootsect和setup程序所占的扇区数SETUPLEN.
    !
head: .word 0   ! current head,当前磁头号。
track: .word 0   ! current track,当前磁道号。

read_it:
! 测试输入的段值。必须位于内存地址64kb边界处,否则进入死循环。清bx寄存器,用于表示当前段内
! 存放数据的开始位置。
!
! es = 0x1000
 mov ax,es
 !//////////////////////////////////////////////
 ! test指令,实现将原操作数用于和目的操作数按位"与"运算,但是结果并不放在目的地址。
 ! test指令会影响到ZF的标志位。如果"与"的结果为0,那么zf=1。
 test ax,#0x0fff
 !/////////////////////////////////////////////
 
die: jne die   ! es must be at 64kB boundary
    ! es的值必须是位于64k地址的边界,否则进入死循环。
 xor bx,bx  ! bx is starting address within segment
    ! bx是段内偏移地址。
rp_read:
! 判断是否已经全部读入数据。比较当前所读段是否就是系统数据的末端所处的段(endseg),
!如果不是,跳转到下面的ok1_read标号处继续读取数据。否则退出子程序返回。
!
!
 mov ax,es
 cmp ax,#ENDSEG  ! have we loaded all yet?
 jb ok1_read
 ret
 
ok1_read:
! 计算和验证当前的磁道需要读取的扇区数,放在ax寄存器中。
! 根据当前磁道还未读取的扇区数以及段内数据字节的开始偏
! 移量,计算如果全部读取这些未读扇区,所读总字节数是否
! 会超过64kb段长度限制。若会超过,则根据此次最多能读入
! 的字节数(64kb - 段内偏移量),反算出此次需要读取的扇区数。
!
 seg cs
 mov ax,sectors  ! 取出每个磁道扇区数
 sub ax,sread  ! 减去当前磁道中已经读取的扇区数
 mov cx,ax  ! cx = ax = 当前磁道未读的扇区数
 shl cx,#9  ! cx = cx * 512
 add cx,bx  ! cx = cx + 段内当前的偏移量
    !  = 此次读操作后,段内共读入的字节数。
    ! CF是进位标志,就是说当执行一个加法或减
    ! 法时,最高位产生进位或借位时,CF就为1,否则为0.
    ! ADD它的功能是将源操作数与目标操作数相加,
    ! 结果保存在目标操作数中,并根据结果置标志位.
    ! 例如:MOV DL,12H !将12H放到数据寄存器低8位中,即DL
       ! add dl,34H 将34H和12H相加,结果保存在DL中,运行后DL为46H
    ! 他们相加后最高位没有进位,所以CF=0,它们是这样相加的
    ! 12H对应的二进制   00010010
                ! 34H对应的二进制 + 00110100
                !                ----------
                !                 01000110
    !最高位是第8位,0+0没有进位,所以CF=0

 jnc ok2_read  ! 如果没有超过64kb字节,则跳转到ok2_read
    ! 进位时转移 jnc, cf = 0时跳转。
 je ok2_read  !
 xor ax,ax  ! 若加上此次将读磁道上所未读扇区时会超过64kb
 sub ax,bx  ! 那么计算此时最多能读入的字节数64kb - 段内读偏移量
 shr ax,#9  ! 再转换成需要读取的扇区数。
 
ok2_read:
 call read_track
 mov cx,ax  ! cx = 该次操作已读取的扇区数
 add ax,sread  ! 当前磁道上已经读取的扇区数。
 
 seg cs
 cmp ax,sectors  ! 如果当前磁道上还有扇区未读,则跳转到ok3_read
 jne ok3_read
!读该磁道的下一个磁头面上的数据。如果已经完成,则去读下一个磁道。
 mov ax,#1
 sub ax,head  ! 判断当前的磁头号
 
 jne ok4_read  ! 如果是0磁头,则再去读1磁头面上的数据。
 inc track  ! 否则去读取下一个磁道。
 
ok4_read:
 mov head,ax  ! 保存当前磁道已读扇区数
 xor ax,ax  ! 清空前磁道已读扇区数
 
ok3_read:
 mov sread,ax  ! 保存当前磁道已读扇区数。
 shl cx,#9  ! 上次已读扇区数 * 512字节
 add bx,cx  ! 调整当前段内数据开始位置
 
 jnc rp_read  ! 如果小于64kb边界值,则跳转到邋rp_read处,继续

 ! 读取数据。否则调整当前段,为下一个段数据做准备。
 ///////////////////////////////////////////////////
 ! 将段基址调整为指向下一个64kb段内存。
 mov ax,es
 add ax,#0x1000
 mov es,ax
 ///////////////////////////////////////////////////
 xor bx,bx  ! 清段内数据开始偏移量
 //////////////////////////////////////////////////

 jmp rp_read

read_track:
! 读取当前磁道上制定开始的扇区和需要读取的扇区数的数据到es:bx处。
! al - 需要读取的扇区数
! es : bx - 缓冲区的位置
!
 !/////////////////////////////////////////////////
 ! 信息保护
 push ax
 push bx
 push cx
 push dx
 !/////////////////////////////////////////////////

 !////////////////////////////////////////////////
 ! 调用中断前的准备工作
 mov dx,track  ! 当前的磁道号
 mov cx,sread  ! 去当前磁道上已读的扇区数
 inc cx   ! cl = 开始读扇区
 mov ch,dl  ! ch = 当前磁道号
 mov dx,head
 mov dh,dl
 mov dl,#0
 and dx,#0x0100
 mov ah,#2
 int 0x13
 !//////////////////////////////////////////////////
 
 jc bad_rt  ! 如果出错的话,跳转到bad_rt
    ! 否则执行下面的代码,回复现场
 pop dx
 pop cx
 pop bx
 pop ax
 ret
! 执行驱动器的复位操作,在跳转到read_track处重试
bad_rt: mov ax,#0
 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
 mov dx,#0x3f2  ! 软驱控制卡的驱动端口,只写
 mov al,#0  ! 关闭马达
 outb   ! 将al中的内容传输到dx指定的端口上去
 pop dx
 ret

sectors:   ! 存放的是当前启动软盘每磁道的扇区数
 .word 0

msg1:
 .byte 13,10
 .ascii "Loading system ..."
 .byte 13,10,13,10

.org 508   ! 标示下面的语句从地址508开始,所以邋root_dev
    ! 在启动扇区的第508开始的2个字节中。
root_dev:   ! 这里存放的是根文件系统所在的设备号,
    ! 在init/main.c中会使用。
 .word ROOT_DEV
boot_flag:   ! 硬盘的有效标示
 .word 0xAA55

.text
endtext:
.data
enddata:
.bss
endbss:

posted @ 2010-01-19 08:29  qiang.xu  阅读(3205)  评论(0编辑  收藏  举报