xref: /netbsd-src/sys/arch/x86/x86/cpu_ucode_amd.c (revision c24c993fe4cf289234b8ce9b47d92eb1278cfbda)
1 /* $NetBSD: cpu_ucode_amd.c,v 1.11 2020/04/25 15:26:18 bouyer Exp $ */
2 /*
3  * Copyright (c) 2012 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Christoph Egger.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: cpu_ucode_amd.c,v 1.11 2020/04/25 15:26:18 bouyer Exp $");
33 
34 #ifdef _KERNEL_OPT
35 #include "opt_xen.h"
36 #include "opt_cpu_ucode.h"
37 #endif
38 
39 #include <sys/param.h>
40 #include <sys/conf.h>
41 #include <sys/cpuio.h>
42 #include <sys/cpu.h>
43 #include <sys/kmem.h>
44 #include <sys/xcall.h>
45 
46 #include <machine/cpufunc.h>
47 #include <machine/specialreg.h>
48 #include <x86/cpu_ucode.h>
49 
50 struct microcode_amd_header {
51 	uint32_t ah_data_code;
52 	uint32_t ah_patch_id;
53 	uint8_t ah_patch_data_id[2];
54 	uint8_t ah_patch_data_len;
55 	uint8_t ah_init_flag;
56 	uint32_t ah_patch_data_checksum;
57 	uint32_t ah_nb_dev_id;
58 	uint32_t ah_sb_dev_id;
59 	uint16_t ah_processor_rev_id;
60 	uint8_t ah_nb_rev_id;
61 	uint8_t ah_sb_rev_id;
62 	uint8_t ah_bios_api_rev;
63 	uint8_t ah_reserved[3];
64 	uint32_t ah_match_reg[8];
65 } __packed;
66 
67 /* equivalence cpu table */
68 struct microcode_amd_equiv_cpu_table {
69 	uint32_t ect_installed_cpu;
70 	uint32_t ect_fixed_errata_mask;
71 	uint32_t ect_fixed_errata_compare;
72 	uint16_t ect_equiv_cpu;
73 	uint16_t ect_reserved;
74 };
75 
76 #define UCODE_MAGIC	0x00414d44
77 
78 struct microcode_amd {
79 	uint8_t *mpb; /* microcode patch block */
80 	size_t mpb_size;
81 	struct microcode_amd_equiv_cpu_table *ect;
82 	size_t ect_size;
83 };
84 
85 struct mpbhdr {
86 #define UCODE_TYPE_EQUIV	0
87 #define UCODE_TYPE_PATCH	1
88 	uint32_t mpb_type;
89 	uint32_t mpb_len;
90 	uint32_t mpb_data[];
91 };
92 
93 static uint32_t
amd_cpufamily(void)94 amd_cpufamily(void)
95 {
96 	uint32_t family;
97 	struct cpu_info *ci = curcpu();
98 
99 	family = CPUID_TO_FAMILY(ci->ci_signature);
100 
101 	return family;
102 }
103 
104 int
cpu_ucode_amd_get_version(struct cpu_ucode_version * ucode,void * ptr,size_t len)105 cpu_ucode_amd_get_version(struct cpu_ucode_version *ucode, void *ptr,
106     size_t len)
107 {
108 	struct cpu_ucode_version_amd *data = ptr;
109 
110 	if (ucode->loader_version != CPU_UCODE_LOADER_AMD
111 	    || amd_cpufamily() < 0x10)
112 		return EOPNOTSUPP;
113 
114 	if (len < sizeof(*data))
115 		return ENOSPC;
116 
117 	data->version = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
118 	return 0;
119 }
120 
121 int
cpu_ucode_amd_firmware_open(firmware_handle_t * fwh,const char * fwname)122 cpu_ucode_amd_firmware_open(firmware_handle_t *fwh, const char *fwname)
123 {
124 	const char *fw_path = "x86/amd";
125 	char _fwname[32];
126 	int error;
127 
128 	if (fwname != NULL && fwname[0] != '\0')
129 		return firmware_open(fw_path, fwname, fwh);
130 
131 	snprintf(_fwname, sizeof(_fwname), "microcode_amd_fam%xh.bin",
132 	    amd_cpufamily());
133 
134 	error = firmware_open(fw_path, _fwname, fwh);
135 	if (error == 0)
136 		return 0;
137 
138 	return firmware_open(fw_path, "microcode_amd.bin", fwh);
139 }
140 
141 #ifndef XENPV
142 struct mc_buf {
143 	uint8_t *mc_buf;
144 	uint32_t mc_equiv_cpuid;
145 	struct mpbhdr *mc_mpbuf;
146 	struct microcode_amd *mc_amd;
147 	int mc_error;
148 };
149 
150 static void
cpu_apply_cb(void * arg0,void * arg1)151 cpu_apply_cb(void *arg0, void *arg1)
152 {
153 	int error = 0;
154 	const struct cpu_ucode_softc *sc = arg0;
155 	struct microcode_amd mc_amd;
156 	struct mc_buf mc;
157 	device_t dev;
158 	int s;
159 
160 	memcpy(&mc, arg1, sizeof(mc));
161 	mc_amd.mpb = mc.mc_amd->mpb;
162 	mc_amd.mpb_size = mc.mc_amd->mpb_size;
163 
164 	dev = curcpu()->ci_dev;
165 	s = splhigh();
166 
167 	do {
168 		uint64_t patchlevel;
169 		struct microcode_amd_header *hdr;
170 
171 		if (mc.mc_mpbuf->mpb_type != UCODE_TYPE_PATCH) {
172 			aprint_debug_dev(dev, "ucode: patch type expected\n");
173 			goto next;
174 		}
175 
176 		hdr = (struct microcode_amd_header *)mc_amd.mpb;
177 		if (hdr->ah_processor_rev_id != mc.mc_equiv_cpuid) {
178 			aprint_debug_dev(dev, "ucode: patch does not "
179 			    "match this cpu "
180 			    "(patch is for cpu id %x, cpu id is %x)\n",
181 			    hdr->ah_processor_rev_id, mc.mc_equiv_cpuid);
182 			goto next;
183 		}
184 
185 		patchlevel = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
186 		if (hdr->ah_patch_id <= patchlevel)
187 			goto next;
188 
189 		/* found matching microcode update */
190 		wrmsr(MSR_UCODE_AMD_PATCHLOADER, (u_long)hdr);
191 
192 		/* check current patch id and patch's id for match */
193 		if (patchlevel == rdmsr(MSR_UCODE_AMD_PATCHLEVEL)) {
194 			aprint_debug_dev(dev, "ucode: update from revision "
195 			    "0x%"PRIx64" to 0x%x failed\n",
196 			    patchlevel, hdr->ah_patch_id);
197 			error = EIO;
198 			goto out;
199 		} else {
200 			/* Success */
201 			error = 0;
202 			goto out;
203 		}
204 
205 next:
206 		/* Check for race:
207 		 * When we booted with -x a cpu might already finished
208 		 * (why doesn't xc_wait() wait for *all* cpus?)
209 		 * and sc->sc_blob is already freed.
210 		 * In this case the calculation below touches
211 		 * non-allocated memory and results in a crash.
212 		 */
213 		if (sc->sc_blob == NULL)
214 			break;
215 
216 		mc.mc_buf += mc.mc_mpbuf->mpb_len +
217 		    sizeof(mc.mc_mpbuf->mpb_type) +
218 		    sizeof(mc.mc_mpbuf->mpb_len);
219 		mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
220 		mc_amd.mpb = (uint8_t *)mc.mc_mpbuf->mpb_data;
221 		mc_amd.mpb_size = mc.mc_mpbuf->mpb_len;
222 
223 	} while ((uintptr_t)((mc.mc_buf) - (uint8_t *)sc->sc_blob) < sc->sc_blobsize);
224 	aprint_error_dev(dev, "ucode: No newer patch available "
225 	    "for this cpu than is already installed.\n");
226 	error = ENOENT;
227 
228 out:
229 	if (error)
230 		((struct mc_buf *)(arg1))->mc_error = error;
231 	splx(s);
232 }
233 
234 int
cpu_ucode_amd_apply(struct cpu_ucode_softc * sc,int cpuno)235 cpu_ucode_amd_apply(struct cpu_ucode_softc *sc, int cpuno)
236 {
237 	int i, error = 0;
238 	uint32_t *magic;
239 	uint32_t cpu_signature;
240 	uint32_t equiv_cpuid = 0;
241 	struct mc_buf mc;
242 	int where;
243 
244 	if (sc->loader_version != CPU_UCODE_LOADER_AMD
245 	    || cpuno != CPU_UCODE_ALL_CPUS)
246 		return EINVAL;
247 
248 	cpu_signature = curcpu()->ci_signature;
249 
250 	KASSERT(sc->sc_blob != NULL);
251 	magic = (uint32_t *)sc->sc_blob;
252 	if (*magic != UCODE_MAGIC) {
253 		aprint_error("ucode: wrong file magic\n");
254 		return EINVAL;
255 	}
256 
257 	mc.mc_buf = &sc->sc_blob[sizeof(*magic)];
258 	mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
259 
260 	/* equivalence table is expected to come first */
261 	if (mc.mc_mpbuf->mpb_type != UCODE_TYPE_EQUIV) {
262 		aprint_error("ucode: missing equivalence table\n");
263 		return EINVAL;
264 	}
265 
266 	mc.mc_amd = kmem_zalloc(sizeof(*mc.mc_amd), KM_SLEEP);
267 	mc.mc_amd->ect = kmem_alloc(mc.mc_mpbuf->mpb_len, KM_SLEEP);
268 	memcpy(mc.mc_amd->ect, mc.mc_mpbuf->mpb_data, mc.mc_mpbuf->mpb_len);
269 	mc.mc_amd->ect_size = mc.mc_mpbuf->mpb_len;
270 
271 	/* check if there is a patch for this cpu present */
272 	for (i = 0; mc.mc_amd->ect[i].ect_installed_cpu != 0; i++) {
273 		if (cpu_signature == mc.mc_amd->ect[i].ect_installed_cpu) {
274 			/* keep extended family and extended model */
275 			equiv_cpuid = mc.mc_amd->ect[i].ect_equiv_cpu & 0xffff;
276 			break;
277 		}
278 	}
279 	if (equiv_cpuid == 0) {
280 		aprint_error("ucode: No patch available for this cpu\n");
281 		error = ENOENT;
282 		goto err1;
283 	}
284 
285 	mc.mc_equiv_cpuid = equiv_cpuid;
286 	mc.mc_buf += mc.mc_mpbuf->mpb_len + sizeof(mc.mc_mpbuf->mpb_type) +
287 	    sizeof(mc.mc_mpbuf->mpb_len);
288 	mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
289 	mc.mc_amd->mpb = (uint8_t *)mc.mc_mpbuf->mpb_data;
290 	mc.mc_amd->mpb_size = mc.mc_mpbuf->mpb_len;
291 
292 	/* Apply it on all cpus */
293 	mc.mc_error = 0;
294 	where = xc_broadcast(0, cpu_apply_cb, sc, &mc);
295 
296 	/* Wait for completion */
297 	xc_wait(where);
298 	error = mc.mc_error;
299 
300 err1:
301 	kmem_free(mc.mc_amd->ect, mc.mc_amd->ect_size);
302 	kmem_free(mc.mc_amd, sizeof(*mc.mc_amd));
303 	return error;
304 }
305 #endif /* ! XENPV */
306