/* readsect - read a single sector at @secno into @dst */
/* 读取编号为secno的一个扇区到dst地址处 */
static void
readsect(void *dst扇区地址, uint32_t secno扇区编号) {
// 等待磁盘准备就绪
waitdisk();
outb(0x1F2, 1); // count = 1 读取一个扇区
outb(0x1F3, secno & 0xFF);
outb(0x1F4, (secno >> 8) & 0xFF);
outb(0x1F5, (secno >> 16) & 0xFF);
outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
outb(0x1F7, 0x20); // 0x20为读取磁盘命令
// 等待磁盘准备就绪
waitdisk();
// 读该扇区
insl(0x1F0, dst, SECTSIZE / 4); //insl每次读取4B,cnt读取次数为 扇区长度 / 4
}
/* *
* readseg - read @count bytes at @offset from kernel into virtual address @va,
* might copy more than asked.
* 从内核的offset偏移处读取count个字节到虚拟地址va中,可能复制的字节数多于请求的count个
* */
static void
readseg(uintptr_t va虚拟地址入口, uint32_t count读取的字节数, uint32_t offset内核偏移量) {
uintptr_t end_va = va + count; //读取地址尾部
// 向下对准到磁盘边界
va -= offset % SECTSIZE;
// 将偏移量转为扇区号,内核始于扇区1
uint32_t secno = (offset / SECTSIZE) + 1;
// If this is too slow, we could read lots of sectors at a time.
// We'd write more to memory than asked, but it doesn't matter --
// we load in increasing order.
for (; va < end_va; va += SECTSIZE, secno ++) {
readsect((void *)va, secno); //依次读取若干扇区
}
}
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian ELF魔数,小端存储
/* ELF文件头 */
struct elfhdr {
uint32_t e_magic; // ELF魔数,7f 45 4c 46
uint8_t e_elf[12]; // ELF格式信息,32/64位,大端/小端存储
uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image
uint16_t e_machine; // ELF体系结构类型,3=x86, 4=68K, etc.
uint32_t e_version; // 文件版本,总为1
uint32_t e_entry; // 可执行程序入口虚拟地址
uint32_t e_phoff; // 程序头表在文件内的字节偏移量
uint32_t e_shoff; // 节头表在文件内的字节偏移量
uint32_t e_flags; // 处理器相关标识,通常为0
uint16_t e_ehsize; // ELF header文件头字节大小
uint16_t e_phentsize; // 程序头表每个条目(entry)的字节大小
uint16_t e_phnum; // 程序头表条目数量,即段的个数
uint16_t e_shentsize; // 节头表每个条目(entry)的字节大小
uint16_t e_shnum; // 节头表中条目的数量,即节的个数
uint16_t e_shstrndx; // string name table在节头表中的索引index
};
struct proghdr {
uint32_t p_type; // 段类型, 如可加载的代码段或数据段、动态链接信息段等
uint32_t p_offset; // 本段在文件内的其实偏移
uint32_t p_va; // 本段在内存中的起始虚拟地址
uint32_t p_pa; // 本段在内存中的起始物理地址,不使用
uint32_t p_filesz; // 本段在文件中的大小
uint32_t p_memsz; // 本段在内存中的大小(如果包含bss节,会更大)
uint32_t p_flags; // 本段相关的标志,如读/写/执行
uint32_t p_align; // 对齐,和硬件页大小一致;如果为0或1,表示不需要对齐。否则,p_align应该是2的正整数幂,p_va和p_offset在对p_align取模后应相等
};
#define ELFHDR ((struct elfhdr *)0x10000) // 暂存空间
void
bootmain(void) {
// 从硬盘读取一页大小数据(4K)到内存0x10000地址处
readseg((uintptr_t)ELFHDR可执行文件虚拟地址入口, SECTSIZE * 8读取的字节数, 0内核偏移量);
// 通过ELF魔数检查是否为合法的ELF文件
if (ELFHDR->e_magic != ELF_MAGIC) {
goto bad;
}
struct proghdr *ph, *eph;
// 加载每个程序头到内存中(忽略程序头的flags)
ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff 程序头表在文件内的字节偏移量); //程序头表首项指针
eph = ph + ELFHDR->e_phnum程序头表数量(段的个数); //程序头表尾指针,eph = ph + 3 * sizeof(struct proghdr)
for (; ph < eph; ph ++) {
// 依次将各个段加载到虚拟内存(此时虚拟内存 = 物理内存),内存地址固定在0x000000—0xFFFFFF之间
readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
}
// 跳转到程序入口处执行,即0x100000处,(void (*)(void))addr为将addr(ELFHDR->e_entry &
// 0xFFFFFF)转换为无参数无返回值的函数指针,再调用函数指针执行该地址处的代码
((void (*)(void))(ELFHDR->e_entry可执行程序入口虚拟地址 & 0xFFFFFF))();
bad:
//内核文件不是ELF文件,死循环
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
/* do nothing */
while (1);
}