物理内存探测
物理内存分布和大小是用bios进行中断调用进行的,而中断调用需要用到bios,bios怎么运用呢?需要在实模式下,也就是bootloader还未加载前使用,BIOS中断获取内存可调用参数为e820h的INT 15h BIOS中断。返回值用di来增长,找到一个个内存的entry,然后用下面结构的缓冲区来保存
struct e820map {
int nr_map;
struct {
long long addr;
long long size;
long type;
} map[E820MAX];
};
中断返回值
eflags的CF位:若INT 15中断执行成功,则不置位,否则置位;
eax:534D4150h ('SMAP') ;
es:di:指向保存地址范围描述符的缓冲区,此时缓冲区内的数据已由BIOS填写完毕
ebx:下一个地址范围描述符的计数地址
ecx :返回BIOS往ES:DI处写的地址范围描述符的字节大小
ah:失败时保存出错代码
实现物理内存的探测
先是做准备:
probe_memory:
//对0x8000处的32位单元清零,即给位于0x8000处的
//struct e820map的成员变量nr_map清零
movl $0, 0x8000
xorl %ebx, %ebx
//表示设置调用INT 15h BIOS中断后,BIOS返回的映射地址描述符的起始地址
movw $0x8004, %di
cont:
//设置下一个BIOS返回的映射地址描述符的起始地址
addw $20, %di
//递增struct e820map的成员变量nr_map
incl 0x8000
//如果INT0x15返回的ebx为零,表示探测结束,否则继续探测
cmpl $0, %ebx
jnz start_probe
finish_probe:
然后设置中断参数
start_probe:
movl $0xE820, %eax // INT 15的中断调用参数
//设置地址范围描述符的大小为20字节,其大小等于struct e820map的成员变量map的大小
movl $20, %ecx
//设置edx为534D4150h (即4个ASCII字符“SMAP”),这是一个约定
movl $SMAP, %edx
//调用int 0x15中断,要求BIOS返回一个用地址范围描述符表示的内存段信息
int $0x15
//如果eflags的CF位为0,则表示还有内存段需要探测
jnc cont
//探测有问题,结束探测
movw $12345, 0x8000
jmp finish_probe
cont:
//设置下一个BIOS返回的映射地址描述符的起始地址
addw $20, %di
//递增struct e820map的成员变量nr_map
incl 0x8000
//如果INT0x15返回的ebx为零,表示探测结束,否则继续探测
cmpl $0, %ebx
jnz start_probe
finish_probe:
上面的代码执行完成后,将内存分布情况存储在0x8000,以结构体e820map的方式存储,在bootloader启动ucore后,由page_init函数来完成对机器的总体管理,且依据e820map的mmap进行管理,为0x8000
ucore的各个部分由一些.a和.o文件组成。而ld脚本把这些文件链接起来,这些各个组成部分在文件中的地址和ld脚本有关。而链接脚本主要规定如何把这些组成部分的section给到kernel文件,并且让它输出各个部分的空间地址的布局
/* Simple linker script for the ucore kernel.
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(kern_entry)
//初始化内核入口
SECTIONS {
/* Load the kernel at this address: "." means the current address */
. = 0xC0100000;
//在这个地址加载内核
.text : {
*(.text .stub .text.* .gnu.linkonce.t.*)
}
PROVIDE(etext = .)