MIT 6.828 main.c 文件分析

#include <inc/x86.h>
#include <inc/elf.h>
 
/*这是一个简单粗略的boot loader,它唯一的工作就是
从硬盘的第一个扇区启动格式为ELF的内核镜像
硬盘布局
这个程序(包括boot.S和main.c)组成了bootloader,
它应该存储在硬盘的第一个扇区
第二个扇区存储着内核映像
内核映像必须为ELF格式的
启动步骤
当CPU启动时,它加载BIOS到内存中并且执行BIOS
BIOS程序初始化设备,设置中断例程,并且将启动装置(例如硬盘)
中的第一个扇区的内容加载到内存,并且跳转到那里
假设这个bootloader存储在硬盘的第一个扇区,这个代码从BIOS接收了CPU控制权
控制从boot.S文件开始--这个文件设置了保护模式和一个栈,这样
C代码就可以运行了,然后再调用bootmain()
这个文件中的bootmain函数接过控制权之后,读取内核文件并且跳转到内核*/
 
//扇区的大小为512
#define SECTSIZE	512
//将内核加载到内存的起始地址
#define ELFHDR		((struct Elf *) 0x10000) // scratch space
 
//该函数的作用是读取一个节的内容,也就是读取一个扇区的内容
void readsect(void*, uint32_t);
//函数的作用是读取一个程序段
void readseg(uint32_t, uint32_t, uint32_t);
 
void
bootmain(void)
{
	//定义了两个程序头表项指针
	struct Proghdr *ph, *eph;
 
	//将硬盘上从第一个扇区开始的4096个字节读到内存中地址为0x10000处
	readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
 
	//检查这是否是一个合法的ELF文件
	if (ELFHDR->e_magic != ELF_MAGIC)
		goto bad;
	
	//找到第一程序头表项的起始地址
	ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
	//程序头表的结束位置
	eph = ph + ELFHDR->e_phnum;
 
	//将内核加载进入内存
	for (; ph < eph; ph++)
		//p_pa就是该程序段应该加载到内存中的位置
		//读取一个程序段的数据到内存中
		readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
 
	//开始执行内核
	((void (*)(void)) (ELFHDR->e_entry))();
 
bad:
	outw(0x8A00, 0x8A00);
	outw(0x8A00, 0x8E00);
	while (1)
		/* do nothing */;
}
 
//这个函数的作用是从ELF文件偏移为offset处,读取count个字节到内存地址为pa处
void
readseg(uint32_t pa, uint32_t count, uint32_t offset)
{
	//段的结束地址
	uint32_t end_pa;
 
	//计算段的结束地址
	end_pa = pa + count;
 
	//将pa设置为512字节对齐的地方
	pa &= ~(SECTSIZE - 1);
 
	//将相对于ELF文件头的偏移量转换为扇区,ELF格式的内核文件存放在第一个扇区中
	offset = (offset / SECTSIZE) + 1;
 
	//开始读取该程序段的内容
	while (pa < end_pa) {
		//每次读取程序的一个节,即一个扇区
		//也就是将offset扇区中的内容,读到物理地址为pa的地方
		readsect((uint8_t*) pa, offset);
		//将pa的值增加512字节
		pa += SECTSIZE;
		//读取下一个扇区
		offset++;
	}
}
 
void
waitdisk(void)
{
	// wait for disk reaady
	while ((inb(0x1F7) & 0xC0) != 0x40)
		/* do nothing */;
}
 
void
readsect(void *dst, uint32_t offset)
{
	// wait for disk to be ready
	waitdisk();
 
	outb(0x1F2, 1);		// count = 1
	outb(0x1F3, offset);
	outb(0x1F4, offset >> 8);
	outb(0x1F5, offset >> 16);
	outb(0x1F6, (offset >> 24) | 0xE0);
	outb(0x1F7, 0x20);	// cmd 0x20 - read sectors
 
	// wait for disk to be ready
	waitdisk();
 
	// read a sector
	insl(0x1F0, dst, SECTSIZE/4);
}