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

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

First shot of the x86_64 port. It uses Multiboot V1. Most of the HAL functions
are empty, and several TSAR-specific files and comments remain; they will be
cleaned up later. The Makefile and LD script will come in another commit.

File size: 10.0 KB
Line 
1/*
2 * hal_boot.S - Kernel boot entry point
3 *
4 * Author        Maxime Villard (2017)
5 *               This code is inspired a lot from the NetBSD boot procedure,
6 *               written by Maxime Villard too.
7 *
8 * Copyright (c) UPMC Sorbonne Universites
9 *
10 * This file is part of ALMOS-kernel.
11 *
12 * ALMOS-kernel is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 2.0 of the License.
15 *
16 * ALMOS-kernel is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with ALMOS-kernel; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26#include <hal_boot.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/* 32bit version of PG_NX */
102#define PG_NX32 0x80000000
103
104#define RELOC(x)        ((x) - KERNBASE)
105
106        .globl  start_x86_64
107        .globl  L4paddr
108
109/*
110 * The multiboot header
111 */
112        .section .boot,"ax",@progbits
113multiboot_header:
114        .align  4
115        .long   MULTIBOOT_HEADER_MAGIC
116        .long   MULTIBOOT_HEADER_FLAGS
117        .long   CHECKSUM
118
119/*
120 * The variables used in the boot procedure.
121 */
122        .section .data
123        .align  4
124
125        .type   L4paddr, @object
126        L4paddr:        .quad   0       /* paddr of L4 */
127
128#define GDT64_LIMIT gdt64_end-gdt64_start-1
129/* Temporary gdt64, with base address in low memory */
130        .type   gdt64_lo, @object
131gdt64_lo:
132        .word   GDT64_LIMIT
133        .quad   RELOC(gdt64_start)
134.align 64
135
136/* Temporary gdt64, with base address in high memory */
137        .type   gdt64_hi, @object
138gdt64_hi:
139        .word   GDT64_LIMIT
140        .quad   gdt64_start
141.align 64
142#undef GDT64_LIMIT
143
144        .type   gdt64_start, @object
145gdt64_start:
146        .quad 0x0000000000000000        /* always empty */
147        .quad 0x00af9a000000ffff        /* kernel CS */
148        .quad 0x00cf92000000ffff        /* kernel DS */
149gdt64_end:
150
151        .type   farjmp64, @object
152farjmp64:
153        .long   RELOC(longmode)
154        .word   GSEL(GCODE_SEL, SEL_KPL)
155.align 64
156
157        /* Space for the temporary stack */
158        .size   tmpstk, tmpstk - .
159        .space  512
160tmpstk:
161
162        .text
163
164start_x86_64:
165        .code32
166
167        /* Warm boot */
168        movw    $0x1234,0x472
169
170        /* Make sure it is a multiboot-compliant bootloader. */
171        cmpl    $MULTIBOOT_INFO_MAGIC,%eax
172        jne     boot_panic
173
174        movl    $RELOC(tmpstk),%esp
175
176        /* Reset the PSL. */
177        pushl   $PSL_MBO
178        popfl
179
180/*
181 * There are four levels of pages in amd64: PML4 -> PDP -> PD -> PT. They will
182 * be referred to as: L4 -> L3 -> L2 -> L1.
183 *
184 * Virtual address space of the kernel:
185 * +---------------+------------+-----------------------------------+---
186 * | TEXT + RODATA | DATA + BSS | L4 -> PROC0 STK -> L3 -> L2 -> L1 |
187 * +---------------+------------+-----------------------------------+---
188 *                             (1)
189 *
190 * ---+-------------+
191 *    | ISA I/O MEM |
192 * ---+-------------+
193 *   (2)
194 *
195 * PROC0 STK is obviously not linked as a page level. It just happens to be
196 * caught between L4 and L3.
197 *
198 * (PROC0 STK + L4 + L3 + L2 + L1) is later referred to as BOOTSTRAP TABLES.
199 *
200 * ISA I/O MEM has no physical page allocated here, just virtual addresses.
201 *
202 * Important note: the kernel segments are properly 4k-aligned
203 * (see kern.ldscript), so there's no need to enforce alignment.
204 */
205
206        /* Find end of kernel image; brings us on (1). */
207        movl    $RELOC(__kernel_end),%edi
208
209        /* Align up for BOOTSTRAP TABLES. */
210        movl    %edi,%esi
211        addl    $PGOFSET,%esi
212        andl    $~PGOFSET,%esi
213
214        /* We are on the BOOTSTRAP TABLES. Save L4's physical address. */
215        movl    $RELOC(L4paddr),%ebp
216        movl    %esi,(%ebp)
217        movl    $0,4(%ebp)
218
219        /* Now, zero out the BOOTSTRAP TABLES (before filling them in). */
220        movl    %esi,%edi
221        xorl    %eax,%eax
222        cld
223        movl    $TABLESIZE,%ecx
224        shrl    $2,%ecx
225        rep
226        stosl                           /* copy eax -> edi */
227
228/*
229 * Build the page tables and levels. We go from L1 to L4, and link the levels
230 * together. Note: RELOC computes &addr - KERNBASE in 32 bits; the value can't
231 * be > 4G, or we can't deal with it anyway, since we are in 32bit mode.
232 */
233        /*
234         * Build L1.
235         */
236        leal    (PROC0_PTP1_OFF)(%esi),%ebx
237
238        /* Skip the area below the kernel text. */
239        movl    $(KERNTEXTOFF_LO - KERNBASE_LO),%ecx
240        shrl    $PGSHIFT,%ecx
241        fillkpt_blank
242
243        /* Map the kernel code RX. */
244        movl    $(KERNTEXTOFF_LO - KERNBASE_LO),%eax    /* start of TEXT */
245        movl    $RELOC(__kernel_data_start),%ecx
246        subl    %eax,%ecx
247        shrl    $PGSHIFT,%ecx
248        orl     $(PG_V|PG_KR),%eax
249        fillkpt
250
251        /* Map the kernel data RW. */
252        movl    $RELOC(__kernel_data_start),%eax
253        movl    $RELOC(__kernel_end),%ecx
254        subl    %eax,%ecx
255        shrl    $PGSHIFT,%ecx
256        orl     $(PG_V|PG_KW),%eax
257        fillkpt_nox
258
259        /* Map the BOOTSTRAP TABLES RW. */
260        movl    $RELOC(__kernel_end),%eax               /* start of BOOTSTRAP TABLES */
261        movl    $TABLESIZE,%ecx         /* length of BOOTSTRAP TABLES */
262        shrl    $PGSHIFT,%ecx
263        orl     $(PG_V|PG_KW),%eax
264        fillkpt_nox
265
266        /* We are on (2). Map ISA I/O MEM RW. */
267        movl    $IOM_BEGIN,%eax
268        movl    $IOM_SIZE,%ecx  /* size of ISA I/O MEM */
269        shrl    $PGSHIFT,%ecx
270        orl     $(PG_V|PG_KW/*|PG_N*/),%eax
271        fillkpt_nox
272
273        /*
274         * Build L2. Linked to L1.
275         */
276        leal    (PROC0_PTP2_OFF)(%esi),%ebx
277        leal    (PROC0_PTP1_OFF)(%esi),%eax
278        orl     $(PG_V|PG_KW),%eax
279        movl    $(NKL2_KIMG_ENTRIES+1),%ecx
280        fillkpt
281
282#if L2_SLOT_KERNBASE > 0
283        /* If needed, set up level 2 entries for actual kernel mapping */
284        leal    (PROC0_PTP2_OFF + L2_SLOT_KERNBASE * PDE_SIZE)(%esi),%ebx
285        leal    (PROC0_PTP1_OFF)(%esi),%eax
286        orl     $(PG_V|PG_KW),%eax
287        movl    $(NKL2_KIMG_ENTRIES+1),%ecx
288        fillkpt
289#endif
290
291        /*
292         * Build L3. Linked to L2.
293         */
294        leal    (PROC0_PTP3_OFF)(%esi),%ebx
295        leal    (PROC0_PTP2_OFF)(%esi),%eax
296        orl     $(PG_V|PG_KW),%eax
297        movl    $NKL3_KIMG_ENTRIES,%ecx
298        fillkpt
299
300#if L3_SLOT_KERNBASE > 0
301        /* If needed, set up level 3 entries for actual kernel mapping */
302        leal    (PROC0_PTP3_OFF + L3_SLOT_KERNBASE * PDE_SIZE)(%esi),%ebx
303        leal    (PROC0_PTP2_OFF)(%esi),%eax
304        orl     $(PG_V|PG_KW),%eax
305        movl    $NKL3_KIMG_ENTRIES,%ecx
306        fillkpt
307#endif
308
309        /*
310         * Build L4 for identity mapping. Linked to L3.
311         */
312        leal    (PROC0_PML4_OFF)(%esi),%ebx
313        leal    (PROC0_PTP3_OFF)(%esi),%eax
314        orl     $(PG_V|PG_KW),%eax
315        movl    $NKL4_KIMG_ENTRIES,%ecx
316        fillkpt
317
318        /* Set up L4 entries for actual kernel mapping */
319        leal    (PROC0_PML4_OFF + L4_SLOT_KERNBASE * PDE_SIZE)(%esi),%ebx
320        leal    (PROC0_PTP3_OFF)(%esi),%eax
321        orl     $(PG_V|PG_KW),%eax
322        movl    $NKL4_KIMG_ENTRIES,%ecx
323        fillkpt
324
325        /* Install recursive top level PDE (one entry) */
326        leal    (PROC0_PML4_OFF + PDIR_SLOT_PTE * PDE_SIZE)(%esi),%ebx
327        leal    (PROC0_PML4_OFF)(%esi),%eax
328        orl     $(PG_V|PG_KW),%eax
329        movl    $1,%ecx
330        fillkpt_nox
331
332        /*
333         * Startup checklist:
334         * 1. Enable PAE (and SSE while here).
335         */
336        movl    %cr4,%eax
337        orl     $(CR4_PAE|CR4_OSFXSR|CR4_OSXMMEXCPT),%eax
338        movl    %eax,%cr4
339
340        /*
341         * 2. Set Long Mode Enable in EFER. Also enable the syscall extensions,
342         *    and NOX.
343         */
344        movl    $MSR_EFER,%ecx
345        rdmsr
346        xorl    %eax,%eax       /* XXX */
347        orl     $(EFER_LME|EFER_SCE|EFER_NXE),%eax
348        wrmsr
349
350        /*
351         * 3. Load %cr3 with pointer to PML4.
352         */
353        movl    %esi,%eax
354        movl    %eax,%cr3
355
356        /*
357         * 4. Enable paging and the rest of it.
358         */
359        movl    %cr0,%eax
360        orl     $(CR0_PE|CR0_PG|CR0_NE|CR0_TS|CR0_MP|CR0_WP|CR0_AM),%eax
361        movl    %eax,%cr0
362        jmp     compat
363compat:
364
365        /*
366         * 5. Not quite done yet, we're now in a compatibility segment, in
367         *    legacy mode. We must jump to a long mode segment. Need to set up
368         *    a temporary GDT with a long mode segment in it to do that.
369         */
370        movl    $RELOC(gdt64_lo),%eax
371        lgdt    (%eax)
372        movl    $RELOC(farjmp64),%eax
373        ljmp    *(%eax)
374
375        .code64
376longmode:
377        /*
378         * 6. Finally, we're in long mode. However, we're still in the identity
379         *    mapped area (could not jump out of that earlier because it would
380         *    have been a > 32bit jump). We can do that now, so here we go.
381         */
382        movabsq $longmode_hi,%rax
383        jmp     *%rax /* XXX XXX: <------ this instruction faults */
384
385longmode_hi:
386        /*
387         * We left the identity mapped area. Base address of
388         * the temporary gdt64 should now be in high memory.
389         */
390        movq    $RELOC(gdt64_hi),%rax
391        lgdt    (%rax)
392
393        /*
394         * We have arrived. There's no need anymore for the identity mapping in
395         * low memory, remove it.
396         */
397        movq    $KERNBASE,%r8
398
399
400        /*
401         * STOP HERE FOR NOW
402         */
4031:      nop
404        jmp     1b
405
406
407boot_panic:
408        jmp     boot_panic
409
Note: See TracBrowser for help on using the repository browser.