源码是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" /> ‍‍XEN启动过程分析

 

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.Sstart入口点上,主要是为进行后续工作做准备,包括简单地设置GDTIDT以及初始化分页等。然后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数据结构中获取。进入Domain0EIP指向_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位体系)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/* 只能由 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_features在xen\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源代码分析(二)——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"
复制代码

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
复制代码


Logo

更多推荐