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 | |
49 | struct { |
50 | uint32_t ; |
51 | uint32_t ; |
52 | uint8_t [2]; |
53 | uint8_t ; |
54 | uint8_t ; |
55 | uint32_t ; |
56 | uint32_t ; |
57 | uint32_t ; |
58 | uint16_t ; |
59 | uint8_t ; |
60 | uint8_t ; |
61 | uint8_t ; |
62 | uint8_t [3]; |
63 | uint32_t [8]; |
64 | } __packed; |
65 | |
66 | /* equivalence cpu table */ |
67 | struct 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 | |
77 | struct 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 | |
84 | struct 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 | |
92 | static uint32_t |
93 | amd_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 | |
103 | int |
104 | cpu_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 |
118 | int |
119 | compat6_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 | |
132 | int |
133 | cpu_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 |
153 | struct 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 | |
161 | static void |
162 | cpu_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 | |
216 | next: |
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 | |
239 | out: |
240 | if (error) |
241 | ((struct mc_buf *)(arg1))->mc_error = error; |
242 | splx(s); |
243 | } |
244 | |
245 | int |
246 | cpu_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 | |
319 | err1: |
320 | kmem_free(mc.mc_amd->ect, mc.mc_amd->ect_size); |
321 | err0: |
322 | kmem_free(mc.mc_amd, sizeof(*mc.mc_amd)); |
323 | return error; |
324 | } |
325 | #endif /* ! XEN */ |
326 | |