内核雏形——从Loader到内核

2023-10-06 03:42
文章标签 内核 loader 雏形

本文主要是介绍内核雏形——从Loader到内核,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《自己动手

Loader需要做两项工作:加载内核到内存,跳入保护模式。我们先来研究第一项,用Loader加载ELF。

加载内核到内存这一步和引导扇区的工作非常相似,只是处理内核时我们需要根据Program header table中的值把内核中相应的段放到正确的位置。


这里有一份inc文件,在后续的asm文件中被引用到

fat12hdr.inc

; FAT12 磁盘的头
; ----------------------------------------------------------------------
BS_OEMName	DB 'ForrestY'	; OEM String, 必须 8 个字节BPB_BytsPerSec	DW 512		; 每扇区字节数
BPB_SecPerClus	DB 1		; 每簇多少扇区
BPB_RsvdSecCnt	DW 1		; Boot 记录占用多少扇区
BPB_NumFATs	DB 2		; 共有多少 FAT 表
BPB_RootEntCnt	DW 224		; 根目录文件数最大值
BPB_TotSec16	DW 2880		; 逻辑扇区总数
BPB_Media	DB 0xF0		; 媒体描述符
BPB_FATSz16	DW 9		; 每FAT扇区数
BPB_SecPerTrk	DW 18		; 每磁道扇区数
BPB_NumHeads	DW 2		; 磁头数(面数)
BPB_HiddSec	DD 0		; 隐藏扇区数
BPB_TotSec32	DD 0		; 如果 wTotalSectorCount 是 0 由这个值记录扇区数BS_DrvNum	DB 0		; 中断 13 的驱动器号
BS_Reserved1	DB 0		; 未使用
BS_BootSig	DB 29h		; 扩展引导标记 (29h)
BS_VolID	DD 0		; 卷序列号
BS_VolLab	DB 'Tinix0.01  '; 卷标, 必须 11 个字节
BS_FileSysType	DB 'FAT12   '	; 文件系统类型, 必须 8个字节  
;------------------------------------------------------------------------; -------------------------------------------------------------------------
; 基于 FAT12 头的一些常量定义,如果头信息改变,下面的常量可能也要做相应改变
; -------------------------------------------------------------------------
FATSz			equ	9	; BPB_FATSz16
RootDirSectors		equ	14	; 根目录占用空间: RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec – 1)) / BPB_BytsPerSec; 但如果按照此公式代码过长
SectorNoOfRootDirectory	equ	19	; Root Directory 的第一个扇区号	= BPB_RsvdSecCnt + (BPB_NumFATs * FATSz)
SectorNoOfFAT1		equ	1	; FAT1 的第一个扇区号	= BPB_RsvdSecCnt
DeltaSectorNo		equ	17	; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo

这份boot.asm是loader的代码

;%define	_BOOT_DEBUG_	; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试%ifdef	_BOOT_DEBUG_org  0100h			; 调试状态, 做成 .COM 文件, 可调试
%elseorg  07c00h			; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif;================================================================================================
%ifdef	_BOOT_DEBUG_
BaseOfStack		equ	0100h	; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack		equ	07c00h	; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%endifBaseOfLoader		equ	09000h	; LOADER.BIN 被加载到的位置 ----  段地址
OffsetOfLoader		equ	0100h	; LOADER.BIN 被加载到的位置 ---- 偏移地址
;================================================================================================jmp short LABEL_START		; Start to boot.nop				; 这个 nop 不可少; 下面是 FAT12 磁盘的头, 之所以包含它是因为下面用到了磁盘的一些信息
%include	"fat12hdr.inc"LABEL_START:	mov	ax, csmov	ds, axmov	es, axmov	ss, axmov	sp, BaseOfStack; 清屏mov	ax, 0600h		; AH = 6,  AL = 0hmov	bx, 0700h		; 黑底白字(BL = 07h)mov	cx, 0			; 左上角: (0, 0)mov	dx, 0184fh		; 右下角: (80, 50)int	10h			; int 10hmov	dh, 0			; "Booting  "call	DispStr			; 显示字符串xor	ah, ah	; ┓xor	dl, dl	; ┣ 软驱复位int	13h	; ┛; 下面在 A 盘的根目录寻找 LOADER.BINmov	word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:cmp	word [wRootDirSizeForLoop], 0	; ┓jz	LABEL_NO_LOADERBIN		; ┣ 判断根目录区是不是已经读完dec	word [wRootDirSizeForLoop]	; ┛ 如果读完表示没有找到 LOADER.BINmov	ax, BaseOfLoadermov	es, ax			; es <- BaseOfLoadermov	bx, OffsetOfLoader	; bx <- OffsetOfLoader	于是, es:bx = BaseOfLoader:OffsetOfLoadermov	ax, [wSectorNo]	; ax <- Root Directory 中的某 Sector 号mov	cl, 1call	ReadSectormov	si, LoaderFileName	; ds:si -> "LOADER  BIN"mov	di, OffsetOfLoader	; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100cldmov	dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:cmp	dx, 0										; ┓循环次数控制,jz	LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR	; ┣如果已经读完了一个 Sector,dec	dx											; ┛就跳到下一个 Sectormov	cx, 11
LABEL_CMP_FILENAME:cmp	cx, 0jz	LABEL_FILENAME_FOUND	; 如果比较了 11 个字符都相等, 表示找到
dec	cxlodsb				; ds:si -> alcmp	al, byte [es:di]jz	LABEL_GO_ONjmp	LABEL_DIFFERENT		; 只要发现不一样的字符就表明本 DirectoryEntry 不是
; 我们要找的 LOADER.BIN
LABEL_GO_ON:inc	dijmp	LABEL_CMP_FILENAME	;	继续循环LABEL_DIFFERENT:and	di, 0FFE0h						; else ┓	di &= E0 为了让它指向本条目开头add	di, 20h							;     ┃mov	si, LoaderFileName					;     ┣ di += 20h  下一个目录条目jmp	LABEL_SEARCH_FOR_LOADERBIN;    ┛LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:add	word [wSectorNo], 1jmp	LABEL_SEARCH_IN_ROOT_DIR_BEGINLABEL_NO_LOADERBIN:mov	dh, 2			; "No LOADER."call	DispStr			; 显示字符串
%ifdef	_BOOT_DEBUG_mov	ax, 4c00h		; ┓int	21h			; ┛没有找到 LOADER.BIN, 回到 DOS
%elsejmp	$			; 没有找到 LOADER.BIN, 死循环在这里
%endifLABEL_FILENAME_FOUND:			; 找到 LOADER.BIN 后便来到这里继续mov	ax, RootDirSectorsand	di, 0FFE0h		; di -> 当前条目的开始add	di, 01Ah		; di -> 首 Sectormov	cx, word [es:di]push	cx			; 保存此 Sector 在 FAT 中的序号add	cx, axadd	cx, DeltaSectorNo	; 这句完成时 cl 里面变成 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)mov	ax, BaseOfLoadermov	es, ax			; es <- BaseOfLoadermov	bx, OffsetOfLoader	; bx <- OffsetOfLoader	于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoadermov	ax, cx			; ax <- Sector 号LABEL_GOON_LOADING_FILE:push	ax			; ┓push	bx			; ┃mov	ah, 0Eh			; ┃ 每读一个扇区就在 "Booting  " 后面打一个点, 形成这样的效果:mov	al, '.'			; ┃mov	bl, 0Fh			; ┃ Booting ......int	10h			; ┃pop	bx			; ┃pop	ax			; ┛mov	cl, 1call	ReadSectorpop	ax			; 取出此 Sector 在 FAT 中的序号call	GetFATEntrycmp	ax, 0FFFhjz	LABEL_FILE_LOADEDpush	ax			; 保存 Sector 在 FAT 中的序号mov	dx, RootDirSectorsadd	ax, dxadd	ax, DeltaSectorNoadd	bx, [BPB_BytsPerSec]jmp	LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:mov	dh, 1			; "Ready."call	DispStr			; 显示字符串; *****************************************************************************************************jmp	BaseOfLoader:OffsetOfLoader	; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处; 开始执行 LOADER.BIN 的代码; Boot Sector 的使命到此结束
; *****************************************************************************************************;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop	dw	RootDirSectors	; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo		dw	0		; 要读取的扇区号
bOdd			db	0		; 奇数还是偶数;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName		db	"LOADER  BIN", 0	; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength		equ	9
BootMessage:		db	"Booting  "; 9字节, 不够则用空格补齐. 序号 0
Message1		db	"Ready.   "; 9字节, 不够则用空格补齐. 序号 1
Message2		db	"No LOADER"; 9字节, 不够则用空格补齐. 序号 2
;============================================================================;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;	显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:mov	ax, MessageLengthmul	dhadd	ax, BootMessagemov	bp, ax			; ┓mov	ax, ds			; ┣ ES:BP = 串地址mov	es, ax			; ┛mov	cx, MessageLength	; CX = 串长度mov	ax, 01301h		; AH = 13,  AL = 01hmov	bx, 0007h		; 页号为0(BH = 0) 黑底白字(BL = 07h)mov	dl, 0int	10h			; int 10hret;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
;	从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
ReadSector:; -----------------------------------------------------------------------; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号); -----------------------------------------------------------------------; 设扇区号为 x;                           ┌ 柱面号 = y >> 1;       x           ┌ 商 y ┤; -------------- => ┤      └ 磁头号 = y & 1;  每磁道扇区数     │;                   └ 余 z => 起始扇区号 = z + 1push	bpmov	bp, spsub	esp, 2			; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]mov	byte [bp-2], clpush	bx			; 保存 bxmov	bl, [BPB_SecPerTrk]	; bl: 除数div	bl			; y 在 al 中, z 在 ah 中inc	ah			; z ++mov	cl, ah			; cl <- 起始扇区号mov	dh, al			; dh <- yshr	al, 1			; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)mov	ch, al			; ch <- 柱面号and	dh, 1			; dh & 1 = 磁头号pop	bx			; 恢复 bx; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^mov	dl, [BS_DrvNum]		; 驱动器号 (0 表示 A 盘)
.GoOnReading:mov	ah, 2			; 读mov	al, byte [bp-2]		; 读 al 个扇区int	13hjc	.GoOnReading		; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止add	esp, 2pop	bpret;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;	找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
;	需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:push	espush	bxpush	axmov	ax, BaseOfLoader	; ┓sub	ax, 0100h		; ┣ 在 BaseOfLoader 后面留出 4K 空间用于存放 FATmov	es, ax			; ┛pop	axmov	byte [bOdd], 0mov	bx, 3mul	bx			; dx:ax = ax * 3mov	bx, 2div	bx			; dx:ax / 2  ==>  ax <- 商, dx <- 余数cmp	dx, 0jz	LABEL_EVENmov	byte [bOdd], 1
LABEL_EVEN:;偶数xor	dx, dx			; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)mov	bx, [BPB_BytsPerSec]div	bx			; dx:ax / BPB_BytsPerSec  ==>	ax <- 商   (FATEntry 所在的扇区相对于 FAT 来说的扇区号);				dx <- 余数 (FATEntry 在扇区内的偏移)。push	dxmov	bx, 0			; bx <- 0	于是, es:bx = (BaseOfLoader - 100):00 = (BaseOfLoader - 100) * 10hadd	ax, SectorNoOfFAT1	; 此句执行之后的 ax 就是 FATEntry 所在的扇区号mov	cl, 2call	ReadSector		; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区pop	dxadd	bx, dxmov	ax, [es:bx]cmp	byte [bOdd], 1jnz	LABEL_EVEN_2shr	ax, 4
LABEL_EVEN_2:and	ax, 0FFFhLABEL_GET_FAT_ENRY_OK:pop	bxpop	esret
;----------------------------------------------------------------------------times 	510-($-$$)	db	0	; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw 	0xaa55				; 结束标志

接下来是loader.asm 让它把内核放进内存

org  0100h;================================================================================================
BaseOfStack		equ	0100hBaseOfKernelFile	equ	 08000h	; KERNEL.BIN 被加载到的位置 ----  段地址
OffsetOfKernelFile	equ	     0h	; KERNEL.BIN 被加载到的位置 ---- 偏移地址;================================================================================================jmp	LABEL_START		; Start; 下面是 FAT12 磁盘的头, 之所以包含它是因为下面用到了磁盘的一些信息
%include	"fat12hdr.inc"LABEL_START:			; <--- 从这里开始 *************mov	ax, csmov	ds, axmov	es, axmov	ss, axmov	sp, BaseOfStackmov	dh, 0			; "Loading  "call	DispStr			; 显示字符串; 下面在 A 盘的根目录寻找 KERNEL.BINmov	word [wSectorNo], SectorNoOfRootDirectory	xor	ah, ah	; ┓xor	dl, dl	; ┣ 软驱复位int	13h	; ┛
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:cmp	word [wRootDirSizeForLoop], 0	; ┓jz	LABEL_NO_KERNELBIN		; ┣ 判断根目录区是不是已经读完, 如果读完表示没有找到 KERNEL.BINdec	word [wRootDirSizeForLoop]	; ┛mov	ax, BaseOfKernelFilemov	es, ax			; es <- BaseOfKernelFilemov	bx, OffsetOfKernelFile	; bx <- OffsetOfKernelFile	于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFilemov	ax, [wSectorNo]		; ax <- Root Directory 中的某 Sector 号mov	cl, 1call	ReadSectormov	si, KernelFileName	; ds:si -> "KERNEL  BIN"mov	di, OffsetOfKernelFile	; es:di -> BaseOfKernelFile:???? = BaseOfKernelFile*10h+????cldmov	dx, 10h
LABEL_SEARCH_FOR_KERNELBIN:cmp	dx, 0					; ┓jz	LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR	; ┣ 循环次数控制, 如果已经读完了一个 Sector, 就跳到下一个 Sectordec	dx					; ┛mov	cx, 11
LABEL_CMP_FILENAME:cmp	cx, 0			; ┓jz	LABEL_FILENAME_FOUND	; ┣ 循环次数控制, 如果比较了 11 个字符都相等, 表示找到dec	cx			; ┛lodsb				; ds:si -> alcmp	al, byte [es:di]	; if al == es:dijz	LABEL_GO_ONjmp	LABEL_DIFFERENT
LABEL_GO_ON:inc	dijmp	LABEL_CMP_FILENAME	;	继续循环LABEL_DIFFERENT:and	di, 0FFE0h		; else┓	这时di的值不知道是什么, di &= e0 为了让它是 20h 的倍数add	di, 20h			;     ┃mov	si, KernelFileName	;     ┣ di += 20h  下一个目录条目jmp	LABEL_SEARCH_FOR_KERNELBIN;   ┛LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:add	word [wSectorNo], 1jmp	LABEL_SEARCH_IN_ROOT_DIR_BEGINLABEL_NO_KERNELBIN:mov	dh, 2			; "No KERNEL."call	DispStr			; 显示字符串
%ifdef	_LOADER_DEBUG_mov	ax, 4c00h		; ┓int	21h			; ┛没有找到 KERNEL.BIN, 回到 DOS
%elsejmp	$			; 没有找到 KERNEL.BIN, 死循环在这里
%endifLABEL_FILENAME_FOUND:			; 找到 KERNEL.BIN 后便来到这里继续mov	ax, RootDirSectorsand	di, 0FFF0h		; di -> 当前条目的开始push	eaxmov	eax, [es : di + 01Ch]		; ┓mov	dword [dwKernelSize], eax	; ┛保存 KERNEL.BIN 文件大小pop	eaxadd	di, 01Ah		; di -> 首 Sectormov	cx, word [es:di]push	cx			; 保存此 Sector 在 FAT 中的序号add	cx, axadd	cx, DeltaSectorNo	; 这时 cl 里面是 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)mov	ax, BaseOfKernelFilemov	es, ax			; es <- BaseOfKernelFilemov	bx, OffsetOfKernelFile	; bx <- OffsetOfKernelFile	于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFilemov	ax, cx			; ax <- Sector 号LABEL_GOON_LOADING_FILE:push	ax			; ┓push	bx			; ┃mov	ah, 0Eh			; ┃ 每读一个扇区就在 "Loading  " 后面打一个点, 形成这样的效果:mov	al, '.'			; ┃mov	bl, 0Fh			; ┃ Loading ......int	10h			; ┃pop	bx			; ┃pop	ax			; ┛mov	cl, 1call	ReadSectorpop	ax			; 取出此 Sector 在 FAT 中的序号call	GetFATEntrycmp	ax, 0FFFhjz	LABEL_FILE_LOADEDpush	ax			; 保存 Sector 在 FAT 中的序号mov	dx, RootDirSectorsadd	ax, dxadd	ax, DeltaSectorNoadd	bx, [BPB_BytsPerSec]jmp	LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:call	KillMotor		; 关闭软驱马达mov	dh, 1			; "Ready."call	DispStr			; 显示字符串jmp	$;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop	dw	RootDirSectors	; Root Directory 占用的扇区数
wSectorNo		dw	0		; 要读取的扇区号
bOdd			db	0		; 奇数还是偶数
dwKernelSize		dd	0		; KERNEL.BIN 文件大小;============================================================================
;字符串
;----------------------------------------------------------------------------
KernelFileName		db	"KERNEL  BIN", 0	; KERNEL.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength		equ	9
LoadMessage:		db	"Loading  "
Message1		db	"Ready.   "
Message2		db	"No KERNEL"
;============================================================================;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;	显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:mov	ax, MessageLengthmul	dhadd	ax, LoadMessagemov	bp, ax			; ┓mov	ax, ds			; ┣ ES:BP = 串地址mov	es, ax			; ┛mov	cx, MessageLength	; CX = 串长度mov	ax, 01301h		; AH = 13,  AL = 01hmov	bx, 0007h		; 页号为0(BH = 0) 黑底白字(BL = 07h)mov	dl, 0add	dh, 3			; 从第 3 行往下显示int	10h			; int 10hret
;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
;	从序号(Directory Entry 中的 Sector 号)为 ax 的的 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
ReadSector:; -----------------------------------------------------------------------; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号); -----------------------------------------------------------------------; 设扇区号为 x;                           ┌ 柱面号 = y >> 1;       x           ┌ 商 y ┤; -------------- => ┤      └ 磁头号 = y & 1;  每磁道扇区数     │;                   └ 余 z => 起始扇区号 = z + 1push	bpmov	bp, spsub	esp, 2			; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]mov	byte [bp-2], clpush	bx			; 保存 bxmov	bl, [BPB_SecPerTrk]	; bl: 除数div	bl			; y 在 al 中, z 在 ah 中inc	ah			; z ++mov	cl, ah			; cl <- 起始扇区号mov	dh, al			; dh <- yshr	al, 1			; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)mov	ch, al			; ch <- 柱面号and	dh, 1			; dh & 1 = 磁头号pop	bx			; 恢复 bx; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^mov	dl, [BS_DrvNum]		; 驱动器号 (0 表示 A 盘)
.GoOnReading:mov	ah, 2			; 读mov	al, byte [bp-2]		; 读 al 个扇区int	13hjc	.GoOnReading		; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止add	esp, 2pop	bpret;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;	找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
;	需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:push	espush	bxpush	axmov	ax, BaseOfKernelFile	; ┓sub	ax, 0100h		; ┣ 在 BaseOfKernelFile 后面留出 4K 空间用于存放 FATmov	es, ax			; ┛pop	axmov	byte [bOdd], 0mov	bx, 3mul	bx			; dx:ax = ax * 3mov	bx, 2div	bx			; dx:ax / 2  ==>  ax <- 商, dx <- 余数cmp	dx, 0jz	LABEL_EVENmov	byte [bOdd], 1
LABEL_EVEN:;偶数xor	dx, dx			; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)mov	bx, [BPB_BytsPerSec]div	bx			; dx:ax / BPB_BytsPerSec  ==>	ax <- 商   (FATEntry 所在的扇区相对于 FAT 来说的扇区号);				dx <- 余数 (FATEntry 在扇区内的偏移)。push	dxmov	bx, 0			; bx <- 0	于是, es:bx = (BaseOfKernelFile - 100):00 = (BaseOfKernelFile - 100) * 10hadd	ax, SectorNoOfFAT1	; 此句执行之后的 ax 就是 FATEntry 所在的扇区号mov	cl, 2call	ReadSector		; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区pop	dxadd	bx, dxmov	ax, [es:bx]cmp	byte [bOdd], 1jnz	LABEL_EVEN_2shr	ax, 4
LABEL_EVEN_2:and	ax, 0FFFhLABEL_GET_FAT_ENRY_OK:pop	bxpop	esret
;----------------------------------------------------------------------------;----------------------------------------------------------------------------
; 函数名: KillMotor
;----------------------------------------------------------------------------
; 作用:
;	关闭软驱马达
KillMotor:push	dxmov	dx, 03F2hmov	al, 0out	dx, alpop	dxret
;----------------------------------------------------------------------------

这个loader实际上实在找kernel.bin并将它加载,反之显示"NO KERNEL“.


我们可以试试写一个很简陋的”内核”,当然它并不算是内核

kernel.asm

[section .text]	; 代码在此global _start	; 导出 _start_start:	; 跳到这里来的时候,我们假设 gs 指向显存mov	ah, 0Fh				; 0000: 黑底    1111: 白字mov	al, 'K'mov	[gs:((80 * 1 + 39) * 2)], ax	; 屏幕第 1 行, 第 39 列。jmp	$
我们将编译好的kernel.bin boot.bin loader.bin用FloppyWriter写入软盘,用Vmware加载它,就可以在屏幕上看到结果啦~~

这篇关于内核雏形——从Loader到内核的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/152753

相关文章

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

Ubuntu22.04回退系统内核

文章目录 起因回退操作卸载内核禁止内核升级 起因 最近因为系统内核自动升级,导致显卡驱动检测不到,炼丹环境被破坏。无奈只能重装驱动,于是跟着手册操作发现驱动要求的是内核版本是5.15.0-25-generic,而我通过uname -r发现这时候的内核版本是6.8.0-40-generic,看来只能回退了。 我搜索了网上很多的文章,没有一篇文章能够完全解决这个问题,所以在我多次尝

跟我一起玩《linux内核设计的艺术》第1章(四)——from setup.s to head.s,这回一定让main滚出来!(已解封)

看到书上1.3的大标题,以为马上就要见着main了,其实啊,还早着呢,光看setup.s和head.s的代码量就知道,跟bootsect.s没有可比性,真多……这确实需要包括我在内的大家多一些耐心,相信见着main后,大家的信心和干劲会上一个台阶,加油! 既然上篇已经玩转gdb,接下来的讲解肯定是边调试边分析书上的内容,纯理论讲解其实我并不在行。 setup.s: 目标:争取把setup.

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte

linux 内核提权总结(demo+exp分析) -- 任意读写(四)

hijack_modprobe_path篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     原理同hijack_prctl, 当用户执行错误格式的elf文件时内核调用call_usermod

linux 内核提权总结(demo+exp分析) -- 任意读写(三)

hijack_prctl篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   prctl函数: 用户态函数,可用于定制进程参数,非常适合和内核进行交互 用户态执行prctl函数后触发prctl系统