//Bootloader
//
//Part of the boot sector ,along with bootasm.s which calls bootmain()
//bootasm.S has put the processor into processor into protected 32-bit mode
//bootmain() loads an ELF kernel image from the disk starting at
//sector 1 and then jumps to the kernel entry routine
#include "type.h"
#include"elf.h"
#include"x86.h"
#define SECTSIZE 512
void readseg(uint ,uint ,uint);
void bootmain(void)
{
struct elfhdr *elf;
struct proghdr*ph,*eph;
void (*entry)(void);
elf=(struct elfhdr *)0x10000;
readseg ((uint)elf,8*sectsize,0);
if (elf->magic!=ELF_MAGIC)
goto bad;
// Load each program segment (ignores ph flags).
// ph代表ELF段表首地址
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
//eph代表ELF段表末地址
eph = ph + elf->phnum;
//循环读取每段
for(;ph<eph;ph++)
readseg(ph->va&0xFFFFFF,ph->memsz,ph->offset);
//call the entry point from the elf header
//Does not return!
entry =(void(*)(void))(elf->entry&0xFFFFFF)
entry();
//错误处理
bad:
crt[pos++] = ('E'&0xff) | 0x0700; // black on white
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
for(;;)
;
}
void
readsect(void *dst, uint offset)
{
// Issue command.
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
// Read data.
waitdisk();
insl(0x1F0, dst, SECTSIZE/4);
}
// Read 'count' bytes at 'offset' from kernel into virtual address 'va'.
// Might copy more than asked.
void
readseg(uint va, uint count, uint offset)
{
uint eva;
eva = va + count;
// Round down to sector boundary.
va &= ~(SECTSIZE - 1);
// Translate from bytes to sectors; kernel starts at sector 1.
offset = (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 < eva; va += SECTSIZE, offset++)
readsect((uchar*)va, offset);
}
注释:1:Bootloader 需要将磁盘上的内核映像加载在内存中,内存映像是elf 格式文件
类似于“字典“,由头部(header)和许多节区组成;
elf 文件结构:
2:#define SECTSIZE 512 //一个扇区的大小
#define ELFHDR ((struct Elf*)0x10000)
定义ELF文件头的位置,在内存的0x10000处
void readsect(void*,uint32_t);//读取磁盘上的一个扇区,扇区的偏移为参数
void readseg(uint32_t,uint32_t,uint32_t);
读取ELF文件中的一个段,第一参数表示链接地址,转换后为加载地址,第二参数表示段的字节数,第三参数为该段相对于文件头的偏移;
3:elfhdr.h的数据结构:
struct elf
{
uint magic; //
uchar elf[12];
ushort type;
ushort machine;
uint version;
uint entry;//ELF程序的入口虚拟地址
uint phoff;//程序头表的相对于文件头的偏移地址
uint shoff;//section headertable在文件中的偏移量
uint flags;
ushort ehsize;//ELF header的大小
ushort phentsize;//程序头表中的每个条目的大小
ushort phnum;//程序头表中的条目数
ushort shentsize;//
ushort shnum;
ushort shstrndx;
};
4:proghdr.h
struct proghdr
{
uint type;//段类型
uint offset;//段相对于文件头的偏移
uint va;//段的第一个字节将被放到内存虚拟地址
uint pa;
uint filesz;//段在文件中的大小
uint memsz;段在内存映射中所占的字节数
uint flags;
uint align;
};
5:void readseg(uint va,uint count,uint offset)//函数readseg调用readsect函数以扇区为单位
{
uint eva;
eva=va+count;
va&=~(SECTSIZE-1);
offset=(offset/SECTSIZE)+1;//以扇区为单位
for(;va<eva;va+=SECTSIZE,offset++)
readsect((uchar*)va,offset);
}
readsect(void *dst,uint offset)
{//使用LBA模式对磁盘进行读操作
waitdisk();
outb(0x1F2,1);//每次读写前都要设置读写扇区的数量,最小读写数量是1,
outb(0x1F3,offset);//如是LBA模式表示LBA参数0~7 位
outb(0x1F4,offset>>8);//如是LBA模式表示LBA参数8~15 位
outb(0x1F5,offset>>16);//如是LBA模式表示LBA参数16~23 位
outb(0x1F6,(offset>>24)|0xE0);//0~3位,如是LBA模式就是24~27 ,第4位:0代表主盘 ,1代表从盘 第6位:1=LBA模式,0=chs模式 ;第5位和第7位必为1;
outb(0x1F0,0x20); //读数据,当0x1f7不为忙状态时,可以读!
waitdisk();
insl(0x1F0,dst,SECTSIZE/4);//读磁盘上数据到内存中;这里的命令是内嵌汇编指令的宏定义在x86.h中:static inline void insl(int port,void *addr,int cnt)
{
asm volatile ("cld/n/trepne/n/tinsl":"=D"(addr),"=c"(cnt):"d"(port),"0"(addr),"1"(cnt):"memory","cc");
}
其中"=D"表示输出寄存器EDI(变址寄存器之一)的缩写,可以当指针使用;
“=c"代表ecx;"0"代表输入寄存器EDI,“1”代表输入寄存器ecx;"="表示输出操作数(目标操作数)是只写的!
“memory”表示modify部分;表示操作完成后内存中的内容已经改变