仿照着写个bootloader (二)

  <x86>第八章的内容大概是编写一个加载器,从磁盘加载前面写的程序,然后跳转到该程序中执行并输出hello world。咱不急,不跨这么大步子,先写个程序从磁盘上读数据吧~

  我把要读取的数据存放在磁盘第二个扇区,格式如下:前2个字节记录需要读取的数据长度,以字节计算,后面紧跟实际数据。

  关于IDE接口寄存器的介绍,这里转一个连接,因为网上都转自博主的文章:http://blog.chinaunix.net/uid-20235103-id-1970841.html

 

DiskDataReg		equ 0x01f0
DiskErrReg		equ 0x01f1
DiskSectCntReg	equ 0x01f2
DiskLoLBAAddr	equ 0x01f3
DiskMeLBAAddr	equ 0x01f4
DiskHiLBAAddr	equ 0x01f5
DiskModReg		equ 0x01f6
DiskCmdStatReg	equ 0x01f7

DiskReadCmd		equ 0x20
DiskWriteCmd	equ 0x30

Arg1Off		equ 0x04 ;第一个参数相对bp偏移
Arg2Off		equ 0x06 ;第二个参数相对bp偏移
Arg3Off		equ 0x08 ;第三个参数相对bp偏移
Arg4Off		equ 0x0A ;第四个参数相对bp偏移

StackBase	equ 0x0000
StackEnd	equ 0x2000
DataBase	equ 0x0300
DataEnd		equ 0x1FFF

section code align=16 vstart=0x7c00
jmp entry

SetSectAddr:
	;设置逻辑扇区的地址
	push bp
	mov bp,sp
	push bx
	;发扇区总数
	mov dx,DiskSectCntReg
	mov al,byte [bp+Arg4Off]
	out dx,al
	;发扇区地址
	mov dx,DiskLoLBAAddr
	mov al,byte [bp+Arg3Off]
	out dx,al
	
	mov dx,DiskMeLBAAddr
	mov al,byte [bp+Arg3Off+1]
	out dx,al
	
	mov dx,DiskHiLBAAddr
	mov al,byte [bp+Arg2Off]
	out dx,al
	
	mov dx,DiskModReg
	mov al,byte [bp+Arg2Off+1]
	out dx,al
	
	pop bx
	mov sp,bp
	pop bp
	
	ret

WaitDiskReady:
	;等待磁盘就绪
	mov dx,DiskCmdStatReg
.waits:
	in al,dx
	and al,0x88
	cmp al,0x08
	jnz .waits
	
	;判断是否有错误
	mov dx,DiskErrReg
.getErr:
	in al,dx
	cmp al,0x00
	;al不为0出错
	jnz .resume
	;没有出错 返回0
	xor ax,ax
	ret
	
.resume:
	;出错 返回
	mov ax,0x01
	ret
	
ReadFromDisk:

	call SetSectAddr
	
	;发读命令
	mov dx,DiskCmdStatReg
	mov al,DiskReadCmd
	out dx,al
	
	call WaitDiskReady
	
	;读取保存在扇区上的数据的有效长度
	mov dx,DiskDataReg
	in ax,dx
	shr ax,0x01
	;比较要读取数据的长度和有效数据的长度,取最短的
	;磁盘上保存的数据,长度按字节计数,但是DiskDataReg
	;端口是16位端口,每次读取1字,因此循环次数需要除2
	mov cx,ax
	mov dx,DiskDataReg
.readw:
	in ax,dx
	mov [bx],ax
	add bx,2
	loop .readw
	
	;代码没写好 没返回地址 只能自己构造返回地址了
	lea ax,[RetFromReadDisk]
	push ax
	ret
	
entry:
	xor ax,ax
	mov ax,DataBase
	mov ds,ax
	
	mov ax,StackBase
	mov ss,ax
	mov sp,StackEnd
	;读取扇区总数
	push word 0x0001
	;LBA地址的0-15位
	push word 0x0001
	;LBA地址的16-31位
	push word 0xe000
	;需要从扇区上读取的最大字节数
	push word 0x0200
	jmp ReadFromDisk
RetFromReadDisk:	
	jmp $
times 510-($-$$) db 0
db 0x55,0xaa
len dw 0x000c ;后面有效数据的长度
string db 'Hello world',0
程序第134行,定义0x55 0xaa使得产生的bin文件正好撑满一个扇区,后面在保留一些数据,那就挤到第二个扇区去了。

在我没想出其他保存数据到磁盘任意位置前就先这么凑合吧

本来想完成写磁盘的程序,没成功,所以代码有点不伦不类,比如130行,本可以用call调用子过程的,现在用了jmp代替。虽然完成了过程调用,但是没有返回地址,怎么回来呢?

call指令会把下一条指令的地址压入堆栈,即sp指向的内存存入返回地址,然后sp=sp-2,然后跳转到目标地址执行。

当程序返回时,ret指令从堆栈中取出sp指向的地址,然后使sp=sp+2,返回。

现在,jmp到目标地址执行,堆栈中没有返回地址,如果直接调用ret,此时堆栈顶部的数值就是返回地址,至于是否合法,执行完了才知道。如果往堆栈中压入一个地址,ret执行时就会跳到这个指定的地址,怎么指定这个地址?用lea~取标号的内存地址,push进堆栈,程序110、111行的目的就是如此,最终返回到131行RetFromReadDisk标号处。

    一些简单的缓冲区溢出程序也是这样构造目标地址进行跳转的~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值