xref: /dragonfly/usr.sbin/cpucontrol/intel.c (revision 7ce1da6a736446c2bb49379f6647bd0d5551b920)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <err.h>
38 #include <errno.h>
39 
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/mman.h>
43 #include <sys/ioctl.h>
44 #include <sys/ioccom.h>
45 #include <sys/cpuctl.h>
46 
47 #include <machine/cpufunc.h>
48 #include <machine/specialreg.h>
49 
50 #include "cpucontrol.h"
51 #include "intel.h"
52 
53 #define   DEFAULT_UCODE_SIZE  2000 /* Size of update data if not specified. */
54 
55 int
intel_probe(int fd)56 intel_probe(int fd)
57 {
58           char vendor[13];
59           int error;
60           cpuctl_cpuid_args_t idargs = {
61                     .level  = 0,
62           };
63 
64           error = ioctl(fd, CPUCTL_CPUID, &idargs);
65           if (error < 0) {
66                     WARN(0, "ioctl()");
67                     return (1);
68           }
69           ((uint32_t *)vendor)[0] = idargs.data[1];
70           ((uint32_t *)vendor)[1] = idargs.data[3];
71           ((uint32_t *)vendor)[2] = idargs.data[2];
72           vendor[12] = '\0';
73           if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) != 0)
74                     return (1);
75           return (0);
76 }
77 
78 void
intel_update(const char * dev,const char * path)79 intel_update(const char *dev, const char *path)
80 {
81           int fd, devfd;
82           struct stat st;
83           uint32_t *fw_image;
84           int have_ext_table;
85           uint32_t sum;
86           unsigned int i;
87           size_t payload_size;
88           intel_fw_header_t *fw_header;
89           intel_cpu_signature_t *ext_table;
90           intel_ext_header_t *ext_header;
91           uint32_t sig, signature, flags;
92           int32_t revision;
93           ssize_t ext_size;
94           size_t ext_table_size;
95           void *fw_data;
96           size_t data_size, total_size;
97           cpuctl_msr_args_t msrargs = {
98                     .msr = MSR_IA32_PLATFORM_ID,
99           };
100           cpuctl_cpuid_args_t idargs = {
101                     .level  = 1,        /* Signature. */
102           };
103           cpuctl_update_args_t args;
104           int error;
105 
106           assert(path);
107           assert(dev);
108 
109           fd = -1;
110           fw_image = MAP_FAILED;
111           ext_table = NULL;
112           ext_header = NULL;
113           devfd = open(dev, O_RDWR);
114           if (devfd < 0) {
115                     WARN(0, "could not open %s for writing", dev);
116                     return;
117           }
118           error = ioctl(devfd, CPUCTL_CPUID, &idargs);
119           if (error < 0) {
120                     WARN(0, "ioctl(%s)", dev);
121                     goto fail;
122           }
123           signature = idargs.data[0];
124           error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
125           if (error < 0) {
126                     WARN(0, "ioctl(%s)", dev);
127                     goto fail;
128           }
129 
130           /*
131            * MSR_IA32_PLATFORM_ID contains flag in BCD in bits 52-50.
132            */
133           flags = 1 << ((msrargs.data >> 50) & 7);
134           msrargs.msr = MSR_BIOS_SIGN;
135           error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
136           if (error < 0) {
137                     WARN(0, "ioctl(%s)", dev);
138                     goto fail;
139           }
140           revision = msrargs.data >> 32; /* Revision in the high dword. */
141           WARNX(2, "found cpu type %#x family %#x model %#x stepping %#x.",
142               (signature >> 12) & 0x03, (signature >> 8) & 0x0f,
143               (signature >> 4) & 0x0f, (signature >> 0) & 0x0f);
144           /*
145            * Open firmware image.
146            */
147           fd = open(path, O_RDONLY, 0);
148           if (fd < 0) {
149                     WARN(0, "open(%s)", path);
150                     goto fail;
151           }
152           error = fstat(fd, &st);
153           if (error != 0) {
154                     WARN(0, "fstat(%s)", path);
155                     goto fail;
156           }
157           if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
158                     WARNX(2, "file too short: %s", path);
159                     goto fail;
160           }
161 
162           /*
163            * mmap the whole image.
164            */
165           fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
166               MAP_PRIVATE, fd, 0);
167           if  (fw_image == MAP_FAILED) {
168                     WARN(0, "mmap(%s)", path);
169                     goto fail;
170           }
171           fw_header = (intel_fw_header_t *)fw_image;
172           if (fw_header->header_version != INTEL_HEADER_VERSION ||
173               fw_header->loader_revision != INTEL_LOADER_REVISION) {
174                     WARNX(2, "%s is not a valid intel firmware: version mismatch",
175                         path);
176                     goto fail;
177           }
178           /*
179            * According to spec, if data_size == 0, then size of ucode = 2000.
180            */
181           if (fw_header->data_size == 0)
182                     data_size = DEFAULT_UCODE_SIZE;
183           else
184                     data_size = fw_header->data_size;
185           if (fw_header->total_size == 0)
186                     total_size = data_size + sizeof(*fw_header);
187           else
188                     total_size = fw_header->total_size;
189           if (total_size > (unsigned)st.st_size || st.st_size < 0) {
190                     WARNX(2, "file too short: %s", path);
191                     goto fail;
192           }
193           payload_size = data_size + sizeof(*fw_header);
194 
195           /*
196            * Check the primary checksum.
197            */
198           sum = 0;
199           for (i = 0; i < (payload_size / sizeof(uint32_t)); i++)
200                     sum += *((uint32_t *)fw_image + i);
201           if (sum != 0) {
202                     WARNX(2, "%s: update data checksum invalid", path);
203                     goto fail;
204           }
205 
206           /*
207            * Check if there is an extended signature table.
208            */
209           ext_size = total_size - payload_size;
210           have_ext_table = 0;
211 
212           if (ext_size > (signed)sizeof(*ext_header)) {
213                     ext_header =
214                         (intel_ext_header_t *)((char *)fw_image + payload_size);
215                     ext_table = (intel_cpu_signature_t *)(ext_header + 1);
216 
217                     /*
218                      * Check the extended table size.
219                      */
220                     ext_table_size = sizeof(*ext_header) +
221                         ext_header->sig_count * sizeof(*ext_table);
222                     if (ext_table_size + payload_size > total_size) {
223                               WARNX(2, "%s: broken extended signature table", path);
224                               goto no_table;
225                     }
226 
227                     /*
228                      * Check the extended table signature.
229                      */
230                     sum = 0;
231                     for (i = 0; i < (ext_table_size / sizeof(uint32_t)); i++)
232                               sum += *((uint32_t *)ext_header + i);
233                     if (sum != 0) {
234                               WARNX(2,
235                                   "%s: extended signature table checksum invalid",
236                                   path);
237                               goto no_table;
238                     }
239                     have_ext_table = 1;
240           }
241 
242 no_table:
243           fw_data = fw_header + 1; /* Pointer to the update data. */
244 
245           /*
246            * Check if the given image is ok for this cpu.
247            */
248           if (signature == fw_header->cpu_signature &&
249               (flags & fw_header->cpu_flags) != 0)
250                     goto matched;
251           else if (have_ext_table != 0) {
252                     for (i = 0; i < ext_header->sig_count; i++) {
253                               sig = ext_table[i].cpu_signature;
254                               if (signature == sig &&
255                                   (flags & ext_table[i].cpu_flags) != 0)
256                                         goto matched;
257                     }
258           } else
259                     goto fail;
260 
261 matched:
262           if (revision >= fw_header->revision) {
263                     WARNX(1, "skipping %s of rev %#x: up to date",
264                         path, fw_header->revision);
265                     goto fail;
266           }
267           fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
268               path, dev, revision, fw_header->revision);
269           args.data = fw_data;
270           args.size = data_size;
271           error = ioctl(devfd, CPUCTL_UPDATE, &args);
272           if (error < 0) {
273                     error = errno;
274                     fprintf(stderr, "failed.\n");
275                     errno = error;
276                     WARN(0, "ioctl()");
277                     goto fail;
278           }
279           fprintf(stderr, "done.\n");
280 
281 fail:
282           if (fw_image != MAP_FAILED)
283                     if (munmap(fw_image, st.st_size) != 0)
284                               warn("munmap(%s)", path);
285           if (devfd >= 0)
286                     close(devfd);
287           if (fd >= 0)
288                     close(fd);
289 }
290