]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/x86/x86/ucode.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ release_70 branch
[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, uint64_t *, uint64_t *);
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 static struct ucode_ops *ucode_loader;
76
77 /* Variables used for reporting success or failure. */
78 enum {
79         NO_ERROR,
80         NO_MATCH,
81         VERIFICATION_FAILED,
82 } ucode_error = NO_ERROR;
83 static uint64_t ucode_nrev, ucode_orev;
84
85 static void
86 log_msg(void *arg __unused)
87 {
88
89         if (ucode_nrev != 0) {
90                 printf("CPU microcode: updated from %#jx to %#jx\n",
91                     (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev);
92                 return;
93         }
94
95         switch (ucode_error) {
96         case NO_MATCH:
97                 printf("CPU microcode: no matching update found\n");
98                 break;
99         case VERIFICATION_FAILED:
100                 printf("CPU microcode: microcode verification failed\n");
101                 break;
102         default:
103                 break;
104         }
105 }
106 SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL);
107
108 int
109 ucode_intel_load(void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
110 {
111         uint64_t nrev, orev;
112         uint32_t cpuid[4];
113
114         orev = rdmsr(MSR_BIOS_SIGN) >> 32;
115
116         /*
117          * Perform update.  Flush caches first to work around seemingly
118          * undocumented errata applying to some Broadwell CPUs.
119          */
120         wbinvd();
121         if (unsafe)
122                 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
123         else
124                 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
125         wrmsr(MSR_BIOS_SIGN, 0);
126
127         /*
128          * Serialize instruction flow.
129          */
130         do_cpuid(0, cpuid);
131
132         /*
133          * Verify that the microcode revision changed.
134          */
135         nrev = rdmsr(MSR_BIOS_SIGN) >> 32;
136         if (nrevp != NULL)
137                 *nrevp = nrev;
138         if (orevp != NULL)
139                 *orevp = orev;
140         if (nrev <= orev)
141                 return (EEXIST);
142         return (0);
143 }
144
145 static int
146 ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid)
147 {
148         uint32_t cksum, *data, size;
149         int i;
150
151         if (resid < sizeof(struct ucode_intel_header))
152                 return (1);
153         size = hdr->total_size;
154         if (size == 0)
155                 size = UCODE_INTEL_DEFAULT_DATA_SIZE +
156                     sizeof(struct ucode_intel_header);
157
158         if (hdr->header_version != 1)
159                 return (1);
160         if (size % 16 != 0)
161                 return (1);
162         if (resid < size)
163                 return (1);
164
165         cksum = 0;
166         data = (uint32_t *)hdr;
167         for (i = 0; i < size / sizeof(uint32_t); i++)
168                 cksum += data[i];
169         if (cksum != 0)
170                 return (1);
171         return (0);
172 }
173
174 static void *
175 ucode_intel_match(uint8_t *data, size_t *len)
176 {
177         struct ucode_intel_header *hdr;
178         struct ucode_intel_extsig_table *table;
179         struct ucode_intel_extsig *entry;
180         uint64_t platformid;
181         size_t resid;
182         uint32_t data_size, flags, regs[4], sig, total_size;
183         int i;
184
185         do_cpuid(1, regs);
186         sig = regs[0];
187
188         platformid = rdmsr(MSR_IA32_PLATFORM_ID);
189         flags = 1 << ((platformid >> 50) & 0x7);
190
191         for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
192                 hdr = (struct ucode_intel_header *)data;
193                 if (ucode_intel_verify(hdr, resid) != 0) {
194                         ucode_error = VERIFICATION_FAILED;
195                         break;
196                 }
197
198                 data_size = hdr->data_size;
199                 total_size = hdr->total_size;
200                 if (data_size == 0)
201                         data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
202                 if (total_size == 0)
203                         total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
204                             sizeof(struct ucode_intel_header);
205                 if (data_size > total_size + sizeof(struct ucode_intel_header))
206                         table = (struct ucode_intel_extsig_table *)
207                             ((uint8_t *)(hdr + 1) + data_size);
208                 else
209                         table = NULL;
210
211                 if (hdr->processor_signature == sig) {
212                         if ((hdr->processor_flags & flags) != 0) {
213                                 *len = data_size;
214                                 return (hdr + 1);
215                         }
216                 } else if (table != NULL) {
217                         for (i = 0; i < table->signature_count; i++) {
218                                 entry = &table->entries[i];
219                                 if (entry->processor_signature == sig &&
220                                     (entry->processor_flags & flags) != 0) {
221                                         *len = data_size;
222                                         return (hdr + 1);
223                                 }
224                         }
225                 }
226         }
227         return (NULL);
228 }
229
230 /*
231  * Release any memory backing unused microcode blobs back to the system.
232  * We copy the selected update and free the entire microcode file.
233  */
234 static void
235 ucode_release(void *arg __unused)
236 {
237         char *name, *type;
238         caddr_t file;
239         int release;
240
241         if (early_ucode_data == NULL)
242                 return;
243         release = 1;
244         TUNABLE_INT_FETCH("debug.ucode.release", &release);
245         if (!release)
246                 return;
247
248 restart:
249         file = 0;
250         for (;;) {
251                 file = preload_search_next_name(file);
252                 if (file == 0)
253                         break;
254                 type = (char *)preload_search_info(file, MODINFO_TYPE);
255                 if (type == NULL || strcmp(type, "cpu_microcode") != 0)
256                         continue;
257
258                 name = preload_search_info(file, MODINFO_NAME);
259                 preload_delete_name(name);
260                 goto restart;
261         }
262 }
263 SYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL);
264
265 void
266 ucode_load_ap(int cpu)
267 {
268 #ifdef SMP
269         KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
270             ("cpu %d not present", cpu));
271
272         if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
273                 return;
274 #endif
275
276         if (ucode_data != NULL)
277                 (void)ucode_loader->load(ucode_data, false, NULL, NULL);
278 }
279
280 static void *
281 map_ucode(vm_paddr_t free, size_t len)
282 {
283
284 #ifdef __i386__
285         for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
286                 pmap_kenter(pa, pa);
287 #else
288         (void)len;
289 #endif
290         return ((void *)free);
291 }
292
293 static void
294 unmap_ucode(vm_paddr_t free, size_t len)
295 {
296
297 #ifdef __i386__
298         for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
299                 pmap_kremove((vm_offset_t)pa);
300 #else
301         (void)free;
302         (void)len;
303 #endif
304 }
305
306 /*
307  * Search for an applicable microcode update, and load it.  APs will load the
308  * selected update once they come online.
309  *
310  * "free" is the address of the next free physical page.  If a microcode update
311  * is selected, it will be copied to this region prior to loading in order to
312  * satisfy alignment requirements.
313  */
314 size_t
315 ucode_load_bsp(uintptr_t free)
316 {
317         union {
318                 uint32_t regs[4];
319                 char vendor[13];
320         } cpuid;
321         uint8_t *addr, *fileaddr, *match;
322         char *type;
323         uint64_t nrev, orev;
324         caddr_t file;
325         size_t i, len;
326         int error;
327
328         KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
329
330         do_cpuid(0, cpuid.regs);
331         cpuid.regs[0] = cpuid.regs[1];
332         cpuid.regs[1] = cpuid.regs[3];
333         cpuid.vendor[12] = '\0';
334         for (i = 0; i < nitems(loaders); i++)
335                 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
336                         ucode_loader = &loaders[i];
337                         break;
338                 }
339         if (ucode_loader == NULL)
340                 return (0);
341
342         file = 0;
343         fileaddr = match = NULL;
344         for (;;) {
345                 file = preload_search_next_name(file);
346                 if (file == 0)
347                         break;
348                 type = (char *)preload_search_info(file, MODINFO_TYPE);
349                 if (type == NULL || strcmp(type, "cpu_microcode") != 0)
350                         continue;
351
352                 fileaddr = preload_fetch_addr(file);
353                 len = preload_fetch_size(file);
354                 match = ucode_loader->match(fileaddr, &len);
355                 if (match != NULL) {
356                         addr = map_ucode(free, len);
357                         /* We can't use memcpy() before ifunc resolution. */
358                         memcpy_early(addr, match, len);
359                         match = addr;
360
361                         error = ucode_loader->load(match, false, &nrev, &orev);
362                         if (error == 0) {
363                                 ucode_data = early_ucode_data = match;
364                                 ucode_nrev = nrev;
365                                 ucode_orev = orev;
366                                 return (len);
367                         }
368                         unmap_ucode(free, len);
369                 }
370         }
371         if (fileaddr != NULL && ucode_error == NO_ERROR)
372                 ucode_error = NO_MATCH;
373         return (0);
374 }
375
376 /*
377  * Reload microcode following an ACPI resume.
378  */
379 void
380 ucode_reload(void)
381 {
382
383         ucode_load_ap(PCPU_GET(cpuid));
384 }
385
386 /*
387  * Replace an existing microcode update.
388  */
389 void *
390 ucode_update(void *newdata)
391 {
392
393         newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
394             (uintptr_t)newdata);
395         if (newdata == early_ucode_data)
396                 newdata = NULL;
397         return (newdata);
398 }