Xen启动过程概述
源码是xen-3.1.0xen支持很多种平台:x86_32、x86_64、powerpc等下面代码的分析都是基于x86_32的。虽然现在服务器绝大多数已经是64位的,但自己使用的环境是32位的,对32位平台比较熟悉。xen内核是通过修改Linux内核实现的,只做了部分修改,启动过程与Linux很像。首先是xen-3.1.0-src\xen\arch\x86\boot
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位来实现的。
2.x86_32.S是从Grub进入Xen的入口文件。Grub根据镜像头信息获得入口地址,然后读入整个镜像,最后把控制权交给Xen。
3.__startxen()函数中首先会从启动信息中获取物理内存分配情况,初始化E820内存图。
4.之后是分页初始化、IRQ中断初始化、调度程序初始化、异常处理程序表初始化、时间设置、安全机制设置等初始化工作,并且Xen会初始化一个空闲虚拟域(Idle Domain),当没有合适的虚拟域可以运行的时候,Xen会选择空闲虚拟域来运行,这很类似于Linux中的init进程。
5.当Xen初始化工作结束后,便开始设置Domain0的数据结构。
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位体系)。
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
更多推荐
所有评论(0)