文章目录
前言
本文主要记录了《操作系统真象还原》1-3章从BIOS读取MBR引导记录,到跳转到OS加载器的全部过程和代码
一、开始实验前的一些基本问题解答?
section的含义?
用于标记逻辑块,便于编程,编译器会将多个相同的section组合到一起
vstart的含义?
告诉编译器从该地址开始编地址,即基本地址,默认基本地址为 0
$ 和 $$区别?
$代表本行地址,编译器会将其替换为地址,具体地址由vstart给出
$$代表本section的起始地址
实模式的特点?
该模式下CPU的寻址,寄存器大小,都是16位的,最多可访问20位的地址空间,即1M,通过段基址+段内偏移来访问内存
缺点:用户程序和操作系统程序放在一个特权级,容易导致安全隐患
CPU如何和硬盘进行交互?
通过和硬盘控制器的寄存器,即端口进行数据交互来达到硬盘交互的目的。具体使用al,还是ax,根据传输的数据位数来确定。
输入:
in al,dx
in ax,dx
输出:
out dx,ax
out dx,al
out 立即数,ax或al
CPU和IO设备交互方式?
- CPU主动查询IO状态
- IO通过中断方式通知CPU获取数据
- DMA,让IO设备直接和内存进行交互
- I/O 处理机传送方式。完全解放CPU,但需要单独硬件支持
程序载入内存方式?
1. 静态装入内存
在编译后或装入时即确定物理地址,装入内存后就无法改变地址,由于内存的换入换出,程序扩张会改变地址。
-
动态重定位
通过段基址+段内偏移的方式确定地址,较为灵活,只需改变基址寄存器中的值就可以动态改变程序装入地址,现在一般使用这种方式。
什么是LBA
用于确定一个扇区逻辑地址,全称Logic Block Address,有28位和48位两种,LBA28支持2的28次方个扇区,每个512字节,最大支持128G,LBA48最大支持 131072TB
二、代码实现
系统启动流程梳理
CPU从0xFFFF0开始执行跳转指令到BIOS引导例程,BIOS检查设备是否正常,建立BIOS中断向量表,将0磁盘0磁道1扇区加载进内存Ox7c00h中,这就是MBR(主引导记录),打印开机字符,读取内核加载器相关代码,最后跳转到内核加载器执行
读取硬盘实现流程
实现流程:
-
选择通道,往该通道的 sector count 寄存器中写入待操作的扇区数。
-
往该通道上的三个 LBA (LBA28共有28位,用于确定)寄存器写入扇区起始地址的低 24 位。
-
往 device 寄存器中写入 LBA 地址的 24-27 位,并置第6 位为1 ,使其为 LBA 模式,设置第4位,选择操作的硬盘( master=0 或 slave =1)
-
往该通道上的 command 寄存器写入操作命令
0x20:读取扇区0x30: 写扇区
-
读取该通道上的 status 寄存器,判断硬盘工作是否完成。
-
如果以上步骤是读硬盘,则循环将dx中数据写入内存中,操作结束,写操作就直接结束
配置头文件
代码如下(示例):
LOADER_BASE_ADDR equ 0x900 ;设置加载器的基本地址
LOADER_START_SECTOR equ 0x2 ;配置读取的扇区号
主引导记录代码mbr.S
%include "boot.inc"
;设置编码从0x7c00开始
SECTION MBR vstart=0x7c00
;初始化寄存器
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
;设置显存的位置
mov ax,0xb800
mov gs,ax
;调用BIOS中断清除屏幕
mov ax, 0x600
mov bx, 0x700
mov cx, 0
mov dx, 0x184f
int 0x10
mov byte [gs:0x00],'H'
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'e'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'l'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'l'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'o'
mov byte [gs:0x09],0xA4
mov ax,LOADER_START_SECTOR
mov bx,LOADER_BASE_ADDR
mov cx,1
call rd_disk
;跳转到0x900开始执行加载OS程序
jmp LOADER_BASE_ADDR
rd_disk:
mov esi,eax
mov di,cx
;配置读取的扇区总数
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi
;往LBA中写入地址低24位
mov dx,0x1f3
out dx,al
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
shr eax,cl
mov dx,0x1f5
out dx,al
;往device寄存器中写入高24~27位
shr eax,cl
;高4位置位1110
and al,0x0f
or al,0xe0
mov dx,0x1f6
out dx,al
;发出读取指令
mov dx,0x1f7
mov al,0x20
out dx,al
;判断是否读取完毕
no_ready:
nop
in al,dx
;高四位和低四位只有第一位不变,其他都置0,便于判断是否硬盘读取结束
and al,0x88
cmp al,0x8
jnz no_ready
;计算移动次数,扇区数*512/2 每次移动两个字节
mov ax,di
mov dx,0x100
mul dx
mov cx,ax
mov dx,0x1f0
write_to_disk:
in ax,dx
mov [bx],ax
add bx,2
loop write_to_disk
ret
times 510-($-$$) db 0
db 0x55,0xaa
编译代码,并写入启动硬盘
接下来把库目录路径链接给MBR.S
nasm -I include/ -o mbr.bin mbr.S
把MBR 写入进磁盘0中,注意按照自己的路径来
dd if=/你自己的路径/bochs/mbr.bin of=/你自己的路径/bochs/hd60M.img bs=512 count=1 conv=notrunc
编译Loader.S
nasm -I include/ -o boot/loader.bin boot/loader.S
向磁盘2写入 seek = 2跳过0和1两个块
dd if=/你自己的路径/boot/loader.bin of=/你自己的路径/bochs/hd60M.img bs=512 count=1 seek=2 conv=notrunc
启动bochs
bin/bochs -f bochsrc.disk
总结
以上就是今天要讲的内容,文章可能还有一些没讲明白的地方,欢迎留言