summaryrefslogtreecommitdiff
path: root/src/cpu/intel/microcode/microcode_asm.S
blob: 5173ae5a0cafda1719a1d863f5b1e27d4d38ef5d (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
/* SPDX-License-Identifier: GPL-2.0-only */

/*
 * input %esp: return address (not pointer to return address!)
 * clobber the content of eax, ebx, ecx, edx, esi, edi, and ebp
 */

#include <cpu/x86/post_code.h>
#include <cpu/x86/msr.h>

#define CBFS_FILE_MAGIC 0
#define CBFS_FILE_LEN (CBFS_FILE_MAGIC + 8)
#define CBFS_FILE_TYPE (CBFS_FILE_LEN + 4)
#define CBFS_FILE_CHECKSUM (CBFS_FILE_TYPE + 4)
#define CBFS_FILE_OFFSET (CBFS_FILE_CHECKSUM + 4)

#define HEADER_VER_OFFSET 0
#define UPDATE_VER_OFFSET 4
#define DATE_OFFSET 8
#define PROCESSOR_SIG_OFFSET 12
#define CHKSUM_OFFSET 16
#define LOADER_REV_OFFSET 20
#define PROCESSOR_FLAG 24
#define DATA_SIZE_OFFSET 28
#define TOTAL_OFFSET 32
#define HEADER_SIZE 48

/*
 * The microcode header is 48 bytes wide and has the following
 * structure:
 *   Header Version      : 32bit
 *   Update Revision     : 32bit
 *   Date                : 32bit
 *   Processor Signature : 32bit
 *   Checksum            : 32bit
 *   Loader Revision     : 32bit
 *   Processor Flags     : 32bit
 *   Data Size           : 32bit
 *   Total Size          : 32bit
 *   Reserved            : 96bit
 *
 * We only check if the Processor signature and flags match and check
 * if the revision of the update is newer than what is installed
 */

.code32
.section .text
.global update_bsp_microcode

update_bsp_microcode:
	/* Keep return address */
	movl	%esp, %edx
	/* find microcodes in cbfs */
	leal	microcode_name, %esi
	movl	$1f, %esp
	jmp	walkcbfs_asm

1:
	/* restore return address */
	movl	%edx, %esp

	cmpl	$0, %eax
	je	end_microcode_update
	movl	CBFS_FILE_OFFSET(%eax), %ebx
	bswap	%ebx
	addl	%eax, %ebx
	movl	%ebx, %esi

	movl	CBFS_FILE_LEN(%eax), %edi
	bswap	%edi
	addl	%esi, %edi

	/*
	 * Microcode revision -> %ebx
	 * Processor flags -> %ebp
	 * Current installed microcode revision -> %edx
	 */

	/* Processor flags
	 * rdmsr 0x17
	 * pf = 1 << ((msr.hi >> 18) & 7) */
	movl	$IA32_PLATFORM_ID, %ecx
	rdmsr
	shr	$18, %edx
	andl	$7, %edx
	movl	$1, %eax
	/* needs to be %cl for shl */
	movl	%edx, %ecx
	shl	%cl, %eax
	movl	%eax, %ebp

	/* Fetch the current microcode revision*/
	xorl	%eax, %eax
	xorl	%edx, %edx
	movl	$IA32_BIOS_SIGN_ID, %ecx
	wrmsr
	movl	$0x1, %eax
	cpuid

	/* Processor family+model signature=cpuid_eax(1) */
	movl	%eax, %ebx

	movl	$IA32_BIOS_SIGN_ID, %ecx
	rdmsr

check_microcode_entry:
	/* Test if header revision is non zero */
	cmpl	$0, HEADER_VER_OFFSET(%esi)
	je	end_microcode_update

	/* Processor family+model signature=cpuid_eax(1) */
	cmpl	PROCESSOR_SIG_OFFSET(%esi), %ebx
	jne	next_entry

	/* Processor flags */
	test	PROCESSOR_FLAG(%esi), %ebp
	jz	next_entry

	/* Check if revision is higher than current */
	cmpl	UPDATE_VER_OFFSET(%esi), %edx
	/* Don't upgrade if already greater or equal */
	jge	end_microcode_update

	/* Do actual update */
	movl	%esi, %eax
	addl	$HEADER_SIZE, %eax
	xorl	%edx, %edx
	movl	$IA32_BIOS_UPDT_TRIG, %ecx
	wrmsr

	jmp	end_microcode_update

next_entry:
	movl	TOTAL_OFFSET(%esi), %eax
	cmpl	$0, %eax
	jne	1f
	/* Newer microcode updates include a size field, whereas older
	 * containers set it at 0 and are exactly 2048 bytes long */
	addl	$2048, %esi
	jmp	check_end
1:
	addl	%eax, %esi

check_end:
	cmpl	%esi, %edi
	ja	check_microcode_entry

end_microcode_update:
	jmp	*%esp

microcode_name:
	.string	"cpu_microcode_blob.bin"

_update_bsp_microcode_end: