source: trunk/hal/x86_64/core/hal_boot.S @ 473

Last change on this file since 473 was 368, checked in by max@…, 7 years ago

Switch the VM space, and explicitly disable the FPU.

File size: 12.0 KB
Line 
1/*
2 * hal_boot.S - Kernel boot entry point
3 *
4 * Copyright (c) 2017 Maxime Villard
5 * This code is inspired a lot from the NetBSD boot procedure, written by
6 * Maxime Villard too. XXX copyright
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#define x86_ASM
25#include <hal_boot.h>
26#include <hal_multiboot.h>
27#include <hal_register.h>
28#include <hal_kentry.h>
29#include <hal_segmentation.h>
30
31#define MULTIBOOT_HEADER_MAGIC  0x1BADB002
32#define MULTIBOOT_HEADER_FLAGS  0x00000000
33#define MULTIBOOT_INFO_MAGIC    0x2BADB002
34#define CHECKSUM -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
35
36#if L2_SLOT_KERNBASE > 0
37#define TABLE_L2_ENTRIES (2 * (NKL2_KIMG_ENTRIES + 1))
38#else
39#define TABLE_L2_ENTRIES (NKL2_KIMG_ENTRIES + 1)
40#endif
41
42#if L3_SLOT_KERNBASE > 0
43#define TABLE_L3_ENTRIES (2 * NKL3_KIMG_ENTRIES)
44#else
45#define TABLE_L3_ENTRIES NKL3_KIMG_ENTRIES
46#endif
47
48#define PROC0_PML4_OFF  0
49#define PROC0_STK_OFF   (PROC0_PML4_OFF + 1 * PAGE_SIZE)
50#define PROC0_PTP3_OFF  (PROC0_STK_OFF + STKPAGES * PAGE_SIZE)
51#define PROC0_PTP2_OFF  (PROC0_PTP3_OFF + NKL4_KIMG_ENTRIES * PAGE_SIZE)
52#define PROC0_PTP1_OFF  (PROC0_PTP2_OFF + TABLE_L3_ENTRIES * PAGE_SIZE)
53#define TABLESIZE \
54  ((NKL4_KIMG_ENTRIES + TABLE_L3_ENTRIES + TABLE_L2_ENTRIES + 1 + STKPAGES) \
55    * PAGE_SIZE)
56
57/*
58 * fillkpt - Fill in a kernel page table
59 *      eax = pte (page frame | control | status)
60 *      ebx = page table address
61 *      ecx = number of pages to map
62 *
63 * Each entry is 8 (PDE_SIZE) bytes long: we must set the 4 upper bytes to 0.
64 */
65#define fillkpt \
66        cmpl    $0,%ecx                 ;       /* zero-sized? */       \
67        je      2f                      ; \
681:      movl    $0,(PDE_SIZE-4)(%ebx)   ;       /* upper 32 bits: 0 */  \
69        movl    %eax,(%ebx)             ;       /* store phys addr */   \
70        addl    $PDE_SIZE,%ebx          ;       /* next PTE/PDE */      \
71        addl    $PAGE_SIZE,%eax         ;       /* next phys page */    \
72        loop    1b                      ; \
732:                                      ;
74
75/*
76 * fillkpt_nox - Same as fillkpt, but sets the NX/XD bit.
77 */
78#define fillkpt_nox \
79        cmpl    $0,%ecx                 ;       /* zero-sized? */       \
80        je      2f                      ; \
811:      movl    $PG_NX32,(PDE_SIZE-4)(%ebx);    /* upper 32 bits: NX */ \
82        movl    %eax,(%ebx)             ;       /* store phys addr */   \
83        addl    $PDE_SIZE,%ebx          ;       /* next PTE/PDE */      \
84        addl    $PAGE_SIZE,%eax         ;       /* next phys page */    \
85        loop    1b                      ; \
862:                                      ;
87
88/*
89 * fillkpt_blank - Fill in a kernel page table with blank entries
90 *      ebx = page table address
91 *      ecx = number of pages to map
92 */
93#define fillkpt_blank   \
94        cmpl    $0,%ecx                 ;       /* zero-sized? */       \
95        je      2f                      ; \
961:      movl    $0,(PDE_SIZE-4)(%ebx)   ;       /* upper 32 bits: 0 */  \
97        movl    $0,(%ebx)               ;       /* lower 32 bits: 0 */  \
98        addl    $PDE_SIZE,%ebx          ;       /* next PTE/PDE */      \
99        loop    1b                      ; \
1002:                                      ;
101
102/*
103 * killkpt - Destroy a kernel page table (long mode)
104 *      rbx = page table address
105 *      rcx = number of pages to destroy
106 */
107#define killkpt \
1081:      movq    $0,(%rbx)       ; \
109        addq    $PDE_SIZE,%rbx  ; \
110        loop    1b              ;
111
112/* 32bit version of PG_NX */
113#define PG_NX32 0x80000000
114
115#define RELOC(x)        ((x) - KERNBASE)
116
117        .globl  start_x86_64
118        .globl  L4paddr
119        .globl  iom_base
120
121/*
122 * The multiboot header
123 */
124        .section .boot,"ax",@progbits
125multiboot_header:
126        .align  4
127        .long   MULTIBOOT_HEADER_MAGIC
128        .long   MULTIBOOT_HEADER_FLAGS
129        .long   CHECKSUM
130
131/*
132 * The variables used in the boot procedure.
133 */
134        .section .data
135        .align  4
136
137        .type   L4paddr, @object
138        L4paddr:        .quad   0       /* paddr of L4 */
139
140        .type   iom_base, @object
141        iom_base:       .quad   0       /* virt. addr. of ISA I/O MEM */
142
143
144#define GDT64_LIMIT gdt64_end-gdt64_start-1
145/* Temporary gdt64, with base address in low memory */
146        .type   gdt64_lo, @object
147gdt64_lo:
148        .word   GDT64_LIMIT
149        .quad   RELOC(gdt64_start)
150.align 64
151
152/* Temporary gdt64, with base address in high memory */
153        .type   gdt64_hi, @object
154gdt64_hi:
155        .word   GDT64_LIMIT
156        .quad   gdt64_start
157.align 64
158#undef GDT64_LIMIT
159
160        .type   gdt64_start, @object
161gdt64_start:
162        .quad 0x0000000000000000        /* always empty */
163        .quad 0x00af9a000000ffff        /* kernel CS */
164        .quad 0x00cf92000000ffff        /* kernel DS */
165gdt64_end:
166
167        .type   farjmp64, @object
168farjmp64:
169        .long   RELOC(longmode)
170        .word   GDT_FIXED_SEL(GDT_KCODE_SEL, SEL_KPL)
171.align 64
172
173        /* Space for the temporary stack */
174        .size   tmpstk, tmpstk - .
175        .space  512
176tmpstk:
177
178        .text
179
180start_x86_64:
181        .code32
182
183        /* Warm boot */
184        movw    $0x1234,0x472
185        cld
186
187        /* Make sure it is a multiboot-compliant bootloader. */
188        cmpl    $MULTIBOOT_INFO_MAGIC,%eax
189        je      1f
190        ret     /* what to do? */
1911:
192
193        movl    $RELOC(tmpstk),%esp
194
195        /* Reset the PSL. */
196        pushl   $PSL_MBO
197        popfl
198
199        /*
200         * Copy the various multiboot structures
201         */
202        movl    %ebx,%esi                       /* src */
203        movl    $RELOC(mb_info),%edi            /* dst */
204        movl    $MULTIBOOT_INFO_SIZE,%ecx       /* len */
205        rep
206        movsb   /* copy esi -> edi */
207
208        testl   $MULTIBOOT_INFO_HAS_LOADER_NAME,MB_MI_FLAGS(%ebx)
209        jz      1f
210        movl    MB_MI_LOADER_NAME(%ebx),%esi    /* src */
211        movl    $RELOC(mb_loader_name),%edi     /* dst */
212copy_loop:
213        cmpb    $0,(%esi)
214        je      copy_end
215        movsb   /* copy esi -> edi */
216        jmp     copy_loop
217copy_end:
218        movsb   /* copy esi -> edi */
2191:
220
221        testl   $MULTIBOOT_INFO_HAS_MMAP,MB_MI_FLAGS(%ebx)
222        jz      1f
223        movl    MB_MI_MMAP_ADDR(%ebx),%esi      /* src */
224        movl    $RELOC(mb_mmap),%edi            /* dst */
225        movl    MB_MI_MMAP_LENGTH(%ebx),%ecx    /* len */
226
227        rep
228        movsb   /* copy esi -> edi */
2291:
230
231        testl   $MULTIBOOT_INFO_HAS_CMDLINE,MB_MI_FLAGS(%ebx)
232        jz      1f
233        movl    MB_MI_CMDLINE(%ebx),%esi        /* src */
234        movl    $RELOC(mb_cmdline),%edi         /* dst */
235        movl    $PAGE_SIZE,%ecx /* len */
236
237        rep
238        movsb   /* copy esi -> edi */
2391:
240
241/*
242 * There are four levels of pages in amd64: PML4 -> PDP -> PD -> PT. They will
243 * be referred to as: L4 -> L3 -> L2 -> L1.
244 *
245 * Virtual address space of the kernel:
246 * +---------------+------+-----------------------------------+-------------+
247 * | TEXT + RODATA | DATA | L4 -> PROC0 STK -> L3 -> L2 -> L1 | ISA I/O MEM |
248 * +---------------+------+-----------------------------------+-------------+
249 *                       (1)                                 (2)
250 *
251 * PROC0 STK is obviously not linked as a page level. It just happens to be
252 * caught between L4 and L3.
253 *
254 * (PROC0 STK + L4 + L3 + L2 + L1) is later referred to as BOOTSTRAP TABLES.
255 *
256 * ISA I/O MEM has no physical page allocated here, just virtual addresses.
257 *
258 * Important note: the kernel segments are properly 4k-aligned
259 * (see kernel_x86.ld), so there's no need to enforce alignment.
260 */
261
262        /* Find end of kernel image; brings us on (1). */
263        movl    $RELOC(__kernel_end),%edi
264
265        /* Align up for BOOTSTRAP TABLES. */
266        movl    %edi,%esi
267        addl    $PGOFSET,%esi
268        andl    $~PGOFSET,%esi
269
270        /* We are on the BOOTSTRAP TABLES. Save L4's physical address. */
271        movl    $RELOC(L4paddr),%ebp
272        movl    %esi,(%ebp)
273        movl    $0,4(%ebp)
274
275        /* Now, zero out the BOOTSTRAP TABLES (before filling them in). */
276        movl    %esi,%edi
277        xorl    %eax,%eax
278        cld
279        movl    $TABLESIZE,%ecx
280        shrl    $2,%ecx
281        rep
282        stosl                           /* copy eax -> edi */
283
284/*
285 * Build the page tables and levels. We go from L1 to L4, and link the levels
286 * together. Note: RELOC computes &addr - KERNBASE in 32 bits; the value can't
287 * be > 4G, or we can't deal with it anyway, since we are in 32bit mode.
288 */
289        /*
290         * Build L1.
291         */
292        leal    (PROC0_PTP1_OFF)(%esi),%ebx
293
294        /* Skip the area below the kernel text. */
295        movl    $(KERNTEXTOFF_LO - KERNBASE_LO),%ecx
296        shrl    $PGSHIFT,%ecx
297        fillkpt_blank
298
299        /* Map the kernel code RX. */
300        movl    $(KERNTEXTOFF_LO - KERNBASE_LO),%eax    /* start of TEXT */
301        movl    $RELOC(__kernel_data_start),%ecx
302        subl    %eax,%ecx
303        shrl    $PGSHIFT,%ecx
304        orl     $(PG_V|PG_KR|PG_G),%eax
305        fillkpt
306
307        /* Map the kernel data RW. */
308        movl    $RELOC(__kernel_data_start),%eax
309        movl    $RELOC(__kernel_end),%ecx
310        subl    %eax,%ecx
311        shrl    $PGSHIFT,%ecx
312        orl     $(PG_V|PG_KW|PG_G),%eax
313        fillkpt_nox
314
315        /* Map the BOOTSTRAP TABLES RW. */
316        movl    $RELOC(__kernel_end),%eax               /* start of BOOTSTRAP TABLES */
317        movl    $TABLESIZE,%ecx         /* length of BOOTSTRAP TABLES */
318        shrl    $PGSHIFT,%ecx
319        orl     $(PG_V|PG_KW|PG_G),%eax
320        fillkpt_nox
321
322        /* We are on (2). Map ISA I/O MEM RW. */
323        movl    $IOM_BEGIN,%eax
324        movl    $IOM_SIZE,%ecx  /* size of ISA I/O MEM */
325        shrl    $PGSHIFT,%ecx
326        orl     $(PG_V|PG_KW|PG_G),%eax
327        fillkpt_nox
328
329        /*
330         * Build L2. Linked to L1.
331         */
332        leal    (PROC0_PTP2_OFF)(%esi),%ebx
333        leal    (PROC0_PTP1_OFF)(%esi),%eax
334        orl     $(PG_V|PG_KW),%eax
335        movl    $(NKL2_KIMG_ENTRIES+1),%ecx
336        fillkpt
337
338#if L2_SLOT_KERNBASE > 0
339        /* If needed, set up level 2 entries for actual kernel mapping */
340        leal    (PROC0_PTP2_OFF + L2_SLOT_KERNBASE * PDE_SIZE)(%esi),%ebx
341        leal    (PROC0_PTP1_OFF)(%esi),%eax
342        orl     $(PG_V|PG_KW),%eax
343        movl    $(NKL2_KIMG_ENTRIES+1),%ecx
344        fillkpt
345#endif
346
347        /*
348         * Build L3. Linked to L2.
349         */
350        leal    (PROC0_PTP3_OFF)(%esi),%ebx
351        leal    (PROC0_PTP2_OFF)(%esi),%eax
352        orl     $(PG_V|PG_KW),%eax
353        movl    $NKL3_KIMG_ENTRIES,%ecx
354        fillkpt
355
356#if L3_SLOT_KERNBASE > 0
357        /* If needed, set up level 3 entries for actual kernel mapping */
358        leal    (PROC0_PTP3_OFF + L3_SLOT_KERNBASE * PDE_SIZE)(%esi),%ebx
359        leal    (PROC0_PTP2_OFF)(%esi),%eax
360        orl     $(PG_V|PG_KW),%eax
361        movl    $NKL3_KIMG_ENTRIES,%ecx
362        fillkpt
363#endif
364
365        /*
366         * Build L4 for identity mapping. Linked to L3.
367         */
368        leal    (PROC0_PML4_OFF)(%esi),%ebx
369        leal    (PROC0_PTP3_OFF)(%esi),%eax
370        orl     $(PG_V|PG_KW),%eax
371        movl    $NKL4_KIMG_ENTRIES,%ecx
372        fillkpt
373
374        /* Set up L4 entries for actual kernel mapping */
375        leal    (PROC0_PML4_OFF + L4_SLOT_KERNBASE * PDE_SIZE)(%esi),%ebx
376        leal    (PROC0_PTP3_OFF)(%esi),%eax
377        orl     $(PG_V|PG_KW),%eax
378        movl    $NKL4_KIMG_ENTRIES,%ecx
379        fillkpt
380
381        /* Install recursive top level PDE (one entry) */
382        leal    (PROC0_PML4_OFF + PDIR_SLOT_PTE * PDE_SIZE)(%esi),%ebx
383        leal    (PROC0_PML4_OFF)(%esi),%eax
384        orl     $(PG_V|PG_KW),%eax
385        movl    $1,%ecx
386        fillkpt_nox
387
388        /*
389         * Startup checklist:
390         * 1. Enable PAE (and SSE while here).
391         */
392        movl    %cr4,%eax
393        orl     $(CR4_PAE|CR4_OSFXSR|CR4_OSXMMEXCPT|CR4_PGE),%eax
394        movl    %eax,%cr4
395
396        /*
397         * 2. Set Long Mode Enable in EFER. Also enable the syscall extensions,
398         *    and NOX.
399         */
400        movl    $MSR_EFER,%ecx
401        rdmsr
402        xorl    %eax,%eax       /* XXX */
403        orl     $(EFER_LME|EFER_SCE|EFER_NXE),%eax
404        wrmsr
405
406        /*
407         * 3. Load %cr3 with pointer to PML4.
408         */
409        movl    %esi,%eax
410        movl    %eax,%cr3
411
412        /*
413         * 4. Enable paging and the rest of it.
414         */
415        movl    %cr0,%eax
416        orl     $(CR0_PE|CR0_PG|CR0_NE|CR0_TS|CR0_MP|CR0_WP|CR0_AM),%eax
417        andl    $~(CR0_CD|CR0_NW),%eax
418        orl     $(CR0_EM),%eax  /* FPU not implemented yet */
419        movl    %eax,%cr0
420        jmp     compat
421compat:
422
423        /*
424         * 5. Not quite done yet, we're now in a compatibility segment, in
425         *    legacy mode. We must jump to a long mode segment. Need to set up
426         *    a temporary GDT with a long mode segment in it to do that.
427         */
428        movl    $RELOC(gdt64_lo),%eax
429        lgdt    (%eax)
430        movl    $RELOC(farjmp64),%eax
431        ljmp    *(%eax)
432
433        .code64
434longmode:
435        /*
436         * 6. Finally, we're in long mode. However, we're still in the identity
437         *    mapped area (could not jump out of that earlier because it would
438         *    have been a > 32bit jump). We can do that now, so here we go.
439         */
440        movabsq $longmode_hi,%rax
441        jmp     *%rax
442
443longmode_hi:
444        /*
445         * We left the identity mapped area. Base address of
446         * the temporary gdt64 should now be in high memory.
447         */
448        movq    $RELOC(gdt64_hi),%rax
449        lgdt    (%rax)
450
451        /*
452         * We have arrived. There's no need anymore for the identity mapping in
453         * low memory, remove it.
454         */
455        movq    $KERNBASE,%r8
456
457#if L2_SLOT_KERNBASE > 0
458        movq    $(NKL2_KIMG_ENTRIES+1),%rcx
459        leaq    (PROC0_PTP2_OFF)(%rsi),%rbx     /* old, phys address */
460        addq    %r8,%rbx                        /* new, virt address */
461        killkpt
462#endif
463
464#if L3_SLOT_KERNBASE > 0
465        movq    $NKL3_KIMG_ENTRIES,%rcx
466        leaq    (PROC0_PTP3_OFF)(%rsi),%rbx     /* old, phys address */
467        addq    %r8,%rbx                        /* new, virt address */
468        killkpt
469#endif
470
471        movq    $NKL4_KIMG_ENTRIES,%rcx
472        leaq    (PROC0_PML4_OFF)(%rsi),%rbx     /* old, phys address of PML4 */
473        addq    %r8,%rbx                        /* new, virt address of PML4 */
474        killkpt
475
476        /* Save the virtual address of ISA I/O MEM. */
477        movq    $(TABLESIZE+KERNBASE),%rdx
478        addq    %rsi,%rdx
479        movq    %rdx,iom_base(%rip)
480
481        /* Set up bootstrap stack. */
482        leaq    (PROC0_STK_OFF)(%rsi),%rax
483        addq    %r8,%rax
484        leaq    (STKSIZE)(%rax),%rsp
485        xorq    %rbp,%rbp                       /* mark end of frames */
486
487        xorw    %ax,%ax
488        movw    %ax,%gs
489        movw    %ax,%fs
490
491        /* The first physical page available. */
492        leaq    (TABLESIZE)(%rsi),%rdi
493
494        call    init_x86_64
495
Note: See TracBrowser for help on using the repository browser.