操作系统实践之引导程序

来源:互联网 发布:linux压缩文件命令 zip 编辑:程序博客网 时间:2024/05/13 10:53

 引导程序在引导扇区中,只有512字节的大小,引导程序难以完成许多工作,比如进入保护模式,加载内核,因此引导程序需要将这个工作交给一个拥有更大空间的程序,那就是Loader,引导程序只需将Loader加载入内存就好了,剩下的事情就由Loader来完成了。在《Linux内核完全剖析中》中可以看到,bootsect.S就是引导程序,而Setup.S则充当了Loader的功能。无论是加载Loader还是内核,都需要在实模式下调用BIOS中断来完成。源代码如下:

;�fine    _BOOT_DEBUG_    做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试

%ifdef    _BOOT_DEBUG_
    org  0100h            调试状态, 做成 .COM 文件, 可调试
%else
    org  07c00h            Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif

%ifdef    _BOOT_DEBUG_
BaseOfStack        equ    0100h    调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack        equ    07c00h    Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%endif

BaseOfLoader        equ    09000h    LOADER.BIN 被加载到的位置 ----  段地址
OffsetOfLoader        equ    0100h    LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors        equ    14    根目录占用空间
SectorNoOfRootDirectory    equ    19    Root Directory 的第一个扇区号
DeltaSectorNo        equ    17    DeltaSectorNo BPB_RsvdSecCnt (BPB_NumFATs FATSz) 2

jmp short LABEL_START
nop
下面是 FAT12 磁盘的头
BS_OEMName    DB 'ForrestY'    OEM String, 必须 个字节
BPB_BytsPerSec    DW 512        每扇区字节数
BPB_SecPerClus    DB        每簇多少扇区
BPB_RsvdSecCnt    DW        Boot 记录占用多少扇区
BPB_NumFATs    DB        共有多少 FAT 
BPB_RootEntCnt    DW 224        根目录文件数最大值
BPB_TotSec16    DW 2880        逻辑扇区总数
BPB_Media    DB 0xF0        媒体描述符
BPB_FATSz16    DW        每FAT扇区数
BPB_SecPerTrk    DW 18        每磁道扇区数
BPB_NumHeads    DW        磁头数(面数)
BPB_HiddSec    DD        隐藏扇区数
BPB_TotSec32    DD        如果 wTotalSectorCount 是 由这个值记录扇区数
BS_DrvNum    DB        中断 13 的驱动器号
BS_Reserved1    DB        未使用
BS_BootSig    DB 29h        扩展引导标记 (29h)
BS_VolID    DD        卷序列号
BS_VolLab    DB 'Tinix0.01  '卷标, 必须 11 个字节
BS_FileSysType    DB 'FAT12      文件系统类型, 必须 8个字节
;引导代码
LABEL_START:
mov axcs
mov dsax
mov esax
mov ssax
mov spBaseOfStack

;清屏
mov ax0600h
mov bx0700h
mov cx0
mov dx0184fh
int 10h

mov dh0
call DispStr 

;软驱复位
xor ahah 
xor dldl
int 13h

;现在在根目录中寻找Loader.bin
mov word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop], ;判断是否遍历完了整个根目录
jz LABEL_NO_LOADERBIN
dec dword [wRootDirSizeForLoop] 
mov axBaseOfLoader
mov esax
mov bxOffsetOfLoader
mov ax[wSectorNo]
mov cl1;读取一个扇区
call ReadSector
mov siLoaderFileName
mov diOffsetOfLoader
cld
mov dx10h ;需要遍历每个扇区的16个条目
;在目录的一个扇区中寻找Loader.bin
;这个扇区已经被加载到内存的BaseOfLoader:OffsetOfLoader处
LABEL_SEARCH_FOR_LOADERBIN:
  cmp dx;读完一个扇区,则读下一个扇区
  jz LABEL_GOTO_NEXT_SECTOT_IN_ROOT_DIR
  dec dx
  mov cx11 ;文件名称+后缀一共11个字符
LABEL_CMP_FILENAME:
  cmp cx0
  jz LABEL_FILENAME_FOUND
  dec cx
  lodsb 
  cmp al,byte [es:di]
  jz LABEL_GO_ON
  jmp LABEL_DIFFERENT
LABEL_GO_ON:
  inc di
  jmp LABEL_CMP_FILENAME
LABEL_DIFFERENT:
  and di0FFF0h
  add di20h
  mov siLoaderFileName
  jmp LABEL_SEARCH_FOR_LOADERBIN ;在下一个目录中查找
LABEL_GOTO_NEXT_SECTOT_IN_ROOT_DIR:
  inc word [wSectorNo]
  jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN
;没有找到Loader.bin
LABEL_NO_LOADERBIN:
  mov dh2
  call DispStr
%ifdef _BOOT_DEBUG_
  mov ax4c00h
  int 21h
%else
  jmp $
%endif

;找到Loader.bin
LABEL_FILENAME_FOUND:
  mov axRootDirSectors//根目录扇区数
  and di0FFF0h
  add di01Ah
  mov cxword [es:di];将Loader.bin对应的开始簇号存入cx
  push cx
  add cxax
  add cxDeltaSectorNo ;此时cx变成了Loader.bin的起始扇区号
  mov axBaseOfLoader
  mov esax
  mov bxOffsetOfLoader
  mov axcx
LABEL_GOON_LOADING_FILE:
  push ax
  push bx
  mov ah0Eh
  mov al'.'
  mov bl0Fh
  int 10h
  pop bx
  pop ax
  mov cl1
  call ReadSector ;//将Loader.bin所在的扇区载入内容 
  pop ax;簇号
  call GetFATEntry
  cmp ax0FFFh ;看是否有还占用了其它扇区
  jz LABEL_FILE_LOADED
  push ax
  mov dxRootDirSectors
  add axdx
  add axDeltaSectorNo
  add bx[BPB_BytsPerSec] ;bx=bx+512
  jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
  mov dh1
  call DispStr
  jmp BaseOfLoader:OffsetOfLoader

;变量
wRootDirSizeForLoop dw RootDirSectors
wSectorNo dw ;正在读取的扇区号
bOdd db 0

LoaderFileName        db    "LOADER  BIN"   LOADER.BIN 之文件名
MessageLength        equ    9
BootMessage:        db    "Booting  "9字节, 不够则用空格补齐. 序号 0
Message1        db    "Ready.   "9字节, 不够则用空格补齐. 序号 1
Message2        db    "No LOADER"9字节, 不够则用空格补齐. 序号 2

DispStr:
mov axMessageLength
mul dh
add axBootMessage
mov bpax   ;┓
mov axds   ;┣ es:bp=串地址
mov esax   ;┛        
mov cxMessageLength            cx 串长度
mov ax01301h        ah 13, al 01h
mov bx000ch        ;页号为0(bh 0) 黑底红字(bl 0ch,高亮)
mov dl0
int 10h                10h 中断
ret

ReadSector: ;从ax个扇区开始,将cl个扇区读入到es:bx中
push bp
mov bpsp
sub esp2
mov byte [bp-2], cl ;保存cl
push bx
mov bl[BPB_SecPerTrk] ;每磁道扇区数
div bl
inc ah
mov clah ;起始扇区号(在当前柱面上的)
mov dhal
shr al1
mov chal;//柱面号
and dh1//磁头号
pop bx;
mov dl[BS_DrvNum]
.GoOnReading:
mov ah2
mov albyte [bp-2] ;读取的扇区数
int 13h
jc .GoOnReading ;//读取错误时,CF置为1
add esp2
pop bp
ret

GetFATEntry:
  push es
  push bx
  push ax
  mov axBaseOfLoader
  sub ax0100h
  mov esax
  pop ax
  mov byte [bOdd], 0
  mov bx3
  mul bx
  mov bx,2
  div bx
  cmp dx0;判断余数是否为0
  jz LABEL_EVEN
  mov byte [bOdd], 1
LABEL_EVEN:
  xor dxdx
  mov bx[BPB_BytsPerSec]
  div bx
  push dx
  mov bx0
  add ax1
  mov cl2
  call ReadSector
  pop dx
  add bxdx
  mov ax[es:bx]
  cmp byte [bOdd], 1
  jnz LABEL_EVEN_2
  shr ax4
LABEL_EVEN_2:
  and ax0FFFh
  pop bx
  pop es 
  ret
times 510-($-$$) db    ;填充剩余的空间,使生成的二进制代码恰好为512字节
dw 0xaa55            ;结束标志
 

0 0
原创粉丝点击