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