1 #include <inc/x86.h> 2 #include <inc/elf.h> 3 4 /********************************************************************** 5 * 这是一个非常简单的引导加载程序,它的唯一任务是从第一个IDE硬盘引导ELF内核映像。 6 * 7 * 磁盘布局 8 * * 引导加载程序由boot.S和main.C组成,这两个文件应该储存在磁盘的第一个扇区。 9 * 10 * * 第二个扇区是内核映像(kernel image)。 11 * 12 * * kernel image 必须是ELF格式。 13 * 14 * 启动步骤 15 * * 计算机启动时,CPU将BIOS加载到内存中并执行它。 16 * 17 * * BIOS初始化设备,初始化中断,将启动设备(磁盘或者CD)的第一个扇区(包含boot.S和main.C)读取进内存, 18 * 并跳转执行。 19 * 20 * * 首先执行的是boot.S,boot.S启动了保护模式,并且设置了一个堆栈(使得C代码可以运行), 21 * 然后调用了本文件也就是main.C中的bootmain函数。 22 * 23 * *之后由本文件的bootmain()接管,主要功能是读入内核并跳转到它。 24 * 25 **********************************************************************/ 26 27 //扇区的大小为512 28 #define SECTSIZE 512 29 //将内核加载到内存的起始地址 30 #define ELFHDR ((struct Elf *) 0x10000) // scratch space 31 32 //该函数的作用是读取一个节的内容,也就是读取一个扇区的内容 33 void readsect(void*, uint32_t); 34 //函数的作用是读取一个程序段 35 void readseg(uint32_t, uint32_t, uint32_t); 36 37 void 38 bootmain(void) 39 { 40 //定义了两个程序头表项指针 41 struct Proghdr *ph, *eph; 42 43 //将硬盘上从第一个扇区开始的4096个字节读到内存中地址为0x10000处 44 readseg((uint32_t) ELFHDR, SECTSIZE*8, 0); 45 46 //检查这是否是一个合法的ELF文件 47 if (ELFHDR->e_magic != ELF_MAGIC) 48 goto bad; 49 50 //找到第一程序头表项的起始地址 51 ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 52 //程序头表的结束位置 53 eph = ph + ELFHDR->e_phnum; 54 55 //将内核加载进入内存 56 for (; ph < eph; ph++) 57 //p_pa就是该程序段应该加载到内存中的位置 58 //读取一个程序段的数据到内存中 59 readseg(ph->p_pa, ph->p_memsz, ph->p_offset); 60 61 //开始执行内核 62 ((void (*)(void)) (ELFHDR->e_entry))(); 63 64 bad: 65 outw(0x8A00, 0x8A00); 66 outw(0x8A00, 0x8E00); 67 while (1) 68 /* do nothing */; 69 } 70 71 //这个函数的作用是从ELF文件偏移为offset处,读取count个字节到内存地址为pa处 72 void 73 readseg(uint32_t pa, uint32_t count, uint32_t offset) 74 { 75 //段的结束地址 76 uint32_t end_pa; 77 78 //计算段的结束地址 79 end_pa = pa + count; 80 81 //将pa设置为512字节对齐的地方 82 pa &= ~(SECTSIZE - 1); 83 84 //将相对于ELF文件头的偏移量转换为扇区,ELF格式的内核文件存放在第一个扇区中 85 offset = (offset / SECTSIZE) + 1; 86 87 //开始读取该程序段的内容 88 while (pa < end_pa) { 89 //每次读取程序的一个节,即一个扇区 90 //也就是将offset扇区中的内容,读到物理地址为pa的地方 91 readsect((uint8_t*) pa, offset); 92 //将pa的值增加512字节 93 pa += SECTSIZE; 94 //读取下一个扇区 95 offset++; 96 } 97 } 98 99 void 100 waitdisk(void) 101 { 102 // wait for disk reaady 103 while ((inb(0x1F7) & 0xC0) != 0x40) 104 /* do nothing */; 105 } 106 107 void 108 readsect(void *dst, uint32_t offset) 109 { 110 // wait for disk to be ready 111 waitdisk(); 112 113 outb(0x1F2, 1); // count = 1 114 outb(0x1F3, offset); 115 outb(0x1F4, offset >> 8); 116 outb(0x1F5, offset >> 16); 117 outb(0x1F6, (offset >> 24) | 0xE0); 118 outb(0x1F7, 0x20); // cmd 0x20 - read sectors 119 120 // wait for disk to be ready 121 waitdisk(); 122 123 // read a sector 124 insl(0x1F0, dst, SECTSIZE/4); 125 }