Linux0.11版源代码分析——boot/bootsect.s

  近日阅读赵炯博士的《Linux内核完全注释》一文,完全沉醉於linux内核代码中,忽然有了系统学习Linux代码的想法。查阅了网上的大量资料,决定听从前辈的建议,在工作之余,每天抽出两个小时,从低版本开始看起,一步一步追随先贤的步伐,阅读linux代码,学习Linux内核的设计理念。

  因为是刚刚开始学习,尤其前面boot相关方面,因为汇编语言很多知识都已经忘记,很多分析都借鉴了赵炯博士的《Linux内核完全注释》一文中的内容,这里对赵博士表示深深地感谢。

  废话少说,下面就是学赵博士的风格,对bootsect.s文件中代码的注释。

!
! 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
!
!
! SYS_SIZE是要加载系统的长度,clicks (16 bytes)为一节,16位
! 0x3000是0x30000字节 = 196kB,对当前版本的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被bois启动程序加载至0x7c00,然后将自己移动到0x90000地址处
! 然后跳转到那里
!
! 使用BIOS中断将setup直接加载到自己的后面0x90200处,然后将system加载到0x10000处 
!
! 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.
!
! 注意!当前内核系统长度最大为8*65536字节,即使在将来,这也不是问题。
! 我想保持它简单明了 。512kb的内核长度足够了,尤其没有象minix那样包
! 含高速缓冲。
!
! loader程序做的尽可能的简单了,持续出现读错误将导致死循环,只能手工重启。
! 只要可能,通过一次加载所有扇区。
! 

.globl begtext, begdata, begbss, endtext, enddata, endbss                        !定义6个全局标识符
.text                                                                            !文本段
begtext:
.data                                                                            !数据段
begdata:
.bss                                                                             !未初始化数据段
begbss:
.text
                                                                                 !以上将文本段,数据段和未初始化数据段定义在一起,即不分段
SETUPLEN = 4                ! nr of setup-sectors                                !setup所占扇区数量
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.                              !ROOT_DEV:0x000 - 作为启动的软驱类型
!        0x301 - first partition on first drive etc                              !         0x301 - 第一个驱动器上的第一个分区
ROOT_DEV = 0x306                                                                 !

entry start                                                                      !入口地址,跳转至标号start处
start:
    mov    ax,#BOOTSEG                                                           !ds指向0x7c0
    mov    ds,ax                                                                 !
    mov    ax,#INITSEG                                                           !es指向0x9000
    mov    es,ax                                                                 !
    mov    cx,#256                                                               !初始化计数器
    sub    si,si                                                                  !si清零,源地址  :ds:si 0x07c0:0x0000
    sub    di,di                                                                 !di清零,目的地址:es:di 0x9000:0x0000
    rep                                                                          !重复执行,cx递减至0
    movw                                                                         !从源地址复制到目的地址,一次移动两个字节
    jmpi    go,INITSEG                                                            !段间跳转,跳至INITSEG段go标号处
go:    mov    ax,cs                                                               !cs指向0x90000处,将ds、es、ss指向移动后的代码段处
    mov    ds,ax
    mov    es,ax
! put stack at 0x9ff00.                                                          
    mov    ss,ax
    mov    sp,#0xFF00        ! arbitrary value >>512                              !堆栈指针指向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—7mov    bx,#0x0200        ! address = 512, in INITSEG                          !es:bx为目的地址
    mov    ax,#0x0200+SETUPLEN    ! service 2, nr of sectors                      !ah中是int 0x13中断的功能号 al是需要执行的扇区数量
    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                                                                   !执行中断,复位驱动器
    j    load_setup                                                               !跳转至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                           !ah为0x08,中断int 0x13执行读取磁盘信息,并且返回信息在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,读取光标位置
    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                                             !ok,我们已经打印一些消息了
! we want to load the system (at 0x10000)                                        !现在,我们准备加载system到0x01000

    mov    ax,#SYSSEG
    mov    es,ax        ! segment of 0x010000                                    !es指向 0x010000
    call    read_it                                                              !调用子程序read_it,进行加载
    call    kill_motor                                                            !调用子程序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 (2,28) (1.44M软驱)还是 /dev/at0 (2,8)(1.2M软驱)


    seg cs
    mov    ax,root_dev                                                               !判断根设备是否被指定
    cmp    ax,#0
    jne    root_defined                                                              !如果指定,则跳转至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_defined:                                                                       !已确定根设备
    seg cs
    mov    root_dev,ax                                                              !保存根设备号

! after that (everyting loaded), we jump to                                         !所有的都加载完毕后,我们跳转到setup程序执行
! the setup-routine loaded directly after
! the bootblock:

    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)
!
! 这个程序加载system到地址0x10000处,确定没有跨越64k边界
! 我们试图尽可能快地加载,只要可能,我们加载整条磁道的数据
! 
!
! 输入:    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                          !如果不是,则死循环
    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功能号读磁盘到内存
    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:

  bootsect.s代码是磁盘引导程序,驻留在磁盘的第一个扇区中(引导扇区,0磁道,0磁头,第一个扇区)。在PC机加电之后,ROM BIOS进行自检,通过自检后,ROM BIOS会把引导扇区代码bootsect加载到内存地址0x7c00出,并从该地址开始执行。在bootsect代码执行期间,它会将自己移动到内存地址0x90000处,并继续执行。之后发生的事情都在代码中了。

转载于:https://www.cnblogs.com/xfwei/archive/2012/08/26/2657227.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值