xref: /aosp_15_r20/external/coreboot/src/cpu/intel/microcode/microcode.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /* Microcode update for Intel PIII and later CPUs */
4 
5 #include <cbfs.h>
6 #include <console/console.h>
7 #include <cpu/cpu.h>
8 #include <cpu/intel/microcode.h>
9 #include <cpu/x86/msr.h>
10 #include <smp/spinlock.h>
11 #include <stdio.h>
12 #include <types.h>
13 
14 DECLARE_SPIN_LOCK(microcode_lock)
15 
16 struct microcode {
17 	u32 hdrver;	/* Header Version */
18 	u32 rev;	/* Update Revision */
19 	u32 date;	/* Date */
20 	u32 sig;	/* Processor Signature */
21 
22 	u32 cksum;	/* Checksum */
23 	u32 ldrver;	/* Loader Revision */
24 	u32 pf;		/* Processor Flags */
25 
26 	u32 data_size;	/* Data Size */
27 	u32 total_size;	/* Total Size */
28 
29 	u32 reserved[3];
30 };
31 
32 struct ext_sig_table {
33 	u32 ext_sig_cnt;
34 	u32 ext_tbl_chksm;
35 	u32 res[3];
36 };
37 
38 struct ext_sig_entry {
39 	u32 sig;
40 	u32 pf;
41 	u32 chksm;
42 };
43 
read_microcode_rev(void)44 static inline u32 read_microcode_rev(void)
45 {
46 	/* Some Intel CPUs can be very finicky about the
47 	 * CPUID sequence used.  So this is implemented in
48 	 * assembly so that it works reliably.
49 	 */
50 	msr_t msr;
51 	asm volatile (
52 		"xorl %%eax, %%eax\n\t"
53 		"xorl %%edx, %%edx\n\t"
54 		"movl $0x8b, %%ecx\n\t"
55 		"wrmsr\n\t"
56 		"movl $0x01, %%eax\n\t"
57 		"cpuid\n\t"
58 		"movl $0x08b, %%ecx\n\t"
59 		"rdmsr\n\t"
60 		: /* outputs */
61 		"=a" (msr.lo), "=d" (msr.hi)
62 		: /* inputs */
63 		: /* trashed */
64 		 "ebx", "ecx"
65 	);
66 	return msr.hi;
67 }
68 
69 #define MICROCODE_CBFS_FILE "cpu_microcode_blob.bin"
70 
load_microcode(const struct microcode * ucode_patch)71 static int load_microcode(const struct microcode *ucode_patch)
72 {
73 	u32 current_rev;
74 	msr_t msr;
75 
76 	msr.lo = (unsigned long)ucode_patch + sizeof(struct microcode);
77 	msr.hi = 0;
78 	wrmsr(IA32_BIOS_UPDT_TRIG, msr);
79 
80 	current_rev = read_microcode_rev();
81 	if (current_rev == ucode_patch->rev) {
82 		printk(BIOS_INFO, "microcode: updated to revision "
83 		    "0x%x date=%04x-%02x-%02x\n", read_microcode_rev(),
84 		    ucode_patch->date & 0xffff, (ucode_patch->date >> 24) & 0xff,
85 		    (ucode_patch->date >> 16) & 0xff);
86 		return 0;
87 	}
88 
89 	return -1;
90 }
91 
intel_microcode_load_unlocked(const void * microcode_patch)92 void intel_microcode_load_unlocked(const void *microcode_patch)
93 {
94 	u32 current_rev;
95 	const struct microcode *m = microcode_patch;
96 
97 	if (!m) {
98 		printk(BIOS_WARNING, "microcode: failed because no ucode was found\n");
99 		return;
100 	}
101 
102 	current_rev = read_microcode_rev();
103 
104 	/* No use loading the same revision. */
105 	if (current_rev == m->rev) {
106 		printk(BIOS_INFO, "microcode: Update skipped, already up-to-date\n");
107 		return;
108 	}
109 
110 #if ENV_RAMSTAGE
111 	/*SoC specific check to update microcode*/
112 	if (soc_skip_ucode_update(current_rev, m->rev)) {
113 		printk(BIOS_DEBUG, "Skip microcode update\n");
114 		return;
115 	}
116 #endif
117 
118 	printk(BIOS_INFO, "microcode: load microcode patch\n");
119 	if (load_microcode(m) < 0)
120 		printk(BIOS_ERR, "microcode: Update failed\n");
121 }
122 
get_current_microcode_rev(void)123 uint32_t get_current_microcode_rev(void)
124 {
125 	return read_microcode_rev();
126 }
127 
get_microcode_rev(const void * microcode)128 uint32_t get_microcode_rev(const void *microcode)
129 {
130 	return ((struct microcode *)microcode)->rev;
131 }
132 
get_microcode_size(const void * microcode)133 uint32_t get_microcode_size(const void *microcode)
134 {
135 	return ((struct microcode *)microcode)->total_size;
136 }
137 
get_microcode_checksum(const void * microcode)138 uint32_t get_microcode_checksum(const void *microcode)
139 {
140 	return ((struct microcode *)microcode)->cksum;
141 }
142 
143 
ucode_get_ext_sig_table(const struct microcode * ucode)144 static struct ext_sig_table *ucode_get_ext_sig_table(const struct microcode *ucode)
145 {
146 	struct ext_sig_table *ext_tbl;
147 	/* header + ucode data blob size */
148 	u32 size = ucode->data_size + sizeof(struct microcode);
149 
150 	ssize_t ext_tbl_len = ucode->total_size - size;
151 
152 	if (ext_tbl_len < (ssize_t)sizeof(struct ext_sig_table))
153 		return NULL;
154 
155 	ext_tbl = (struct ext_sig_table *)((uintptr_t)ucode + size);
156 
157 	if (ext_tbl_len < (sizeof(struct ext_sig_table) +
158 				ext_tbl->ext_sig_cnt * sizeof(struct ext_sig_entry)))
159 		return NULL;
160 
161 	return ext_tbl;
162 }
163 
find_cbfs_microcode(void)164 static const void *find_cbfs_microcode(void)
165 {
166 	const struct microcode *ucode_updates;
167 	struct ext_sig_table *ext_tbl;
168 	size_t microcode_len;
169 	u32 eax;
170 	u32 pf, rev, sig, update_size;
171 	msr_t msr;
172 	struct cpuinfo_x86 c;
173 
174 	rev = read_microcode_rev();
175 	eax = cpuid_eax(1);
176 	get_fms(&c, eax);
177 	sig = eax;
178 
179 	pf = 0;
180 	if ((c.x86_model >= 5) || (c.x86 > 6)) {
181 		msr = rdmsr(IA32_PLATFORM_ID);
182 		pf = 1 << ((msr.hi >> 18) & 7);
183 	}
184 
185 	printk(BIOS_DEBUG, "microcode: sig=0x%x pf=0x%x revision=0x%x\n",
186 			sig, pf, rev);
187 
188 	if (CONFIG(CPU_INTEL_MICROCODE_CBFS_SPLIT_BINS)) {
189 		char cbfs_filename[25];
190 		snprintf(cbfs_filename, sizeof(cbfs_filename), "cpu_microcode_%x.bin", sig);
191 		ucode_updates = cbfs_map(cbfs_filename, &microcode_len);
192 	} else {
193 		ucode_updates = cbfs_map(MICROCODE_CBFS_FILE, &microcode_len);
194 	}
195 	if (ucode_updates == NULL)
196 		return NULL;
197 
198 	while (microcode_len >= sizeof(*ucode_updates)) {
199 		/* Newer microcode updates include a size field, whereas older
200 		 * containers set it at 0 and are exactly 2048 bytes long */
201 		if (ucode_updates->total_size) {
202 			update_size = ucode_updates->total_size;
203 		} else {
204 			printk(BIOS_SPEW, "Microcode size field is 0\n");
205 			update_size = 2048;
206 		}
207 
208 		/* Checkpoint 1: The microcode update falls within CBFS */
209 		if (update_size > microcode_len) {
210 			printk(BIOS_WARNING, "Microcode header corrupted!\n");
211 			break;
212 		}
213 
214 		if ((ucode_updates->sig == sig) && (ucode_updates->pf & pf))
215 			return ucode_updates;
216 
217 
218 		/* Check if there is extended signature table */
219 		ext_tbl = ucode_get_ext_sig_table(ucode_updates);
220 
221 		if (ext_tbl != NULL) {
222 			int i;
223 			struct ext_sig_entry *entry = (struct ext_sig_entry *)(ext_tbl + 1);
224 
225 			for (i = 0; i < ext_tbl->ext_sig_cnt; i++, entry++) {
226 				if ((sig == entry->sig) && (pf & entry->pf)) {
227 					return ucode_updates;
228 				}
229 			}
230 		}
231 
232 		ucode_updates = (void *)((char *)ucode_updates + update_size);
233 		microcode_len -= update_size;
234 	}
235 
236 	return NULL;
237 }
238 
intel_microcode_find(void)239 const void *intel_microcode_find(void)
240 {
241 	static bool microcode_checked;
242 	static const void *ucode_update;
243 
244 	if (ENV_CACHE_AS_RAM) {
245 		printk(BIOS_ERR, "Microcode Error: Early microcode patching is not supported due"
246 				"to NEM limitation\n");
247 		return NULL;
248 	}
249 
250 	if (microcode_checked)
251 		return ucode_update;
252 
253 	/*
254 	 * Since this function caches the found microcode (NULL or a valid
255 	 * microcode pointer), it is expected to be run from BSP before starting
256 	 * any other APs. This sequence is not multithread safe otherwise.
257 	 */
258 	ucode_update = find_cbfs_microcode();
259 	microcode_checked = true;
260 
261 	return ucode_update;
262 }
263 
intel_update_microcode_from_cbfs(void)264 void intel_update_microcode_from_cbfs(void)
265 {
266 	const void *patch = intel_microcode_find();
267 
268 	spin_lock(&microcode_lock);
269 
270 	intel_microcode_load_unlocked(patch);
271 
272 	spin_unlock(&microcode_lock);
273 }
274 
intel_reload_microcode(void)275 void intel_reload_microcode(void)
276 {
277 	if (!CONFIG(RELOAD_MICROCODE_PATCH))
278 		return;
279 
280 	const struct microcode *m = intel_microcode_find();
281 
282 	if (!m) {
283 		printk(BIOS_WARNING, "microcode: failed because no ucode was found\n");
284 		return;
285 	}
286 
287 	printk(BIOS_INFO, "microcode: Re-load microcode patch\n");
288 
289 	if (load_microcode(m) < 0)
290 		printk(BIOS_ERR, "microcode: Re-load failed\n");
291 }
292 
293 #if ENV_RAMSTAGE
soc_skip_ucode_update(u32 current_patch_id,u32 new_patch_id)294 __weak int soc_skip_ucode_update(u32 current_patch_id,
295 	u32 new_patch_id)
296 {
297 	return 0;
298 }
299 #endif
300