source: trunk/hal/x86_64/hal_boot.S @ 29

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

Update. The kernel now enables the GDT/IDT, and has trap entries. A
x86_printf function is added for debugging purposes only. The new Makefile
will come in another commit.

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