Chinaunix首页 | 论坛 | 博客
  • 博客访问: 37073
  • 博文数量: 6
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 75
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-10 00:12
文章存档

2011年(6)

我的朋友

分类: LINUX

2011-05-04 10:36:31

、BIOS装载数据 

       BIOS保存在ROM (保存在非易失性存储中)中,PC机加电后自动装载并运行BIOS,这是所有PC机默认的做法。

    BIOS根据BIOS中的设置(不同的系统BIOS的样式有所区别),检查各存储介质(软盘、光盘、硬盘)的主引导扇区的最后两个字节是否为魔数(0xAA55)-魔数这里表示特殊的数字。不同的介质由不同的程序处理,从硬盘启动则装载masterboot.s程序到内存的固定位置(0x7C00,软盘则装载bootblock.s程序内存的固定位置(0x7C00)。其他情况调用bootblock.s jumpboot.s

      在载入之后,开始执行之前内存的分布如下:
0x00000~0x003FF : 中断向量表
0x00400~0x004FF : BIOS数据区
0x00500~0x07BFF : 自由内存区
0x07C00~0x07DFF : 引导程序加载区
0x07E00~0x9FFFF :  自由内存区
0xA0000~0xBFFFF : 显存区
0xC0000~0xFFFFF : BIOS中断处理程序区



二  masterboot.s将自身拷贝到地址BUFFER处并跳转到那里开始执行。

该段代码会通过搜索分区表查找出活动分区,将活动分区的第一个扇区中的数据读入到LOADOFF(0x7C00)处并且从将指令指针指向此处。对MINIX3来说第一个扇区存放的是boot 代码。

通过 int 0x13        ! Call the BIOS for a read      BIOS读中断 BIOS去读取,这里只是负责定位。

移动自身代码的代码:

    cli  !关闭中断
    mov    ss, ax            ! ds = es = ss = Vector segment
    mov    sp, #LOADOFF !将 loadoff地址赋值给sp寄存器
    sti

! Copy this code to safety, then jump to it.
    mov    si, sp            ! si = start of this code
    push    si            ! Also where we'll return to eventually
    mov    di, #BUFFER        ! Buffer area
    mov    cx, #512/2        ! One sector
    cld   !重置标志位
   rep    movs !循环拷贝直到结束

结束处的代码如下:

 rdok:    cmp    LOADOFF+MAGIC, #0xAA55
    jne    nosig        ! Error if signature wrong
    ret            ! Return with carry still clear 

!ret这个指令表示运行到此返回,提取并且执行指令寄存器中的指令。


三 执行boot程序,类似执行一个可执行程序

 boot 程序 从 makefile 来看,程序如下

boot:    boothead.s boot.o bootimage.o rawfs86.o
    $(LD86) -o $@ \
        boothead.s boot.o bootimage.o rawfs86.o $(LIBS)
    install -S 8kb boot

boot由boothead.s、boot.c、bootimage.c和rawfs.c四个文件组成。


boothead.s的如下代码来看

BOOTOFF        =    0x7C00    ! 0x0000:BOOTOFF load a bootstrap here

确实是将此程序装载到 0x7C00 处。

boothead.s程序中会调用如下函数 _boot()  --汇编调用C函数时,通常需要在函数名前加下划线

no_ext:


! Time to switch to a higher level language (not much higher)
    call    _boot

在 boot.c文件中 存在 boot 函数

void boot(void)
/* Load Minix and start it, among other things. */
{

    /* Initialize tables. */
    initialize();

    /* Get environment variables from the parameter sector. */
    get_parameters();

    while (1) {
        /* While there are commands, execute them! */

        while (cmds != nil) execute();

        /* The "monitor" is just a "read one command" thing. */
        monitor();
    }
}
由此可见 boot 函数实际是用来执行命令的。
execute函数关键部分如下

       char *body;
        int ok= 0;

        name= poptoken();

        switch (res) {
        case R_BOOT:    bootminix();    ok= 1;    break;
        case R_DELAY:    delay("500");    ok= 1;    break;
        case R_LS:    ls(null);    ok= 1;    break;
        case R_MENU:    menu();        ok= 1;    break;
        case R_SAVE:    save_parameters(); ok= 1;break;
        case R_SET:    show_env();    ok= 1;    break;
        case R_HELP:    help();        ok= 1;    break;
        case R_EXIT:    exit(0);
        case R_OFF:    off();        ok= 1;    break;

调用 bootminix函数,此函数定义在bootimage.c中

void bootminix(void)
/* Load Minix and run it.  (Given the size of this program it is surprising
 * that it ever gets to that.)
 */
{
    char *image;

    if ((image= select_image(b_value("image"))) == nil) return;

    exec_image(image);

    switch (errno) {
    case ENOEXEC:
        printf("%s contains a bad program header\n", image);
        break;
    case ENOMEM:
        printf("Not enough memory to load %s\n", image);
        break;
    case EIO:
        printf("Unsuspected EOF on %s\n", image);
    case 0:
        /* No error or error already reported. */;
    }
    free(image);
}
此函数调用exec_image()函数执行一个文件镜像。
代码如下
void exec_image(char *image)
/* Get a Minix image into core, patch it up and execute. */
{
    char *delayvalue;
    int i;
    struct image_header hdr;
    char *buf;
    u32_t vsec, addr, limit, aout, n;
    struct process *procp;        /* Process under construction. */
    long a_text, a_data, a_bss, a_stack;
    int banner= 0;
    long processor= a2l(b_value("processor"));
    u16_t mode;
    char *console;
    char params[SECTOR_SIZE];
    extern char *sbrk(int);

    /* The stack is pretty deep here, so check if heap and stack collide. */
    (void) sbrk(0);

    printf("\nLoading ");
    pretty_image(image);
    printf(".\n\n");

    vsec= 0;            /* Load this sector from image next. */
    addr= mem[0].base;        /* Into this memory block. */
    limit= mem[0].base + mem[0].size;
    if (limit > caddr) limit= caddr;

    /* Allocate and clear the area where the headers will be placed. */
    aout = (limit -= PROCESS_MAX * A_MINHDR);

    /* Clear the area where the headers will be placed. */
    raw_clear(aout, PROCESS_MAX * A_MINHDR);

    /* Read the many different processes: */
    for (i= 0; vsec < image_size; i++) {
        if (i == PROCESS_MAX) {
            printf("There are more then %d programs in %s\n",
                PROCESS_MAX, image);
            errno= 0;
            return;
        }
        procp= &process[i];

        /* Read header. */
        for (;;) {
            if ((buf= get_sector(vsec++)) == nil) return;

            memcpy(&hdr, buf, sizeof(hdr));

            if (BADMAG(hdr.process)) { errno= ENOEXEC; return; }

            /* Check the optional label on the process. */
            if (selected(hdr.name)) break;

            /* Bad label, skip this process. */
            vsec+= proc_size(&hdr);
        }

        /* Sanity check: an 8086 can't run a 386 kernel. */
        if (hdr.process.a_cpu == A_I80386 && processor < 386) {
            printf("You can't run a 386 kernel on this 80%ld\n",
                processor);
            errno= 0;
            return;
        }

        /* Get the click shift from the kernel text segment. */
        if (i == KERNEL) {
            if (!get_clickshift(vsec, &hdr)) return;
            addr= align(addr, click_size);
        }

        /* Save a copy of the header for the kernel, with a_syms
         * misused as the address where the process is loaded at.
         */
        hdr.process.a_syms= addr;
        raw_copy(aout + i * A_MINHDR, mon2abs(&hdr.process), A_MINHDR);

        if (!banner) {
            printf("     cs       ds     text     data      bss");
            if (k_flags & K_CHMEM) printf("    stack");
            putch('\n');
            banner= 1;
        }

        /* Segment sizes. */
        a_text= hdr.process.a_text;
        a_data= hdr.process.a_data;
        a_bss= hdr.process.a_bss;
        if (k_flags & K_CHMEM) {
            a_stack= hdr.process.a_total - a_data - a_bss;
            if (!(hdr.process.a_flags & A_SEP)) a_stack-= a_text;
        } else {
            a_stack= 0;
        }

        /* Collect info about the process to be. */
        procp->cs= addr;

        /* Process may be page aligned so that the text segment contains
         * the header, or have an unmapped zero page against vaxisms.
         */
        procp->entry= hdr.process.a_entry;
        if (hdr.process.a_flags & A_PAL) a_text+= hdr.process.a_hdrlen;
        if (hdr.process.a_flags & A_UZP) procp->cs-= click_size;

        /* Separate I&D: two segments.  Common I&D: only one. */
        if (hdr.process.a_flags & A_SEP) {
            /* Read the text segment. */
            if (!get_segment(&vsec, &a_text, &addr, limit)) return;

            /* The data segment follows. */
            procp->ds= addr;
            if (hdr.process.a_flags & A_UZP) procp->ds-= click_size;
            procp->data= addr;
        } else {
            /* Add text to data to form one segment. */
            procp->data= addr + a_text;
            procp->ds= procp->cs;
            a_data+= a_text;
        }

        printf("%07lx  %07lx %8ld %8ld %8ld",
            procp->cs, procp->ds,
            hdr.process.a_text, hdr.process.a_data,
            hdr.process.a_bss
        );
        if (k_flags & K_CHMEM) printf(" %8ld", a_stack);

        printf("  %s\n", hdr.name);

        /* Read the data segment. */
        if (!get_segment(&vsec, &a_data, &addr, limit)) return;

        /* Make space for bss and stack unless... */
        if (i != KERNEL && (k_flags & K_CLAIM)) a_bss= a_stack= 0;

        /* Note that a_data may be negative now, but we can look at it
         * as -a_data bss bytes.
         */

        /* Compute the number of bss clicks left. */
        a_bss+= a_data;
        n= align(a_bss, click_size);
        a_bss-= n;

        /* Zero out bss. */
        if (addr + n > limit) { errno= ENOMEM; return; }
        raw_clear(addr, n);
        addr+= n;

        /* And the number of stack clicks. */
        a_stack+= a_bss;
        n= align(a_stack, click_size);
        a_stack-= n;

        /* Add space for the stack. */
        addr+= n;

        /* Process endpoint. */
        procp->end= addr;

        if (i == 0 && (k_flags & K_HIGH)) {
            /* Load the rest in extended memory. */
            addr= mem[1].base;
            limit= mem[1].base + mem[1].size;
        }
    }

    if ((n_procs= i) == 0) {
        printf("There are no programs in %s\n", image);
        errno= 0;
        return;
    }

    /* Check the kernel magic number. */
    if (get_word(process[KERNEL].data + MAGIC_OFF) != KERNEL_D_MAGIC) {
        printf("Kernel magic number is incorrect\n");
        errno= 0;
        return;
    }

    /* Patch sizes, etc. into kernel data. */
    patch_sizes();

#if !DOS
    if (!(k_flags & K_MEML)) {
        /* Copy the a.out headers to the old place. */
        raw_copy(HEADERPOS, aout, PROCESS_MAX * A_MINHDR);
    }
#endif

    /* Do delay if wanted. */
    if((delayvalue = b_value("bootdelay")) != nil > 0) {
        delay(delayvalue);
    }

    /* Run the trailer function just before starting Minix. */
    if (!run_trailer()) { errno= 0; return; }

    /* Translate the boot parameters to what Minix likes best. */
    if (!params2params(params, sizeof(params))) { errno= 0; return; }

    /* Set the video to the required mode. */
    if ((console= b_value("console")) == nil || (mode= a2x(console)) == 0) {
        mode= strcmp(b_value("chrome"), "color") == 0 ? COLOR_MODE :
                                MONO_MODE;
    }
    set_mode(mode);

    /* Close the disk. */
    (void) dev_close();

    /* Minix. */
    minix(process[KERNEL].entry, process[KERNEL].cs,
            process[KERNEL].ds, params, sizeof(params), aout);

    if (!(k_flags & K_BRET)) {
        extern u32_t reboot_code;
        raw_copy(mon2abs(params), reboot_code, sizeof(params));
    }
    parse_code(params);

    /* Return from Minix.  Things may have changed, so assume nothing. */
    fsok= -1;
    errno= 0;

    /* Read leftover character, if any. */
    scan_keyboard();
}

此函数调用
Minix 函数
    minix(process[KERNEL].entry, process[KERNEL].cs,
            process[KERNEL].ds, params, sizeof(params), aout);

Minix 函数函数是汇编函数,定义在 boothead.s中
! void minix(u32_t koff, u32_t kcs, u32_t kds,
!                char *bootparams, size_t paramsize, u32_t aout);
!    Call Minix.
_minix:
    push    bp
    mov    bp, sp        ! Pointer to arguments

    mov    dx, #0x03F2    ! Floppy motor drive control bits
    movb    al, #0x0C    ! Bits 4-7 for floppy 0-3 are off
    outb    dx        ! Kill the motors
    push    ds
    xor    ax, ax        ! Vector & BIOS data segments
    mov    ds, ax
    andb    0x043F, #0xF0    ! Clear diskette motor status bits of BIOS
    pop    ds
    cli            ! No more interruptions

    test    _k_flags, #K_I386 ! Switch to 386 mode?
    jnz    minix386
! Call Minix in 386 mode.
minix386:
  cseg    mov    cs_real-2, cs    ! Patch CS and DS into the instructions that
  cseg    mov    ds_real-2, ds    ! reload them when switching back to real mode
    .data1    0x0F,0x20,0xC0    ! mov    eax, cr0
    orb    al, #0x01    ! Set PE (protection enable) bit
    .data1    o32
    mov    msw, ax        ! Save as protected mode machine status word

    mov    dx, ds        ! Monitor ds
    mov    ax, #p_gdt    ! dx:ax = Global descriptor table
    call    seg2abs
    mov    p_gdt_desc+2, ax
    movb    p_gdt_desc+4, dl ! Set base of global descriptor table

    mov    ax, 12(bp)
    mov    dx, 14(bp)    ! Kernel ds (absolute address)
    mov    p_ds_desc+2, ax
    movb    p_ds_desc+4, dl ! Set base of kernel data segment

    mov    dx, ss        ! Monitor ss
    xor    ax, ax        ! dx:ax = Monitor stack segment
    call    seg2abs        ! Minix starts with the stack of the monitor
    mov    p_ss_desc+2, ax
    movb    p_ss_desc+4, dl

    mov    ax, 8(bp)
    mov    dx, 10(bp)    ! Kernel cs (absolute address)
    mov    p_cs_desc+2, ax
    movb    p_cs_desc+4, dl

    mov    dx, cs        ! Monitor cs
    xor    ax, ax        ! dx:ax = Monitor code segment
    call    seg2abs
    mov    p_mcs_desc+2, ax
    movb    p_mcs_desc+4, dl

    push    #MCS_SELECTOR
    test    _k_flags, #K_INT86 ! Generic INT86 support?
    jz    0f
    push    #int86        ! Far address to INT86 support
    jmp    1f
0:    push    #bios13        ! Far address to BIOS int 13 support
1:
    test    _k_flags, #K_MEML ! New memory arrangements?
    jz    0f
    .data1    o32
    push    20(bp)        ! Address of a.out headers
0:
    push    #0
    push    18(bp)        ! 32 bit size of parameters on stack
    push    #0
    push    16(bp)        ! 32 bit address of parameters (ss relative)

    test    _k_flags, #K_RET ! Can the kernel return?
    jz    noret386
    push    #MCS_SELECTOR
    push    #ret386        ! Monitor far return address
noret386:

    push    #0
    push    #CS_SELECTOR
    push    6(bp)
    push    4(bp)        ! 32 bit far address to kernel entry point

    call    real2prot    ! Switch to protected mode
    mov    ax, #DS_SELECTOR ! Kernel data
    mov    ds, ax
    mov    ax, #ES_SELECTOR ! Flat 4 Gb
    mov    es, ax
    .data1    o32        ! Make a far call to the kernel
    retf

Minix函数主要做两件事情
将CPU由实模式切换到保护模式;
在保护模式下调用内核的入口点;

 rawfs.c

提供一些关于文件系统的基础函数。

备注 如果需要查看某些C标准库函数的汇编实现,可以查看boothead.s

内核的入口点在 kernel目录下的mpx386.S中的
.sect .text
!*===========================================================================*
!*                MINIX                         *
!*===========================================================================*
MINIX:                ! this is the entry point for the MINIX kernel
    jmp    over_flags    ! skip over the next few bytes
    .data2    CLICK_SHIFT    ! for the monitor: memory granularity
flags:
    .data2    0x01FD        ! boot monitor flags:
                !    call in 386 mode, make bss, make stack,
                !    load high, don't patch, will return,
                !    uses generic INT, memory vector,
                !    new boot code return
    nop            ! extra byte to sync up disassembler
over_flags:

! Set up a C stack frame on the monitor stack.  (The monitor sets cs and ds
! right.  The ss descriptor still references the monitor data segment.)
    movzx    esp, sp        ! monitor stack is a 16 bit stack
    push    ebp
    mov    ebp, esp
    push    esi
    push    edi
    cmp    4(ebp), 0    ! monitor return vector is
    jz    noret        ! nonzero if return possible
    inc    (_mon_return)
noret:    mov    (_mon_sp), esp    ! save stack pointer for later return

! Copy the monitor global descriptor table to the address space of kernel and
! switch over to it.  Prot_init() can then update it with immediate effect.

    sgdt    (_gdt+GDT_SELECTOR)        ! get the monitor gdtr
    mov    esi, (_gdt+GDT_SELECTOR+2)    ! absolute address of GDT
    mov    ebx, _gdt            ! address of kernel GDT
    mov    ecx, 8*8            ! copying eight descriptors
copygdt:
 eseg    movb    al, (esi)
    movb    (ebx), al
    inc    esi
    inc    ebx
    loop    copygdt
    mov    eax, (_gdt+DS_SELECTOR+2)    ! base of kernel data
    and    eax, 0x00FFFFFF            ! only 24 bits
    add    eax, _gdt            ! eax = vir2phys(gdt)
    mov    (_gdt+GDT_SELECTOR+2), eax    ! set base of GDT
    lgdt    (_gdt+GDT_SELECTOR)        ! switch over to kernel GDT

! Locate boot parameters, set up kernel segment registers and stack.
    mov    ebx, 8(ebp)    ! boot parameters offset
    mov    edx, 12(ebp)    ! boot parameters length
    mov    eax, 16(ebp)    ! address of a.out headers
    mov    (_aout), eax
    mov    ax, ds        ! kernel data
    mov    es, ax
    mov    fs, ax
    mov    gs, ax
    mov    ss, ax
    mov    esp, k_stktop    ! set sp to point to the top of kernel stack

! Call C startup code to set up a proper environment to run main().
    push    edx
    push    ebx
    push    SS_SELECTOR
    push    DS_SELECTOR
    push    CS_SELECTOR
    call    _cstart        ! cstart(cs, ds, mds, parmoff, parmlen)
    add    esp, 5*4

! Reload gdtr, idtr and the segment registers to global descriptor table set
! up by prot_init().

    lgdt    (_gdt+GDT_SELECTOR)
    lidt    (_gdt+IDT_SELECTOR)

    jmpf    CS_SELECTOR:csinit
csinit:
    o16    mov    ax, DS_SELECTOR
    mov    ds, ax
    mov    es, ax
    mov    fs, ax
    mov    gs, ax
    mov    ss, ax
    o16    mov    ax, TSS_SELECTOR    ! no other TSS is used
    ltr    ax
    push    0            ! set flags to known good state
    popf                ! esp, clear nested task and int enable

    jmp    _main            ! main()

    这部分汇编代码主要做的工作包括:
* 设置内核栈
    为后面要调用的C代码(就是kernel任务的代码)设置好栈帧。boot monitor已经设置好了cs、ds,但是
并没有设置好栈,此时的栈仍然是monitor使用的16位的栈,ss也还指向monitor的数据段。
    1.  判断boot monitor是否设置好了minix退出时的返回地址,如果设置好了,将C的全局变量mon_return
        增加1(设置为真)。
    2.  将此时,也就是boot monitor的栈指针值保存在C全局变量mon_sp中;
    3.  将monitor的GDT表拷贝到内核空间的gdt表头部,后续内核保护模式初始化的prot_init()会使用。
    4.  将gdtr切换到内核的GDT表,此时,ds设置为内核的数据段。
    5.  保存monitor通过栈传递过来的参数:
        a.  将monitor保存的image中进程头信息数组的地址放在aout中;
        b.  内核的数据段的值设置给es, fs, gs, ss寄存器。
        c.  设置esp,此时,才真正设置好了C的执行栈。
        d.  将内核启动参数的地址、偏移量保存在C变量params_size,params_offset中,将monitor的数据
            段基值保存在mon_ds中。
    6.  调用C的初始化代码cstart(cs, ds, mds, params_offset, params_len)继续初始化环境;
        cstart()函数定义在kernel/start.c文件中,它用于在调用main()之前完成一些系统初始化,例如
        初始化kinfo中的信息,将启动参数拷贝到内核空间的缓冲区等,这些主要是把一些信息记录在全局
        的C变量中,方便后续的C代码访问。
        但是,该函数最重要的工作是初始化两个表:GDT和IDT,前者是i386保护模式的核心数据结构,后者
        是中断处理的核心数据结构。
    7.  重新加载GDT和IDT,并使用ljmp指令迫使新加载的这两个表其作用;
    8.  使用DS_SELECTOR初始化所有的数据、堆栈段寄存器,不再使用TSS;

* 调用main()
    如你所见,这确实是Minix3的真正的入口,定义在文件kernel/main.c中。


阅读(2874) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~