8fdd75fb1776c68958a8b19636c16791a019668b
[coreboot.git] / src / cpu / x86 / smm / smmhandler_tseg.S
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2008 coresystems GmbH
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; version 2 of
9  * the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19  * MA 02110-1301 USA
20  */
21
22 /*
23  * +--------------------------------+ 0xffff
24  * |  Save State Map Node 0         |
25  * |  Save State Map Node 1         |
26  * |  Save State Map Node 2         |
27  * |  Save State Map Node 3         |
28  * |  ...                           |
29  * +--------------------------------+ 0xf000
30  * |                                |
31  * |                                |
32  * | EARLY DATA (lock, vectors)     |
33  * +--------------------------------+ 0x8400
34  * | SMM Entry Node 0 (+ stack)     |
35  * +--------------------------------+ 0x8000
36  * | SMM Entry Node 1 (+ stack)     |
37  * | SMM Entry Node 2 (+ stack)     |
38  * | SMM Entry Node 3 (+ stack)     |
39  * | ...                            |
40  * +--------------------------------+ 0x7400
41  * |                                |
42  * | SMM Handler                    |
43  * |                                |
44  * +--------------------------------+ TSEG
45  *
46  */
47
48 #define LAPIC_ID        0xfee00020
49 #define SMM_STACK_SIZE  (0x400 - 0x10)
50
51 /* Values for the xchg lock */
52 #define SMI_LOCKED      0
53 #define SMI_UNLOCKED    1
54
55 #define __PRE_RAM__
56 #if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE
57 #include <northbridge/intel/sandybridge/sandybridge.h>
58 #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
59 #else
60 #error "Northbridge must define TSEG_BAR."
61 #endif
62
63 /* initially SMM is some sort of real mode. Let gcc know
64  * how to treat the SMM handler stub
65  */
66
67 .section ".handler", "a", @progbits
68
69 .code16
70
71 /**
72  * SMM code to enable protected mode and jump to the
73  * C-written function void smi_handler(u32 smm_revision)
74  *
75  * All the bad magic is not all that bad after all.
76  */
77 smm_handler_start:
78         movl    $(TSEG_BAR), %eax       /* Get TSEG base from PCIE */
79         addr32  movl (%eax), %edx       /* Save TSEG_BAR in %edx */
80         andl    $~1, %edx               /* Remove lock bit */
81
82         /* Obtain lock */
83         movl    %edx, %ebx
84         addl    $(smm_lock), %ebx
85         movw    $SMI_LOCKED, %ax
86         addr32  xchg %ax, (%ebx)
87         cmpw    $SMI_UNLOCKED, %ax
88
89         /* Proceed if we got the lock */
90         je      smm_check_prot_vector
91
92         /* If we did not get the lock, wait for release */
93 wait_for_unlock:
94         addr32  movw (%ebx), %ax
95         cmpw    $SMI_LOCKED, %ax
96         je      wait_for_unlock
97         rsm
98
99 smm_check_prot_vector:
100         /* See if we need to adjust protected vector */
101         movl    %edx, %eax
102         addl    $(smm_prot_vector), %eax
103         addr32  movl (%eax), %ebx
104         cmpl    $(smm_prot_start), %ebx
105         jne     smm_check_gdt_vector
106
107         /* Adjust vector with TSEG offset */
108         addl    %edx, %ebx
109         addr32  movl %ebx, (%eax)
110
111 smm_check_gdt_vector:
112         /* See if we need to adjust GDT vector */
113         movl    %edx, %eax
114         addl    $(smm_gdt_vector + 2), %eax
115         addr32  movl (%eax), %ebx
116         cmpl    $(smm_gdt - smm_handler_start), %ebx
117         jne     smm_load_gdt
118
119         /* Adjust vector with TSEG offset */
120         addl    %edx, %ebx
121         addr32  movl %ebx, (%eax)
122
123 smm_load_gdt:
124         movl    $(smm_gdt_vector), %ebx
125         addl    %edx, %ebx        /* TSEG base in %edx */
126         data32  lgdt (%ebx)
127
128         movl    %cr0, %eax
129         andl    $0x1FFAFFD1, %eax /* CD,NW,PG,AM,WP,NE,TS,EM,MP = 0 */
130         orl     $0x1, %eax        /* PE = 1 */
131         movl    %eax, %cr0
132
133         /* Enable protected mode */
134         movl    $(smm_prot_vector), %eax
135         addl    %edx, %eax
136         data32  ljmp *(%eax)
137
138 .code32
139 smm_prot_start:
140         /* Use flat data segment */
141         movw    $0x10, %ax
142         movw    %ax, %ds
143         movw    %ax, %es
144         movw    %ax, %ss
145         movw    %ax, %fs
146         movw    %ax, %gs
147
148         /* Get this CPU's LAPIC ID */
149         movl    $LAPIC_ID, %esi
150         movl    (%esi), %ecx
151         shr     $24, %ecx
152
153         /* calculate stack offset by multiplying the APIC ID
154          * by 1024 (0x400), and save that offset in ebp.
155          */
156         shl     $10, %ecx
157         movl    %ecx, %ebp
158
159         /* We put the stack for each core right above
160          * its SMM entry point. Core 0 starts at SMM_BASE + 0x8000,
161          * we spare 0x10 bytes for the jump to be sure.
162          */
163         movl    $0x8010, %eax   /* core 0 address */
164         addl    %edx, %eax      /* addjust for TSEG */
165         subl    %ecx, %eax      /* subtract offset, see above */
166         movl    %eax, %ebx      /* Save bottom of stack in ebx */
167
168         /* clear stack */
169         cld
170         movl    %eax, %edi
171         movl    $(SMM_STACK_SIZE >> 2), %ecx
172         xorl    %eax, %eax
173         rep     stosl
174
175         /* set new stack */
176         addl    $SMM_STACK_SIZE, %ebx
177         movl    %ebx, %esp
178
179         /* Get SMM revision */
180         movl    $0xfefc, %ebx   /* core 0 address */
181         addl    %edx, %ebx      /* addjust for TSEG */
182         subl    %ebp, %ebx      /* subtract core X offset */
183         movl    (%ebx), %eax
184         pushl   %eax
185
186         /* Call 32bit C handler */
187         call    smi_handler
188
189         /* Release lock */
190         movl    $(TSEG_BAR), %eax       /* Get TSEG base from PCIE */
191         movl    (%eax), %ebx            /* Save TSEG_BAR in %ebx */
192         andl    $~1, %ebx               /* Remove lock bit */
193         addl    $(smm_lock), %ebx
194         movw    $SMI_UNLOCKED, %ax
195         xchg    %ax, (%ebx)
196
197         /* To return, just do rsm. It will "clean up" protected mode */
198         rsm
199
200 smm_gdt:
201         /* The first GDT entry can not be used. Keep it zero */
202         .long   0x00000000, 0x00000000
203
204         /* gdt selector 0x08, flat code segment */
205         .word   0xffff, 0x0000
206         .byte   0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */
207
208         /* gdt selector 0x10, flat data segment */
209         .word   0xffff, 0x0000
210         .byte   0x00, 0x93, 0xcf, 0x00
211
212 smm_gdt_end:
213
214 .section ".earlydata", "a", @progbits
215
216 .code16
217
218 .align  4, 0xff
219
220 smm_lock:
221         .word   SMI_UNLOCKED
222
223 .align  4, 0xff
224
225 smm_prot_vector:
226         .long   smm_prot_start
227         .short  8
228
229 .align  4, 0xff
230
231 smm_gdt_vector:
232         .word   smm_gdt_end - smm_gdt - 1
233         .long   smm_gdt - smm_handler_start
234
235 .section ".jumptable", "a", @progbits
236
237 /* This is the SMM jump table. All cores use the same SMM handler
238  * for simplicity. But SMM Entry needs to be different due to the
239  * save state area. The jump table makes sure all CPUs jump into the
240  * real handler on SMM entry.
241  */
242
243 /* This code currently supports up to 16 CPU cores. If more than 16 CPU cores
244  * shall be used, below table has to be updated, as well as smm_tseg.ld
245  */
246
247 /* When using TSEG do a relative jump and fix up the CS later since we
248  * do not know what our TSEG base is yet.
249  */
250
251 .code16
252 jumptable:
253         /* core 15 */
254         jmp smm_handler_start
255 .align 1024, 0x00
256         /* core 14 */
257         jmp smm_handler_start
258 .align 1024, 0x00
259         /* core 13 */
260         jmp smm_handler_start
261 .align 1024, 0x00
262         /* core 12 */
263         jmp smm_handler_start
264 .align 1024, 0x00
265         /* core 11 */
266         jmp smm_handler_start
267 .align 1024, 0x00
268         /* core 10 */
269         jmp smm_handler_start
270 .align 1024, 0x00
271         /* core 9 */
272         jmp smm_handler_start
273 .align 1024, 0x00
274         /* core 8 */
275         jmp smm_handler_start
276 .align 1024, 0x00
277         /* core 7 */
278         jmp smm_handler_start
279 .align 1024, 0x00
280         /* core 6 */
281         jmp smm_handler_start
282 .align 1024, 0x00
283         /* core 5 */
284         jmp smm_handler_start
285 .align 1024, 0x00
286         /* core 4 */
287         jmp smm_handler_start
288 .align 1024, 0x00
289         /* core 3 */
290         jmp smm_handler_start
291 .align 1024, 0x00
292         /* core 2 */
293         jmp smm_handler_start
294 .align 1024, 0x00
295         /* core 1 */
296         jmp smm_handler_start
297 .align 1024, 0x00
298         /* core 0 */
299         jmp smm_handler_start
300 .align 1024, 0x00