操作系统真象还原实验记录之实验三:让mbr使用硬盘

操作系统真象还原之实验三:让mbr使用硬盘

对应书中129页 3.6.1 让mbr使用硬盘

1.相关基础知识总结

1.1 硬盘与硬盘控制器

硬盘控制器是针对硬盘的I/O接口,与显卡与显示器不同的是,显卡与显示器是分开的,硬盘与硬盘控制器是连接在一起的。

硬盘:

0盘0道1扇区(CHS方式从1开始编号)与0盘0道0扇区(LBA方式从0开始编号)

硬盘的并行和串行接口:

以前的接口为并行,叫做PATA(Parallel ATA)。现在刚出道的接口为串行接口,叫做SATA(serial ATA)
连接硬盘与PATA接口的线缆成为IDE线,一根IDE线可以插两块硬盘,一个叫主盘,一个叫从盘。
一个主板有两个IDE插槽,可以插4块支持PATA的硬盘。其中IDE0又叫做Primary通道,IDE1又叫做Secondary通道。
即使现在的SATA也支持PATA编程。向上兼容是计算机源源不断向上发展的根基。
所以后面的代码按下图的PATA接口中寄存器端口号来也没啥问题。

硬盘控制器的端口寄存器:

在这里插入图片描述
功能的描述太多了见书P126
在SATA技术中,每个SATA接口都配备了一套独立的控制和状态寄存器集合。这些寄存器集合广义上可以定义为命令/控制寄存器、状态寄存器、以及用于配置和管理数据传输的其他寄存器,它们通过内存映射I/O(MMIO)的方式被系统访问,而非传统PATA接口采用的I/O端口地址。

因此,当主板提供了多个SATA接口时,实质上意味着系统能够通过这些独立的SATA接口内部的SATA端口集合(上述广义的寄存器集合),各自控制和通信与之相连的SATA设备,从而实现对多个硬盘的并行读写操作。每个SATA接口上的寄存器集合确保了对单一连接设备的全面管理,而无需像PATA那样通过主通道和副通道的概念来区分不同的设备连接。

数据传送方式

(1)无条件传送方式
(2)查询传送方式
(3)中断传送方式
(4)DMA传送方式
(5)I/O处理机传送方式

本次实验的代码采用的是第二种软件查询的方式。

2.实验记录

2.1实验目的

1.编写loader.s,该程序功能是在显示屏上打印“2 LOADER”字段,然后将该文件编译后使用dd命令刻入0盘0道2扇区(LBA方式)
2.改写mbr.s,该程序功能是可以将硬盘中0盘0道2扇区的内容写入内存,然后跳转执行写好的loader.s。
3.编写boot.inc,里面写配置信息

2.2实验代码

2.2.1 mbr.s
; mbr.S

; 主引导程序
; --------------------------------------------------
%include "boot.inc";
;LOADER_BASE_ADDR equ 0x900  将loader加载到内存0x900
;LOADER_START_SECTOR equ 0x2  loader位于磁盘第2块扇区

SECTION MBR vstart=0x7c00 ; 把起始地址编译为 0x7c00
    mov ax, cs     ; cs 代码段寄存器
    mov ds, ax     ; dx 数据段寄存器
    mov es, ax     ; es 附加段寄存器
    mov ss, ax     ; ss 堆栈段寄存器
    mov fs, ax     ; fs 80386 后添加的寄存器,无全称
    mov sp, 0x7c00 ; sp 堆栈指针寄存器
     
    mov ax, 0xb800;
    mov gs, ax;
	
; 清屏
; --------------------------------------------------
; INT 0x10    功能号: 0x06    功能描述:上卷窗口
; --------------------------------------------------
; 输入:
; AH 功能号 = 0x06
; AL = 上卷的行数(如果为0,表示全部)
; BH = 上卷行属性
; (CL, CH) = 窗口左上角的 (X, Y) 位置
; (DL, DH) = 窗口右下角的 (X, Y) 位置
; 无返回值:
    mov ax, 0x600
    mov bx, 0x700
    mov cx, 0
    mov dx, 0x184f ; 右下角: (80, 25)
                   ; VGA 文本模式种,一行只能容纳 80 个字符,共 25 行
                   ; 下标从 0 开始,所以 0x18=24, 0x4f=79
        
    int 0x10       ; int 0x10


	;;;;;下面代码是新增功能;;;;;;;;;;;;;;;;;;;;
	mov eax, LOADER_START_SECTOR; 磁盘中loader的LBA地址
	mov bx, LOADER_BASE_ADDR;      loader加入内存的起始地址	
	mov cx, 1; 待读入内存的扇区数
	call rd_disk_m_16;

	jmp LOADER_BASE_ADDR;
;--------------------------------------------------------------
;功能:读取硬盘n个扇区
	rd_disk_m_16:
;----------------------------------------------------------
	mov esi, eax;   备份eax  al在in/out指令会被使用	
	mov di, cx;    备份cx cl会在接下来代码中频繁使用
;读写硬盘:
;第一步:设置要读取的扇区数 1
	mov dx, 0x1f2; 配置的硬盘是ata0-master 是Primary通道  主盘
		       ;sector count寄存器是0x1f2端口
	mov al, cl;
	out dx, al; 从内存把扇区数1输出到端口号0x1f2; 
	mov eax, esi; 恢复eax

;第二步 将LBA地址存入0x1f3~0x1f6	
;LBA地址7~0位写入端口0x1f3
	mov dx, 0x1f3;
	out dx, al;

;LBA地址15~8位写入端口0x1f4
	mov cl, 8;
	shr eax, cl;
	mov dx, 0x1f4;
	out dx, al;

;LBA地址23~16位写入端口0x1f5
	shr eax, cl;
	mov dx, 0x1f5;
	out dx, al;

;LBA地址24~27位写入端口0x1f6
	shr eax, cl;
	and al, 0x0f;  与运算al中低四位为LBA地址24~27位
	or al, 0xe0; 或运算拼出0x1f6的高四位1110 第6位为1表示LBA
	mov dx, 0x1f6; 
	out dx, al;

;第三步 向0x1f7端口写入读命令,0x20  
	mov dx, 0x1f7;
	mov al, 0x20;
    out dx, al;  命令 写入端口0x1f7后,硬盘立即开始工作,将数据放入硬	
;	盘控制器的缓冲区

;第四步 检测硬盘状态,判断loader是否已经读入0x1f0端口中
  .not_ready:
	nop;
	in al, dx; 将端口0x1f7的status写入al;
	and al, 0x88; 获得status的第3位和第7位;
	cmp al, 0x08; 与第3位相减作比较 会影响ZF CF PF
  	jnz .not_ready;   ZF不等于0就跳,相当于循环等缓冲区中的数据准备好为止

;第5步 将0x1f0端口的数据搬向内存
  ;5.1 计算搬运次数
	mov ax, di;	
	mov dx, 256;	0x1f0端口是16比特
	mul dx;	    di*512字节/2字节=搬运次数  16位乘法乘积32位
		;高16位在dx,低16位在ax;
	mov cx, ax;   dx=1 乘积高16位是0,故把低16位移入cx
  ;5.2 循环搬运至内存         
	mov dx, 0x1f0;
  .go_on_read:     ;我们的loader只有一个扇区512字节
    in ax, dx;		;bx的寻址范围位64KB 65536字节	
	mov [bx], ax;	该循环不能加载大于64KB的程序于内存
	add bx, 2;
	loop .go_on_read; cx不等于0 就回到循环处继续搬
	ret    ;搬运完loader 回到LOADER_BASE_ADDR;

  
  times 510-($-$$) db 0 ; 填充文件末尾的魔数 0xaa55 和当前位置之间的空间
                          ; 保证编译后生成的文件大小为 512 字节(硬盘一个扇区的大小)
    db 0x55, 0xaa

代码功能总结:代码第1、2、3步将LBA地址、扇区数放入相应I/O端口,再将读命令0x20放入Command端口,硬盘控制器就会自动将硬盘的数据读入Data端口,再存入硬盘控制器的缓冲区。
代码的第四步就是利用查询传输方式,这段代码一直循环,也就是一直占用cpu查询Status端口。直到规定好的硬盘数据全部读入缓冲区,Status第3位被置为1,程序才会继续执行第五步。
第五步就是将缓冲区数据以2个字节为单位循环先读入Data端口,再用in指令读入内存。

2.2.2 loader.s
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
	
	mov byte [gs:0x00], '2';
	mov byte [gs:0x01], 0xA4;

	mov byte [gs:0x02], ' ';
	mov byte [gs:0x03], 0xA4;

	mov byte [gs:0x04], 'L';
	mov byte [gs:0x05], 0xA4;

	mov byte [gs:0x06], 'O';
	mov byte [gs:0x07], 0xA4;

	mov byte [gs:0x08], 'A';
	mov byte [gs:0x09], 0xA4;

	mov byte [gs:0x0a], 'D';
	mov byte [gs:0x0b], 0xA4;

	mov byte [gs:0x0c], 'E';
	mov byte [gs:0x0d], 0xA4;

	mov byte [gs:0x0e], 'R';
	mov byte [gs:0x0f], 0xA4;

	jmp $;


2.2.3 boot.inc
;----------------------loader AND kernel-----------------------------
LOADER_BASE_ADDR equ 0x900  ;loader.s加载到内存地址0x900
LOADER_START_SECTOR equ 0x2  ;loader.s刻入硬盘0盘0道2扇区(LBA)

2.3实验记录

1.编译loader.s

nasm -o loader.bin loader.s

2.编译mbr.s

nasm -o mbr.bin mbr.s 

3.将mbr.bin刻入第0扇区

dd if=/home/Seven/bochs2.68/bin/mbr.bin of=/home/Seven/bochs2.68/bin/Seven.img bs=512 count=1 seek=0 conv=notrunc

4.将loader.bin刻入第2扇区

dd if=/home/Seven/bochs2.68/bin/loader.bin of=/home/Seven/bochs2.68/bin/Seven.img bs=512 count=1 seek=2 conv=notrunc

5.模拟bochs

效果图在这里插入图片描述

2.4实验结果

在这里插入图片描述
闪烁的2 loader

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值