Xen启动过程概述

源码是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镜像文件也一样。整个引导过程如下图示:

‍‍

 

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压栈,再调用真正的初始化流程函数

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
#include
#include
#include
#include
#include
#include
         .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<
         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<
         cmp     $DIRECTMAP_PHYS_END+0xe3,%eax
         jne     1b
1:      stosl    /* low mappings cover up to 16MB */
         add     $4,%edi
         add     $(1<
         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)

你可能感兴趣的:(云计算)