Linux0.11内核源码解析-bootsect.s

学习资料:

  1. Linux内核完全注释

  1. 操作系统真像还原

  1. 极客时间-Linux内核源码趣读

  1. Linux0.11内核源码

->上电

->80x86架构CPU会自动进入实模式

->从地址0xffff0自动执行程序代码

->bios执行系统检测,从物理地址0初始化中断向量,将第一个引导扇区512字节读入内存绝对地址0x7c00(BIOS把512字节的二进制数据从硬盘搬到内存中,作为操作系统的开发人员,我们只需要把代码放到0盘0磁道1扇区即可,对应的代码是bootsect.s)

->跳转到0x7c00(31KB)

->0x7c00当被执行的时候会把自己搬到0x90000(576KB)处,并把启动设备中后2KB代码(setup.s)读到0x90200处

# ROOT_DEV:    0x000 - same type of floppy as boot. 根文件系统设备使用与引导同样的软驱设备
#        0x301 - first partition on first drive etc 根文件系统设备在第一个硬盘的第一个分区上
#        0x300  /dev/hd0
#        0x301 /dev/hd1
#        ...
#        0x304 /dev/hd4
#        0x305 /dev/hd5 第二个硬盘
#        0x306 /dev/hd6 第二个硬盘第一个分区
    .equ ROOT_DEV, 0x301  
    ljmp    $BOOTSEG, $_start

_start:   
 # ds = 0x07c0
    mov    $BOOTSEG, %ax
    mov    %ax, %ds

    # es = 0x9000
    mov    $INITSEG, %ax
    mov    %ax, %es

    # move 256 word from 0x7c00(DS:SI) to 0x9000(ES:DI)
  #ds:si=0x07c0:0x0000
  #es:di=0x9000:0x0000
    mov    $256, %cx
    sub    %si, %si
    sub    %di, %di
    rep    
    movsw
    #间接跳转0x9000
    ljmp    $INITSEG, $go

# 代码段移动后,重新设置堆栈段
go:    mov    %cs, %ax
    mov    %ax, %ds
    mov    %ax, %es
# put stack at 0x9ff00.
    # cs=0x9000 ss=0x9000 sp=0xff00
    mov    %ax, %ss
    mov    $0xFF00, %sp        # arbitrary value >>512

int 0x13

功能02H

功能描述:读扇区

入口参数:AH=02H

AL=扇区数

CH=柱面

CL=扇区

DH=磁头

DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘

ES:BX=缓冲区的地址

功能00H

功能描述:磁盘系统复位

入口参数:AH=00H

DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘

出口参数:CF=0——操作成功,AH=00H,否则,AH=状态代码,参见功能号01H中的说明

load_setup:
    mov    $0x0000, %dx        # drive 0, head 0
    mov    $0x0002, %cx        # from sector 2, track 0
    mov    $0x0200, %bx        # address = 512, in INITSEG
    .equ    AX, 0x0200+4        # read 4 sector 
    mov     $AX, %ax        # service 2, nr of sectors
    int    $0x13            # read it
    jnc    ok_load_setup        # ok - continue
    mov    $0x0000, %dx
    mov    $0x0000, %ax        # reset the diskette
    int    $0x13
    jmp    load_setup

功能08H

功能描述:读取驱动器参数

入口参数:AH=08H

DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘

出口参数:CF=1——操作失败,AH=状态代码,参见功能号01H中的说明,否则, BL=01H — 360K

=02H — 1.2M

=03H — 720K

=04H — 1.44M

CH=柱面数的低8位

CL的位7-6=柱面数的该2位

CL的位5-0=扇区数

DH=磁头数

DL=驱动器数

ES:DI=磁盘驱动器参数表地址

ok_load_setup:

# Get disk drive parameters, specifically nr of sectors/track

    mov    $0x00, %dl
    mov    $0x0800, %ax        # AH=8 is get drive parameters
    int    $0x13
    mov    $0x00, %ch
    #seg cs
    mov    %cx, %cs:sectors+0    # %cs means sectors is in %cs   保存磁道扇区数
    mov    $INITSEG, %ax
    mov    %ax, %es #磁盘参数中断改掉了es的值,恢复es寄存器

# 后续装载系统模块需要装载240个扇区,是之前装载扇区数量的60倍,在此处显示一条信息提示用户等待

# Print some inane message

    mov    $0x03, %ah        # read cursor pos
    xor    %bh, %bh
    int    $0x10
    
    mov    $24, %cx
    mov    $0x0007, %bx        # page 0, attribute 7 (normal)
    #lea    msg1, %bp
    mov     $msg1, %bp
    mov    $0x1301, %ax        # write string, move cursor
    int    $0x10

把system 240个扇区拷贝到0x10000,关闭软驱电机

# ok, we've written the message, now
# we want to load the system (at 0x10000)

    mov    $SYSSEG, %ax
    mov    %ax, %es        # segment of 0x010000
    call    read_it
    call    kill_motor

检查要使用哪个根文件系统设备,如果指定了设备!=0,就直接使用指定设备,否则根据BIOS报告的磁道扇区数来确定是用那个设备

跳转setup.s 0x90200

# 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.

    #seg cs
    mov    %cs:root_dev+0, %ax  # Root Device根文件系统设备
    cmp    $0, %ax  # 判断根设备号是否为0
    jne    root_defined # ZF = 0时跳转,即ax不为0时,即根设备已设置
    #seg cs
# 根设备未设置
    mov    %cs:sectors+0, %bx 
    mov    $0x0208, %ax        # /dev/ps0 - 1.2Mb
    cmp    $15, %bx  # 取上面获取到每磁道扇区数,如果sectors=15则说明是1.2Mb的驱动器
    je    root_defined
    mov    $0x021c, %ax        # /dev/PS0 - 1.44Mb
    cmp    $18, %bx # 取上面获取到每磁道扇区数,如果sectors=18则说明是1.44Mb的软驱
    je    root_defined
undef_root:  # down机
    jmp undef_root
root_defined:
    #seg cs
    mov    %ax, %cs:root_dev+0

# after that (everyting loaded), we jump to
# the setup-routine loaded directly after
# the bootblock:

    ljmp    $SETUPSEG, $0

如果ES在64k,0x1000边界上,就立刻停止

# 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)
#
sread:    .word 1+ SETUPLEN    # sectors read of current track
head:    .word 0            # current head
track:    .word 0            # current track

read_it:
    mov    %es, %ax
    test    $0x0fff, %ax #  TEST指令按位进行逻辑与运算,与AND指令的区别是两个操作数不会被改变
die:    jne     die            # es must be at 64kB boundary
    xor     %bx, %bx        # bx is starting address within segment

rp_read:
    mov     %es, %ax
    cmp     $ENDSEG, %ax        # have we loaded all yet?
    jb    ok1_read
    ret

# 计算和验证当前磁道需要读取的扇区数,放在ax寄存器中,根据当前磁道未读取的扇区数以及段内数据字节开始偏移位置,
# 计算如果全部读取这些未读扇区,所读总字节数是否超过64KB段长度的限制,若会超过,则根据此次最多读入的字节数(64KB-段内偏移位置),反算出此次需要读取的扇区数
ok1_read:
    #seg cs
    mov    %cs:sectors+0, %ax # 获取磁道扇区数
    sub    sread, %ax # 减去当前磁道已读扇区数
    mov    %ax, %cx # cx = ax = 当前磁道未读扇区数
    shl    $9, %cx  # cx = cx * 512
    add    %bx, %cx # cx = cx + bx段内当前偏移值
    jnc     ok2_read # 没有超过64KB字节,则跳转ok2_read
    je     ok2_read
    xor     %ax, %ax #  加上此次将读磁道上所有未读扇区时会超过64KB
    sub     %bx, %ax # 此时最多能读入字节数(64KB-段内读偏移位置),再转换
    shr     $9, %ax # 读取扇区数

ok2_read:
    call     read_track
    mov     %ax, %cx # 该次操作已读扇区数
    add     sread, %ax # 当前磁道上已经读取的扇区数
    #seg cs
    cmp     %cs:sectors+0, %ax # 如果当前磁道上的还有扇区未读,则跳转到ok3_read
    jne     ok3_read # 读该磁道的下一磁头面上的数据,如果已经完成,则去读下一磁道
    mov     $1, %ax
    sub     head, %ax # 判断当前磁头号
    jne     ok4_read # 如果是0磁头号,则再去读1磁头面上的扇区数据
    incw    track # 否则去读下一磁道

ok4_read:
    mov    %ax, head # 保存当前磁头号
    xor    %ax, %ax # 清当前磁道已读扇区数

ok3_read:
    mov    %ax, sread # 保存当前磁道已读扇区
    shl    $9, %cx # 上次已读扇区数*512字节
    add    %cx, %bx # 调整当前段数据开始位置
    jnc    rp_read # 小于64KB边界值,则跳转到rp_read,继续读数据
    mov    %es, %ax  # 否则调整当前段,为读下一段数据作准备
    add    $0x1000, %ax # 将段基址指向为下一个64KB内存开始处
    mov    %ax, %es
    xor    %bx, %bx # 清段内数据开始偏移值
    jmp    rp_read

# 读当前磁道上指定开始扇区和需读扇区数的数据到es:bx,参见第67行下对BIOS磁盘读中断
# int 0x13, ah=2
# al 需读扇区数;es:bx 缓冲区开始位置
read_track:
    push    %ax
    push    %bx
    push    %cx
    push    %dx
    mov    track, %dx # 取当前磁道
    mov    sread, %cx # 取当前磁道上已读扇区数
    inc    %cx # cl开始读扇区
    mov    %dl, %ch # ch当前磁道号
    mov    head, %dx # 取当前磁头号
    mov    %dl, %dh # dh 磁头号
    mov    $0, %dl # dl 磁动器号,0表示当前A驱动器
    and    $0x0100, %dx # 磁头号大于1
    mov    $2, %ah # ah = 2,读磁盘扇区功能号
    int    $0x13
    jc    bad_rt # 出错则跳转bad_rt
    pop    %dx
    pop    %cx
    pop    %bx
    pop    %ax
    ret

# 执行驱动器复位操作,再跳转到read_track重试
bad_rt:    mov    $0, %ax
    mov    $0, %dx
    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    $0x3f2, %dx
    mov    $0, %al
    outsb
    pop    %dx
    ret

sectors:
    .word 0

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

    .org 508
root_dev:
    .word ROOT_DEV
boot_flag:
    .word 0xAA55

1. 把 bootsect.s 编译成 bootsect 放在硬盘的 1 扇区;2. 把 setup.s 编译成 setup 放在硬盘的 2~5 扇区;3. 把剩下的全部代码(head.s 作为开头,与各种 .c 和其他 .s 等文件一起)编译并链接成 system,放在硬盘的随后 240 个扇区

为什么不把系统模块直接加载到物理地址0x0000处运行,而要在setup中进行移动,这是因为setup程序代码开始部分需要利用BIOS的中断获取机器的一些参数,BIOS初始化的时候会在物理内存开始处放置大小为0x400字节(1kb)的中断向量表,因此需要在BIOS中断调用后才能将这个区域覆盖掉。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内核源代码为基础,介绍了完整的linux内核实现原理 ·选择早期linux内核进行介绍,排除了目前内核中复杂而庞大的实现细节 ·整个内核代码仅有14000行 ·提供可运行相关内核的完整实验系统 本书对早期的linux操作系统内核(v0.11)全部源代码文件进行了详细的注释和说明,旨在让读者能够在短时间内对linux的工作机理获得全面而深刻的理解,为进一步学习和研究linux系统打下坚实的基础。书中首先介绍了linux系统的发展历史,着重说明了各个内核版本之间的重要区别,给出了选择0.11版作为研究对象的原因;然后依据内核源代码的组织结构对所有代码进行了详细注释。在注释的同时,还介绍了读者应该了解的相关知识,并给出了相关的硬件信息。本书还介绍了内核源代码的组织结构及相互关系。 本书适合作为计算机专业学生学习操作系统课程的实践教材和参考书,也适合linux操作系统爱好者自学,还可供具有一定基础的技术人员作为嵌入式开发应用的参考书。 *************************************************************** 请注意: 下载完,评论的同时,请点击评论框上方的五角星(共5个五角星),这样你的被扣的积分就可以返还了。 如果只评论,不点击小五角星,积分不会返还。 一定要先下载完,再评论。如果先评论后下载,或者在下载的过程中评论,积分同样不会返还。 *************************************************************** 更多linux、ARM和C语言资源请参考: http://blog.csdn.net/arkofnoach/archive/2010/10/23/5960560.aspx

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值