summaryrefslogtreecommitdiff
path: root/src/cpu/x86/smm/smmhandler_tseg.S
blob: 380935acdadf1e64e50dbde3628214da5d1ae255 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2008 coresystems GmbH
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of
 * the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
 * MA 02110-1301 USA
 */

/*
 * +--------------------------------+
 * | SMM Handler C Code             |
 * +--------------------------------+ 0x14000
 * | SMM Handler Heap               |
 * +--------------------------------+ 0x10000
 * |  Save State Map Node 0         |
 * |  Save State Map Node 1         |
 * |  Save State Map Node 2         |
 * |  Save State Map Node 3         |
 * |  ...                           |
 * +--------------------------------+ 0xf000
 * |                                |
 * |                                |
 * | EARLY DATA (lock, vectors)     |
 * +--------------------------------+ 0x8400
 * | SMM Entry Node 0 (+ stack)     |
 * +--------------------------------+ 0x8000
 * | SMM Entry Node 1 (+ stack)     |
 * | SMM Entry Node 2 (+ stack)     |
 * | SMM Entry Node 3 (+ stack)     |
 * | ...                            |
 * +--------------------------------+ 0x7400
 * |                                |
 * | SMM Handler Assembly Stub      |
 * |                                |
 * +--------------------------------+ TSEG
 *
 */

#define LAPIC_ID	0xfee00020
#define SMM_STACK_SIZE	(0x400 - 0x10)

/* Values for the xchg lock */
#define SMI_LOCKED	0
#define SMI_UNLOCKED	1

#define __PRE_RAM__
#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE
#include <northbridge/intel/sandybridge/sandybridge.h>
#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
#elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM
#include <northbridge/intel/nehalem/nehalem.h>
#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
#elif CONFIG_NORTHBRIDGE_INTEL_HASWELL
#include <northbridge/intel/haswell/haswell.h>
#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
#else
#if CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE
#include <northbridge/intel/fsp_sandybridge/northbridge.h>
#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
#else
#error "Northbridge must define TSEG_BAR."
#endif
#endif



/* initially SMM is some sort of real mode. Let gcc know
 * how to treat the SMM handler stub
 */

.section ".handler", "a", @progbits

.code16

/**
 * SMM code to enable protected mode and jump to the
 * C-written function void smi_handler(u32 smm_revision)
 *
 * All the bad magic is not all that bad after all.
 */
smm_handler_start:
	movl	$(TSEG_BAR), %eax	/* Get TSEG base from PCIE */
	addr32	movl (%eax), %edx	/* Save TSEG_BAR in %edx */
	andl	$~1, %edx		/* Remove lock bit */

	/* Obtain lock */
	movl	%edx, %ebx
	addl	$(smm_lock), %ebx
	movw	$SMI_LOCKED, %ax
	addr32	xchg %ax, (%ebx)
	cmpw	$SMI_UNLOCKED, %ax

	/* Proceed if we got the lock */
	je	smm_check_prot_vector

	/* If we did not get the lock, wait for release */
wait_for_unlock:
	pause
	addr32	movw (%ebx), %ax
	cmpw	$SMI_LOCKED, %ax
	je	wait_for_unlock
	rsm

smm_check_prot_vector:
	/* See if we need to adjust protected vector */
	movl	%edx, %eax
	addl	$(smm_prot_vector), %eax
	addr32	movl (%eax), %ebx
	cmpl	$(smm_prot_start), %ebx
	jne	smm_check_gdt_vector

	/* Adjust vector with TSEG offset */
	addl	%edx, %ebx
	addr32	movl %ebx, (%eax)

smm_check_gdt_vector:
	/* See if we need to adjust GDT vector */
	movl	%edx, %eax
	addl	$(smm_gdt_vector + 2), %eax
	addr32	movl (%eax), %ebx
	cmpl	$(smm_gdt - smm_handler_start), %ebx
	jne	smm_load_gdt

	/* Adjust vector with TSEG offset */
	addl	%edx, %ebx
	addr32	movl %ebx, (%eax)

smm_load_gdt:
	movl    $(smm_gdt_vector), %ebx
	addl	%edx, %ebx        /* TSEG base in %edx */
	data32  lgdt (%ebx)

	movl    %cr0, %eax
	andl    $0x1FFAFFD1, %eax /* CD,NW,PG,AM,WP,NE,TS,EM,MP = 0 */
	orl     $0x1, %eax        /* PE = 1 */
	movl    %eax, %cr0

	/* Enable protected mode */
	movl	$(smm_prot_vector), %eax
	addl	%edx, %eax
	data32	ljmp *(%eax)

.code32
smm_prot_start:
	/* Use flat data segment */
	movw    $0x10, %ax
	movw    %ax, %ds
	movw    %ax, %es
	movw    %ax, %ss
	movw    %ax, %fs
	movw    %ax, %gs

	/* Get this CPU's LAPIC ID */
	movl	$LAPIC_ID, %esi
	movl	(%esi), %ecx
	shr	$24, %ecx

	/* calculate stack offset by multiplying the APIC ID
	 * by 1024 (0x400), and save that offset in ebp.
	 */
	shl	$10, %ecx
	movl	%ecx, %ebp

	/* We put the stack for each core right above
	 * its SMM entry point. Core 0 starts at SMM_BASE + 0x8000,
	 * we spare 0x10 bytes for the jump to be sure.
	 */
	movl	$0x8010, %eax	/* core 0 address */
	addl	%edx, %eax	/* addjust for TSEG */
	subl	%ecx, %eax	/* subtract offset, see above */
	movl	%eax, %ebx	/* Save bottom of stack in ebx */

	/* clear stack */
	cld
	movl	%eax, %edi
	movl	$(SMM_STACK_SIZE >> 2), %ecx
	xorl	%eax, %eax
	rep	stosl

	/* set new stack */
	addl	$SMM_STACK_SIZE, %ebx
	movl	%ebx, %esp

	/* Get SMM revision */
	movl	$0xfefc, %ebx	/* core 0 address */
	addl	%edx, %ebx	/* addjust for TSEG */
	subl	%ebp, %ebx	/* subtract core X offset */
	movl	(%ebx), %eax
	pushl	%eax

	/* Call 32bit C handler */
	call	smi_handler

	/* Release lock */
	movl	$(TSEG_BAR), %eax	/* Get TSEG base from PCIE */
	movl	(%eax), %ebx		/* Save TSEG_BAR in %ebx */
	andl	$~1, %ebx		/* Remove lock bit */
	addl	$(smm_lock), %ebx
	movw	$SMI_UNLOCKED, %ax
	xchg	%ax, (%ebx)

	/* To return, just do rsm. It will "clean up" protected mode */
	rsm

smm_gdt:
	/* The first GDT entry can not be used. Keep it zero */
	.long	0x00000000, 0x00000000

	/* gdt selector 0x08, flat code segment */
	.word	0xffff, 0x0000
	.byte	0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */

	/* gdt selector 0x10, flat data segment */
	.word	0xffff, 0x0000
	.byte	0x00, 0x93, 0xcf, 0x00

smm_gdt_end:

.section ".earlydata", "a", @progbits

.code16

.align	4, 0xff

smm_lock:
	.word	SMI_UNLOCKED

.align	4, 0xff

smm_prot_vector:
	.long	smm_prot_start
	.short	8

.align	4, 0xff

smm_gdt_vector:
	.word	smm_gdt_end - smm_gdt - 1
	.long	smm_gdt - smm_handler_start

.section ".jumptable", "a", @progbits

/* This is the SMM jump table. All cores use the same SMM handler
 * for simplicity. But SMM Entry needs to be different due to the
 * save state area. The jump table makes sure all CPUs jump into the
 * real handler on SMM entry.
 */

/* This code currently supports up to 16 CPU cores. If more than 16 CPU cores
 * shall be used, below table has to be updated, as well as smm_tseg.ld
 */

/* When using TSEG do a relative jump and fix up the CS later since we
 * do not know what our TSEG base is yet.
 */

.code16
jumptable:
	/* core 15 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 14 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 13 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 12 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 11 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 10 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 9 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 8 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 7 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 6 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 5 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 4 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 3 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 2 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 1 */
	jmp smm_handler_start
.align 1024, 0x00
	/* core 0 */
	jmp smm_handler_start
.align 1024, 0x00