main.C

  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 }

 

上一篇:Stata数据处理:一文读懂微观数据库清理(下)


下一篇:聊聊Stata中的profile文件-第二季