2 * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <sys/ioccom.h>
34 #include <sys/cpuctl.h>
36 #include <machine/cpufunc.h>
37 #include <machine/specialreg.h>
47 #include "cpucontrol.h"
54 cpuctl_cpuid_args_t idargs
;
60 error
= ioctl(fd
, CPUCTL_CPUID
, &idargs
);
65 ((uint32_t *)vendor
)[0] = idargs
.data
[1];
66 ((uint32_t *)vendor
)[1] = idargs
.data
[3];
67 ((uint32_t *)vendor
)[2] = idargs
.data
[2];
69 if (strncmp(vendor
, AMD_VENDOR_ID
, sizeof(AMD_VENDOR_ID
)) != 0)
73 error
= ioctl(fd
, CPUCTL_CPUID
, &idargs
);
78 signature
= idargs
.data
[0];
79 family
= ((signature
>> 8) & 0x0f) + ((signature
>> 20) & 0xff);
86 * NB: the format of microcode update files is not documented by AMD.
87 * It has been reverse engineered from studying Coreboot, illumos and Linux
91 amd10h_update(const char *dev
, const char *path
)
94 cpuctl_cpuid_args_t idargs
;
95 cpuctl_msr_args_t msrargs
;
96 cpuctl_update_args_t args
;
97 const amd_10h_fw_header_t
*fw_header
;
98 const amd_10h_fw_header_t
*selected_fw
;
99 const equiv_cpu_entry_t
*equiv_cpu_table
;
100 const section_header_t
*section_header
;
101 const container_header_t
*container_header
;
102 const uint8_t *fw_data
;
105 size_t selected_size
;
118 fw_image
= MAP_FAILED
;
119 devfd
= open(dev
, O_RDWR
);
121 WARN(0, "could not open %s for writing", dev
);
125 error
= ioctl(devfd
, CPUCTL_CPUID
, &idargs
);
130 signature
= idargs
.data
[0];
132 msrargs
.msr
= 0x0000008b;
133 error
= ioctl(devfd
, CPUCTL_RDMSR
, &msrargs
);
135 WARN(0, "ioctl(%s)", dev
);
138 revision
= (uint32_t)msrargs
.data
;
140 WARNX(1, "found cpu family %#x model %#x "
141 "stepping %#x extfamily %#x extmodel %#x.",
142 (signature
>> 8) & 0x0f, (signature
>> 4) & 0x0f,
143 (signature
>> 0) & 0x0f, (signature
>> 20) & 0xff,
144 (signature
>> 16) & 0x0f);
145 WARNX(1, "microcode revision %#x", revision
);
148 * Open the firmware file.
150 fd
= open(path
, O_RDONLY
, 0);
152 WARN(0, "open(%s)", path
);
155 error
= fstat(fd
, &st
);
157 WARN(0, "fstat(%s)", path
);
160 if (st
.st_size
< 0 || (size_t)st
.st_size
<
161 (sizeof(*container_header
) + sizeof(*section_header
))) {
162 WARNX(2, "file too short: %s", path
);
165 fw_size
= st
.st_size
;
168 * mmap the whole image.
170 fw_image
= (uint8_t *)mmap(NULL
, st
.st_size
, PROT_READ
,
172 if (fw_image
== MAP_FAILED
) {
173 WARN(0, "mmap(%s)", path
);
178 container_header
= (const container_header_t
*)fw_data
;
179 if (container_header
->magic
!= AMD_10H_MAGIC
) {
180 WARNX(2, "%s is not a valid amd firmware: bad magic", path
);
183 fw_data
+= sizeof(*container_header
);
184 fw_size
-= sizeof(*container_header
);
186 section_header
= (const section_header_t
*)fw_data
;
187 if (section_header
->type
!= AMD_10H_EQUIV_TABLE_TYPE
) {
188 WARNX(2, "%s is not a valid amd firmware: "
189 "first section is not CPU equivalence table", path
);
192 if (section_header
->size
== 0) {
193 WARNX(2, "%s is not a valid amd firmware: "
194 "first section is empty", path
);
197 fw_data
+= sizeof(*section_header
);
198 fw_size
-= sizeof(*section_header
);
200 if (section_header
->size
> fw_size
) {
201 WARNX(2, "%s is not a valid amd firmware: "
202 "file is truncated", path
);
205 if (section_header
->size
< sizeof(*equiv_cpu_table
)) {
206 WARNX(2, "%s is not a valid amd firmware: "
207 "first section is too short", path
);
210 equiv_cpu_table
= (const equiv_cpu_entry_t
*)fw_data
;
211 fw_data
+= section_header
->size
;
212 fw_size
-= section_header
->size
;
215 for (i
= 0; equiv_cpu_table
[i
].installed_cpu
!= 0; i
++) {
216 printf("TEST %s %08x %08x\n", path
, signature
, equiv_cpu_table
[i
].installed_cpu
);
217 if (signature
== equiv_cpu_table
[i
].installed_cpu
) {
218 equiv_id
= equiv_cpu_table
[i
].equiv_cpu
;
219 WARNX(3, "equiv_id: %x", equiv_id
);
224 WARNX(2, "CPU is not found in the equivalence table");
230 while (fw_size
>= sizeof(*section_header
)) {
231 section_header
= (const section_header_t
*)fw_data
;
232 fw_data
+= sizeof(*section_header
);
233 fw_size
-= sizeof(*section_header
);
234 if (section_header
->type
!= AMD_10H_uCODE_TYPE
) {
235 WARNX(2, "%s is not a valid amd firmware: "
236 "section has incorret type", path
);
239 if (section_header
->size
> fw_size
) {
240 WARNX(2, "%s is not a valid amd firmware: "
241 "file is truncated", path
);
244 if (section_header
->size
< sizeof(*fw_header
)) {
245 WARNX(2, "%s is not a valid amd firmware: "
246 "section is too short", path
);
249 fw_header
= (const amd_10h_fw_header_t
*)fw_data
;
250 fw_data
+= section_header
->size
;
251 fw_size
-= section_header
->size
;
253 if (fw_header
->processor_rev_id
!= equiv_id
)
254 continue; /* different cpu */
255 if (fw_header
->patch_id
<= revision
)
256 continue; /* not newer revision */
257 if (fw_header
->nb_dev_id
!= 0 || fw_header
->sb_dev_id
!= 0) {
258 WARNX(2, "Chipset-specific microcode is not supported");
261 WARNX(3, "selecting revision: %x", fw_header
->patch_id
);
262 revision
= fw_header
->patch_id
;
263 selected_fw
= fw_header
;
264 selected_size
= section_header
->size
;
268 WARNX(2, "%s is not a valid amd firmware: "
269 "file is truncated", path
);
273 if (selected_fw
!= NULL
) {
274 WARNX(1, "selected ucode size is %zu", selected_size
);
275 fprintf(stderr
, "%s: updating cpu %s to revision %#x... ",
276 path
, dev
, revision
);
278 args
.data
= __DECONST(void *, selected_fw
);
279 args
.size
= selected_size
;
280 error
= ioctl(devfd
, CPUCTL_UPDATE
, &args
);
282 fprintf(stderr
, "failed.\n");
286 fprintf(stderr
, "done.\n");
289 msrargs
.msr
= 0x0000008b;
290 error
= ioctl(devfd
, CPUCTL_RDMSR
, &msrargs
);
292 WARN(0, "ioctl(%s)", dev
);
295 new_rev
= (uint32_t)msrargs
.data
;
296 if (new_rev
!= revision
)
297 WARNX(0, "revision after update %#x", new_rev
);
304 if (fw_image
!= MAP_FAILED
)
305 if (munmap(fw_image
, st
.st_size
) != 0)
306 warn("munmap(%s)", path
);