1/*	$OpenBSD: ucode.c,v 1.6 2023/09/10 09:32:31 jsg Exp $	*/
2/*
3 * Copyright (c) 2018 Stefan Fritsch <fritsch@genua.de>
4 * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/systm.h>
21#include <sys/mutex.h>
22#include <sys/malloc.h>
23
24#include <uvm/uvm_extern.h>
25
26#include <machine/cpu.h>
27#include <machine/cpufunc.h>
28#include <machine/specialreg.h>
29#include <machine/biosvar.h>
30
31/* #define UCODE_DEBUG */
32#ifdef UCODE_DEBUG
33#define DPRINTF(x)	do { if (cpu_ucode_debug > 0) printf x; } while (0)
34#define DPRINTFN(n, x)	do { if (cpu_ucode_debug >= (n)) printf x; } while (0)
35int cpu_ucode_debug = 1;
36#else
37#define DPRINTF(x)	do { ; } while (0)
38#define DPRINTFN(n, x)	do { ; } while (0)
39#endif
40
41struct intel_ucode_header {
42	uint32_t	header_version;
43	uint32_t	update_revision;
44	uint32_t	date;
45	uint32_t	processor_sig;
46	uint32_t	checksum;
47	uint32_t	loader_rev;
48	uint32_t	processor_flags;
49	uint32_t	data_size;
50	uint32_t	total_size;
51	uint32_t	reserved[3];
52};
53
54struct intel_ucode_ext_sig_header {
55	uint32_t	ext_sig_count;
56	uint32_t	checksum;
57	uint32_t	reserved[3];
58};
59
60struct intel_ucode_ext_sig {
61	uint32_t	processor_sig;
62	uint32_t	processor_flags;
63	uint32_t	checksum;
64};
65
66#define INTEL_UCODE_DEFAULT_DATA_SIZE		2000
67
68/* Generic */
69char *	 cpu_ucode_data;
70size_t	 cpu_ucode_size;
71
72void	 cpu_ucode_setup(void);
73void	 cpu_ucode_apply(struct cpu_info *);
74
75struct mutex	cpu_ucode_mtx = MUTEX_INITIALIZER(IPL_HIGH);
76
77/* Intel */
78void	 cpu_ucode_intel_apply(struct cpu_info *);
79struct intel_ucode_header *
80	 cpu_ucode_intel_find(char *, size_t, uint32_t);
81int	 cpu_ucode_intel_verify(struct intel_ucode_header *);
82int	 cpu_ucode_intel_match(struct intel_ucode_header *, uint32_t, uint32_t,
83	    uint32_t);
84uint32_t cpu_ucode_intel_rev(void);
85
86struct intel_ucode_header	*cpu_ucode_intel_applied;
87
88void cpu_ucode_amd_apply(struct cpu_info *);
89
90void
91cpu_ucode_setup(void)
92{
93	vaddr_t va;
94	paddr_t pa;
95	int i, npages;
96	size_t size;
97
98	if (bios_ucode == NULL)
99		return;
100
101	if (!bios_ucode->uc_addr || !bios_ucode->uc_size)
102		return;
103
104	cpu_ucode_size = bios_ucode->uc_size;
105	size = round_page(bios_ucode->uc_size);
106	npages = size / PAGE_SIZE;
107
108	va = (vaddr_t)km_alloc(size, &kv_any, &kp_none, &kd_nowait);
109	if (va == 0)
110		return;
111	for (i = 0; i < npages; i++) {
112		pa = bios_ucode->uc_addr + (i * PAGE_SIZE);
113		pmap_enter(pmap_kernel(), va + (i * PAGE_SIZE), pa,
114		    PROT_READ,
115		    PROT_READ | PMAP_WIRED);
116		pmap_update(pmap_kernel());
117	}
118
119	cpu_ucode_data = malloc(cpu_ucode_size, M_DEVBUF, M_WAITOK);
120
121	memcpy((void *)cpu_ucode_data, (void *)va, cpu_ucode_size);
122
123	pmap_remove(pmap_kernel(), va, va + size);
124	pmap_update(pmap_kernel());
125	km_free((void *)va, size, &kv_any, &kp_none);
126}
127
128/*
129 * Called per-CPU.
130 */
131void
132cpu_ucode_apply(struct cpu_info *ci)
133{
134	if (strcmp(cpu_vendor, "GenuineIntel") == 0)
135		cpu_ucode_intel_apply(ci);
136	else if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
137		cpu_ucode_amd_apply(ci);
138}
139
140#define AMD_MAGIC 0x00414d44
141
142struct amd_equiv {
143	uint32_t id;
144	uint32_t a;
145	uint32_t b;
146	uint16_t eid;
147	uint16_t c;
148} __packed;
149
150struct amd_patch {
151	uint32_t type;
152	uint32_t len;
153	uint32_t a;
154	uint32_t level;
155	uint8_t c[16];
156	uint16_t eid;
157} __packed;
158
159void
160cpu_ucode_amd_apply(struct cpu_info *ci)
161{
162	uint64_t level;
163	uint32_t magic, tlen, i;
164	uint16_t eid = 0;
165	uint32_t sig, ebx, ecx, edx;
166	uint64_t start = 0;
167	uint32_t patch_len = 0;
168
169	if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
170		DPRINTF(("%s: no microcode provided\n", __func__));
171		return;
172	}
173
174	/*
175	 * Grab a mutex, because we are not allowed to run updates
176	 * simultaneously on HT siblings.
177	 */
178	mtx_enter(&cpu_ucode_mtx);
179
180	CPUID(1, sig, ebx, ecx, edx);
181
182	level = rdmsr(MSR_PATCH_LEVEL);
183	DPRINTF(("%s: cur patch level 0x%llx\n", __func__, level));
184
185	memcpy(&magic, cpu_ucode_data, 4);
186	if (magic != AMD_MAGIC) {
187		DPRINTF(("%s: bad magic %x\n", __func__, magic));
188		goto out;
189	}
190
191	memcpy(&tlen, &cpu_ucode_data[8], 4);
192
193	/* find equivalence id matching our cpu signature */
194	for (i = 12; i < 12 + tlen;) {
195		struct amd_equiv ae;
196		if (i + sizeof(ae) > cpu_ucode_size) {
197			DPRINTF(("%s: truncated etable\n", __func__));
198			goto out;
199		}
200		memcpy(&ae, &cpu_ucode_data[i], sizeof(ae));
201		i += sizeof(ae);
202		if (ae.id == sig)
203			eid = ae.eid;
204	}
205
206	/* look for newer patch with the equivalence id */
207	while (i < cpu_ucode_size) {
208		struct amd_patch ap;
209		if (i + sizeof(ap) > cpu_ucode_size) {
210			DPRINTF(("%s: truncated ptable\n", __func__));
211			goto out;
212		}
213		memcpy(&ap, &cpu_ucode_data[i], sizeof(ap));
214		if (ap.type == 1 && ap.eid == eid && ap.level > level) {
215			start = (uint64_t)&cpu_ucode_data[i + 8];
216			patch_len = ap.len;
217		}
218		if (i + ap.len + 8 > cpu_ucode_size) {
219			DPRINTF(("%s: truncated patch\n", __func__));
220			goto out;
221		}
222		i += ap.len + 8;
223	}
224
225	if (start != 0) {
226		/* alignment required on fam 15h */
227		uint8_t *p = malloc(patch_len, M_TEMP, M_NOWAIT);
228		if (p == NULL)
229			goto out;
230		memcpy(p, (uint8_t *)start, patch_len);
231		start = (uint64_t)p;
232		wrmsr(MSR_PATCH_LOADER, start);
233		level = rdmsr(MSR_PATCH_LEVEL);
234		DPRINTF(("%s: new patch level 0x%llx\n", __func__, level));
235		free(p, M_TEMP, patch_len);
236	}
237out:
238	mtx_leave(&cpu_ucode_mtx);
239}
240
241void
242cpu_ucode_intel_apply(struct cpu_info *ci)
243{
244	struct intel_ucode_header *update;
245	uint32_t old_rev, new_rev;
246	paddr_t data;
247
248	if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
249		DPRINTF(("%s: no microcode provided\n", __func__));
250		return;
251	}
252
253	/*
254	 * Grab a mutex, because we are not allowed to run updates
255	 * simultaneously on HT siblings.
256	 */
257	mtx_enter(&cpu_ucode_mtx);
258
259	old_rev = cpu_ucode_intel_rev();
260	update = cpu_ucode_intel_applied;
261	if (update == NULL)
262		update = cpu_ucode_intel_find(cpu_ucode_data,
263		    cpu_ucode_size, old_rev);
264	if (update == NULL) {
265		DPRINTF(("%s: no microcode update found\n", __func__));
266		goto out;
267	}
268	if (update->update_revision == old_rev) {
269		DPRINTF(("%s: microcode already up-to-date\n", __func__));
270		goto out;
271	}
272
273	/* Apply microcode. */
274	data = (paddr_t)update;
275	data += sizeof(struct intel_ucode_header);
276	wbinvd();
277	wrmsr(MSR_BIOS_UPDT_TRIG, data);
278
279	new_rev = cpu_ucode_intel_rev();
280	if (new_rev != old_rev) {
281		DPRINTF(("%s: microcode updated cpu %ld rev %#x->%#x (%x)\n",
282		    __func__, ci->ci_cpuid, old_rev, new_rev, update->date));
283		if (cpu_ucode_intel_applied == NULL)
284			cpu_ucode_intel_applied = update;
285	} else {
286		DPRINTF(("%s: microcode update failed cpu %ld rev %#x->%#x != %#x\n",
287		     __func__, ci->ci_cpuid, old_rev, update->update_revision, new_rev));
288	}
289
290out:
291	mtx_leave(&cpu_ucode_mtx);
292}
293
294struct intel_ucode_header *
295cpu_ucode_intel_find(char *data, size_t left, uint32_t current)
296{
297	uint64_t platform_id = (rdmsr(MSR_PLATFORM_ID) >> 50) & 7;
298	uint32_t sig, dummy1, dummy2, dummy3;
299	uint32_t mask = 1UL << platform_id;
300	struct intel_ucode_header *hdr;
301	uint32_t total_size;
302	int n = 0;
303
304	CPUID(1, sig, dummy1, dummy2, dummy3);
305
306	while (left > 0) {
307		hdr = (struct intel_ucode_header *)data;
308		if (left < sizeof(struct intel_ucode_header)) {
309			DPRINTF(("%s:%d: not enough data for header (%zd)\n",
310			    __func__, n, left));
311			break;
312		}
313		/*
314		 * Older microcode has an empty length.  In that case we
315		 * have to use the default length of 2000.
316		 */
317		if (hdr->data_size)
318			total_size = hdr->total_size;
319		else
320			total_size = INTEL_UCODE_DEFAULT_DATA_SIZE +
321			     sizeof(struct intel_ucode_header);
322		if (total_size > left) {
323			DPRINTF(("%s:%d: size %u out of range (%zd)\n",
324			    __func__, n, total_size, left));
325			break;
326		}
327		if (cpu_ucode_intel_verify(hdr)) {
328			DPRINTF(("%s:%d: broken data\n", __func__, n));
329			break;
330		}
331		if (cpu_ucode_intel_match(hdr, sig, mask, current))
332			return hdr;
333		n++;
334		left -= total_size;
335		data += total_size;
336	}
337	DPRINTF(("%s: no update found\n", __func__));
338	return NULL;
339}
340
341int
342cpu_ucode_intel_verify(struct intel_ucode_header *hdr)
343{
344	uint32_t *data = (uint32_t *)hdr;
345	size_t total_size;
346	uint32_t sum;
347	int i;
348
349	CTASSERT(sizeof(struct intel_ucode_header) == 48);
350
351	if ((paddr_t)data % 16 != 0) {
352		DPRINTF(("%s: misaligned microcode update\n", __func__));
353		return 1;
354	}
355	if (hdr->loader_rev != 1) {
356		DPRINTF(("%s: unsupported loader rev\n", __func__));
357		return 1;
358	}
359
360	if (hdr->data_size)
361		total_size = hdr->total_size;
362	else
363		total_size = INTEL_UCODE_DEFAULT_DATA_SIZE +
364		    sizeof(struct intel_ucode_header);
365	if (total_size % 4 != 0) {
366		DPRINTF(("%s: inconsistent size\n", __func__));
367		return 1;
368	}
369
370	sum = 0;
371	for (i = 0; i < total_size / 4; i++)
372		sum += data[i];
373	if (sum != 0) {
374		DPRINTF(("%s: wrong checksum (%#x)\n", __func__, sum));
375		return 1;
376	}
377
378	return 0;
379}
380
381int
382cpu_ucode_intel_match(struct intel_ucode_header *hdr,
383    uint32_t processor_sig, uint32_t processor_mask,
384    uint32_t ucode_revision)
385{
386	struct intel_ucode_ext_sig_header *ehdr;
387	struct intel_ucode_ext_sig *esig;
388	uint32_t data_size, total_size;
389	unsigned i;
390
391	data_size = hdr->data_size;
392	total_size = hdr->total_size;
393
394	/*
395	 * Older microcode has an empty length.  In that case we
396	 * have to use the default length of 2000.
397	 */
398	if (!data_size) {
399		data_size = INTEL_UCODE_DEFAULT_DATA_SIZE;
400		total_size = INTEL_UCODE_DEFAULT_DATA_SIZE +
401		    sizeof(struct intel_ucode_header);
402	}
403
404	if (ucode_revision > hdr->update_revision)
405		return 0;
406	if (hdr->processor_sig == processor_sig &&
407	    (hdr->processor_flags & processor_mask))
408		return 1;
409	if (total_size <= sizeof(struct intel_ucode_header) +
410	    data_size + sizeof(struct intel_ucode_ext_sig_header))
411		return 0;
412
413	ehdr = (void *)((char *)hdr + sizeof(struct intel_ucode_header) +
414	    data_size);
415	esig = (void *)&ehdr[1];
416	for (i = 0; i < ehdr->ext_sig_count; i++) {
417		if (esig[i].processor_sig == processor_sig &&
418		    (esig[i].processor_flags & processor_mask))
419			return 1;
420	}
421
422	return 0;
423}
424
425uint32_t
426cpu_ucode_intel_rev(void)
427{
428	uint32_t eax, ebx, ecx, edx;
429	uint64_t rev;
430
431	wrmsr(MSR_BIOS_SIGN, 0);
432	CPUID(1, eax, ebx, ecx, edx);
433	rev = rdmsr(MSR_BIOS_SIGN);
434	return rev >> 32;
435}
436