xv6 系统中的bootloader 程序的组成之一是bootmain.c部分的功能:
加载硬盘的kernel部分到内存中;
#include"type.h"
#incude"xv6.h"
#include"elf.h"
#define SECTSIZE 512
void readseg(uint ,uint,uint);
void bootmain(void)
{
struct elfhdr *elf, proghdr *ph,*eph;
int elf ,i;
void (*entry)(void);
//Back in boot sector , what should be an ELF binary header has been loaded into memory at address 0x10000.
elf=(struct elfhdr*)0x10000;
The next step is to check the first 4 bytes of header ,the so-called magic number ,are the bytes 0x7F,'E','L',"F",or
ELF_MAGIC.
if(elf->magic!=ELF_MAGIC)
goto bad;
//ALL ELF binary headers are required to begin with this magic number as identication;if the ELF header has
right magic number ,the boot sector assume that the binary is well-formed;There are many other sanity check that
a proper ELF loader would do;as we will see in Chapter 9;
readseg((uint)elf,SECTSIZE*8,0);
ph=(struct proghdr*)((uchar *)elf +elf->phoff);
eph=ph+elf->phnum;
for(;ph<eph;ph++)
{
readseg(struct *ph&0xFFFFFF ,ph->memsz,ph->offset);
entry =(void (*)(void))(elf->entry&0xffffff);
entry();
bad:
crt[pos++] = ('E'&0xff) | 0x0700; // black on white
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
for(;;)
;
}
//
void waitdisk(void)
{
while((inb(0x1f7)&0xc0)!=0x40)
;
}
//Readsect read a single disk sector .it is our first example of a device drive ,albeit a tiny one ,Readsect begins by calling
waitdisk to wait until the disk signals that it is ready to accept a command .the disk does so by setting the top two bits
of its status byte (connected to input port 0x1f7 )to 01 .waitdisk reads the status byte until the bits are set that way .
chapter 6 will examine more effiient ways to wait for hardware status changes ,but busy waiting like this is fine for the
boot sector;
once the disk is ready ,readsect issues a read command .it first write command argument -sector count and the sector
number(offset) to the disk register on output ports 0x1f2-0x1f6 .The bits 0xe0 in the write to port 0x1f6 signals to the disk
that 0x1f3~0x1f6 contain a sector number (so-called linear block address )in contrast to a more complicated cylinder/head/
sector address used in early PC disk .After writing the arguments ,readsect writes to the command register to trigger the read .The command 0x20 is "read sectors ",Now the disk will read data stored in the specified sectors and make it available
in 32-bit pieces on input port 0x1f0 ; waidisk()waits until the disk signals that the data is ready ,and then the call to insl
read the 128 (SECTSIZE/4)32-bit pieces into memory starting at dst ;
inb outb and insl are not ordinary c functions .They are inlined functions whose bodies are assembly language fragments
when gcc sees the call to inb ,the inlined assembly causes it to emit a single inb instruction .This style allows the use of
low-level instructions like inb and outb while still writing the control logic in C instead of assembly.
The implementation of insl (0412) is worth looking at more closely. Rep ins l is actually a tight loop masquerading as a single instruction. The rep prefix executes the following instruction %ecx times, decrementing %ecx after each iteration. The inslinstruction reads a 32-bit value from port %dx into memory at address %edi and then increments %edi by 4. Thus rep insl copies 4×%ecx bytes, in 32-bit chunks, from port %dx into memory starting at address %edi. The register annotation s tell GCC to prepare for the assembly sequence by storing dst in %edi, cnt in %ecx, and port in%dx. Thus the insl function copies 4×cnt bytes from the 32-bit port port into memory starting at dst . The cld instruction clears the processor’s direction flag, so that the insl instruction increments %ed i; when the flag is set , insl decrements %edi instead.
The x86 calling convention does not define the state of the direction flag on entry to a function, so each use of an instruction like insl must initialize it to the desired value
//这是LBA方式pio(Program IO即所有的IO操作是通过cpu访问硬盘的io地址寄存器)读写硬盘;
一般主板上的有2个IDE通道,每个通道可以接2个ide硬盘;如访问第一个硬盘的扇区可以设置它的io地址寄存器0x1f0~0x1f7实现!
void readsect(void *dst,uint offset)
{
waitdisk();
outb(0x1f2,1);代表扇区数
outb(0x1f3,offset);//LBA参数的0~7位,扇区号
outb(0x1f4,offset>>8); //LBA参数的8~15,柱面的低8位
outb(0x1f5,offset>>16);//LBA参数的16~23,柱面的高8位
outb(0x1f6,offset>>24|0xE0);//HD_CURRENT 驱动器/磁头寄存器 -- 驱动器号/磁头号(101dhhhh, d=驱动器号,h=磁头号)
outb(0x1f7,0x20);/cmd读扇区命令
//读数据
waitdisk();
insl(0x1f0,dst,SECTSIZE/4);
//Readseg read at least count bytess from disk offfset into memory at va; The x86 IDE disk interface operates in terms of 512-byte chunks called sectors ,so readseg may read not only the desired section of memory but also some bytes befores
and afterward ,depend on alignment;For the second program segment in the example above ,the boot sector will call readseg
0x1073e0,0x73e0,ox79e) Due to sector granularity ,this call is equivalent to readseg (uchar*)0x107200,0x7200,0xa00
:it read 0x1e0 bytes before the disired memory region and 0x82 bytes afterward ; in practice,this sloppy behavior turns out not to be a problem ;Readseg begins by computing the ending virtual address ,the first memory address above va that doesnot need to be loaded from disk ,and rounding va down to a sector-aligned disk offset; Then it convert the offset from
a byte offset to a sector offset ; it add 1because the kernel start at disk sector 1(disk sector 0 is the bootsector ).finally ,it call
readsec t to read each sector into memory.
void readseg(uint va,uint count,uint offset)
{
uint eva;
eva=va +count;
va&=~(SECTSIZE-1);
offset=(offset/SECTSIZE)+1;
for(;va<eva;va+=SECTSIZE,offset++)
//Readsect reads a single disk sector .It is our first example of a device driver,albeit a tiny one,Readsect begin by calling wait
disk to wait until the disk signals that it is ready to accept a command .The disk does so by setting the top two bits of its status byte (connected to input port 0x1f7 )to 01 .Waitdisk() reads the status byte until the bits are set that way .Chapter6
will examine more efficient ways to wait for hardware status changes,but busy waiting like this is fine for the boot sector
once the disk is ready ,readsect issues a read command.it first writes command arguments -the sector count and the sector number (offset) -to the disk register on output port 0x1f2~0x1f6
readsect((char*) va,offset);
}
void readseg(uint va,)
http://blog.csdn.net/justin_shi/archive/2010/08/10/5802578.aspx
http://topic.csdn.net/u/20080611/10/da3c08d4-80db-4fae-98e4-1fe9c970cf6e.html