您的位置:首页 > 其它

Xen启动过程概述

2017-04-11 20:38 387 查看
源码是xen-3.1.0

xen支持很多种平台:x86_32、x86_64、powerpc等

下面代码的分析都是基于x86_32的。虽然现在服务器绝大多数已经是64位的,但自己使用的环境是32位的,对32位平台比较熟悉。

xen内核是通过修改Linux内核实现的,只做了部分修改,启动过程与Linux很像。

首先是xen-3.1.0-src\xen\arch\x86\boot\x86_32.S

系统启动时处于实模式,在这个文件里,设置一些寄存器以及一些重要的表的值。然后进入保护模式。最后调用__start_xen()函数,这个函数位于xen-3.1.0-src\xen\arch\x86\setup.c里。

__start_xen()函数需要初始化很多东西,比如分页初始化、IRQ中断初始化、调度程序初始化。然后调用init_idle_domain()函数来初始化一个idle域,这个函数会调用domain_create()来创建domain结构体,用于管理各个domain。在创建的过程中,会将各个domain设置成paused状态(idle域除外)。

再回到__start_xen()函数里。在进行一些其他的初始化后,会调用domain_create()来创建dom0,并将dom0至于paused状态。由于domain_create()函数只创建domain管理结构体,并进行一些初始化。但仅仅是个管理结构,不具备实体。这就好比一个进程,只有一个PCB有鸟用??因此需要调用construct_dom0()来填充这个实体。这个函数会用xen内核来启动dom0。紧接着调用domain_unpause_by_systemcontroller()来unpause dom0。这样dom0就处于可调度状态了!然后调用startup_cpu_idle_loop(),这个函数会在系统没有domain可调度的时候运行。就相当于Windows的idle进程,Linux的idle循环。

这样xen内核就开始启动了.....

Xen Hypervisor运行在Ring0,在启动过程中,Xen首先被引导:系统由Grub启动,遵循Multiboot引导规范;然后Linux内核做为module也被引导入内存,同时initrd镜像文件也一样。整个引导过程如下图示:

‍<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /> ‍‍



1 当加电后首先是BIOS自检、Grub引导。Xen遵循Multiboot引导规范,它需要从Grub读入内存信息,通过置位标志位第1位来实现的。

主要过程是从grub-xen(‍head.S, trampoline.S, x86_32.S,位于‍\xen\arch\x86\boot中:),

head.S:‍装入GDT(trampoline_gdt); ‍进入保护模式;‍初始化页表,将线性空间的0-12M和__PAGE_OFFSET-__PAGE_OFFSET+12M都映射到物理地址的0-12M;而将线性空间的12M-16M映射到物理地址的12M-16M;‍解析早期命令行参数;‍跳转到trampoline_boot_cpu_entry;

‍trampoline.S:‍进入实模式,读取内存,磁盘,显示信息;‍再次进入保护模式,装入新的GDT(gdt_table);‍加载前面初始化了的页表,启用分页机制,跳转到__high_start。然后就是x86_32.S文件。

2.x86_32.S是从Grub进入Xen的入口文件。Grub根据镜像头信息获得入口地址,然后读入整个镜像,最后把控制权交给Xen。

可以使用readelf命令来查看编译后生成的Xen的内核,它表明文件类型为可执行文件,并且程序的入口点是Oxl0000(原本这是Linux内核的位置),这表明Xen被引导程序放在了物理内存1M的位置上。

在x86_32.S的start入口点上,主要是为进行后续工作做准备,包括简单地设置GDT、IDT以及初始化分页等。然后start将保存着引导程序启动信息地址的EBX压栈,再调用真正的初始化流程函数。<?xml:namespace
prefix = o ns = "urn:schemas-microsoft-com:office:office" />

3.__startxen()函数中首先会从启动信息中获取物理内存分配情况,初始化E820内存图。

物理内存是只有Xen才有权限进行管理和分配的,无论是Domain0还是DomainU,它们得到的都是Xen给它们的物理内存假相。

4.之后是分页初始化、IRQ中断初始化、调度程序初始化、异常处理程序表初始化、时间设置、安全机制设置等初始化工作,并且Xen会初始化一个空闲虚拟域(Idle
Domain),当没有合适的虚拟域可以运行的时候,Xen会选择空闲虚拟域来运行,这很类似于Linux中的init进程。

5.当Xen初始化工作结束后,便开始设置Domain0的数据结构。

每个虚拟域都有一个struct domain结构体,在这里主要定义了它的ID、共享信息、内存分配、虚拟CPU、事件通道和授权表等。分配好DomainO的数据结构,Xen将通过construct_dom0()函数来将主控域的运行环境设置好。
6。然后Xen将控制权交给Domain0中的Linux,而自己进入idie_loop。主控域得到控制权后,开始自己的引导过程,只是它需要的信息是从xen_start_info数据结构中获取。进入Domain0的EIP指向_start_32,它会再跳转到start_kernel,之后的启动流程就和Linux的正常启动流程基本一样了。


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位体系)。


Xen源代码分析(二)——trampoline.s

汇编文件trampoline.s,为启动汇编程序第二阶段,主要工作为进入实模式,读取内存,磁盘,视频信息然后再次进入保护模式装入新的GDT(gdt_table),英文注释了很大部分,很容易理解。下面的代码注释中,从标号0开始运行,然后是标号1。

1  .code16
2 /* NB. bootsym() is only usable in real mode, or via BOOT_PSEUDORM_DS. */
3 #undef bootsym
4 /*bootsym(s)定义的是s的相对位置*/
5 #define bootsym(s) ((s)-trampoline_start)
6
7 #define bootsym_rel(sym, off, opnd...)     \
8         bootsym(sym),##opnd;               \
9 111:;                                      \
10         .pushsection .trampoline_rel, "a"; \
11         .long 111b - (off) - .;            \
12         .popsection
13
14 #define bootsym_segrel(sym, off)           \
15         $0,$bootsym(sym);                  \
16 111:;                                      \
17         .pushsection .trampoline_seg, "a"; \
18         .long 111b - (off) - .;            \
19         .popsection
20
21         .globl trampoline_realmode_entry
22 trampoline_realmode_entry:
23         mov     %cs,%ax
24         mov     %ax,%ds
25         movb    $0xA5,bootsym(trampoline_cpu_started)
26         cld
27         cli
28         lidt    bootsym(idt_48)
29         lgdt    bootsym(gdt_48)
30         mov     $1,%bl                    # EBX != 0 indicates we are an AP
31         xor     %ax, %ax
32         inc     %ax
33         lmsw    %ax                       # CR0.PE = 1 (enter protected mode)
34         ljmpl   $BOOT_CS32,$bootsym_rel(trampoline_protmode_entry,6)
35
36 idt_48: .word   0, 0, 0 # base = limit = 0
37 gdt_48: .word   6*8-1
38         .long   bootsym_rel(trampoline_gdt,4)
39 trampoline_gdt:
40         /* 0x0000: unused */
41         .quad   0x0000000000000000
42         /* 0x0008: ring 0 code, 32-bit mode */
43         .quad   0x00cf9a000000ffff
44         /* 0x0010: ring 0 code, 64-bit mode */
45         .quad   0x00af9a000000ffff
46         /* 0x0018: ring 0 data */
47         .quad   0x00cf92000000ffff
48         /* 0x0020: real-mode code @ BOOT_TRAMPOLINE */
49         .long   0x0000ffff
50         .long   0x00009a00
51         /* 0x0028: real-mode data @ BOOT_TRAMPOLINE */
52         .long   0x0000ffff
53         .long   0x00009200
54
55         .pushsection .trampoline_rel, "a"
56         .long   trampoline_gdt + BOOT_PSEUDORM_CS + 2 - .
57         .long   trampoline_gdt + BOOT_PSEUDORM_DS + 2 - .
58         .popsection
59
60         .globl cpuid_ext_features
61 cpuid_ext_features:
62         .long   0
63
64         .globl trampoline_xen_phys_start
65 trampoline_xen_phys_start:
66         .long   0
67
68         .globl trampoline_cpu_started
69 trampoline_cpu_started:
70         .byte   0
71
72         .code32
73         /*1: 从实模式跳转到这里运行,也就是正式进入保护模式*/
74 trampoline_protmode_entry:
75         /* Set up a few descriptors: on entry only CS is guaranteed good. */
76         mov     $BOOT_DS,%eax
77         mov     %eax,%ds
78         mov     %eax,%es
79
80         /* Set up FPU. */
81         fninit
82
83         /* Initialise CR4. */
84         mov     $X86_CR4_PAE,%ecx
85         mov     %ecx,%cr4
86
87         /* Load pagetable base register. */
88         mov     $sym_phys(idle_pg_table),%eax
89         add     bootsym_rel(trampoline_xen_phys_start,4,%eax)
90         mov     %eax,%cr3
91
92         /* Set up EFER (Extended Feature Enable Register). */
93         mov     bootsym_rel(cpuid_ext_features,4,%edi)
94         test    $0x20100800,%edi /* SYSCALL/SYSRET, No Execute, Long Mode? */
95         jz      .Lskip_efer
96         movl    $MSR_EFER,%ecx
97         rdmsr
98 #if CONFIG_PAGING_LEVELS == 4
99         btsl    $_EFER_LME,%eax /* Long Mode      */
100         btsl    $_EFER_SCE,%eax /* SYSCALL/SYSRET */
101 #endif
102         btl     $20,%edi        /* No Execute?    */
103         jnc     1f
104         btsl    $_EFER_NX,%eax  /* No Execute     */
105 1:      wrmsr
106 .Lskip_efer:
107
108         mov     $0x80050033,%eax /* hi-to-lo: PG,AM,WP,NE,ET,MP,PE */
109         mov     %eax,%cr0
110         jmp     1f
111 1:
112
113 #if defined(__x86_64__)
114
115         /* Now in compatibility mode. Long-jump into 64-bit mode. */
116         ljmp    $BOOT_CS64,$bootsym_rel(start64,6)
117
118         .code64
119 start64:
120         /* Jump to high mappings. */
121         mov     high_start(%rip),%rax
122         jmpq    *%rax
123
124 high_start:
125         .quad   __high_start
126
127 #else /* !defined(__x86_64__) */
128
129         /* Install relocated selectors. */
130 lgdt    gdt_descr/*正式装载初始化后的GDT,这里装入的GDT为汇编期间最终的GDT,在x86_32.s中定义*/
131         mov     $(__HYPERVISOR_DS),%eax
132         mov     %eax,%ds
133         mov     %eax,%es
134         mov     %eax,%fs
135         mov     %eax,%gs
136         mov     %eax,%ss
137         /*长跳转到x86_32.s的入口__high_start,该变量在head.s中定义,为x86_32.s的入口,x86_32.s
138         在head.s中以包含的方式调用*/
139         ljmp    $(__HYPERVISOR_CS),$__high_start
140
141 #endif
142
143         .code32
144 /*0: 从head.s的ret指令跳转后首先到达这里开始执行,32位代码,此时还处在保护模式下*/
145 trampoline_boot_cpu_entry:
146         cmpb    $0,bootsym_rel(skip_realmode,5)
147         jnz     .Lskip_realmode
148
149         /* Load pseudo-real-mode segments. */
150         mov     $BOOT_PSEUDORM_DS,%eax
151         mov     %eax,%ds
152         mov     %eax,%es
153         mov     %eax,%fs
154         mov     %eax,%gs
155         mov     %eax,%ss
156
157         /* Switch to pseudo-rm CS, enter real mode, and flush insn queue. */
158         mov     %cr0,%eax
159         dec     %eax
160         /*通过下面两个长跳转切换到实模式*/
161         ljmp    $BOOT_PSEUDORM_CS,$bootsym(1f)
162         .code16
163 1:      mov     %eax,%cr0                 # CR0.PE = 0 (leave protected mode)
164
165         /* Load proper real-mode values into %cs, %ds, %es and %ss. */
166         ljmp    bootsym_segrel(1f,2)
167 1:      mov     %cs,%ax
168         mov     %ax,%ds
169         mov     %ax,%es
170         mov     %ax,%ss
171
172         /* Initialise stack pointer and IDT, and enable irqs. */
173         xor     %sp,%sp
174         lidt    bootsym(rm_idt)/*加载IDT*/
175         sti
176
177 #if defined(__x86_64__)
178         /*
179          * Declare that our target operating mode is long mode.
180          * Initialise 32-bit registers since some buggy BIOSes depend on it.
181          */
182         movl    $0xec00,%eax      # declare target operating mode
183         movl    $0x0002,%ebx      # long mode
184         int     $0x15
185 #endif
186
187         /*
188          * Do real-mode work:
189          *  1. Get memory map.
190          *  2. Get Enhanced Disk Drive (EDD) information.
191          *  3. Set video mode.
192          */
193          /*获得内存信息,该函数于mem.s中调用,内存信息存放于e820map变量中,
194          类似Linux内核的处理方式,调用0x15号中断*/
195         call    get_memory_map
196         /*调用于edd.s中*/
197         call    get_edd
198            /*调用于video.s中*/
199         call    video
200
201         /* Disable irqs before returning to protected mode. */
202         cli
203
204         /* Reset GDT and IDT. Some BIOSes clobber GDTR. */
205         lidt    bootsym(idt_48)
206         lgdt    bootsym(gdt_48)
207
208         /* Enter protected mode, and flush insn queue. */
209         xor     %ax,%ax
210         inc     %ax
211         lmsw    %ax                       # CR0.PE = 1 (enter protected mode)
212
213         /* Load proper protected-mode values into all segment registers. */
214         /*跳转到32位代码,为进入保护模式做准备*/
215         ljmpl   $BOOT_CS32,$bootsym_rel(1f,6)
216         .code32
217 1:      mov     $BOOT_DS,%eax
218         mov     %eax,%ds
219         mov     %eax,%es
220         mov     %eax,%fs
221         mov     %eax,%gs
222         mov     %eax,%ss
223
224 .Lskip_realmode:
225         /* EBX == 0 indicates we are the BP (Boot Processor). */
226         xor     %ebx,%ebx
227
228         /* Jump to the common bootstrap entry point. */
229         jmp     trampoline_protmode_entry/*跳转到保护模式入口*/
230
231 skip_realmode:
232         .byte   0
233
234 rm_idt: .word   256*4-1, 0, 0
235 /*这三部分的内容不再这里详细写了*/
236 #include "mem.S"
237 #include "edd.S"
238 #include "video.S"
239 #include "wakeup.S"



Xen源代码分析(三)——x86_32.s

X86_32.s文件,启动汇编程序的最后阶段,主要工作为装入堆栈指针, Xen会在栈顶分配一个cpu_info结构,这个结构包含很多重要的成员:1)客户系统的切换上下文2)当前运行的vcpu指针3)物理处理器编号.

1,IDT的处理,整个idt_table的向量入口都初始化ignore_int,这个中断处理函数打印"Unknown interrupt(cr2=XXXXXXXX)"信息后系统进入循环

2,如果是BSP,跳转到__start_xen否则,跳转到start_secondary

1 .code32
2
3         /* Enable full CR4 features. */
4         mov     mmu_cr4_features,%eax
5         mov     %eax,%cr4
6
7         /* Initialise stack. */
8         /*在栈顶分配一个cpu_info结构(参见下图),这个结构包含很多重要的成员:
9         1)客户系统的切换上下文2)当前运行的vcpu指针3)物理处理器编号*/
10         mov     stack_start,%esp
11         or      $(STACK_SIZE-CPUINFO_sizeof),%esp
12
13         /* Reset EFLAGS (subsumes CLI and CLD). */
14         pushl   $0
15         popf
16
17         lidt    idt_descr/*加载中断描述符表*/
18
19         test    %ebx,%ebx
20         jnz     start_secondary
21
22         /* Initialise IDT with simple error defaults. */
23         lea     ignore_int,%edx
24         mov     $(__HYPERVISOR_CS << 16),%eax
25         mov     %dx,%ax            /* selector = 0x0010 = cs */
26         mov     $0x8E00,%dx        /* interrupt gate - dpl=0, present */
27         lea     idt_table,%edi
28         mov     $256,%ecx
29 1:      mov     %eax,(%edi)
30         mov     %edx,4(%edi)
31         add     $8,%edi
32         loop    1b
33
34         /* Pass off the Multiboot info structure to C land. */
35         pushl   multiboot_ptr
36         call    __start_xen/*调用该函数正式调入C代码初始化中*/
37         ud2     /* Force a panic (invalid opcode). */
38
39 /* This is the default interrupt handler. */
40 int_msg:
41         .asciz "Unknown interrupt (cr2=%08x)\n"
42 hex_msg:
43         .asciz "  %08x"
44         ALIGN
45 ignore_int:
46         pusha
47         cld
48         mov     $(__HYPERVISOR_DS),%eax
49         mov     %eax,%ds
50         mov     %eax,%es
51         mov     %cr2,%eax
52         push    %eax
53         pushl   $int_msg
54         call    printk
55         add     $8,%esp
56         mov     %esp,%ebp
57 0:      pushl   (%ebp)
58         add     $4,%ebp
59         pushl   $hex_msg
60         call    printk
61         add     $8,%esp
62         test    $0xffc,%ebp
63         jnz     0b
64 1:      jmp     1b
65
66         .data
67         ALIGN
68 ENTRY(stack_start)
69         .long cpu0_stack
70
71 /*** DESCRIPTOR TABLES ***/
72
73         ALIGN
74 multiboot_ptr:
75         .long   0
76
77         .word   0
78 idt_descr:
79         .word   256*8-1
80         .long   idt_table
81
82         .word   0
83 gdt_descr:/*在第二阶段装载了*/
84         .word   LAST_RESERVED_GDT_BYTE
85         .long   boot_cpu_gdt_table - FIRST_RESERVED_GDT_BYTE
86
87
88         .align 32
89 ENTRY(idle_pg_table)
90         .long sym_phys(idle_pg_table_l2) + 0*PAGE_SIZE + 0x01, 0
91         .long sym_phys(idle_pg_table_l2) + 1*PAGE_SIZE + 0x01, 0
92         .long sym_phys(idle_pg_table_l2) + 2*PAGE_SIZE + 0x01, 0
93         .long sym_phys(idle_pg_table_l2) + 3*PAGE_SIZE + 0x01, 0
94
95         .section .data.page_aligned, "aw", @progbits
96         .align PAGE_SIZE, 0
97 /* NB. Rings != 0 get access up to MACH2PHYS_VIRT_END. This allows access to */
98 /*     the machine->physical mapping table. Ring 0 can access all memory.    */
99 #define GUEST_DESC(d)                                                   \
100         .long ((MACH2PHYS_VIRT_END - 1) >> 12) & 0xffff,                \
101               ((MACH2PHYS_VIRT_END - 1) >> 12) & (0xf << 16) | (d)
102 ENTRY(boot_cpu_gdt_table)
103         .quad 0x0000000000000000     /* double fault TSS */
104         .quad 0x00cf9a000000ffff     /* 0xe008 ring 0 4.00GB code at 0x0 */
105         .quad 0x00cf92000000ffff     /* 0xe010 ring 0 4.00GB data at 0x0 */
106         GUEST_DESC(0x00c0ba00)       /* 0xe019 ring 1 3.xxGB code at 0x0 */
107         GUEST_DESC(0x00c0b200)       /* 0xe021 ring 1 3.xxGB data at 0x0 */
108         GUEST_DESC(0x00c0fa00)       /* 0xe02b ring 3 3.xxGB code at 0x0 */
109         GUEST_DESC(0x00c0f200)       /* 0xe033 ring 3 3.xxGB data at 0x0 */
110         .fill (PER_CPU_GDT_ENTRY - FLAT_RING3_DS / 8 - 1), 8, 0
111         .quad 0x0000910000000000     /* per-CPU entry (limit == cpu) */
112         .align PAGE_SIZE,0


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  xen