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

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

Create the core/ sub-directory for x86_64.

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