这一篇我们来完善内核加载器的功能,我们知道内存是很重要的区域,我们需要对内存有个大致的描述,哪些可用,那些不可用,内存有多大。因此在内核加载器中我们需要对内存进行检测。内存检测的方法翻译文档如下:
到目前为止,检测PC内存的最佳方式是使用INT 0x15,EAX = 0xE820命令。这个功能在2002年以后构建的所有PC上都可用,在那之前的大多数现有PC上也可用。它是唯一能够检测4G以上内存区域的BIOS功能,被设计为终极内存检测功能。
实际上,这个功能返回一个未排序的列表,可能包含未使用的条目,并且(在罕见/不稳定的情况下)可能返回重叠的区域。每个列表条目存储在ES:DI指定的内存中,并且DI不会自动递增。条目的格式是两个uint64_t和一个uint32_t(在20字节版本中),加上24字节的ACPI 3.0版本中的一个额外的uint32_t。最好始终将列表条目存储为24字节的数量-这样可以保持uint64_t的对齐(至少保持uint64_t的对齐)。在每次调用之前,确保将最后一个uint64_t设置为1,以使你的映射与ACPI兼容。
第一个uint64_t = 基地址
第二个uint64_t = "区域"的长度(如果该值为0,则忽略该条目)
接下来的uint32_t = 区域的 "类型"
类型1:可用(正常)RAM 类型
2:保留-不可用
类型3:ACPI可回收内存
类型4:ACPI NVS内存
类型5:包含错误内存的区域 接下来的uint32_t = ACPI 3.0扩展属性位字段(如果返回的是24字节而不是20字节)
扩展属性的第0位指示是否应该忽略整个条目(如果该位被清除)。这将是一个巨大的兼容性问题,因为大多数当前的操作系统不会读取这个位,并且不会忽略该条目。 扩展属性的第1位指示该条目是否是非易失性的(如果该位被设置)或不是。标准规定,“报告为非易失性的内存可能需要进行表征,以确定其作为传统RAM的适用性。” 扩展属性的其余30位目前未定义。 基本用法: 对于第一次调用该函数,将ES:DI指向列表的目标缓冲区。清除EBX。将EDX设置为魔术数字0x534D4150。将EAX设置为0xE820(注意EAX的高16位应设置为0)。将ECX设置为24。进行INT 0x15。
如果第一次调用函数成功,EAX将设置为0x534D4150,并且Carry标志将被清除。EBX将被设置为某个非零值,必须保留以供下一次调用函数时使用。CL将包含实际存储在ES:DI处的字节数(可能为20)。
对于后续的函数调用:通过增加DI来逐个处理列表条目的大小,将EAX重置为0xE820,并将ECX重置为24。当达到列表末尾时,EBX可能重置为0。如果使用EBX = 0再次调用函数,列表将重新开始。如果EBX没有重置为0,在尝试访问最后一个有效条目之后,函数将返回设置了Carry标志。
注意: 在获取列表之后,可能希望对列表进行排序,合并相邻的相同类型范围,将任何重叠的区域更改为最严格的类型,并将任何无法识别的 "类型 "值更改为类型2。 ACPI可回收内存(类型3)区域可以像正常的 "可用RAM"区域一样使用(并与之合并),只要你在那里存储ACPI表后已经完成使用(即可 "回收")。 类型2、类型4、类型5(保留、ACPI非易失性、错误)标记了在分配物理内存时应避免的区域。 将未列出的区域视为类型2-保留。 你的代码必须能够处理不以任何 "页面边界 "开始或结束的区域。 Bochs中调用INT 15h,EAX = E820的典型输出:
基本知识有了,下面我们用int 0x15来实现下内存检测。
代码完善后在内存检测开始前下了断点,效果图是这样的,首先看到内存中内核加载器的内容,在内核加载器下面是清一色的0
紧接着执行完内存检测,我们看到原本清一色的内存区域有了数字。
我们输出一下看看是什么:
此时我们添加了一个show功能展示,首先是成功进行了内存检测
我们看寄存器:
首先eax表示基地址=0,ebx表示界限=9f000,edx=1表示类型,1表示可用的。
接下来是基地址从9f000开始 界限=1000 类型为2 表示 保留不可用
接下来是从e8000开始 界限=18000 类型依然为2 保留不可用
最后一个是从1M开始也就是实模式结束到保护模式开始32M,类型为1 表示可用
因为我们设定的硬盘大小就是32M
最后内核加载器内存检测的代码如下:
[org 0x1000]
dw 0x55aa
mov si,loading
call print1
xchg bx,bx
detect_memory:
xor ebx,ebx
;es:di 结构体缓存的位置
mov ax,0
mov es,ax
mov edi,ards_buffer
;固定签名
mov edx,0x534d4150
.next:
;子功能号
mov eax,0xe820
;ards 结构体大小
mov ecx,20
int 0x15
;CF=1 表示发生错误
jc error
add di,cx
;结构体数量加一
inc word [ards_count]
cmp ebx,0
jnz .next
mov si,detecting
call print1
xchg bx,bx
mov ecx,[ards_count]
mov si,0
.show:
mov eax, [ards_buffer+si]
mov ebx, [ards_buffer+si+8]
mov edx, [ards_buffer+si+16]
add si, 20
xchg bx,bx
loop .show
jmp $
print1:
mov ah, 0x0e
.next:
mov al, [si]
cmp al, 0
jz .done
int 0x10
inc si
jmp .next
.done:
ret
loading:
db "os loading success", 10, 13, 0; \n\r
detecting:
db "Detecting Memory Success...",10,13,0
error:
mov si, .msg
call print1
hlt; 让 CPU 停止
jmp $
.msg db "Loading Error!!!", 10, 13, 0
ards_count:
dw 0
ards_buffer: