分享

Xen源代码分析

 yikongzi 2013-08-21

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


http://www.cnblogs.com/bulllbat/archive/2012/11/15/2770979.html

启动汇编部分代码是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。

.code16
/* NB. bootsym() is only usable in real mode, or via BOOT_PSEUDORM_DS. */
#undef bootsym
/*bootsym(s)定义的是s的相对位置*/
#define bootsym(s) ((s)-trampoline_start)

#define bootsym_rel(sym, off, opnd...)     \
        bootsym(sym),##opnd;               \
111:;                                      \
        .pushsection .trampoline_rel, "a"; \
        .long 111b - (off) - .;            \
        .popsection

#define bootsym_segrel(sym, off)           \
        $0,$bootsym(sym);                  \
111:;                                      \
        .pushsection .trampoline_seg, "a"; \
        .long 111b - (off) - .;            \
        .popsection

        .globl trampoline_realmode_entry
trampoline_realmode_entry:
        mov     %cs,%ax
        mov     %ax,%ds
        movb    $0xA5,bootsym(trampoline_cpu_started)
        cld
        cli
        lidt    bootsym(idt_48)
        lgdt    bootsym(gdt_48)
        mov     $1,%bl                    # EBX != 0 indicates we are an AP
        xor     %ax, %ax
        inc     %ax
        lmsw    %ax                       # CR0.PE = 1 (enter protected mode)
        ljmpl   $BOOT_CS32,$bootsym_rel(trampoline_protmode_entry,6)

idt_48: .word   0, 0, 0 # base = limit = 0
gdt_48: .word   6*8-1
        .long   bootsym_rel(trampoline_gdt,4)
trampoline_gdt:
        /* 0x0000: unused */
        .quad   0x0000000000000000
        /* 0x0008: ring 0 code, 32-bit mode */
        .quad   0x00cf9a000000ffff
        /* 0x0010: ring 0 code, 64-bit mode */
        .quad   0x00af9a000000ffff
        /* 0x0018: ring 0 data */
        .quad   0x00cf92000000ffff
        /* 0x0020: real-mode code @ BOOT_TRAMPOLINE */
        .long   0x0000ffff
        .long   0x00009a00
        /* 0x0028: real-mode data @ BOOT_TRAMPOLINE */
        .long   0x0000ffff
        .long   0x00009200

        .pushsection .trampoline_rel, "a"
        .long   trampoline_gdt + BOOT_PSEUDORM_CS + 2 - .
        .long   trampoline_gdt + BOOT_PSEUDORM_DS + 2 - .
        .popsection

        .globl cpuid_ext_features
cpuid_ext_features:
        .long   0

        .globl trampoline_xen_phys_start
trampoline_xen_phys_start:
        .long   0

        .globl trampoline_cpu_started
trampoline_cpu_started:
        .byte   0

        .code32
        /*1: 从实模式跳转到这里运行,也就是正式进入保护模式*/
trampoline_protmode_entry:
        /* Set up a few descriptors: on entry only CS is guaranteed good. */
        mov     $BOOT_DS,%eax
        mov     %eax,%ds
        mov     %eax,%es

        /* Set up FPU. */
        fninit

        /* Initialise CR4. */
        mov     $X86_CR4_PAE,%ecx
        mov     %ecx,%cr4

        /* Load pagetable base register. */
        mov     $sym_phys(idle_pg_table),%eax
        add     bootsym_rel(trampoline_xen_phys_start,4,%eax)
        mov     %eax,%cr3

        /* Set up EFER (Extended Feature Enable Register). */
        mov     bootsym_rel(cpuid_ext_features,4,%edi)
        test    $0x20100800,%edi /* SYSCALL/SYSRET, No Execute, Long Mode? */
        jz      .Lskip_efer
        movl    $MSR_EFER,%ecx
        rdmsr
#if CONFIG_PAGING_LEVELS == 4
        btsl    $_EFER_LME,%eax /* Long Mode      */
        btsl    $_EFER_SCE,%eax /* SYSCALL/SYSRET */
#endif
        btl     $20,%edi        /* No Execute?    */
        jnc     1f
        btsl    $_EFER_NX,%eax  /* No Execute     */
1:      wrmsr
.Lskip_efer:

        mov     $0x80050033,%eax /* hi-to-lo: PG,AM,WP,NE,ET,MP,PE */
        mov     %eax,%cr0
        jmp     1f
1:

#if defined(__x86_64__)

        /* Now in compatibility mode. Long-jump into 64-bit mode. */
        ljmp    $BOOT_CS64,$bootsym_rel(start64,6)

        .code64
start64:
        /* Jump to high mappings. */
        mov     high_start(%rip),%rax
        jmpq    *%rax

high_start:
        .quad   __high_start

#else /* !defined(__x86_64__) */

        /* Install relocated selectors. */
lgdt    gdt_descr/*正式装载初始化后的GDT,这里装入的GDT为汇编期间最终的GDT,在x86_32.s中定义*/
        mov     $(__HYPERVISOR_DS),%eax
        mov     %eax,%ds
        mov     %eax,%es
        mov     %eax,%fs
        mov     %eax,%gs
        mov     %eax,%ss
        /*长跳转到x86_32.s的入口__high_start,该变量在head.s中定义,为x86_32.s的入口,x86_32.s
        在head.s中以包含的方式调用*/
        ljmp    $(__HYPERVISOR_CS),$__high_start

#endif

        .code32
/*0: 从head.s的ret指令跳转后首先到达这里开始执行,32位代码,此时还处在保护模式下*/
trampoline_boot_cpu_entry:
        cmpb    $0,bootsym_rel(skip_realmode,5)
        jnz     .Lskip_realmode

        /* Load pseudo-real-mode segments. */
        mov     $BOOT_PSEUDORM_DS,%eax
        mov     %eax,%ds
        mov     %eax,%es
        mov     %eax,%fs
        mov     %eax,%gs
        mov     %eax,%ss

        /* Switch to pseudo-rm CS, enter real mode, and flush insn queue. */
        mov     %cr0,%eax
        dec     %eax
        /*通过下面两个长跳转切换到实模式*/
        ljmp    $BOOT_PSEUDORM_CS,$bootsym(1f)
        .code16
1:      mov     %eax,%cr0                 # CR0.PE = 0 (leave protected mode)

        /* Load proper real-mode values into %cs, %ds, %es and %ss. */
        ljmp    bootsym_segrel(1f,2)
1:      mov     %cs,%ax
        mov     %ax,%ds
        mov     %ax,%es
        mov     %ax,%ss

        /* Initialise stack pointer and IDT, and enable irqs. */
        xor     %sp,%sp
        lidt    bootsym(rm_idt)/*加载IDT*/
        sti

#if defined(__x86_64__)
        /*
         * Declare that our target operating mode is long mode.
         * Initialise 32-bit registers since some buggy BIOSes depend on it.
         */
        movl    $0xec00,%eax      # declare target operating mode
        movl    $0x0002,%ebx      # long mode
        int     $0x15
#endif

        /*
         * Do real-mode work:
         *  1. Get memory map.
         *  2. Get Enhanced Disk Drive (EDD) information.
         *  3. Set video mode.
         */
         /*获得内存信息,该函数于mem.s中调用,内存信息存放于e820map变量中,
         类似Linux内核的处理方式,调用0x15号中断*/
        call    get_memory_map
        /*调用于edd.s中*/
        call    get_edd
           /*调用于video.s中*/
        call    video

        /* Disable irqs before returning to protected mode. */
        cli

        /* Reset GDT and IDT. Some BIOSes clobber GDTR. */
        lidt    bootsym(idt_48)
        lgdt    bootsym(gdt_48)

        /* Enter protected mode, and flush insn queue. */
        xor     %ax,%ax
        inc     %ax
        lmsw    %ax                       # CR0.PE = 1 (enter protected mode)

        /* Load proper protected-mode values into all segment registers. */
        /*跳转到32位代码,为进入保护模式做准备*/
        ljmpl   $BOOT_CS32,$bootsym_rel(1f,6)
        .code32
1:      mov     $BOOT_DS,%eax
        mov     %eax,%ds
        mov     %eax,%es
        mov     %eax,%fs
        mov     %eax,%gs
        mov     %eax,%ss

.Lskip_realmode:
        /* EBX == 0 indicates we are the BP (Boot Processor). */
        xor     %ebx,%ebx

        /* Jump to the common bootstrap entry point. */
        jmp     trampoline_protmode_entry/*跳转到保护模式入口*/

skip_realmode:
        .byte   0

rm_idt: .word   256*4-1, 0, 0
/*这三部分的内容不再这里详细写了*/
#include "mem.S"
#include "edd.S"
#include "video.S"
#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

.code32
       
        /* Enable full CR4 features. */
        mov     mmu_cr4_features,%eax
        mov     %eax,%cr4
       
        /* Initialise stack. */
        /*在栈顶分配一个cpu_info结构(参见下图),这个结构包含很多重要的成员:
        1)客户系统的切换上下文2)当前运行的vcpu指针3)物理处理器编号*/
        mov     stack_start,%esp
        or      $(STACK_SIZE-CPUINFO_sizeof),%esp
       
        /* Reset EFLAGS (subsumes CLI and CLD). */
        pushl   $0
        popf

        lidt    idt_descr/*加载中断描述符表*/

        test    %ebx,%ebx
        jnz     start_secondary

        /* Initialise IDT with simple error defaults. */
        lea     ignore_int,%edx
        mov     $(__HYPERVISOR_CS << 16),%eax
        mov     %dx,%ax            /* selector = 0x0010 = cs */
        mov     $0x8E00,%dx        /* interrupt gate - dpl=0, present */
        lea     idt_table,%edi
        mov     $256,%ecx
1:      mov     %eax,(%edi)
        mov     %edx,4(%edi)
        add     $8,%edi
        loop    1b
               
        /* Pass off the Multiboot info structure to C land. */
        pushl   multiboot_ptr
        call    __start_xen/*调用该函数正式调入C代码初始化中*/
        ud2     /* Force a panic (invalid opcode). */

/* This is the default interrupt handler. */
int_msg:
        .asciz "Unknown interrupt (cr2=%08x)\n"
hex_msg:
        .asciz "  %08x"
        ALIGN
ignore_int:
        pusha
        cld
        mov     $(__HYPERVISOR_DS),%eax
        mov     %eax,%ds
        mov     %eax,%es
        mov     %cr2,%eax
        push    %eax
        pushl   $int_msg
        call    printk
        add     $8,%esp
        mov     %esp,%ebp
0:      pushl   (%ebp)
        add     $4,%ebp
        pushl   $hex_msg
        call    printk
        add     $8,%esp
        test    $0xffc,%ebp
        jnz     0b
1:      jmp     1b

        .data
        ALIGN
ENTRY(stack_start)
        .long cpu0_stack
       
/*** DESCRIPTOR TABLES ***/

        ALIGN
multiboot_ptr:
        .long   0
       
        .word   0   
idt_descr:
        .word   256*8-1
        .long   idt_table

        .word   0
gdt_descr:/*在第二阶段装载了*/
        .word   LAST_RESERVED_GDT_BYTE
        .long   boot_cpu_gdt_table - FIRST_RESERVED_GDT_BYTE


        .align 32
ENTRY(idle_pg_table)
        .long sym_phys(idle_pg_table_l2) + 0*PAGE_SIZE + 0x01, 0
        .long sym_phys(idle_pg_table_l2) + 1*PAGE_SIZE + 0x01, 0
        .long sym_phys(idle_pg_table_l2) + 2*PAGE_SIZE + 0x01, 0
        .long sym_phys(idle_pg_table_l2) + 3*PAGE_SIZE + 0x01, 0

        .section .data.page_aligned, "aw", @progbits
        .align PAGE_SIZE, 0
/* NB. Rings != 0 get access up to MACH2PHYS_VIRT_END. This allows access to */
/*     the machine->physical mapping table. Ring 0 can access all memory.    */
#define GUEST_DESC(d)                                                   \
        .long ((MACH2PHYS_VIRT_END - 1) >> 12) & 0xffff,                \
              ((MACH2PHYS_VIRT_END - 1) >> 12) & (0xf << 16) | (d)
ENTRY(boot_cpu_gdt_table)
        .quad 0x0000000000000000     /* double fault TSS */
        .quad 0x00cf9a000000ffff     /* 0xe008 ring 0 4.00GB code at 0x0 */
        .quad 0x00cf92000000ffff     /* 0xe010 ring 0 4.00GB data at 0x0 */
        GUEST_DESC(0x00c0ba00)       /* 0xe019 ring 1 3.xxGB code at 0x0 */
        GUEST_DESC(0x00c0b200)       /* 0xe021 ring 1 3.xxGB data at 0x0 */
        GUEST_DESC(0x00c0fa00)       /* 0xe02b ring 3 3.xxGB code at 0x0 */
        GUEST_DESC(0x00c0f200)       /* 0xe033 ring 3 3.xxGB data at 0x0 */
        .fill (PER_CPU_GDT_ENTRY - FLAT_RING3_DS / 8 - 1), 8, 0
        .quad 0x0000910000000000     /* per-CPU entry (limit == cpu) */
        .align PAGE_SIZE,0




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多