linux 0.11 version 启动代码分析(bootsect.s)

综述

启动代码位于/boot/,包含三个文件,均为汇编编写

/boot/bootsect.s

启动扇区的代码,位于启动扇区,即磁盘的0磁道,0磁头,第一扇区,bios运行之后会跳转到0x7c00处执行代码,这一段的代码则是将位于0x7c00处等待执行的代码。

!
! 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 为要加载的系统大小,对于当前版本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.sbios启动过程当中被加载到0x7c00处,然后把它自己移动到0x90000处,再跳转到0x90000处执行
! 
! 需要注意的是当前system8×65536字节大小,在没有像minix中包含的缓冲区高速缓存的情况下
!512KB的内核大小是足够的,即使在之后的版本当中也应该是足够的。
!
! 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                       !text,data,bss段放在一起,没有分区
SETUPLEN = 4                ! setup占扇区数量 
BOOTSEG  = 0x07c0           ! bootsect的最初地址
INITSEG  = 0x9000            ! bootsect将会移动到整个地址
SETUPSEG = 0x9020            ! setup的开始位置
SYSSEG   = 0x1000            !system加载的位置
ENDSEG   = SYSSEG + SYSSIZE        ! system加载位置结束                   
!ROOT_DEV:0x000 - 作为启动的软驱类型
!         0x301 - 第一个驱动器上的第一个分区
ROOT_DEV = 0x306                                                                 

entry start                 ! 入口地址为start
start:
    mov    ax,#BOOTSEG                                                           
    mov    ds,ax                                                                 
    mov    ax,#INITSEG                                                           
    mov    es,ax                                                                 
    mov    cx,#256                                                               
    sub    si,si                                                                  
    sub    di,di                                                                 
    rep    !0x7c00:0x0000位置处256字节复制到0x9000:0x0000处,一次两个字节
    movw                                                                         
    jmpi    go,INITSEG ! 跳转至INITSEG段的go处,即复制之后位于0x90000处的下一条语句位置   
go:    mov    ax,cs                                                              
    mov    ds,ax
    mov    es,ax
! put stack at 0x9ff00.   
! cs, ds, es, ss指向0x90000处                                                       
    mov    ss,ax
    mov    sp,#0xFF00        ! arbitrary value >>512                              
! 栈指针sp指向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为开始扇区(位05)与磁道号高二位(位67mov    bx,#0x0200        ! address = 512, in INITSEG   
! es:bx为目的地址,即0x9000:0x0200,0x92000位置

    mov    ax,#0x0200+SETUPLEN    ! service 2, nr of sectors  
! ah中为0x13号中断的功能号,即2号,al中为需要执行的扇区数量
! 0x13号中断2号功能为将指定扇区的内容加载到内存里

    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                                                                   
! 执行0x13号中断,0号功能,复位驱动器
    j    load_setup                                                               
! 回到load_setup重新加载

ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track                    
! 获取磁盘参数,尤其是扇区和磁道的数量

    mov    dl,#0x00                                                                 
! 通过dl指定磁盘
    mov    ax,#0x0800        ! AH=8 is get drive parameters                           ! ah(功能号)为0x08,0x13号中断0x08号功能,执行读取磁盘信息,并且返回信息放在cxint    0x13                                                                     

    mov    ch,#0x00                                                                
!ch设置为0,其实是将磁道号清空
    seg cs 
    mov    sectors,cx                                                               
! 之前提到了扇区数不会超过256,所以al的低高两位为0cx此时为扇区数
! 将扇区数赋值给变量sectors

    mov    ax,#INITSEG
    mov    es,ax                                                                    
! es已改变,重新指向INITSEG

! Print some inane message

    mov    ah,#0x03        ! read cursor pos                                        
! 通过中断0x10,功能号0x03,读取光标位置
    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                                             
! we want to load the system (at 0x10000)                                        
! 接下来将system加载到0x10000

    mov    ax,#SYSSEG
    mov    es,ax        ! segment of 0x010000                                    
! es指向 0x010000
    call    read_it                                                              
! 调用子程序read_it,进行加载
    call    kill_motor                                                            
! 调用子程序kill_motor,关闭软驱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.

! 然后我们检查要使用的root设备,如果设备已经被定义了,那么就直接使用给出的设备
! 否则就根据bios目前给出的扇区数量决定使用/dev/PS0 (2, 28)(1.44m软驱)
! 或者 
! /dev/at0 (2,8) (1.2m软驱)


    seg cs
    mov    ax,root_dev                                                               ! 是否已经指定了root设备?
    cmp    ax,#0
    jne    root_defined                                                              ! root设备指定了,即不为0,则跳到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_rootundef_root,死循环(未指定root_device也不为1.2m软驱和1.4m软驱则死循环)
root_defined:                                                                       
! root设备已经指定
    seg cs
    mov    root_dev,ax                                                              
! 保存root设备号

! after that (everyting loaded), we jump to                                         
! the setup-routine loaded directly after
! the bootblock:
! 所有的都加载完毕之后进入setup

    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)
!
! 
!
! 输入:    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处,此时esax0x1000
    test ax,#0x0fff
die:    jne die            ! es must be at 64kB boundary                          
! es必须为0x1000,如果不是就死机

    xor bx,bx        ! bx is starting address within segment 
!检测通过,清空bx         bx = 0

rp_read:                                                                    
!循环读取
    mov ax,es                                                                
! 根据cs位置判断是否已经加载完毕,第一次的时候esax0x1000
    cmp ax,#ENDSEG        ! have we loaded all yet?                         
    jb ok1_read                                                              
! 没有加载完毕,跳转至ok1_read
    ret         
ok1_read:
    seg cs
    mov ax,sectors                                                               
! 扇区数量放入ax中,(sectors1518sub ax,sread                                                                 
! 减去已读扇区数量 sread5ax减法后为10
    mov cx,ax                                                                    
! 将未读扇区数量放入cxshl 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                                                                     
! axbxcxdx入栈保存
    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功能号2读磁盘到内存
    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:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值