summaryrefslogtreecommitdiffstats
path: root/src/arch/x86/core/ucode_mp.S
blob: 808e881e0541d67d722356999e7e55f1ff8bc1c1 (plain) (blame)
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
/*
 * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * 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; either version 2 of the
 * License, or any later version.
 *
 * 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 Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * You can also choose to distribute this program under the terms of
 * the Unmodified Binary Distribution Licence (as given in the file
 * COPYING.UBDL), provided that you have satisfied its requirements.
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

/** @file
 *
 * Microcode updates
 *
 */

	.section ".note.GNU-stack", "", @progbits
	.text

/* Selectively assemble code for 32-bit/64-bit builds */
#if defined ( __x86_64__ ) && ! defined ( PLATFORM_pcbios )
#define codemp code64
#define AX rax
#define BX rbx
#define CX rcx
#define DX rdx
#define SI rsi
#define DI rdi
#define BP rbp
#define SP rsp
#define if32 if 0
#define if64 if 1
#else
#define codemp code32
#define AX eax
#define BX ebx
#define CX ecx
#define DX edx
#define SI esi
#define DI edi
#define BP ebp
#define SP esp
#define if32 if 1
#define if64 if 0
#endif

/* Standard features CPUID leaf */
#define CPUID_FEATURES 0x00000001

/* BIOS update signature MSR */
#define MSR_BIOS_SIGN_ID 0x0000008b

/** Microcode update control layout
 *
 * This must match the layout of struct ucode_control.
 */
	.struct	0
CONTROL_DESC:
	.space	8
CONTROL_STATUS:
	.space	8
CONTROL_TRIGGER_MSR:
	.space	4
CONTROL_APIC_MAX:
	.space	4
CONTROL_APIC_UNEXPECTED:
	.space	4
CONTROL_APIC_MASK:
	.space	4
CONTROL_APIC_TEST:
	.space	4
CONTROL_VER_CLEAR:
	.space	1
CONTROL_VER_HIGH:
	.space	1
CONTROL_LEN:

/* We use register %ebp/%rbp to hold the address of the update control */
#define CONTROL BP

/* Microcode update descriptor layout
 *
 * This must match the layout of struct ucode_descriptor.
 */
	.struct	0
DESC_SIGNATURE:
	.space	4
DESC_VERSION:
	.space	4
DESC_ADDRESS:
	.space	8
DESC_LEN:

/* We use register %esi/%rsi to hold the address of the descriptor */
#define DESC SI

/** Microcode update status report layout
 *
 * This must match the layout of struct ucode_status.
 */
	.struct	0
STATUS_SIGNATURE:
	.space	4
STATUS_ID:
	.space	4
STATUS_BEFORE:
	.space	4
STATUS_AFTER:
	.space	4
STATUS_LEN:
	.equ	LOG2_STATUS_LEN, 4
	.if	( 1 << LOG2_STATUS_LEN ) - STATUS_LEN
	.error	"LOG2_STATUS_LEN value is incorrect"
	.endif

/* We use register %edi/%rdi to hold the address of the status report */
#define STATUS DI

/*
 * Update microcode
 *
 * Parameters:
 *	%eax/%rdi	Microcode update structure
 *	%edx/%rsi	CPU identifier (APIC ID)
 *	%esp/%rsp	Stack, or NULL to halt AP upon completion
 *
 * This code may run with no stack on an application processor (AP).
 * All values must be held in registers, and no subroutine calls are
 * possible.  No firmware routines may be called.
 *
 * Since cpuid/rdmsr/wrmsr require the use of %eax, %ebx, %ecx, and
 * %edx, we have essentially only three registers available for
 * long-term state.
 */
	.text
	.globl	ucode_update
	.codemp
	.section ".text.ucode_update", "ax", @progbits
ucode_update:

.if64	/* Get input parameters */
	movq	%rdi, %CONTROL
	movl	%esi, %edx
.else
	movl	%eax, %CONTROL
.endif
	/* Check against maximum expected APIC ID */
	cmpl	CONTROL_APIC_MAX(%CONTROL), %edx
	jbe	1f
	movl	%edx, CONTROL_APIC_UNEXPECTED(%CONTROL)
	jmp	done
1:
	/* Calculate per-CPU status report buffer address */
	mov	%DX, %STATUS
	shl	$LOG2_STATUS_LEN, %STATUS
	add	CONTROL_STATUS(%CONTROL), %STATUS

	/* Report APIC ID */
	movl	%edx, STATUS_ID(%STATUS)

	/* Get and report CPU signature */
	movl	$CPUID_FEATURES, %eax
	cpuid
	movl	%eax, STATUS_SIGNATURE(%STATUS)

	/* Check APIC ID mask */
	movl	STATUS_ID(%STATUS), %eax
	andl	CONTROL_APIC_MASK(%CONTROL), %eax
	cmpl	CONTROL_APIC_TEST(%CONTROL), %eax
	jne	done

	/* Clear BIOS_SIGN_ID MSR if applicable */
	movl	$MSR_BIOS_SIGN_ID, %ecx
	xorl	%eax, %eax
	xorl	%edx, %edx
	testb	$0xff, CONTROL_VER_CLEAR(%CONTROL)
	jz	1f
	wrmsr
1:
	/* Get CPU signature to repopulate BIOS_SIGN_ID MSR (for Intel) */
	movl	$CPUID_FEATURES, %eax
	cpuid

	/* Get initial microcode version */
	movl	$MSR_BIOS_SIGN_ID, %ecx
	rdmsr
	testb	$0xff, CONTROL_VER_HIGH(%CONTROL)
	jz	1f
	movl	%edx, %eax
1:	movl	%eax, STATUS_BEFORE(%STATUS)

	/* Get start of descriptor list */
	mov	CONTROL_DESC(%CONTROL), %DESC
	sub	$DESC_LEN, %DESC

1:	/* Walk update descriptor list to find a matching CPU signature */
	add	$DESC_LEN, %DESC
	movl	DESC_SIGNATURE(%DESC), %eax
	testl	%eax, %eax
	jz	noload
	cmpl	STATUS_SIGNATURE(%STATUS), %eax
	jne	1b

	/* Compare (signed) microcode versions */
	movl	STATUS_BEFORE(%STATUS), %eax
	cmpl	DESC_VERSION(%DESC), %eax
	jge	noload

	/* Load microcode update */
	movl	CONTROL_TRIGGER_MSR(%CONTROL), %ecx
	movl	(DESC_ADDRESS + 0)(%DESC), %eax
	movl	(DESC_ADDRESS + 4)(%DESC), %edx
	wrmsr

noload:	/* Clear BIOS_SIGN_ID MSR if applicable */
	movl	$MSR_BIOS_SIGN_ID, %ecx
	xorl	%eax, %eax
	xorl	%edx, %edx
	testb	$0xff, CONTROL_VER_CLEAR(%CONTROL)
	jz	1f
	wrmsr
1:
	/* Get CPU signature to repopulate BIOS_SIGN_ID MSR (for Intel) */
	movl	$CPUID_FEATURES, %eax
	cpuid

	/* Get and report final microcode version */
	movl	$MSR_BIOS_SIGN_ID, %ecx
	rdmsr
	testb	$0xff, CONTROL_VER_HIGH(%CONTROL)
	jz	1f
	movl	%edx, %eax
1:	movl	%eax, STATUS_AFTER(%STATUS)

done:	/* Return to caller (if stack exists), or halt application processor */
	test	%SP, %SP
	jz	1f
	ret
1:	cli
	hlt
	jmp	1b
	.size	ucode_update, . - ucode_update