1/**
2 * \file
3 * \brief Initializes the APs to long mode.
4 * This file starts an AP in real-mode, sets up the needed GDT, switches to
5 * protected-mode, enables paging and the long-mode and switches to long-mode.
6 * After that, it jumps to the entry point of the kernel.
7 */
8
9/*
10 * Copyright (c) 2007, 2008, 2010, ETH Zurich.
11 * All rights reserved.
12 *
13 * This file is distributed under the terms in the attached LICENSE file.
14 * If you do not find this file, copies can be found by writing to:
15 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
16 */
17
18#include <target/x86_64/offsets_target.h>
19#include <init.h>
20#include <x86.h>
21#include <target/x86_32/barrelfish_kpi/paging_target.h>
22#include <target/x86_64/barrelfish_kpi/paging_target.h>
23
24#define PROT_MODE_ENABLE 1
25#define PAGING_ENABLE 31
26#define PROT_CS 0x0018
27#define PROT_DS 0x0030
28#define LONG_MODE_CS 0x0008
29#define PAE 0x20
30#define LME 8
31#define BOOT_AP_KERNEL_SIZE 4096
32#define TABLE_BITS ((1 << 0) | (1 << 1) | (1 << 2))
33#define PAGE_BITS ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 7))
34
35/**
36 * Get doxygen to ignore the rest of this file, as it is very confused otherwise
37 * \cond
38 */
39
40        .text
41        .align 4096
42        .code16
43        .org X86_64_REAL_MODE_OFFSET
44
45// Start the 16bit real-mode code here
46
47        .global x86_64_start_ap
48x86_64_start_ap:
49        cli
50        mov $X86_64_REAL_MODE_SEGMENT,%ax
51        mov %ax,%ds
52        mov $(gdt_ptr - x86_64_start_ap),%si
53        lgdt (%si)
54
55        // Work around for M5's limited support for protected
56        // mode.  Once in protected mode it will treat instruction
57        // fetches as if ES is the segment selector.  Therefore
58        // we initialize it beforehand to the expected value.
59        mov $PROT_CS,%ax
60        mov %ax,%es
61
62        mov %cr0,%eax
63        or $PROT_MODE_ENABLE,%al
64        mov %eax,%cr0
65//      jmp PROT_CS:start_ap_pm
66        .byte 0x66
67        .byte 0xea
68        .long start_ap_pm - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET
69        .word PROT_CS
70
71
72// Start the 32bit protected-mode code here
73
74        .code32
75start_ap_pm:
76        // set up data segment
77        mov $PROT_DS,%eax
78        mov %eax,%ds
79
80        // acquire start-up lock
81        mov $(x86_64_init_ap_lock - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET),%esi
82start_ap_spin:
83        lock bts $0,(%esi)
84        jc start_ap_spin
85
86        // set up stack
87        mov $PROT_DS,%eax
88        mov %eax,%ss
89        mov $(start_ap_stack - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET),%esp
90
91        // Fill the four page tables with identity map
92        // eax = page table index, ebx = page table entry
93        mov $0, %ecx
94        mov $PAGE_BITS, %ebx // P, R/R, U/S & PS bit set
95rep_fill:
96        mov %ebx, init_ap_pdir - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET(%ecx)
97        movl $0, init_ap_pdir - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET + 4(%ecx)
98        add $0x200000, %ebx // 0x200000 is two megabytes
99        add $8, %ecx
100        cmp $4096 * 4, %ecx // 4*4096 / 8 = 2048, 2048*2 = 4GB
101        jne rep_fill
102
103 #ifdef __k1om__
104        mov $0, %ecx
105        mov $PAGE_BITS, %ebx
106 rep_mmio:
107        mov %ebx, init_ap_pdirMMIO - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET(%ecx)
108        movl $0x8, init_ap_pdirMMIO - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET + 4(%ecx)
109        add $0x200000, %ebx
110        add $8, %ecx
111        cmp $4096, %ecx
112        jne rep_mmio
113
114        // add mapping
115        mov $(init_ap_pdpt - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET), %ecx
116        add $256, %ecx
117        mov $(init_ap_pdirMMIO - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET), %ebx
118        addl $TABLE_BITS, %ebx
119        mov %ebx, (%ecx)
120 #endif
121
122        // enable page address extension
123        mov %cr4,%eax
124        or $PAE,%eax
125        mov %eax,%cr4
126
127        // set PML4 pointer to cr3. the right value is copied to the code
128        // here by start_aps_startall
129//      .global pml4_code_ptr
130// pml4_code_ptr:
131//      mov $0xffeeddcc,%eax
132        mov $(init_ap_pml4 - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET), %eax
133        mov %eax,%cr3
134
135        // enable long-mode by setting EFER.LME
136        mov $MSR_IA32_EFER,%ecx
137        rdmsr
138        bts $LME,%eax
139        wrmsr
140
141        // enable paging
142        mov %cr0,%eax
143        bts $PAGING_ENABLE,%eax
144        mov %eax,%cr0
145
146        // jmp to long-mode to the linear address corresponding the
147        // real mode segment REAL_MODE_SEGMENT
148        // jmp LONG_MODE_CS:start_ap_64
149        .byte 0xea
150        .long start_ap_64 - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET
151        .word LONG_MODE_CS
152
153// Start the 64bit long-mode code here
154
155        .code64
156start_ap_64:
157        // initialize bootup stack for the 64bit long mode
158        lea (start_ap_stack)(%rip), %rsp
159        // lea (kernel_stack + KERNEL_STACK_SIZE)(%rip), %rsp
160
161        //we should not return here after the call, but for any case, use a call
162        //instead of a jmp...
163
164        // jmp to the first C initialization function in the kernel
165        // the address is copied here to this code by start_aps_startall
166        // the jmp is a jmp to an absolute address. it is difficult to compute
167        // it here, because it is IP-relative and the IP here is
168        // REAL_MODE_LINEAR_OFFSET + some offset for _every_ AP to be started,
169        // independently of the final kernel location
170
171        lea (x86_64_init_ap_global)(%rip),%rbx
172        mov (%rbx),%rbx
173        mov $KERNEL_BOOT_MAGIC,%rax
174
175        lea (x86_64_init_ap_absolute_entry)(%rip),%rcx
176        mov (%rcx),%rcx
177        call *%rcx
178
179// We should never reach this location...
180loop_ap:
181        hlt
182        jmp loop_ap
183
184
185// Stack for 64bit mode
186        .align 16
187        .fill BOOT_AP_KERNEL_SIZE,1,0
188start_ap_stack:
189
190
191        .align 16
192gdt:
193        .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
194        .byte 0xff,0xff,0x00,0x00,0x00,0x9a,0xaf,0x00 // 64bit code segment, D _cleared_ => "16bit"
195        .byte 0xff,0xff,0x00,0x00,0x00,0x92,0xcf,0x00 // data
196        .byte 0xff,0xff,0x00,0x00,0x00,0x9a,0xcf,0x00 // 32bit code segment for protected-mode
197        .byte 0xff,0xff,0x00,0x80,0x0b,0x92,0xff,0x00 // screen
198        .byte 0xff,0xff,0x00,0x60,0x00,0x9a,0xcf,0x00 // segment at linear address 0x6000
199        .byte 0xff,0xff,0x00,0x00,0x00,0x92,0xaf,0x00 // stack segment in 64bit mode
200
201
202gdt_ptr:
203        .word gdt_ptr - gdt
204        .long gdt - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET
205        .long 0
206
207        // 4GByte identity mapped page-table using 2MByte pages
208        .align X86_64_BASE_PAGE_SIZE
209init_ap_pml4:
210        .quad (init_ap_pdpt - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET) + TABLE_BITS
211        // Fill the rest with zeroes
212        .fill X86_64_BASE_PAGE_SIZE - (1 * 8), 1, 0
213
214        .align X86_64_BASE_PAGE_SIZE
215init_ap_pdpt:
216        .quad (init_ap_pdir  - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET) + TABLE_BITS
217        .quad (init_ap_pdir2 - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET) + TABLE_BITS
218        .quad (init_ap_pdir3 - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET) + TABLE_BITS
219        .quad (init_ap_pdir4 - x86_64_start_ap + X86_64_REAL_MODE_LINEAR_OFFSET) + TABLE_BITS
220        // Fill the rest with zeroes
221        .fill X86_64_BASE_PAGE_SIZE - (4 * 8), 1, 0
222
223        .align X86_64_BASE_PAGE_SIZE
224init_ap_pdir:
225        .fill X86_64_BASE_PAGE_SIZE, 1, 0
226
227        .align X86_64_BASE_PAGE_SIZE
228init_ap_pdir2:
229        .fill X86_64_BASE_PAGE_SIZE, 1, 0
230
231        .align X86_64_BASE_PAGE_SIZE
232init_ap_pdir3:
233        .fill X86_64_BASE_PAGE_SIZE, 1, 0
234
235        .align X86_64_BASE_PAGE_SIZE
236init_ap_pdir4:
237        .fill X86_64_BASE_PAGE_SIZE, 1, 0
238#ifdef __k1om__
239        .align X86_64_BASE_PAGE_SIZE
240init_ap_pdirMMIO:
241        .fill X86_64_BASE_PAGE_SIZE, 1, 0
242#endif
243        // the absolute destination address to the first C function in the kernel.
244        // The address is copied to this variable by start_aps_startall.
245        .global x86_64_init_ap_absolute_entry
246x86_64_init_ap_absolute_entry:
247        .long 0
248        .long 0
249
250        .global x86_64_init_ap_global
251x86_64_init_ap_global:
252        .long 0
253
254        .global x86_64_init_ap_wait
255x86_64_init_ap_wait:
256        .long 0
257
258        .global x86_64_init_ap_lock
259x86_64_init_ap_lock:
260        .byte 0
261
262
263        .global x86_64_start_ap_end
264x86_64_start_ap_end:
265
266/**
267 * \endcond
268 */
269