《操作系统真象还原》1-3章 学习记录


前言

本文主要记录了《操作系统真象还原》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设备交互方式?

  1. CPU主动查询IO状态
  2. IO通过中断方式通知CPU获取数据
  3. DMA,让IO设备直接和内存进行交互
  4. I/O 处理机传送方式。完全解放CPU,但需要单独硬件支持

程序载入内存方式?

1. 静态装入内存

在编译后或装入时即确定物理地址,装入内存后就无法改变地址,由于内存的换入换出,程序扩张会改变地址。
  1. 动态重定位

    通过段基址+段内偏移的方式确定地址,较为灵活,只需改变基址寄存器中的值就可以动态改变程序装入地址,现在一般使用这种方式。

什么是LBA

用于确定一个扇区逻辑地址,全称Logic Block Address,有28位和48位两种,LBA28支持2的28次方个扇区,每个512字节,最大支持128G,LBA48最大支持 131072TB

二、代码实现

系统启动流程梳理

CPU从0xFFFF0开始执行跳转指令到BIOS引导例程,BIOS检查设备是否正常,建立BIOS中断向量表,将0磁盘0磁道1扇区加载进内存Ox7c00h中,这就是MBR(主引导记录),打印开机字符,读取内核加载器相关代码,最后跳转到内核加载器执行

读取硬盘实现流程

实现流程:

  1. 选择通道,往该通道的 sector count 寄存器中写入待操作的扇区数。

  2. 往该通道上的三个 LBA (LBA28共有28位,用于确定)寄存器写入扇区起始地址的低 24 位。

  3. 往 device 寄存器中写入 LBA 地址的 24-27 位,并置第6 位为1 ,使其为 LBA 模式,设置第4位,选择操作的硬盘( master=0 或 slave =1)
    在这里插入图片描述

  4. 往该通道上的 command 寄存器写入操作命令
    0x20:读取扇区

    0x30: 写扇区

  5. 读取该通道上的 status 寄存器,判断硬盘工作是否完成。
    在这里插入图片描述

  6. 如果以上步骤是读硬盘,则循环将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

总结

以上就是今天要讲的内容,文章可能还有一些没讲明白的地方,欢迎留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值