官术网_书友最值得收藏!

  • Linux內核精析
  • 鄭阿奇主編
  • 1213字
  • 2019-01-01 07:31:56

2.2 實模式setup階段

setup用于體系結構相關的硬件的初始化工作。在32位x86平臺,setup的入口點是arch\x86\boot\header.s中的_start,Boot Loader把setup代碼加載到0x07c00上的某處后,將DS寄存器設置為該處的地址,之后再跳轉到DS:200把控制器交由_start,最后跳轉到start_of_setup。相關代碼在arch\x86\boot\header.s中的定義如下:

.section ".inittext", "ax"
start_of_setup:
#ifdef SAFE_RESET_DISK_CONTROLLER
    # 復位磁盤控制器
    movw    $0x0000, %ax       # 復位磁盤控制器
    movb    $0x80, %dl         # 所有的磁盤
    int $0x13
# endif
# Force %es = %ds
    movw    %ds, %ax
    movw    %ax, %es
    cld
    # 如果%ss無效,則重新計算該堆棧指針
    movw    %ss, %dx
    cmpw    %ax, %dx            # %ds == %ss?
    movw    %sp, %dx
    je  2f
    # 如果%ss無效,則建立新堆棧
    movw    $_end, %dx
    testb $CAN_USE_HEAP, loadflags
    jz  1f
    movw    heap_end_ptr, %dx
1:  addw    $STACK_SIZE, %dx
    jnc 2f
    xorw    %dx, %dx            # 防止死循環(huán)
2:  # %dx應該指向堆棧空間的棧頂
    andw    $~3, %dx            # 雙字對齊
    jnz 3f
    movw    $0xfffc, %dx       # 確保%dx非零
3:  movw    %ax, %ss
    movzwl  %dx, %esp           # 清除%esp的上半部
    sti                         # 獲取工作堆棧
    pushw   %ds
    pushw   $6f
    lretw
6:
    cmpl $0x5a5aaa55, setup_sig
    jne setup_bad
    # 清空bss
    movw    $_bss_start, %di
    movw    $_end+3, %cx
    xorl %eax, %eax
    subw    %di, %cx
    shrw $2, %cx
    rep; stosl
    # 跳轉到C代碼
    call main

在該代碼中,Boot Loader跳轉到該段代碼時,DS已經(jīng)設置為setup加載的基地址,最后跳轉到C代碼的main函數(shù)。main函數(shù)在arch\x86\boot\main.c文件中的定義如下:

void main(void)
{
    /* 首先把第一個扇區(qū)的參數(shù)復制到boot_params中 */
    copy_boot_params();
    /* 堆棧結束檢查 */
    if (boot_params.hdr.loadflags & CAN_USE_HEAP) {
                heap_end = (char *)(boot_params.hdr.heap_end_ptr
                            +0x200-STACK_SIZE);
                                                        } else {
                puts("WARNING: Ancient bootloader, some functionality "
                      "may be limited!\n");
                                                        }
                                                        /* 確保有適合CPU的支持 */
                                                        if (validate_cpu()) {
                puts("Unable to boot-please use a kernel appropriate"
                      "for your CPU.\n");
                die();
                                                        }
                                                        /* 通知BIOS將要運行的CPU模式 */
                                                        set_bios_mode();
                                                        /* 檢測內存的布局 */
                                                        detect_memory();
                                                        /* 設置鍵盤重復率 */
                                                        keyboard_set_repeat();
                                                        /* 設置視頻模式 */
                                                        set_video();
                                                        /* 查詢MCA信息 */
                                                        query_mca();
                                                        #ifdef CONFIG_X86_VOYAGER
                query_voyager();
                                                        #endif
                                                        query_ist();
                                                        /* 查詢APM信息 */
                                                        #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
                query_apm_bios();
                                                        #endif
                                                        /* 查詢EDD信息 */
                                                        #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
                query_edd();
                                                        #endif
                                                        /* 進入保護模式 */
                                                        go_to_protected_mode();
                                                    }

該函數(shù)首先通過copy_boot_params()函數(shù)把位于第一個扇區(qū)的參數(shù)復制到boot_params中,該變量位于setup的數(shù)據(jù)段,之后設置系統(tǒng)硬件的相關參數(shù),最后調用go_to_protected_mode()函數(shù)進入保護模式。對于copy_boot_params()函數(shù)的定義如下:

[main-> copy_boot_params]
static void copy_boot_params(void)
{
    struct old_cmdline {
        u16 cl_magic;
        u16 cl_offset;
    };
    const struct old_cmdline * const oldcmd =
        (const struct old_cmdline *)OLD_CL_ADDRESS;
    BUILD_BUG_ON(sizeof boot_params != 4096);
    /* 把hdr結構中的參數(shù)復制到boot_params.hdr中 */
    memcpy(&boot_params.hdr, &hdr, sizeof hdr);
    …
}

該函數(shù)的主要操作是調用memcpy(&boot_params.hdr, &hdr, sizeof hdr)把hdr結構 中 的 參 數(shù) 復 制 到boot_params.hdr中。回 到main() 函 數(shù) 最 后 調 用 的go_to_protected_mode()函數(shù)在文件arch\x86\boot\Pm.c中的定義如下:

[main->go_to_protected_mode]
void go_to_protected_mode(void)
{
    /* 在離開實模式前,需要禁用中斷 */
    realmode_switch_hook();
    /* 將內核/setup移到其最后的區(qū)域 */
    move_kernel_around();
    /* 開啟A20門 */
    if (enable_a20()) {
        puts("A20 gate not responding, unable to boot...\n");
        die();
    }
    /* 復位處理器 */
    reset_coprocessor();
    /* 屏蔽PIC的所有中斷 */
    mask_all_interrupts();
    /* 開始進入保護模式 */
    setup_idt();
    setup_gdt();
    //跳轉至code32_start執(zhí)行
    protected_mode_jump(boot_params.hdr.code32_start,
                (u32)&boot_params + (ds() << 4));
  }

該函數(shù)首先調用realmode_switch_hook()函數(shù),之后進行復位處理器及屏蔽PIC中斷等操作,最后調用protected_mode_jump()開啟CR0寄存器的PE位進入保護模式,并跳轉到code32_start執(zhí)行。參數(shù)boot_params.hdr.code32_start在header.s中的定義如下:

…
code32_start:              # 32位代碼的起始地址
#ifndef_BIG_KERNEL_
    .long   0x1000          # 0x1000 = zImage默認地址
#else
    .long   0x100000        # 0x100000 =大內核的默認地址(bzImage)
#endif
…

從該段代碼可以看出,程序將跳轉到0x1000或0x100000處繼續(xù)執(zhí)行;參數(shù)(u32)&boot_params + (ds() << 4) 為boot_params的線性地址,由于此時仍為實模式,因此線性地址為段地址左移4 位(乘以16)再加上偏移。protected_mode_jump在arch\x86\boot \pmjump.S中的定義如下:

/*
* void protected_mode_jump(u32 entrypoint, u32 bootparams);
*/
protected_mode_jump:
    movl    %edx, %esi     # 指向boot_params表
    movl    %eax, 2f        # 2f處放置將要跳轉的指令
    …
    movl    %cr0, %edx
    orb $1, %dl             # 保護模式(PE)位
    movl    %edx, %cr0
    jmp 1f
1:
    movw    %cx, %ds
    movw    %cx, %es
    movw    %cx, %fs
    movw    %cx, %gs
    movw    %cx, %ss
    # 跳轉至32位入口
    .byte 0x66, 0xea       # 跳轉指令操作碼
2:  .long   0               # 偏移
    .word   _BOOT_CS       # 段
    .size protected_mode_jump, .-protected_mode_jump

由于arch\x86\kernel\head_32.S是編譯到內核vmlinux中的,然后vmlinux經(jīng)objcopy處理再經(jīng)過壓縮,最后被當做數(shù)據(jù)段的內容和arch\x86\boot\compressed\head_32.0鏈接,因此執(zhí)行arch\x86\boot\compressed\head_32.S中的startup_32,將數(shù)據(jù)段中的壓縮內核解壓后,跳轉至arch\x86\kernel\head_32.S的startup_32。

主站蜘蛛池模板: 鄂伦春自治旗| 西藏| 双流县| 梁山县| 邯郸市| 平阴县| 维西| 临沭县| 子长县| 玉山县| 平利县| 云阳县| 太和县| 水城县| 安溪县| 竹溪县| 镶黄旗| 商河县| 肥东县| 清丰县| 荔波县| 个旧市| 岳阳市| 伊川县| 应城市| 和硕县| 蒙山县| 平舆县| 龙泉市| 彝良县| 苏尼特左旗| 岢岚县| 赤壁市| 龙岩市| 平定县| 永安市| 南澳县| 五大连池市| 云龙县| 舟曲县| 驻马店市|