]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/cpucontrol/amd10h.c
ssh: Update to OpenSSH 9.6p1
[FreeBSD/FreeBSD.git] / usr.sbin / cpucontrol / amd10h.c
1 /*-
2  * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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.
24  */
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/mman.h>
29 #include <sys/ioctl.h>
30 #include <sys/ioccom.h>
31 #include <sys/cpuctl.h>
32
33 #include <machine/cpufunc.h>
34 #include <machine/specialreg.h>
35
36 #include <assert.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <err.h>
43
44 #include "cpucontrol.h"
45 #include "amd.h"
46
47 int
48 amd10h_probe(int fd)
49 {
50         char vendor[13];
51         cpuctl_cpuid_args_t idargs;
52         uint32_t family;
53         uint32_t signature;
54         int error;
55
56         idargs.level = 0;
57         error = ioctl(fd, CPUCTL_CPUID, &idargs);
58         if (error < 0) {
59                 WARN(0, "ioctl()");
60                 return (1);
61         }
62         ((uint32_t *)vendor)[0] = idargs.data[1];
63         ((uint32_t *)vendor)[1] = idargs.data[3];
64         ((uint32_t *)vendor)[2] = idargs.data[2];
65         vendor[12] = '\0';
66         if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0)
67                 return (1);
68
69         idargs.level = 1;
70         error = ioctl(fd, CPUCTL_CPUID, &idargs);
71         if (error < 0) {
72                 WARN(0, "ioctl()");
73                 return (1);
74         }
75         signature = idargs.data[0];
76         family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff);
77         if (family < 0x10)
78                 return (1);
79         return (0);
80 }
81
82 /*
83  * NB: the format of microcode update files is not documented by AMD.
84  * It has been reverse engineered from studying Coreboot, illumos and Linux
85  * source code.
86  */
87 void
88 amd10h_update(const struct ucode_update_params *params)
89 {
90         cpuctl_cpuid_args_t idargs;
91         cpuctl_msr_args_t msrargs;
92         cpuctl_update_args_t args;
93         const amd_10h_fw_header_t *fw_header;
94         const amd_10h_fw_header_t *selected_fw;
95         const equiv_cpu_entry_t *equiv_cpu_table;
96         const section_header_t *section_header;
97         const container_header_t *container_header;
98         const uint8_t *fw_data;
99         const uint8_t *fw_image;
100         const char *dev, *path;
101         size_t fw_size;
102         size_t selected_size;
103         uint32_t revision;
104         uint32_t new_rev;
105         uint32_t signature;
106         uint16_t equiv_id;
107         int devfd;
108         unsigned int i;
109         int error;
110
111         dev = params->dev_path;
112         path = params->fw_path;
113         devfd = params->devfd;
114         fw_image = params->fwimage;
115         fw_size = params->fwsize;
116
117         assert(path);
118         assert(dev);
119
120         idargs.level = 1;
121         error = ioctl(devfd, CPUCTL_CPUID, &idargs);
122         if (error < 0) {
123                 WARN(0, "ioctl()");
124                 goto done;
125         }
126         signature = idargs.data[0];
127
128         msrargs.msr = MSR_BIOS_SIGN;
129         error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
130         if (error < 0) {
131                 WARN(0, "ioctl(%s)", dev);
132                 goto done;
133         }
134         revision = (uint32_t)msrargs.data;
135
136         WARNX(1, "found cpu family %#x model %#x "
137             "stepping %#x extfamily %#x extmodel %#x.",
138             ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff),
139             (signature >> 4) & 0x0f,
140             (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
141             (signature >> 16) & 0x0f);
142         WARNX(1, "microcode revision %#x", revision);
143
144         /*
145          * Open the firmware file.
146          */
147         WARNX(1, "checking %s for update.", path);
148         if (fw_size <
149             (sizeof(*container_header) + sizeof(*section_header))) {
150                 WARNX(2, "file too short: %s", path);
151                 goto done;
152         }
153
154         /*
155          * mmap the whole image.
156          */
157         fw_data = fw_image;
158         container_header = (const container_header_t *)fw_data;
159         if (container_header->magic != AMD_10H_MAGIC) {
160                 WARNX(2, "%s is not a valid amd firmware: bad magic", path);
161                 goto done;
162         }
163         fw_data += sizeof(*container_header);
164         fw_size -= sizeof(*container_header);
165
166         section_header = (const section_header_t *)fw_data;
167         if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
168                 WARNX(2, "%s is not a valid amd firmware: "
169                     "first section is not CPU equivalence table", path);
170                 goto done;
171         }
172         if (section_header->size == 0) {
173                 WARNX(2, "%s is not a valid amd firmware: "
174                     "first section is empty", path);
175                 goto done;
176         }
177         fw_data += sizeof(*section_header);
178         fw_size -= sizeof(*section_header);
179
180         if (section_header->size > fw_size) {
181                 WARNX(2, "%s is not a valid amd firmware: "
182                     "file is truncated", path);
183                 goto done;
184         }
185         if (section_header->size < sizeof(*equiv_cpu_table)) {
186                 WARNX(2, "%s is not a valid amd firmware: "
187                     "first section is too short", path);
188                 goto done;
189         }
190         equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
191         fw_data += section_header->size;
192         fw_size -= section_header->size;
193
194         equiv_id = 0;
195         for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
196                 if (signature == equiv_cpu_table[i].installed_cpu) {
197                         equiv_id = equiv_cpu_table[i].equiv_cpu;
198                         WARNX(3, "equiv_id: %x, signature %8x,"
199                             " equiv_cpu_table[%d] %8x", equiv_id, signature,
200                             i, equiv_cpu_table[i].installed_cpu);
201                         break;
202                 }
203         }
204         if (equiv_id == 0) {
205                 WARNX(2, "CPU is not found in the equivalence table");
206                 goto done;
207         }
208
209         selected_fw = NULL;
210         selected_size = 0;
211         while (fw_size >= sizeof(*section_header)) {
212                 section_header = (const section_header_t *)fw_data;
213                 fw_data += sizeof(*section_header);
214                 fw_size -= sizeof(*section_header);
215                 if (section_header->type != AMD_10H_uCODE_TYPE) {
216                         WARNX(2, "%s is not a valid amd firmware: "
217                             "section has incorrect type", path);
218                         goto done;
219                 }
220                 if (section_header->size > fw_size) {
221                         WARNX(2, "%s is not a valid amd firmware: "
222                             "file is truncated", path);
223                         goto done;
224                 }
225                 if (section_header->size < sizeof(*fw_header)) {
226                         WARNX(2, "%s is not a valid amd firmware: "
227                             "section is too short", path);
228                         goto done;
229                 }
230                 fw_header = (const amd_10h_fw_header_t *)fw_data;
231                 fw_data += section_header->size;
232                 fw_size -= section_header->size;
233
234                 if (fw_header->processor_rev_id != equiv_id) {
235                         WARNX(1, "firmware processor_rev_id %x, equiv_id %x",
236                             fw_header->processor_rev_id, equiv_id);
237                         continue; /* different cpu */
238                 }
239                 if (fw_header->patch_id <= revision) {
240                         WARNX(1, "patch_id %x, revision %x",
241                             fw_header->patch_id, revision);
242                         continue; /* not newer revision */
243                 }
244                 if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
245                         WARNX(2, "Chipset-specific microcode is not supported");
246                 }
247
248                 WARNX(3, "selecting revision: %x", fw_header->patch_id);
249                 revision = fw_header->patch_id;
250                 selected_fw = fw_header;
251                 selected_size = section_header->size;
252         }
253
254         if (fw_size != 0) {
255                 WARNX(2, "%s is not a valid amd firmware: "
256                     "file is truncated", path);
257                 goto done;
258         }
259
260         if (selected_fw != NULL) {
261                 WARNX(1, "selected ucode size is %zu", selected_size);
262                 fprintf(stderr, "%s: updating cpu %s to revision %#x... ",
263                     path, dev, revision);
264
265                 args.data = __DECONST(void *, selected_fw);
266                 args.size = selected_size;
267                 error = ioctl(devfd, CPUCTL_UPDATE, &args);
268                 if (error < 0) {
269                         fprintf(stderr, "failed.\n");
270                         warn("ioctl()");
271                         goto done;
272                 }
273                 fprintf(stderr, "done.\n");
274         }
275
276         msrargs.msr = MSR_BIOS_SIGN;
277         error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
278         if (error < 0) {
279                 WARN(0, "ioctl(%s)", dev);
280                 goto done;
281         }
282         new_rev = (uint32_t)msrargs.data;
283         if (new_rev != revision)
284                 WARNX(0, "revision after update %#x", new_rev);
285
286 done:
287         return;
288 }