Lab1 练习4分析bootloader加载ELF格式的OS的过程

练习4:分析bootloader加载ELF格式的OS的过程。(要求在报告中写出分析)

通过阅读bootmain.c,了解bootloader如何加载ELF文件。通过分析源代码和通过qemu来运行并调试bootloader&OS,

  • bootloader如何读取硬盘扇区的?
  • bootloader是如何加载ELF格式的OS?

提示:可阅读“硬盘访问概述”,“ELF执行文件格式概述”这两小节。

地址空间内容 https://chyyuu.gitbooks.io/ucore_os_docs/content/lab1/lab1_3_2_2_address_space.html

硬盘访问内容 https://chyyuu.gitbooks.io/ucore_os_docs/content/lab1/lab1_3_2_3_dist_accessing.html

ELF文件格式概述 https://chyyuu.gitbooks.io/ucore_os_docs/content/lab1/lab1_3_2_4_elf.html

实验报告:

bootmain.c

#include <defs.h>
#include <x86.h>
#include <elf.h>

/* *********************************************************************

 * This a dirt simple boot loader, whose sole job is to boot
 * an ELF kernel image from the first IDE hard disk.
   *
 * DISK LAYOUT
 * * This program(bootasm.S and bootmain.c) is the bootloader.
 * It should be stored in the first sector of the disk.
    *
 * * The 2nd sector onward holds the kernel image.
     *
 * * The kernel image must be in ELF format.
     *
 * BOOT UP STEPS
 * * when the CPU boots it loads the BIOS into memory and executes it
     *
 * * the BIOS intializes devices, sets of the interrupt routines, and
 * reads the first sector of the boot device(e.g., hard-drive)
 * into memory and jumps to it.
    *
 * * Assuming this boot loader is stored in the first sector of the
 * hard-drive, this code takes over...
    *
 * * control starts in bootasm.S -- which sets up protected mode,
 * and a stack so C code then run, then calls bootmain()
    *
 * * bootmain() in this file takes over, reads in the kernel and jumps to it.
 * */

#define SECTSIZE        512
#define ELFHDR          ((struct elfhdr *)0x10000)      // scratch space

/* waitdisk - wait for disk ready */
static void
waitdisk(void) {
    while ((inb(0x1F7) & 0xC0) != 0x40)
        /* do nothing */;
}

/* readsect从设备的第secno扇区读取数据到dst位置*/
static void
readsect(void *dst, uint32_t secno) {
    // wait for disk to be ready
    waitdisk();

    outb(0x1F2, 1);                         //要读写的扇区数count = 1
    outb(0x1F3, secno & 0xFF);				
    outb(0x1F4, (secno >> 8) & 0xFF);
    outb(0x1F5, (secno >> 16) & 0xFF);
    outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
    	    // 上面四条指令联合制定了扇区号
	        // 在这4个字节线联合构成的32位参数中
	        //   29-31位强制设为1
	        //   28位(=0)表示访问"Disk 0"
	        //   0-27位是28位的偏移量
    outb(0x1F7, 0x20);                      // 0x20命令,读取扇区
    

    // wait for disk to be ready
    waitdisk();
    
    // read a sector
    insl(0x1F0, dst, SECTSIZE / 4);

}

/* *

 * readseg简单包装了readsect,可以从设备读取任意长度的内容。

 * */

   static void
   readseg(uintptr_t va, uint32_t count, uint32_t offset) {
    uintptr_t end_va = va + count;

    // round down to sector boundary
    va -= offset % SECTSIZE;

    // translate from bytes to sectors; kernel starts at sector 1
    uint32_t secno = (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 < end_va; va += SECTSIZE, secno ++) {
        readsect((void *)va, secno);
    }
   }

/* bootmain - the entry of bootloader */
void
bootmain(void) {
     // 首先读取ELF的头部
    readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);

    // 通过储存在头部的幻数判断是否是合法的ELF文件 若不合法 则跳入bad段无线循环
    if (ELFHDR->e_magic != ELF_MAGIC) {
        goto bad;
    }
    
    struct proghdr *ph, *eph;
    
    // ELF头部有描述ELF文件应加载到内存什么位置的描述表,
	// 先将描述表的头地址存在ph
    ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
    eph = ph + ELFHDR->e_phnum;
    // 按照描述表将ELF文件中数据载入内存
    for (; ph < eph; ph ++) {
        readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
    }
    
	    // ELF文件0x1000位置后面的0xd1ec比特被载入内存0x00100000
	    // ELF文件0xf000位置后面的0x1d20比特被载入内存0x0010e000
	    // 根据ELF头部储存的入口信息,找到内核的入口
    ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();

bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);

    /* do nothing */
    while (1);

}
posted @ 2021-01-04 09:51  lsxkugou  阅读(174)  评论(0编辑  收藏  举报