1/* $NetBSD: cpu_ucode_amd.c,v 1.7 2013/11/15 08:47:55 msaitoh 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.7 2013/11/15 08:47:55 msaitoh Exp $");
33
34#include "opt_xen.h"
35#include "opt_cpu_ucode.h"
36#include "opt_compat_netbsd.h"
37
38#include <sys/param.h>
39#include <sys/conf.h>
40#include <sys/cpuio.h>
41#include <sys/cpu.h>
42#include <sys/kmem.h>
43#include <sys/xcall.h>
44
45#include <machine/cpufunc.h>
46#include <machine/specialreg.h>
47#include <x86/cpu_ucode.h>
48
49struct microcode_amd_header {
50 uint32_t ah_data_code;
51 uint32_t ah_patch_id;
52 uint8_t ah_patch_data_id[2];
53 uint8_t ah_patch_data_len;
54 uint8_t ah_init_flag;
55 uint32_t ah_patch_data_checksum;
56 uint32_t ah_nb_dev_id;
57 uint32_t ah_sb_dev_id;
58 uint16_t ah_processor_rev_id;
59 uint8_t ah_nb_rev_id;
60 uint8_t ah_sb_rev_id;
61 uint8_t ah_bios_api_rev;
62 uint8_t ah_reserved[3];
63 uint32_t ah_match_reg[8];
64} __packed;
65
66/* equivalence cpu table */
67struct microcode_amd_equiv_cpu_table {
68 uint32_t ect_installed_cpu;
69 uint32_t ect_fixed_errata_mask;
70 uint32_t ect_fixed_errata_compare;
71 uint16_t ect_equiv_cpu;
72 uint16_t ect_reserved;
73};
74
75#define UCODE_MAGIC 0x00414d44
76
77struct microcode_amd {
78 uint8_t *mpb; /* microcode patch block */
79 size_t mpb_size;
80 struct microcode_amd_equiv_cpu_table *ect;
81 size_t ect_size;
82};
83
84struct mpbhdr {
85#define UCODE_TYPE_EQUIV 0
86#define UCODE_TYPE_PATCH 1
87 uint32_t mpb_type;
88 uint32_t mpb_len;
89 uint32_t mpb_data[];
90};
91
92static uint32_t
93amd_cpufamily(void)
94{
95 uint32_t family;
96 struct cpu_info *ci = curcpu();
97
98 family = CPUID_TO_FAMILY(ci->ci_signature);
99
100 return family;
101}
102
103int
104cpu_ucode_amd_get_version(struct cpu_ucode_version *ucode)
105{
106 struct cpu_ucode_version_amd data;
107
108 if (ucode->loader_version != CPU_UCODE_LOADER_AMD || amd_cpufamily() < 0x10)
109 return EOPNOTSUPP;
110 if (!ucode->data)
111 return 0;
112
113 data.version = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
114 return copyout(&data, ucode->data, sizeof(data));
115}
116
117#ifdef COMPAT_60
118int
119compat6_cpu_ucode_amd_get_version(struct compat6_cpu_ucode *ucode)
120{
121 uint64_t uclevel;
122
123 if (amd_cpufamily() < 0x10)
124 return EOPNOTSUPP;
125
126 uclevel = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
127 ucode->version = uclevel;
128 return 0;
129}
130#endif /* COMPAT60 */
131
132int
133cpu_ucode_amd_firmware_open(firmware_handle_t *fwh, const char *fwname)
134{
135 const char *fw_path = "x86/amd";
136 char _fwname[32];
137 int error;
138
139 if (fwname != NULL && fwname[0] != '\0')
140 return firmware_open(fw_path, fwname, fwh);
141
142 snprintf(_fwname, sizeof(_fwname), "microcode_amd_fam%xh.bin",
143 amd_cpufamily());
144
145 error = firmware_open(fw_path, _fwname, fwh);
146 if (error == 0)
147 return 0;
148
149 return firmware_open(fw_path, "microcode_amd.bin", fwh);
150}
151
152#ifndef XEN
153struct mc_buf {
154 uint8_t *mc_buf;
155 uint32_t mc_equiv_cpuid;
156 struct mpbhdr *mc_mpbuf;
157 struct microcode_amd *mc_amd;
158 int mc_error;
159};
160
161static void
162cpu_apply_cb(void *arg0, void *arg1)
163{
164 int error = 0;
165 const struct cpu_ucode_softc *sc = arg0;
166 struct microcode_amd mc_amd;
167 struct mc_buf mc;
168 device_t dev;
169 int s;
170
171 memcpy(&mc, arg1, sizeof(mc));
172 mc_amd.mpb = mc.mc_amd->mpb;
173 mc_amd.mpb_size = mc.mc_amd->mpb_size;
174
175 dev = curcpu()->ci_dev;
176 s = splhigh();
177
178 do {
179 uint64_t patchlevel;
180 struct microcode_amd_header *hdr;
181
182 if (mc.mc_mpbuf->mpb_type != UCODE_TYPE_PATCH) {
183 aprint_debug_dev(dev, "ucode: patch type expected\n");
184 goto next;
185 }
186
187 hdr = (struct microcode_amd_header *)mc_amd.mpb;
188 if (hdr->ah_processor_rev_id != mc.mc_equiv_cpuid) {
189 aprint_debug_dev(dev, "ucode: patch does not "
190 "match this cpu "
191 "(patch is for cpu id %x, cpu id is %x)\n",
192 hdr->ah_processor_rev_id, mc.mc_equiv_cpuid);
193 goto next;
194 }
195
196 patchlevel = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
197 if (hdr->ah_patch_id <= patchlevel)
198 goto next;
199
200 /* found matching microcode update */
201 wrmsr(MSR_UCODE_AMD_PATCHLOADER, (u_long)hdr);
202
203 /* check current patch id and patch's id for match */
204 if (patchlevel == rdmsr(MSR_UCODE_AMD_PATCHLEVEL)) {
205 aprint_debug_dev(dev, "ucode: update from revision "
206 "0x%"PRIx64" to 0x%x failed\n",
207 patchlevel, hdr->ah_patch_id);
208 error = EIO;
209 goto out;
210 } else {
211 /* Success */
212 error = 0;
213 goto out;
214 }
215
216next:
217 /* Check for race:
218 * When we booted with -x a cpu might already finished
219 * (why doesn't xc_wait() wait for *all* cpus?)
220 * and sc->sc_blob is already freed.
221 * In this case the calculation below touches
222 * non-allocated memory and results in a crash.
223 */
224 if (sc->sc_blob == NULL)
225 break;
226
227 mc.mc_buf += mc.mc_mpbuf->mpb_len +
228 sizeof(mc.mc_mpbuf->mpb_type) +
229 sizeof(mc.mc_mpbuf->mpb_len);
230 mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
231 mc_amd.mpb = (uint8_t *)mc.mc_mpbuf->mpb_data;
232 mc_amd.mpb_size = mc.mc_mpbuf->mpb_len;
233
234 } while ((uintptr_t)((mc.mc_buf) - (uint8_t *)sc->sc_blob) < sc->sc_blobsize);
235 aprint_error_dev(dev, "ucode: No newer patch available "
236 "for this cpu than is already installed.\n");
237 error = ENOENT;
238
239out:
240 if (error)
241 ((struct mc_buf *)(arg1))->mc_error = error;
242 splx(s);
243}
244
245int
246cpu_ucode_amd_apply(struct cpu_ucode_softc *sc, int cpuno)
247{
248 int i, error = 0;
249 uint32_t *magic;
250 uint32_t cpu_signature;
251 uint32_t equiv_cpuid = 0;
252 struct mc_buf mc;
253 int where;
254
255 if (sc->loader_version != CPU_UCODE_LOADER_AMD
256 || cpuno != CPU_UCODE_ALL_CPUS)
257 return EINVAL;
258
259 cpu_signature = curcpu()->ci_signature;
260
261 KASSERT(sc->sc_blob != NULL);
262 magic = (uint32_t *)sc->sc_blob;
263 if (*magic != UCODE_MAGIC) {
264 aprint_error("ucode: wrong file magic\n");
265 return EINVAL;
266 }
267
268 mc.mc_buf = &sc->sc_blob[sizeof(*magic)];
269 mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
270
271 /* equivalence table is expected to come first */
272 if (mc.mc_mpbuf->mpb_type != UCODE_TYPE_EQUIV) {
273 aprint_error("ucode: missing equivalence table\n");
274 return EINVAL;
275 }
276
277 mc.mc_amd = kmem_zalloc(sizeof(*mc.mc_amd), KM_NOSLEEP);
278 if (mc.mc_amd == NULL)
279 return ENOMEM;
280
281 mc.mc_amd->ect = kmem_alloc(mc.mc_mpbuf->mpb_len, KM_NOSLEEP);
282 if (mc.mc_amd->ect == NULL) {
283 error = ENOMEM;
284 goto err0;
285 }
286
287 memcpy(mc.mc_amd->ect, mc.mc_mpbuf->mpb_data, mc.mc_mpbuf->mpb_len);
288 mc.mc_amd->ect_size = mc.mc_mpbuf->mpb_len;
289
290 /* check if there is a patch for this cpu present */
291 for (i = 0; mc.mc_amd->ect[i].ect_installed_cpu != 0; i++) {
292 if (cpu_signature == mc.mc_amd->ect[i].ect_installed_cpu) {
293 /* keep extended family and extended model */
294 equiv_cpuid = mc.mc_amd->ect[i].ect_equiv_cpu & 0xffff;
295 break;
296 }
297 }
298 if (equiv_cpuid == 0) {
299 aprint_error("ucode: No patch available for this cpu\n");
300 error = ENOENT;
301 goto err1;
302 }
303
304 mc.mc_equiv_cpuid = equiv_cpuid;
305 mc.mc_buf += mc.mc_mpbuf->mpb_len + sizeof(mc.mc_mpbuf->mpb_type) +
306 sizeof(mc.mc_mpbuf->mpb_len);
307 mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
308 mc.mc_amd->mpb = (uint8_t *)mc.mc_mpbuf->mpb_data;
309 mc.mc_amd->mpb_size = mc.mc_mpbuf->mpb_len;
310
311 /* Apply it on all cpus */
312 mc.mc_error = 0;
313 where = xc_broadcast(0, cpu_apply_cb, sc, &mc);
314
315 /* Wait for completion */
316 xc_wait(where);
317 error = mc.mc_error;
318
319err1:
320 kmem_free(mc.mc_amd->ect, mc.mc_amd->ect_size);
321err0:
322 kmem_free(mc.mc_amd, sizeof(*mc.mc_amd));
323 return error;
324}
325#endif /* ! XEN */
326