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_32/offsets_target.h>
19#include <init.h>
20#include <x86.h>
21
22#define PROT_MODE_ENABLE 1
23#define PAGING_ENABLE 31
24#define PROT_CS 8
25#define PROT_DS 16
26#define PAE 0x20
27#define LME 8
28#define BOOT_AP_KERNEL_SIZE 4096
29
30/**
31 * Get doxygen to ignore the rest of this file, as it is very confused otherwise
32 * \cond
33 */
34
35	.text
36        .align 4096
37	.code16
38	.org X86_32_REAL_MODE_OFFSET
39
40
41//start the 16bit real-mode code here
42
43	.global x86_32_start_ap
44x86_32_start_ap:
45        cli
46	mov $X86_32_REAL_MODE_SEGMENT,%ax
47	mov %ax,%ds
48	mov $(x86_32_init_ap_lock - x86_32_start_ap),%si
49start_ap_spin:
50	xor %ax,%ax
51	lock bts %ax,(%si)
52	jc start_ap_spin
53	mov $(gdt_ptr - x86_32_start_ap),%esi
54	lgdt (%esi)
55	mov %cr0,%eax
56	or $PROT_MODE_ENABLE,%al
57	mov %eax,%cr0
58//	jmp PROT_CS:start_ap_pm
59	.byte 0x66
60	    .byte 0xea
61	    .long start_ap_pm - x86_32_start_ap + X86_32_REAL_MODE_LINEAR_OFFSET
62	    .word PROT_CS
63
64//start the 32bit protected-mode code here
65
66        .code32
67start_ap_pm:
68        // set up stack
69	mov $PROT_DS,%eax
70	mov %eax,%ss
71	mov %eax,%ds
72	mov %eax,%es
73	mov %eax,%fs
74	mov %eax,%gs
75	mov $(start_ap_stack - x86_32_start_ap + X86_32_REAL_MODE_LINEAR_OFFSET),%esp
76
77        // jmp to the first C initialization function in the kernel
78	// the address is copied here to this code by start_aps_startall
79	// the jmp is a jmp to an absolute address. it is difficult to compute
80	// it here, because it is IP-relative and the IP here is
81	// REAL_MODE_LINEAR_OFFSET + some offset for _every_ AP to be started,
82	// independently of the final kernel location
83	mov x86_32_init_ap_global - x86_32_start_ap + X86_32_REAL_MODE_LINEAR_OFFSET,%ebx
84	mov $KERNEL_BOOT_MAGIC,%eax
85        mov x86_32_init_ap_absolute_entry - x86_32_start_ap + X86_32_REAL_MODE_LINEAR_OFFSET,%ecx
86
87//we should not return here after the call, but for any case, use a call
88//instead of a jmp...
89	call *%ecx
90
91//we should never reach this location...
92loop_ap:
93        hlt
94	jmp loop_ap
95
96
97//stack for 64bit mode
98
99        .align 16
100        .fill BOOT_AP_KERNEL_SIZE,1,0
101start_ap_stack:
102
103
104        .align 16
105gdt:
106        .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
107        .byte 0xff,0xff,0x00,0x00,0x00,0x9a,0xcf,0x00 // 32bit code segment for protected-mode
108        .byte 0xff,0xff,0x00,0x00,0x00,0x93,0xcf,0x00 // 32bit data segment for protected-mode
109
110gdt_ptr:
111        .word gdt_ptr - gdt
112	.long gdt - x86_32_start_ap + X86_32_REAL_MODE_LINEAR_OFFSET
113	.long 0
114
115        // the absolute destination address to the first C function in the kernel.
116	// The address is copied to this variable by start_aps_startall.
117	.global x86_32_init_ap_absolute_entry
118x86_32_init_ap_absolute_entry:
119        .long 0
120	.long 0
121
122        .global x86_32_init_ap_global
123x86_32_init_ap_global:
124        .long 0
125	.long 0
126
127
128        .global x86_32_init_ap_wait
129x86_32_init_ap_wait:
130        .long 0
131
132	.global x86_32_init_ap_apic_id
133x86_32_init_ap_apic_id:
134        .byte 0
135
136        .global x86_32_init_ap_lock
137x86_32_init_ap_lock:
138        .byte 0
139
140
141        .global x86_32_start_ap_end
142x86_32_start_ap_end:
143
144/**
145 * \endcond
146 */
147