aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/i386/boot/boot.S
blob: e882428a210409a8ee7e6d18d22ceff04b36b80d (plain)
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
#include <xeno/config.h>
#include <hypervisor-ifs/hypervisor-if.h>
#include <asm/page.h>

#define  SECONDARY_CPU_FLAG 0xA5A5A5A5
                
       	.text

ENTRY(start)
        jmp hal_entry

        .align	4

/*** MULTIBOOT HEADER ****/
        /* Magic number indicating a Multiboot header. */
	.long	0x1BADB002
	/* Flags to bootloader (see Multiboot spec). */
	.long	0x00000002
	/* Checksum: must be the negated sum of the first two fields. */
	.long	-0x1BADB004
        
hal_entry:
        /* Set up a few descriptors: on entry only CS is guaranteed good. */
        lgdt    %cs:nopaging_gdt_descr-__PAGE_OFFSET
        mov     $(__HYPERVISOR_DS),%ecx
        mov     %ecx,%ds
        mov     %ecx,%es
        ljmp    $(__HYPERVISOR_CS),$(1f)-__PAGE_OFFSET
1:      lss     stack_start-__PAGE_OFFSET,%esp

        /* Reset EFLAGS (subsumes CLI and CLD). */
	pushl	$0
	popf

        /* CPU type checks. We need P6+. */
        mov     $0x200000,%edx
        pushfl
        pop     %ecx
        and     %edx,%ecx
        jne     bad_cpu            # ID bit should be clear
        pushl   %edx
        popfl
        pushfl
        pop     %ecx
        and     %edx,%ecx
        je      bad_cpu            # ID bit should be set

        /* Set up CR0. */
        mov     %cr0,%ecx
        and     $0x00000011,%ecx   # save ET and PE
        or      $0x00050022,%ecx   # set AM, WP, NE and MP
        mov     %ecx,%cr0

        /* Set up FPU. */
        fninit
        
        /* Set up CR4, except global flag which Intel requires should be     */
        /* left until after paging is enabled (IA32 Manual Vol. 3, Sec. 2.5) */
        mov     %cr4,%ecx
        or      mmu_cr4_features-__PAGE_OFFSET,%ecx
        mov     %ecx,mmu_cr4_features-__PAGE_OFFSET
        and     $0x7f,%ecx /* disable GLOBAL bit */
        mov     %ecx,%cr4
                
#ifdef CONFIG_SMP
        /* Is this a non-boot processor? */
        cmp     $(SECONDARY_CPU_FLAG),%ebx
        jne     continue_boot_cpu
        
        call    start_paging
        lidt    idt_descr                        
        jmp     start_secondary
#endif
        
continue_boot_cpu:
	add     $__PAGE_OFFSET,%ebx
        push    %ebx /* Multiboot info struct */
        push    %eax /* Multiboot magic value */

        /* Initialize BSS (no nasty surprises!) */
        mov     $__bss_start-__PAGE_OFFSET,%edi
        mov     $_end-__PAGE_OFFSET,%ecx
        sub     %edi,%ecx
        xor     %eax,%eax
        rep     stosb

        /* Copy all modules (dom0 + initrd if present) out of the Xen heap */
        mov     (%esp),%eax
        cmp     $0x2BADB002,%eax
        jne     skip_dom0_copy
        sub     $__PAGE_OFFSET,%ebx          /* turn back into a phys addr */
        mov     0x14(%ebx),%edi              /* mbi->mods_count */
        dec     %edi                         /* mbi->mods_count-- */
        jb      skip_dom0_copy               /* skip if no modules */
        mov     0x18(%ebx),%eax              /* mbi->mods_addr */
        mov     (%eax),%ebx                  /* %ebx = mod[0]->mod_start */
        shl     $4,%edi                    
        add     %edi,%eax
        mov     0x4(%eax),%eax               /* %eax = mod[mod_count-1]->end */
        mov     %eax,%ecx
        sub     %ebx,%ecx                    /* %ecx = byte len of all mods */
        mov     $(MAX_DIRECTMAP_ADDRESS), %edi
        add     %ecx, %edi                   /* %edi = src + length */        
        shr     $2,%ecx                      /* %ecx = length/4 */
1:      sub     $4,%eax                      /* %eax = src, %edi = dst */
        sub     $4,%edi
        mov     (%eax),%ebx
        mov     %ebx,(%edi)
        loop 1b
skip_dom0_copy:              

        /* Initialize low and high mappings of all memory with 4MB pages */
        mov     $idle0_pg_table-__PAGE_OFFSET,%edi
        mov     $0x1e3,%eax                  /* PRESENT+RW+A+D+4MB+GLOBAL */
1:      mov     %eax,__PAGE_OFFSET>>20(%edi) /* high mapping */
        stosl                                /* low mapping */
        add     $(1<<L2_PAGETABLE_SHIFT),%eax
        cmp     $MAX_DIRECTMAP_ADDRESS+0x1e3,%eax
        jne     1b

        call    start_paging        
        call    setup_idt
        lidt    idt_descr
                
        /* Call into main C routine. This should never return.*/
       	call	cmain
        ud2     /* Force a panic (invalid opcode). */

start_paging:
        mov     $idle0_pg_table-__PAGE_OFFSET,%eax
        mov     %eax,%cr3
        mov     %cr0,%eax
        or      $0x80010000,%eax /* set PG and WP bits */
        mov     %eax,%cr0
        jmp     1f
1:      /* Install relocated selectors (FS/GS unused). */
        lgdt    gdt_descr
        mov     $(__HYPERVISOR_DS),%ecx
        mov     %ecx,%ds
        mov     %ecx,%es
        mov     %ecx,%ss
        ljmp    $(__HYPERVISOR_CS),$1f
1:      /* Paging enabled, so we can now enable GLOBAL mappings in CR4. */
        movl    mmu_cr4_features,%ecx
        movl    %ecx,%cr4
        /* Relocate ESP */
        add     $__PAGE_OFFSET,%esp
        /* Relocate EIP via return jump */
        pop     %ecx
        add     $__PAGE_OFFSET,%ecx
        jmp     *%ecx
    
            
/*** INTERRUPT INITIALISATION ***/
        
setup_idt:
        lea     ignore_int,%edx
        mov     $(__HYPERVISOR_CS << 16),%eax
        mov     %dx,%ax            /* selector = 0x0010 = cs */
        mov     $0x8E00,%dx        /* interrupt gate - dpl=0, present */

        lea     SYMBOL_NAME(idt_table),%edi
        mov     $256,%ecx
1:      mov     %eax,(%edi)
        mov     %edx,4(%edi)
        add     $8,%edi
        loop    1b
        ret

/* This is the default interrupt handler. */
int_msg:
        .asciz "Unknown interrupt\n"
        ALIGN
ignore_int:
        cld
        push    %eax
        push    %ecx
        push    %edx
        pushl   %es
        pushl   %ds
        mov     $(__HYPERVISOR_DS),%eax
        mov     %eax,%ds
        mov     %eax,%es
        pushl   $int_msg
        call    SYMBOL_NAME(printf)
1:      jmp     1b
        pop     %eax
        popl    %ds
        popl    %es
        pop     %edx
        pop     %ecx
        pop     %eax
        iret


bad_cpu_msg:
        .asciz  "Bad CPU type. Need P6+."
        ALIGN
bad_cpu:
/* NB. We assume the UART is set up correctly. */
        mov     $bad_cpu_msg,%esi
1:      lodsb
        test    %al,%al
        je      1f
        push    %eax
        call    putchar_serial
        add     $4,%esp
        jmp     1b
1:      jmp     1b
                   
        
/*** STACK LOCATION ***/
        
ENTRY(stack_start)
        .long SYMBOL_NAME(cpu0_stack) + 4000 - __PAGE_OFFSET
        .long __HYPERVISOR_DS
        
/*** DESCRIPTOR TABLES ***/

.globl SYMBOL_NAME(idt)
.globl SYMBOL_NAME(gdt)        

        ALIGN
        
        .word   0    
idt_descr:
	.word	256*8-1
SYMBOL_NAME(idt):
        .long	SYMBOL_NAME(idt_table)

        .word   0
gdt_descr:
	.word	(LAST_RESERVED_GDT_ENTRY*8)+7
SYMBOL_NAME(gdt):       
        .long   SYMBOL_NAME(gdt_table)	/* gdt base */

        .word   0
nopaging_gdt_descr:
        .word   (LAST_RESERVED_GDT_ENTRY*8)+7
        .long   SYMBOL_NAME(gdt_table)-__PAGE_OFFSET
        
        ALIGN
/* NB. Rings != 0 get access up to 0xFC400000. This allows access to the */
/*     machine->physical mapping table. Ring 0 can access all memory.    */
ENTRY(gdt_table)
        .fill FIRST_RESERVED_GDT_ENTRY,8,0
        .quad 0x0000000000000000     /* unused */
        .quad 0x00cf9a000000ffff     /* 0x0808 ring 0 4.00GB code at 0x0 */
        .quad 0x00cf92000000ffff     /* 0x0810 ring 0 4.00GB data at 0x0 */
        .quad 0x00cfba000000c3ff     /* 0x0819 ring 1 3.95GB code at 0x0 */
        .quad 0x00cfb2000000c3ff     /* 0x0821 ring 1 3.95GB data at 0x0 */
        .quad 0x00cffa000000c3ff     /* 0x082b ring 3 3.95GB code at 0x0 */
        .quad 0x00cff2000000c3ff     /* 0x0833 ring 3 3.95GB data at 0x0 */
        .quad 0x0000000000000000     /* unused                           */
        .fill 2*NR_CPUS,8,0          /* space for TSS and LDT per CPU    */

# The following adds 12kB to the kernel file size.
        .org 0x1000
ENTRY(idle0_pg_table)
        .org 0x2000
ENTRY(cpu0_stack)
        .org 0x3000
ENTRY(stext)
ENTRY(_stext)