]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/x86/x86/ucode.c
Merge OpenSSL 1.0.2p.
[FreeBSD/FreeBSD.git] / sys / x86 / x86 / ucode.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 The FreeBSD Foundation
5  *
6  * This software was developed by Mark Johnston under sponsorship from
7  * the FreeBSD Foundation.
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/cpuset.h>
36 #include <sys/kernel.h>
37 #include <sys/linker.h>
38 #include <sys/malloc.h>
39 #include <sys/pcpu.h>
40 #include <sys/smp.h>
41 #include <sys/systm.h>
42
43 #include <machine/atomic.h>
44 #include <machine/cpufunc.h>
45 #include <x86/specialreg.h>
46 #include <machine/stdarg.h>
47 #include <x86/ucode.h>
48 #include <x86/x86_smp.h>
49
50 #include <vm/vm.h>
51 #include <vm/pmap.h>
52 #include <vm/vm_extern.h>
53 #include <vm/vm_kern.h>
54 #include <vm/vm_param.h>
55
56 static void     *ucode_intel_match(uint8_t *data, size_t *len);
57 static int      ucode_intel_verify(struct ucode_intel_header *hdr,
58                     size_t resid);
59
60 static struct ucode_ops {
61         const char *vendor;
62         int (*load)(void *, bool);
63         void *(*match)(uint8_t *, size_t *);
64 } loaders[] = {
65         {
66                 .vendor = INTEL_VENDOR_ID,
67                 .load = ucode_intel_load,
68                 .match = ucode_intel_match,
69         },
70 };
71
72 /* Selected microcode update data. */
73 static void *early_ucode_data;
74 static void *ucode_data;
75
76 static char errbuf[128];
77
78 static void __printflike(1, 2)
79 log_err(const char *fmt, ...)
80 {
81         va_list ap;
82
83         va_start(ap, fmt);
84         vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
85         va_end(ap);
86 }
87
88 static void
89 print_err(void *arg __unused)
90 {
91
92         if (errbuf[0] != '\0')
93                 printf("microcode load error: %s\n", errbuf);
94 }
95 SYSINIT(ucode_print_err, SI_SUB_CPU, SI_ORDER_FIRST, print_err, NULL);
96
97 int
98 ucode_intel_load(void *data, bool unsafe)
99 {
100         uint64_t rev0, rev1;
101         uint32_t cpuid[4];
102
103         rev0 = rdmsr(MSR_BIOS_SIGN);
104
105         /*
106          * Perform update.  Flush caches first to work around seemingly
107          * undocumented errata applying to some Broadwell CPUs.
108          */
109         wbinvd();
110         if (unsafe)
111                 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
112         else
113                 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
114         wrmsr(MSR_BIOS_SIGN, 0);
115
116         /*
117          * Serialize instruction flow.
118          */
119         do_cpuid(0, cpuid);
120
121         rev1 = rdmsr(MSR_BIOS_SIGN);
122         if (rev1 <= rev0)
123                 return (EEXIST);
124         return (0);
125 }
126
127 static int
128 ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid)
129 {
130         uint32_t cksum, *data, size;
131         int i;
132
133         if (resid < sizeof(struct ucode_intel_header)) {
134                 log_err("truncated update header");
135                 return (1);
136         }
137         size = hdr->total_size;
138         if (size == 0)
139                 size = UCODE_INTEL_DEFAULT_DATA_SIZE +
140                     sizeof(struct ucode_intel_header);
141
142         if (hdr->header_version != 1) {
143                 log_err("unexpected header version %u", hdr->header_version);
144                 return (1);
145         }
146         if (size % 16 != 0) {
147                 log_err("unexpected update size %u", hdr->total_size);
148                 return (1);
149         }
150         if (resid < size) {
151                 log_err("truncated update");
152                 return (1);
153         }
154
155         cksum = 0;
156         data = (uint32_t *)hdr;
157         for (i = 0; i < size / sizeof(uint32_t); i++)
158                 cksum += data[i];
159         if (cksum != 0) {
160                 log_err("checksum failed");
161                 return (1);
162         }
163         return (0);
164 }
165
166 static void *
167 ucode_intel_match(uint8_t *data, size_t *len)
168 {
169         struct ucode_intel_header *hdr;
170         struct ucode_intel_extsig_table *table;
171         struct ucode_intel_extsig *entry;
172         uint64_t platformid;
173         size_t resid;
174         uint32_t data_size, flags, regs[4], sig, total_size;
175         int i;
176
177         do_cpuid(1, regs);
178         sig = regs[0];
179
180         platformid = rdmsr(MSR_IA32_PLATFORM_ID);
181         flags = 1 << ((platformid >> 50) & 0x7);
182
183         for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
184                 hdr = (struct ucode_intel_header *)data;
185                 if (ucode_intel_verify(hdr, resid) != 0)
186                         break;
187
188                 data_size = hdr->data_size;
189                 total_size = hdr->total_size;
190                 if (data_size == 0)
191                         data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
192                 if (total_size == 0)
193                         total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
194                             sizeof(struct ucode_intel_header);
195                 if (data_size > total_size + sizeof(struct ucode_intel_header))
196                         table = (struct ucode_intel_extsig_table *)
197                             ((uint8_t *)(hdr + 1) + data_size);
198                 else
199                         table = NULL;
200
201                 if (hdr->processor_signature == sig) {
202                         if ((hdr->processor_flags & flags) != 0) {
203                                 *len = data_size;
204                                 return (hdr + 1);
205                         }
206                 } else if (table != NULL) {
207                         for (i = 0; i < table->signature_count; i++) {
208                                 entry = &table->entries[i];
209                                 if (entry->processor_signature == sig &&
210                                     (entry->processor_flags & flags) != 0) {
211                                         *len = data_size;
212                                         return (hdr + 1);
213                                 }
214                         }
215                 }
216         }
217         return (NULL);
218 }
219
220 /*
221  * Release any memory backing unused microcode blobs back to the system.
222  * We copy the selected update and free the entire microcode file.
223  */
224 static void
225 ucode_release(void *arg __unused)
226 {
227         char *name, *type;
228         caddr_t file;
229         int release;
230
231         if (early_ucode_data == NULL)
232                 return;
233         release = 1;
234         TUNABLE_INT_FETCH("debug.ucode.release", &release);
235         if (!release)
236                 return;
237
238 restart:
239         file = 0;
240         for (;;) {
241                 file = preload_search_next_name(file);
242                 if (file == 0)
243                         break;
244                 type = (char *)preload_search_info(file, MODINFO_TYPE);
245                 if (type == NULL || strcmp(type, "cpu_microcode") != 0)
246                         continue;
247
248                 name = preload_search_info(file, MODINFO_NAME);
249                 preload_delete_name(name);
250                 goto restart;
251         }
252 }
253 SYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL);
254
255 void
256 ucode_load_ap(int cpu)
257 {
258 #ifdef SMP
259         KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
260             ("cpu %d not present", cpu));
261
262         if (!cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
263                 return;
264 #endif
265
266         if (ucode_data != NULL)
267                 (void)ucode_intel_load(ucode_data, false);
268 }
269
270 static void *
271 map_ucode(vm_paddr_t free, size_t len)
272 {
273
274 #ifdef __i386__
275         for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
276                 pmap_kenter(pa, pa);
277 #else
278         (void)len;
279 #endif
280         return ((void *)free);
281 }
282
283 static void
284 unmap_ucode(vm_paddr_t free, size_t len)
285 {
286
287 #ifdef __i386__
288         for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
289                 pmap_kremove((vm_offset_t)pa);
290 #else
291         (void)free;
292         (void)len;
293 #endif
294 }
295
296 /*
297  * Search for an applicable microcode update, and load it.  APs will load the
298  * selected update once they come online.
299  *
300  * "free" is the address of the next free physical page.  If a microcode update
301  * is selected, it will be copied to this region prior to loading in order to
302  * satisfy alignment requirements.
303  */
304 size_t
305 ucode_load_bsp(uintptr_t free)
306 {
307         union {
308                 uint32_t regs[4];
309                 char vendor[13];
310         } cpuid;
311         struct ucode_ops *loader;
312         uint8_t *addr, *fileaddr, *match;
313         char *type;
314         caddr_t file;
315         size_t i, len, ucode_len;
316
317         KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
318
319         do_cpuid(0, cpuid.regs);
320         cpuid.regs[0] = cpuid.regs[1];
321         cpuid.regs[1] = cpuid.regs[3];
322         cpuid.vendor[12] = '\0';
323         for (i = 0, loader = NULL; i < nitems(loaders); i++)
324                 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
325                         loader = &loaders[i];
326                         break;
327                 }
328         if (loader == NULL)
329                 return (0);
330
331         file = 0;
332         fileaddr = match = NULL;
333         ucode_len = 0;
334         for (;;) {
335                 file = preload_search_next_name(file);
336                 if (file == 0)
337                         break;
338                 type = (char *)preload_search_info(file, MODINFO_TYPE);
339                 if (type == NULL || strcmp(type, "cpu_microcode") != 0)
340                         continue;
341
342                 fileaddr = preload_fetch_addr(file);
343                 len = preload_fetch_size(file);
344                 match = loader->match(fileaddr, &len);
345                 if (match != NULL) {
346                         addr = map_ucode(free, len);
347                         /* We can't use memcpy() before ifunc resolution. */
348                         for (i = 0; i < len; i++)
349                                 addr[i] = match[i];
350                         match = addr;
351
352                         if (loader->load(match, false) == 0) {
353                                 ucode_data = match;
354                                 ucode_len = len;
355                                 early_ucode_data = ucode_data;
356                                 break;
357                         }
358                         unmap_ucode(free, len);
359                 }
360         }
361         if (fileaddr != NULL && ucode_data == NULL)
362                 log_err("no matching update found");
363         return (ucode_len);
364 }
365
366 /*
367  * Reload microcode following an ACPI resume.
368  */
369 void
370 ucode_reload(void)
371 {
372
373         ucode_load_ap(PCPU_GET(cpuid));
374 }
375
376 /*
377  * Replace an existing microcode update.
378  */
379 void *
380 ucode_update(void *newdata)
381 {
382
383         newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
384             (uintptr_t)newdata);
385         if (newdata == early_ucode_data)
386                 newdata = NULL;
387         return (newdata);
388 }