Xen源代码分析(一)——head.s

      启动汇编部分代码是xen 的引导启动程序,位于./xen/arch/x86/boot目录下。代码描述了从xen加载到调用第一个C函数“__start_xen”之间的初始化系统环境过程。主要涉及的文件流程为head.S->trampoline.S->x86_32.s,其中head.s为冲GRUB进入XEN的入口文件,首先看看head.s部分都做了什么(只看32位体系)。

/* 只能由 grub 来引导,head.S 是从GRUB进入XEN 的入口文件;

开始执行的第一个汇编文件,包括初始化页表,解析早期命令行参数等工作

*/

#include <xen/config.h>

#include <xen/multiboot.h>

#include <public/xen.h>

#include <asm/asm_defns.h>

#include <asm/desc.h>

#include <asm/page.h>

#include <asm/msr.h>

        .text

        .code32

/*xen运行时,cpu已经处于保护模式了,和LINUX内核的处理方式一致,虚拟地址等于物理地址加上固定值*/

/*xen\include\asm-x86\x86_32\page.h中有__XEN_VIRT_START的定义*/

#define sym_phys(sym)     ((sym) - __XEN_VIRT_START)

 

/**

*xen 编译时的映像布局由xen\arch\x86\xen.lds.S 控制:

         ...

#ifdef __x86_64__

#define FORMAT "elf64-x86-64"

#else

#define FORMAT "elf32-i386"

#endif

 

ENTRY(start)

 

#endif

 

OUTPUT_FORMAT(FORMAT, FORMAT, FORMAT)

 

#ifdef __x86_64__

OUTPUT_ARCH(i386:x86-64)

#else

OUTPUT_ARCH(i386)

#endif

 

PHDRS

{

  text PT_LOAD ;

}

SECTIONS

{

  . = __XEN_VIRT_START + 0x100000;

  _start = .;

  .text : {

        _stext = .;            //Text and read-only data

       *(.text)

       *(.text.cold)

       *(.text.unlikely)

       *(.fixup)

       *(.gnu.warning)

       _etext = .;             //End of text section

  } :text = 0x9090

                 ...

**/

 

/*根据INTEL手册GDT第一项无用,故而从0x08开始*/

/*ring0,code,32-bit mode*/

#define BOOT_CS32        0x0008

 

/*ring0,code,64-bit mode*/

#define BOOT_CS64        0x0010

 

/*ring0,data*/

#define BOOT_DS          0x0018

 

/*real-mode code*/

#define BOOT_PSEUDORM_CS 0x0020

 

/*5 real-mode data*/

#define BOOT_PSEUDORM_DS 0x0028

 

ENTRY(start)

        jmp     __start

 

        .align 4

/*** MULTIBOOT HEADER ****/

#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_HEADER_MODS_ALIGNED | \

                                MULTIBOOT_HEADER_WANT_MEMORY)

        /* Magic number indicating a Multiboot header. */

        .long   MULTIBOOT_HEADER_MAGIC

        /* Flags to bootloader (see Multiboot spec). */

        .long   MULTIBOOT_HEADER_FLAGS

        /* Checksum: must be the negated sum of the first two fields. */

        .long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

/**

* 上面的定义是给grub看的,表明支持multiboot详细内容见multiboot协议

**/

        .section .init.text, "ax"

       

/*.asciz is just like .ascii, but each string is followed by a zero

byte.*/

.Lbad_cpu_msg: .asciz "ERR: Not a 64-bit CPU!"

.Lbad_ldr_msg: .asciz "ERR: Not a Multiboot bootloader!"

 

bad_cpu:/*打印bad cpu错误*/

        mov     $(sym_phys(.Lbad_cpu_msg)),%esi # Error message

        jmp     print_err

not_multiboot:/*打印非多启动错误*/

        mov     $(sym_phys(.Lbad_ldr_msg)),%esi # Error message

print_err:/*这里的打印用的是最基本的往显卡缓存写入数据的方式*/

        mov     $0xB8000,%edi  # VGA framebuffer

1:      mov     (%esi),%bl

        test    %bl,%bl        # Terminate on '\0' sentinel

2:      je      2b

        mov     $0x3f8+5,%dx   # UART Line Status Register

3:      in      %dx,%al

        test    $0x20,%al      # Test THR Empty flag

        je      3b

        mov     $0x3f8+0,%dx   # UART Transmit Holding Register

        mov     %bl,%al

        out     %al,%dx        # Send a character over the serial line

        movsb                  # Write a character to the VGA framebuffer

        mov     $7,%al

        stosb                  # Write an attribute to the VGA framebuffer

        jmp     1b

 

gdt_boot_descr:/*GDT定义,传统模式下的全局描述符表寄存器(GDTR)48位,由16位的界限和32位的基地址构成。由于段描述符总是8字节长,故界限的值应为8N-1

                   Trampoline_gdt共定义了6个描述符项,界限是6*8-1*/

        .word   6*8-1

        .long   sym_phys(trampoline_gdt)

 

__start:

        cld

        cli

        

        /* Initialise GDT and basic data segments. */

        lgdt    %cs:sym_phys(gdt_boot_descr)

        mov     $BOOT_DS,%ecx

        mov     %ecx,%ds

        mov     %ecx,%es

        mov     %ecx,%ss

         /*

         验证并存储多重启动信息,详见多重启动规范

         boot loader引导32位操作系统的时候,机器必须有如下的状态:

         EAX

         必须包含魔数0X2BADB002,这个值告诉操作系统目前它是由兼容的Multiboot boot loader引导的。

         EBX

         必须包含boot loader提供的多重引导信息结构的32位物理地址。

         CS

         必须是32位的读/执行的代码段,偏移是0以及界限是 0XFFFFFFFF。具体值没有定义。

         SS

         必须是32位的读/执行数据段,偏移是0以及界限是 0XFFFFFFFF。具体值没有定义。

         A20 GATE

         必须enable

         CR0:

         31(PG)必须清除,第0(PE)必须设置。其他位没有定义。

         EFLAGS

         17(VM)位必须清除,第9(IF)必须清除,其他位没有定义。

         */

        /* Check for Multiboot bootloader */

        cmp     $0x2BADB002,%eax

        jne     not_multiboot

 

        /* Set up trampoline segment 64k below EBDA */

        movzwl  0x40e,%eax          /* EBDA segment */

        cmp     $0xa000,%eax        /* sanity check (high) */

        jae     0f

        cmp     $0x4000,%eax        /* sanity check (low) */

        jae     1f

0:

        movzwl  0x413,%eax          /* use base memory size on failure */

        shl     $10-4,%eax

1:

        sub     $0x1000,%eax

 

        /* From arch/x86/smpboot.c: start_eip had better be page-aligned! */

        xor     %al, %al

        shl     $4, %eax

        mov     %eax,sym_phys(trampoline_phys)

 

        /* Save the Multiboot info struct (after relocation) for later use. */

        mov     $sym_phys(cpu0_stack)+1024,%esp

        push    %ebx

        call    reloc

        mov     %eax,sym_phys(multiboot_ptr)

 

        /* Initialize BSS (no nasty surprises!) */

        /*初始化BSS段,存放程序中未初始化的全局变量。

                   BSS段在xen\arch\x86\x86_32\xen.lds.S中定义*/

        mov     $sym_phys(__bss_start),%edi

        mov     $sym_phys(_end),%ecx

        sub     %edi,%ecx

        xor     %eax,%eax

        rep     stosb

 

/*

查询并保存CPU拓展信息。

CPUID指令可提供关于处理器的实现及其能力的完整信息,任意特权级的软件都可以使用它。

EAX寄存器用于决定CPUID生成什么信息

EAX = 0x80000000,返回信息:

EAX: Maximum Input Value for Extended Function CPUID Information. PIV之后的CPU,均大于0x80000000

EBX: Reserved

ECX: Reserved

EDX: Reserved

EAX = 0x80000001,返回信息:

EAX     Extended Processor Signature and Feature Bits.

EBX       Reserved

ECX       Bit 0: LAHF/SAHF available in 64-bit mode

Bits 31-1 Reserved

EDX      Bits 10-0: Reserved

Bit 11: SYSCALL/SYSRET available (when in 64-bit mode)

Bits 19-12: Reserved = 0

Bit 20: Execute Disable Bit available

Bits 28-21: Reserved = 0

Bit 29: Intel? 64 Architecture available if 1

Bits 31-30: Reserved = 0

cpuid_ext_featuresxen\arch\x86\boot\trampoline.S中定义。boot_cpu_data\xen\include\asm-x86 \ processor.h中定义,是cpuinfo_x86的实例。

CPUINFO86_ext_features xen\arch\x86\x86_32 \ asm-offsets.c中定义:OFFSET(CPUINFO86_ext_features, struct cpuinfo_x86, x86_capability[1]);

OFFSET解释如下:

         #define offsetof (s, m) (size_t)&(((s*)0)->m)

                   m为结构体s中的一项,返回m距结构体起始地址的偏移量。ANSI C中常数0允许转换成任何类型的指针,但转换后指针为NULL。例中&(((s*)0)->m)这一步,并不访问m元素,只是获取m的地址,编译时不生成访问m的代码。

         #define DEFINE(_sym,_val)  __asm__  __volatile__  (“\n->” #_sym “%0” #_val:: “i”(_val))

          #是注释符号;%0是占位符,这里指代“i”(_val)

         #define OFFSET(_sym, _str, _mem)  DEFINE(_sym, offsetof(_str, _mem))

         这条宏是将_str结构体的_mem项的偏移量赋值给_sym

*/

        /* Interrogate CPU extended features via CPUID. */

        mov     $0x80000000,%eax

        cpuid

        xor     %edx,%edx

        cmp     $0x80000000,%eax    # any function > 0x80000000?

        jbe     1f

        mov     $0x80000001,%eax

        cpuid

1:      mov     %edx,sym_phys(cpuid_ext_features)

        mov     %edx,sym_phys(boot_cpu_data)+CPUINFO86_ext_features

 

#if defined(__x86_64__)

        /* Check for availability of long mode. */

        bt      $29,%edx

        jnc     bad_cpu

        /* Initialise L2 identity-map and xen page table entries (16MB). */

        mov     $sym_phys(l2_identmap),%edi

        mov     $sym_phys(l2_xenmap),%esi

        mov     $sym_phys(l2_bootmap),%edx

        mov     $0x1e3,%eax                  /* PRESENT+RW+A+D+2MB+GLOBAL */

        mov     $8,%ecx

1:      mov     %eax,(%edi)

        add     $8,%edi

        mov     %eax,(%esi)

        add     $8,%esi

        mov     %eax,(%edx)

        add     $8,%edx

        add     $(1<<L2_PAGETABLE_SHIFT),%eax

        loop    1b

        /* Initialise L3 identity-map page directory entries. */

        mov     $sym_phys(l3_identmap),%edi

        mov     $(sym_phys(l2_identmap)+7),%eax

        mov     $4,%ecx

1:      mov     %eax,(%edi)

        add     $8,%edi

        add     $PAGE_SIZE,%eax

        loop    1b

        /* Initialise L3 xen-map page directory entry. */

        mov     $(sym_phys(l2_xenmap)+7),%eax

        mov     %eax,sym_phys(l3_xenmap) + l3_table_offset(XEN_VIRT_START)*8

        /* Initialise L3 boot-map page directory entry. */

        mov     $(sym_phys(l2_bootmap)+7),%eax

        mov     %eax,sym_phys(l3_bootmap) + 0*8

        /* Hook identity-map, xen-map, and boot-map L3 tables into PML4. */

        mov     $(sym_phys(l3_bootmap)+7),%eax

        mov     %eax,sym_phys(idle_pg_table) + 0*8

        mov     $(sym_phys(l3_identmap)+7),%eax

        mov     %eax,sym_phys(idle_pg_table) + l4_table_offset(DIRECTMAP_VIRT_START)*8

        mov     $(sym_phys(l3_xenmap)+7),%eax

        mov     %eax,sym_phys(idle_pg_table) + l4_table_offset(XEN_VIRT_START)*8

#else

/*32位下2M页面大小,开PAE方式映射,在这里我们也看出32位内核为XEN需要开启PAE,初始化页表,

将线性空间的0-12M__PAGE_OFFSET-__PAGE_OFFSET+12M都映射到物理地址的0-12M;而将线性空间的12M-16M映射到物理地址的12M-16M(注意,这时并没有启用分页机制):

*/

        /* Initialize low and high mappings of memory with 2MB pages */

        mov     $sym_phys(idle_pg_table_l2),%edi

        mov     $0xe3,%eax                   /* PRESENT+RW+A+D+2MB */

1:      mov     %eax,__PAGE_OFFSET>>18(%edi) /* high mapping */

        stosl                                /* low mapping */

        add     $4,%edi

        add     $(1<<L2_PAGETABLE_SHIFT),%eax

        cmp     $DIRECTMAP_PHYS_END+0xe3,%eax

        jne     1b

1:      stosl   /* low mappings cover up to 16MB */

        add     $4,%edi

        add     $(1<<L2_PAGETABLE_SHIFT),%eax

        cmp     $(16<<20)+0xe3,%eax

        jne     1b

#endif

 

        /* Initialize 4kB mappings of first 2MB or 4MB of memory. */

        mov     $sym_phys(l1_identmap),%edi

        mov     $0x263,%eax                  /* PRESENT+RW+A+D+SMALL_PAGES */

#if defined(__x86_64__)

        or      $0x100,%eax                  /* GLOBAL */

#endif

        xor     %ecx,%ecx

1:      stosl

        add     $4,%edi

        add     $PAGE_SIZE,%eax

        inc     %ecx

        /* VGA hole (0xa0000-0xc0000) should be mapped UC. */

        cmp     $0xa0,%ecx

        jne     2f

        or      $0x10,%eax                   /* +PCD */

2:      cmp     $0xc0,%ecx

        jne     2f

        and     $~0x10,%eax                  /* -PCD */

2:      cmp     $L1_PAGETABLE_ENTRIES,%ecx

        jne     1b

        sub     $(PAGE_SIZE-0x63),%edi

#if defined(__x86_64__)

        mov     %edi,sym_phys(l2_identmap)

        mov     %edi,sym_phys(l2_xenmap)

        mov     %edi,sym_phys(l2_bootmap)

#else

        mov     %edi,sym_phys(idle_pg_table_l2)

        mov     %edi,sym_phys(idle_pg_table_l2) + (__PAGE_OFFSET>>18)

#endif

 

        /* Apply relocations to bootstrap trampoline. */

        mov     sym_phys(trampoline_phys),%edx

        mov     $sym_phys(__trampoline_rel_start),%edi

        mov     %edx,sym_phys(trampoline_phys)

1:

        mov     (%edi),%eax

        add     %edx,(%edi,%eax)

        add     $4,%edi

        cmp     $sym_phys(__trampoline_rel_stop),%edi

        jb      1b

 

        /* Patch in the trampoline segment. */

        shr     $4,%edx

        mov     $sym_phys(__trampoline_seg_start),%edi

1:

        mov     (%edi),%eax

        mov     %dx,(%edi,%eax)

        add     $4,%edi

        cmp     $sym_phys(__trampoline_seg_stop),%edi

        jb      1b

 

        call    cmdline_parse_early

 

        /* Switch to low-memory stack.  */

        mov     sym_phys(trampoline_phys),%edi

        lea     0x10000(%edi),%esp

        lea     trampoline_boot_cpu_entry-trampoline_start(%edi),%eax

        pushl   $BOOT_CS32

        push    %eax

 

        /* Copy bootstrap trampoline to low memory, below 1MB. */

        mov     $sym_phys(trampoline_start),%esi

        mov     $trampoline_end - trampoline_start,%ecx

        rep     movsb

 

        /* Jump into the relocated trampoline. */

        /*由上面的push代码段和IP后在这里执行ret相当于两个pop指令,直接跳转到trampoline.s*/

        lret

 

#include "cmdline.S"

 

reloc:

#include "reloc.S"

 

        .align 16

        .globl trampoline_start, trampoline_end

/*第二阶段初始化,实模式*/

trampoline_start:

#include "trampoline.S"

trampoline_end:

 

        .text

/*第三阶段初始化*/

__high_start:

#ifdef __x86_64__

#include "x86_64.S"

#else

#include "x86_32.S"

#endif

你可能感兴趣的:(Xen源代码分析(一)——head.s)