标签:des style blog io ar color os 使用 sp
加载程序不是内核的重点,我们只需知道内核被加载到什么地方,内核的入口地址是什么即可,一些技术细节没必要过分关心。
xv6的内核加载程序由两个文件组成:bootasm.S和bootmain.c
bootasm.s的主要目的就是由实模式进入保护模式,下面是bootasm.s的代码注释:
1 #加载器被BIOS加载到0x7c00地址处,此时cs = 0, eip = 7c00。其他寄存器的值 2 #不关心 3 4 #include "asm.h" 5 #include "mmu.h" 6 .code16 7 .global start 8 start: 9 cli 10 11 xorw %ax, %ax 12 movw %ax, %ds 13 movw %ax, %es 14 movw %ax, %ss 15 16 #打开A20总线,这是个历史遗留问题 17 seta20.1: 18 inb $0x64,%al 19 testb $0x2,%al 20 jnz seta20.1 21 22 movb $0xd1,%al 23 outb %al,$0x64 24 25 seta20.2: 26 inb $0x64,%al 27 testb $0x2,%al 28 jnz seta20.2 29 30 movb $0xdf,%al 31 outb %al,$0x60 32 33 #进入保护模式 34 lgdt gdtdesc 35 movl %cr0, %eax 36 orl $CR0_PE, %eax 37 movl %eax, %cr0 38 39 ljmp $(SEG_KCODE<<3), $start32 40 41 #保护模式代码 42 .code32 43 start32: 44 #设置段寄存器的值 45 movw $(SEG_KDATA<<3), %ax 46 movw %ax, %ds 47 movw %ax, %es 48 movw %ax, %ss 49 movw $0, %ax 50 movw %ax, %fs 51 movw $ax, %gs 52 53 #设置栈指针 54 movl $start, %esp 55 call bootmain 56 57 #不应该从bootmain函数中返回,如果返回了,进入无限循环 58 59 spin: 60 jmp spin 61 #加载器使用的GDT 62 .p2align 2 63 gdt: 64 SEG_NULLASM 65 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) #代码段 66 SEG_ASM(STA_W, 0x0, 0xffffffff) #数据段 67 68 gdtdesc: 69 .word (gdtdesc - gdt - 1) 70 .long gdt
bootmain.c主要是把内核可执行文件加载到内存中,xv6内核的可执行文件是elf格式,所以要对elf文件格式有所了解。
这里只用到elf文件格式的两个表头,只需了解这两个表头的含义即可。
第一个表头是elf头部结构,位于elf文件的首部
#define EI_NIDENT 16 typedef struct{ unsigned char e_ident[EI_NIDENT]; //目标文件标识信息 Elf32_Half e_type; //目标文件类型 Elf32_Half e_machine; //目标体系结构类型 Elf32_Word e_version; //目标文件版本 Elf32_Addr e_entry; //程序入口的虚拟地址,若没有,可为0 Elf32_Off e_phoff; //程序头部表格(Program Header Table)的偏移量(按字节计算),若没有,可为0 Elf32_Off e_shoff; //节区头部表格(Section Header Table)的偏移量(按字节计算),若没有,可为0 Elf32_Word e_flags; //保存与文件相关的,特定于处理器的标志。标志名称采用 EF_machine_flag的格式。 Elf32_Half e_ehsize; //ELF 头部的大小(以字节计算)。 Elf32_Half e_phentsize; //程序头部表格的表项大小(按字节计算)。 Elf32_Half e_phnum; //程序头部表格的表项数目。可以为 0。 Elf32_Half e_shentsize; //节区头部表格的表项大小(按字节计算)。 Elf32_Half e_shnum; //节区头部表格的表项数目。可以为 0。 Elf32_Half e_shstrndx; //节区头部表格中与节区名称字符串表相关的表项的索引。如果文件没有节区名称字符串表,此参数可以为 SHN_UNDEF。 }Elf32_Ehdr;
我们只用到这个结构中的两项:e_phoff(程序头部表格的偏移量)和 e_phnum(程序头部表格的表项数目)。
第二个结构是程序头部,指示该elf文件的哪些部分需要被加载到内存中去,一个程序头部结构对应一个要被加载入内存的文件段。
typedef struct { Elf32_Word p_type; //段类型 Elf32_Off p_offset; //段位置 Elf32_Addr p_vaddr; //给出段的第一个字节将被放到内存中的虚拟地址 Elf32_Addr p_paddr; //仅用于与物理地址相关的系统中 Elf32_Word p_filesz; //给出段在文件映像中所占的字节数 Elf32_Word p_memsz; //给出段在内存映像中占用的字节数 Elf32_Word p_flags; //与段相关的标志 Elf32_Word p_align; //对齐 } Elf32_phdr;
该结构中的p_offset指明该段在文件中的偏移,p_filesz指明该段的大小,p_paddr指明要被加载进的物理地址,有了这三个信息,就可以加载内核可执行文件了。
bootmain.c所做的工作就是根据以上两个结构的内容把内核可执行文件的某些部分加载进指定的物理内存处。
最后跳到内核的入口处,开始执行内核代码,内核被加载成功。
标签:des style blog io ar color os 使用 sp
原文地址:http://www.cnblogs.com/zhanjunling/p/4133157.html