现在的位置: 首页 > 综合 > 正文

Linux0.11内核读书笔记/boot/bootsect.s

2013年08月28日 ⁄ 综合 ⁄ 共 8530字 ⁄ 字号 评论关闭

果冻

QQ:457283

Http://blog.csdn.net/jznsmail

! 本程序完成的主要功能
! 1.bootsect.s从0x7c00处开始执行
! 2.将自己复制到0x90000处
! 3.将setup.s程序从磁盘第2扇区读取到0x90200处
! 4.将system读取到0x10000处
! 5.获取根文件系统设备号
! 6.显示信息
!
!  程序执行示意图
!     +---+---+---+ 0xA0000
!     |      |     |      |
!     |      |     |      |
!     +---+---+---+
!     |      |     |      |
!     |      |     |      |
!     |      |     | S  |
!     |      |     |      |
!     |      |     |      |
!     +---+---+---+ 0x90200
!     |      | B  | B  |
!     +---+---+---+ 0x90000
!     |      |     |      |
!     |      |     |      |
!     +---+---+---+
!     |      |     |      |
!     |      |     |      |
!     |      |     |      |
!     |      |     |      |
!     |      |     | K  |
!     |      |     |      |
!     |      |     |      |
!     |      |     |      |
!     +---+---+---+ 0x10000
!     |      |     |      |
!     |      |     |      |
!     |      |     |      |
!     |      |     |      |
!     +---+---+---+
!     |  B |     |      |
!     +---+---+---+ 0x7c00
!     |      |     |      |
!     +---+---+---+ 0x0000
!        1    2    3 
!     B - bootsect.s程序
!     S - setup.s程序
!     K - system模块
!
! 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
!
! 编译连接后system模块的大小
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.

! 定义6个全局标识符 代码段,数据段,未初始化数据段起始结束地址
.globl begtext, begdata, begbss, endtext, enddata, endbss
! 代码段
.text
begtext:
! 数据段
.data
begdata:
! 未初始化数据段
.bss
begbss:
! 代码段
.text

SETUPLEN = 4    ! nr of setup-sectors
     ! setup程序的扇区数
BOOTSEG  = 0x07c0   ! original address of boot-sector
     ! boot-sector的原始段地址
INITSEG  = 0x9000   ! we move boot here - out of the way
     ! 将boot-sector移动到这里
SETUPSEG = 0x9020   ! setup starts here
     ! setup程序在这里开始
SYSSEG   = 0x1000   ! system loaded at 0x10000 (65536).
     ! system模块加载到0x10000(64K)
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 ! 设备号,指根文件系统是第2个硬盘的第一个分区
   ! 设备号 = 主设备号*256 + 次设备号(=(major<<8)+ minor)
   ! 主设备号:1-内存,2-磁盘,3-硬盘,4-ttyx,5-tty,6-并口,7-非命名管道
   ! 0x300 = 3*256 + 0 第1个硬盘
   ! 0x301 = 3*256 + 1 第1个硬盘,第1个分区
   ! 0x306 = 3*256 + 6 第2个硬盘,第1个分区

entry _start  ! 程序入口点
_start:
 ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
 mov ax,#BOOTSEG  ! 将数据段DS寄存器设置为0x07C0
 mov ds,ax
 mov ax,#INITSEG  ! 将附加段ES寄存器设置为0x9000
 mov es,ax
 mov cx,#256  ! 移动数值CX=256字 = 512字节
 sub si,si   ! 源地址   ds:si = 0x07c0:0x0000
 sub di,di   ! 目的地址 es:di = 0x9000:0x0000
 rep    ! 重复执行,直到CX=0
 movw    ! 移动一个字
 ! 上面的代码作用是将bootsect自身从位置0x07c00移动到0x90000处,共512个字节
 jmpi go,INITSEG  ! 间接跳转,INITSEG为跳转到的段地址
     ! 以下代码是从0x90000处执行的
go: mov ax,cs   ! 将ds,es和ss设置成0x9000,此时cs跳转到0x9000了
 mov ds,ax
 mov es,ax
! put stack at 0x9ff00.  ! 将堆栈指针指向0x9ff00(0x9000:0xff00)
 mov ss,ax
 mov sp,#0xFF00  ! arbitrary value >>512
     ! 因为0x90000 - 0x90200放置bootsect,0x90200开始放置大约4个扇区的setup
     ! 程序,因此堆栈指针sp要指向0x200 + 0x200 * 4 + 堆栈大小的位置

! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
load_setup:
 mov dx,#0x0000  ! drive 0, head 0
     ! 驱动器0,磁头0
 mov cx,#0x0002  ! sector 2, track 0
     ! 扇区2,磁道0
 mov bx,#0x0200  ! address = 512, in INITSEG
     ! 缓冲区偏移量,es在上面的代码已经设置成0x9000
 mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
     ! 读取4个盘扇区到内存
 int 0x13   ! read it
     ! 调用中断
 jnc ok_load_setup  ! ok - continue
     ! CF没置位表示读取成功,跳转到ok_load_setup
 mov dx,#0x0000  ! 复位驱动器和磁头
 mov ax,#0x0000  ! reset the diskette
 int 0x13   ! 复位
 j load_setup  ! 不断重试
! 以上代码是利用BIOS的INT 0x13中断,将setup模块从磁盘第2个扇区开始读到
! 0x90200处,一共读4个扇区,如果读取出错则复位驱动器,并且重试
! INT 0x13
! ah = 0x02 读取磁盘扇区到内存 al = 需要读出的扇区数量
! ch = 磁道(柱面)号低8位  cl = 开始扇区(0-5位),磁道号高2位(6-7)
! dh = 磁头号    dl = 驱动器号(如果是硬盘位7要置位)
! es:bx = 指向数据缓冲区  如果出错CF标志置位

! 成功载入setup模块
ok_load_setup:

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

 mov dl,#0x00  ! 设置驱动器号为0
 mov ax,#0x0800  ! AH=8 is get drive parameters
 int 0x13   ! 调用中断
 mov ch,#0x00  
 seg cs    ! 表示下一条语句操作在CS段寄存器所指的段中,
 mov sectors,cx  ! 保存每磁道扇区数
 mov ax,#INITSEG  
 mov es,ax   ! 因为读取磁盘参数时覆盖了ES的值,这里重新改回
! 以上代码是读取磁盘驱动器参数,特别是扇区数量
! INT 0x13
! ah = 0x08   dl = 驱动器号(如果是硬盘则要置位7为1)
! 返回信息:
! 如果出错CF置位,ah = 状态码
! ah = 0, al = 0   bl = 驱动器类型(AT/PS2)
! ch = 最大磁道号低8位 cl = 每磁道最大扇区数(位0-5),最大磁道号高2位(位6-7)
! dh = 最大磁头数  dl = 驱动器数量
! es:di = 软驱磁盘参数表

! 显示信息'Loading system ...回车换行'共24个字符
! Print some inane message

 mov ah,#0x03  ! read cursor pos
 xor bh,bh   ! 读取光标位置
 int 0x10
 
 mov cx,#24   ! 共24个字符
 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
! 以下代码将system模块加载到0x10000处
! we want to load the system (at 0x10000)

 mov ax,#SYSSEG
 mov es,ax  ! segment of 0x010000
 call read_it ! 读取system模块,es为输入参数
 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.
! Linux中软驱的主设备号是2,次设备号 = type*4 + nr,其中nr为0-3分别对应软驱A,B,C,D type是
! 软驱类型2-1.2M 7-1.44M
! 因为7 * 4 + 0 = 28,所以/dev/PS0(2, 28)是指1.44M A驱动器,其设备号是0x21c
! 同理/dev/at0 (2, 8)指的是1.2M A驱动器,设备号是0x0208

 seg cs
 mov ax,root_dev  ! 获取根设备号
 cmp ax,#0   ! 如果不为0跳转到root_defined,证明已经给定根设备号
 jne root_defined
 ! 检测根设备号
 seg cs    ! 下面一句代码在CS所指的段中
 mov bx,sectors  ! 取得每磁道扇区数 15-1.2mb驱动器 18-1.44MB驱动器
 mov ax,#0x0208  ! /dev/ps0 - 1.2Mb
 cmp bx,#15   ! 判断每磁道扇区数是否为15
 je root_defined  ! 等于,则AX中就是引导的驱动器的设备号
 mov ax,#0x021c  ! /dev/PS0 - 1.44Mb
 cmp bx,#18   
 je root_defined
undef_root:    ! 如果都不同,死机
 jmp undef_root
root_defined:
 seg cs
 mov root_dev,ax  ! 保存设备号

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

 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)
!
sread: .word 1+SETUPLEN  ! sectors read of current track
     ! 当前磁道中已读扇区数,开始已经读取1个扇区的bootsect
head: .word 0   ! current head
     ! 当前磁头号
track: .word 0   ! current track
     ! 当前磁道号

read_it:
 mov ax,es   
 test ax,#0x0fff  ! 将es里的值与0x0fff相与,看是否处于0x1000位置
die: jne die   ! es must be at 64kB boundary 不处于0x1000位置则进入死循环
 xor bx,bx   ! bx is starting address within segment
     ! bx是段内偏移地址
rp_read:
 mov ax,es   ! 比较es值与ENDSEG,看是否达到段末端,如果不是则跳转到ok1_read继续读取,否则返回
 cmp ax,#ENDSEG  ! have we loaded all yet?
 jb ok1_read
 ret

ok1_read:
 seg cs    ! 设置下面一条指令操作在CS所指的段中
 mov ax,sectors  ! 读取每磁道扇区数
 sub ax,sread   ! 减掉当前已读扇区数
 mov cx,ax   ! cx = 当前未读扇区数
 shl cx,#9   ! cx = cx << 9 = cx * 512字节 = 共有多少字节没有读取
 add cx,bx   ! cx = cx + 段内当前偏移量 判断未读取字节数 + 段内偏移是否超过64KB
 jnc ok2_read   ! 未超过64KB字节,跳转到ok2_read执行
 je ok2_read
 ! 以下情况是未读取字节数+段内偏移超过了64KB
 xor ax,ax   ! 清空ax
 sub ax,bx   ! ax = ax - bx 得到最多还能读入的字节数
 shr ax,#9   ! 转换成还需要读取的扇区数
ok2_read:
 call read_track
 mov cx,ax   ! cx = 上面那条调用后已经读出的扇区数
 add ax,sread   ! 更新已经读取的扇区数
 seg cs    ! 下一条指令操作在CS所指的段中
 cmp ax,sectors  ! 如果当前磁道上还有扇区未读,跳转到ok3_read
 jne ok3_read
 mov ax,#1   ! 判断磁头号
 sub ax,head   ! 如果为0磁头,则再去读1磁头面上的扇区数据
 jne ok4_read
 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继续读数据
 mov ax,es   
 add ax,#0x1000  ! 将段基地址调整为指向下一个64KB内存开始处
 mov es,ax
 xor bx,bx   ! 清除段内偏移量
 jmp rp_read   ! 跳转到rp_read继续执行

! 读当前磁道上指定开始扇区和需要读扇区数的数据到es:bx开始处
read_track:
 ! ax,bx,cx,dx如栈
 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   ! dh = 磁头号
 mov dl,#0   ! dl = 驱动器号(0表示当前驱动器A)
 and dx,#0x0100  ! 词头号不大于1
 mov ah,#2   ! ah = 2,读磁盘扇区功能号
 int 0x13   ! 调用中断
 jc bad_rt   ! 出错,跳转到bad_rt
 pop dx    ! 弹出dx, cx, bx, ax
 pop cx
 pop bx
 pop ax
 ret
! 进行驱动器复位操作(磁盘中断功能号0),再跳转到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   ! A驱动器,关闭FDC,静止DMA和中断请求,关闭马达
 outb    ! 将al内容输出到dx指定端口
 pop dx
 ret

sectors:    ! 用来保存磁道扇区数
 .word 0

msg1:
 .byte 13,10   ! 回车,换行ASCII码
 .ascii "Loading system ..."
 .byte 13,10,13,10  ! 共24个ASCII码字符

.org 508    ! 表示下面的语句从508(0x1FC)开始
root_dev:    ! 保存根文件系统所在的设备号(init/main.c用到)
 .word ROOT_DEV
boot_flag:    ! 硬盘有效标识
 .word 0xAA55

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

抱歉!评论已关闭.