Usb Boot Loader(2)

USB Boot Loader(1)完成了把编译成的二进制文件拷贝到DBR区域,实现了Loader.bin突破512字节的限制,但是最大仅仅为4K,若果想要把操作系统代码也载入内存,显然4K也是太小了,那么就把Loader.bin当做一个跳板,模仿DBR内的代码实现加载任意大小的文件。

修改Loader.asm 的内容,主要完成了循环查找KERNEL.BIN文件在U盘中存储的位置,并且载入内存后,跳转到KERNEL.BIN加载内存的位置。这样就完全突破了执行代码大小的限制。

loader.asm

;
;nasm loader.asm -o LOADER.BIN
;
org 0100h;必须为0100h,因为boot.asm中跳转来时cs为09000h,保证地址偏移正确
SectorsOfMBR		equ 63		;MBR 大小
BaseOfFat			equ 07900h
BaseOfKernel		equ	08000h	; KERNEL.BIN 被加载到的位置 ----  段地址
OffsetOfKernel		equ	0100h	; KERNEL.BIN 被加载到的位置 ----  偏移地址
BaseOfLoader		equ	09000h	; LOADER.BIN 被加载到的位置 ----  段地址
OffsetOfLoader		equ	0100h	; LOADER.BIN 被加载到的位置 ----  偏移地址

BaseOfStack			equ	0100h	; 堆栈基址

	jmp short START
	nop				; 这个 nop 不可少
; 下面是 FAT32 磁盘的头 磁盘头的数据写入到一个inc文件中
%include "fat32head.inc"
;offset 90Bytes
;[section .begin]
START:
	pop ax			;BS_DrvNum
	mov byte[BS_DrvNum], al
	mov ax, cs
	mov es, ax
	mov ds, ax
	mov ss, ax
	mov	sp, BaseOfStack
	
	; 清屏
	mov	ax, 0600h		; AH = 6,  AL = 0h
	mov	bx, 0700h		; 黑底白字(BL = 07h)
	mov	cx, 0			; 左上角: (0, 0)
	mov	dx, 0184fh		; 右下角: (80, 50)
	int	10h				; int 10h
	;;Loading
	mov ax, KernelLoading
	call DispStr
;;计算根目录扇区位置
		
	xor eax, eax
	mov al, [BPB_NumFATs]
	mov ebx, [BPB_SecPerFat]
	mul ebx
	xor ebx, ebx
	mov bx, word[BPB_RsvdSecCnt]
	add eax, ebx
	mov dword[SectorNoOfRootDirectory], eax
	add eax, SectorsOfMBR ;;sectors of MBR
		;;读取根目录两个扇区
	push eax
	push 2
	push OffsetOfKernel
	push BaseOfKernel
	call ReadSector
	add  sp , 10
	
	push es
	mov ax, BaseOfKernel
	mov es, ax
	mov di, OffsetOfKernel
	mov	si, LoaderFileName	; ds:si -> "KERNEL  BIN"
	cld

	mov dx, 20h		;;两个扇区有32个目录项
LABEL_SEARCH_FOR_KERNEL_BIN:
	cmp	dx, 0				   ; `. 循环次数控制,
	jz	LABEL_NO_KERNEL_BIN ;  / 如果已经读完了一个 Sector,
	dec	dx				   ; /  就跳到下一个 Sector
	mov	cx, 11
	LABEL_CMP_FILENAME:
		cmp	cx, 0
		jz	LABEL_FILENAME_FOUND	; 如果比较了 11 个字符都相等, 表示找到
		dec	cx
		lodsb				; ds:si -> al
		cmp	al, byte [es:di]
		jz	LABEL_GO_ON
		jmp	LABEL_DIFFERENT		; 只要发现不一样的字符就表明本 DirectoryEntry
					; 不是我们要找的 LOADER.BIN
	LABEL_GO_ON:
		inc	di
		jmp	LABEL_CMP_FILENAME	; 继续循环

LABEL_DIFFERENT:
		and	di, 0FFE0h		;  di &= E0 为了让它指向本条目开头
		add	di, 20h			;       |
		mov	si, LoaderFileName	;       | di += 20h  下一个目录条目
		jmp	LABEL_SEARCH_FOR_KERNEL_BIN;    /
LABEL_NO_KERNEL_BIN:	
		pop es
		mov ax,	KernelNoNoFound
		;mov ax, OffsetOfLoader
		Call	DispStr			; 调用显示字符串例程
		jmp	$			; 无限循环
LABEL_FILENAME_FOUND:
;;寻找KERNEL并加载
;;计算文件所在扇区
		and	di, 0FFE0h		; else `. di &= E0 为了让它指向本条目开头
		mov ax, [es:di + 1ah]
		mov word[ClusterNum], ax
		mov ax, [es:di + 14h]
		mov word[ClusterNum + 2], ax;得到存放文件的簇号
		
		mov si , OffsetOfKernel
.1:		
		mov  eax, [ClusterNum]
		cmp  eax, 00FFFFFFFh;判断是否为最后一个文件簇
		je	 .2
		
		xor eax, eax
		mov al, [BPB_SecPerClus]
		mov ebx, [ClusterNum]
		sub ebx, 2
		mul ebx
		mov ebx, [SectorNoOfRootDirectory]
		add eax, ebx ;;eax 记录文件扇区位置
		add eax, SectorsOfMBR
		
		push si
		;;读取根目一个簇		
		push eax
		push 8
		push si
		push BaseOfKernel
		call ReadSector
		add  sp , 10
			;打点
		push ax
		push bx
		mov ah, 0Eh
		mov al, '.'
		mov bl, 0Fh
		int 10h
		pop bx
		pop ax
		;计算文件簇号对应的 FAT表 所在的簇号
		xor edx, edx
		xor eax, eax
		mov dx, [ClusterNum + 2]
		mov ax, [ClusterNum]
		mov bx, 128 ;一个fat扇区(512B)有128个表 512/4 = 128
		div	bx ;ax 商      dx余数
		
		xor ebx, ebx
		mov bx, word[BPB_RsvdSecCnt]
		add eax, ebx
		add eax, SectorsOfMBR
		
		push eax
		push dx
		;;读取一个fat扇区
		push eax
		push 1
		push 0
		push BaseOfFat
		call ReadSector
		add  sp , 10
		pop dx		
		pop eax
			
		;计算FAT表项内的值	
		push es
		shl  dx, 2   ;2^2 = 4Byte = 一个FAT表大小
		mov  di, dx
		mov  ax, BaseOfFat
		mov  es, ax
		mov  eax, dword[es:di]
		
		pop  es
		pop  si
		add  si, 8*512
		mov  dword[ClusterNum], eax
		jmp  .1
.2:
		pop es
				
		jmp BaseOfKernel:OffsetOfKernel

		;mov ax,	KernelFound
		;Call	DispStr			; 调用显示字符串例程		
		;jmp	$			; 无限循环



ReadSector:;;(Base,Offset, sectors_count,sector_num)
	;;读取根目一个簇
		mov ax, word[esp + 2]
		mov word[disk_addr_pkt + buffer_addr+2], ax
		mov ax, [esp + 4]
		mov word[disk_addr_pkt + buffer_addr], ax
		mov ax, [esp + 6]
		mov word[disk_addr_pkt + block_count], ax
		mov ax, [esp + 8]
		mov word[disk_addr_pkt + block_num], ax
		mov ax, [esp + 10]
		mov word[disk_addr_pkt + block_num + 2], ax
		mov byte[disk_addr_pkt + packet_size], 10h
		mov byte[disk_addr_pkt + reserved], 0		
		mov dword[disk_addr_pkt + block_num + 4], 0

		mov ah, 42h
		mov dl, [BS_DrvNum]
		mov si, disk_addr_pkt
		int 13h
	ret
DispStr:
	push bp
	mov bp, ax; ES:BP = 串地址
	mov	cx, 16			; CX = 串长度
	mov	ax, 01301h		; AH = 13,  AL = 01h
	mov	bx, 000ch		; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
	mov	dx, 0
	int	10h	; int 10h
	pop bp
	ret
LoaderFileName		db	"KERNEL  BIN", 0 ; KERNEL.BIN 之文件名
KernelFound			db  "Kernel  Found   "
KernelNoNoFound		db	"Kernel No Found!"
KernelLoading		db	"On      Loading"
;=================变量 =============
struc disk_addr_pkt
	packet_size:  		resb  1  ; 磁盘参数包的尺寸,必须为10H
	reserved:    		resb  1  ;   保留,必须为零
	block_count:  		resw  1  ;   传输的扇区数
	buffer_addr: 		resd  1  ;   内存缓冲区地址(段:偏移)
	block_num:    		resq  1    ;   起始绝对扇区号(即起始扇区的LBA号码)
endstruc
ClusterNum					DD      0
SectorNoOfRootDirectory 	DD 		0
;==============================

Kernel.asm如下:

;/*
;nasm kernel.asm -o KERNEL.BIN
;*/
org 0100h

mov ax,	cs
mov es, ax
mov ds, ax
mov ss, ax
call DispStr
jmp $

DispStr:
	mov ax, BootMessage
	mov bp, ax
	mov cx, 20
	mov ax, 0x1301
	mov bx, 0x0c
	mov dl, 0
	int 10h
	ret

BootMessage: db "Hello Kernel world!!"

思考:

1、编写过程中特别要注意溢出这一问题,注意像eax、ax、ah、al等寄存器的范围。

2、要注意在跳转过程中或者在调用routine时,某些寄存器的改变像es等,不然会出现打印不出字符串或者打印乱码等的问题

3、成功打印出“Hello Kernel world!!”后,就可以结合前面用bochs调试的程序的代码,在loader.asm 中跳转到保护模式下, 把操作系统的代码写入到Kernel.bin里面,通过Loader.bin 实现操作系统的加载。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
有没有想过,像使用U盘一样升级STM32固件,非常简单,非常方便 1: 插入电脑USB接口 2: 把升级固件拖到设备盘符 3: 升级完成 抛弃繁琐的USB DFU,抛弃落后的串口升级,让我们来谈谈U盘升级STM32 1. 为什么设计这个BOOT LOADER 在电子产品开发过程中,为了满足市场需要,经常是先开发出一个简单可用的版本,然后逐步迭代升级,修复bug,并增强系统功能 一个稳定,简单,安全的升级方式,就变得非常重要 对于嵌入式系统来说,常见的升级方式为 串口升级(私有协议或者X-Modem) USB升级(DFU) U盘升级(OTG) 网络升级 无线升级(OTA) 从技术来说,这几种升级方式大同小异,原理类似:都是一个Loader代理接收数据通道的数据,然后解密,烧录到FLASH中;但用户体验完全不同,拿串口升级来说,首先用户需要一个串口软件,然后对于没有硬件串口的PC来说,就需要一个USB转串口设备,对于不同PC平台,串口软件就不一样,这需要学习成本,过程繁琐;所以在一些需要用户自行升级远程设备的情况下,即便是通过电话指导,80%的用户仍然不知道怎么升级,导致失败 USB的DFU升级,也是类似的问题,它设计的初衷就是面向专业用户的,而不是小白!所以需要安装DFU软件,按照手册来一步步升级 OTA升级和网络升级,体验好些,可用做到无感升级,但不适合所有场景 而U盘升级,用户学习成本最低,U盘大家都知道,然后拷贝一个Bin文件进去,插入设备,重启设备,就完成升级了,非常简单。类似的变种,比如手机升级,是最先进的,直接将手机模拟成U盘,然后用户拷贝数据到手机,重启就好了,非常简单 在嵌入式系统中,还没这么方便的升级手段,虽然ARM的Mbed有一种类似的固件更新功能,但它是专门为调试器设计的,不能内嵌到用户MCU中 所以,我将手机升级的方案引入到嵌入式系统中,从而为大家提供一个实现稳定,安全,零学习成本的升级方案 经过一段时间的学习研究,有了这个USB MSD Bootloader 2. 功能特点 只占用15K FLASH空间 简单易用,直接拖拽文件进行固件升级,无需任何专业知识 采用USB大容量设备类,不用安装任何驱动 支持各种系统(Windows/Linux/Mac/Android) 不用开发任何上位机,提高产品效率 支持各种加密算法(AES256等),轻松安全升级 自动识别Bin,Hex,自定义加密固件(后缀为sec)文件 支持MD5文件校验机制,保证固件升级的完整性 显示设备升级状态信息 支持长文件名升级 多种措施保证系统健壮性,保证Bootloader不会被误擦除,保证APP合法性 支持用户自定义加密算法和完整校验算法,极致安全 3. 系统原理 系统开机上电后,Bootloader接管系统,初始化USB硬件,等待USB连接 Bootloader在启动后1秒内,检测USB是否连接PC:如果连接PC,则进入固件升级模式,执行第3步;超时则跳转第8步,尝试执行用户APP Bootloader模拟成MSD设备,构建FAT16虚拟文件系统,U盘名为”Bootloader”,容量为100M,但具体实际可用空间,根据用户MCU来确定,建议不要复制除APP之外的无关文件 当用户复制文件到U盘时,Bootloader会判断文件后缀和判断文件size,如果size大于实际的MCU可用FLASH或者文件后缀不合法,则进入错误状态,更新状态文件,重新枚举USB 文件后缀和size通过检测后,Bootloader会截获PC发送文件数据流,并写入MCU 对应的Flash中 如果写入过程中出错,则终止操作,擦除APP内容,进入错误状态,更新状态文件,重新枚举USB 成功写入后,Bootloader更新状态文件,重新枚举USB,显示升级完成;但不会运行APP,只有拔掉USB后,再次重启,才会进入第8步,尝试运行APP Bootloader检查APP固件的栈和入口函数合法性,只有通过检测后,才开始执行APP。检测判断条件是栈指针必须在RAM地址空间内,入口函数地址必须处于THUMB模式,并LSB为1 停止USB设备,关掉所有的中断,执行APP,APP开始接管系统 4. 支持芯片 STM32F101/3/5/7 重点来了,点击下面链接,下载固件

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值