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