--- /dev/null
+/* $OpenBSD: ucode.c,v 1.1 2018/08/23 14:47:52 jsg Exp $ */
+/*
+ * Copyright (c) 2018 Stefan Fritsch <fritsch@genua.de>
+ * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+#include <machine/biosvar.h>
+
+/* #define UCODE_DEBUG */
+#ifdef UCODE_DEBUG
+#define DPRINTF(x) do { if (cpu_ucode_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (cpu_ucode_debug >= (n)) printf x; } while (0)
+int cpu_ucode_debug = 1;
+#else
+#define DPRINTF(x) do { ; } while (0)
+#define DPRINTFN(n, x) do { ; } while (0)
+#endif
+
+struct intel_ucode_header {
+ uint32_t header_version;
+ uint32_t update_revision;
+ uint32_t date;
+ uint32_t processor_sig;
+ uint32_t checksum;
+ uint32_t loader_rev;
+ uint32_t processor_flags;
+ uint32_t data_size;
+ uint32_t total_size;
+ uint32_t reserved[3];
+};
+
+struct intel_ucode_ext_sig_header {
+ uint32_t ext_sig_count;
+ uint32_t checksum;
+ uint32_t reserved[3];
+};
+
+struct intel_ucode_ext_sig {
+ uint32_t processor_sig;
+ uint32_t processor_flags;
+ uint32_t checksum;
+};
+
+#define INTEL_UCODE_DEFAULT_DATA_SIZE 2000
+
+/* Generic */
+char * cpu_ucode_data;
+size_t cpu_ucode_size;
+
+void cpu_ucode_setup(void);
+void cpu_ucode_apply(struct cpu_info *);
+
+/* Intel */
+void cpu_ucode_intel_apply(struct cpu_info *);
+struct intel_ucode_header *
+ cpu_ucode_intel_find(char *, size_t, uint32_t);
+int cpu_ucode_intel_verify(struct intel_ucode_header *);
+int cpu_ucode_intel_match(struct intel_ucode_header *, uint32_t, uint32_t,
+ uint32_t);
+uint32_t cpu_ucode_intel_rev(void);
+
+struct intel_ucode_header *cpu_ucode_intel_applied;
+struct mutex cpu_ucode_intel_mtx = MUTEX_INITIALIZER(IPL_HIGH);
+
+void
+cpu_ucode_setup(void)
+{
+ vaddr_t va;
+ paddr_t pa;
+ int i, npages;
+ size_t size;
+
+ if (bios_ucode == NULL)
+ return;
+
+ if (!bios_ucode->uc_addr || !bios_ucode->uc_size)
+ return;
+
+ cpu_ucode_size = bios_ucode->uc_size;
+ size = round_page(bios_ucode->uc_size);
+ npages = size / PAGE_SIZE;
+
+ va = uvm_km_valloc(kernel_map, size);
+ if (va == 0)
+ return;
+ for (i = 0; i < npages; i++) {
+ pa = bios_ucode->uc_addr + (i * PAGE_SIZE);
+ pmap_enter(pmap_kernel(), va + (i * PAGE_SIZE), pa,
+ PROT_READ,
+ PROT_READ | PMAP_WIRED);
+ pmap_update(pmap_kernel());
+ }
+
+ cpu_ucode_data = malloc(cpu_ucode_size, M_DEVBUF, M_WAITOK);
+
+ memcpy((void *)cpu_ucode_data, (void *)va, cpu_ucode_size);
+
+ pmap_remove(pmap_kernel(), va, va + size);
+ pmap_update(pmap_kernel());
+ uvm_km_free(kernel_map, va, size);
+}
+
+/*
+ * Called per-CPU.
+ */
+void
+cpu_ucode_apply(struct cpu_info *ci)
+{
+ if (strcmp(cpu_vendor, "GenuineIntel") == 0)
+ cpu_ucode_intel_apply(ci);
+}
+
+void
+cpu_ucode_intel_apply(struct cpu_info *ci)
+{
+ struct intel_ucode_header *update;
+ uint32_t old_rev, new_rev;
+ paddr_t data;
+
+ if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
+ DPRINTF(("%s: no microcode provided\n", __func__));
+ return;
+ }
+
+ /*
+ * Grab a mutex, because we are not allowed to run updates
+ * simultaneously on HT siblings.
+ */
+ mtx_enter(&cpu_ucode_intel_mtx);
+
+ old_rev = cpu_ucode_intel_rev();
+ update = cpu_ucode_intel_applied;
+ if (update == NULL)
+ update = cpu_ucode_intel_find(cpu_ucode_data,
+ cpu_ucode_size, old_rev);
+ if (update == NULL) {
+ DPRINTF(("%s: no microcode update found\n", __func__));
+ goto out;
+ }
+ if (update->update_revision == old_rev) {
+ DPRINTF(("%s: microcode already up-to-date\n", __func__));
+ goto out;
+ }
+
+ /* Apply microcode. */
+ data = (paddr_t)update;
+ data += sizeof(struct intel_ucode_header);
+ wrmsr(MSR_BIOS_UPDT_TRIG, data);
+
+ new_rev = cpu_ucode_intel_rev();
+ if (new_rev != old_rev) {
+ DPRINTF(("%s: microcode updated cpu %ld rev %#x->%#x (%x)\n",
+ __func__, ci->ci_cpuid, old_rev, new_rev, update->date));
+ if (cpu_ucode_intel_applied == NULL)
+ cpu_ucode_intel_applied = update;
+ } else {
+ DPRINTF(("%s: microcode update failed cpu %ld rev %#x->%#x != %#x\n",
+ __func__, ci->ci_cpuid, old_rev, update->update_revision, new_rev));
+ }
+
+out:
+ mtx_leave(&cpu_ucode_intel_mtx);
+}
+
+struct intel_ucode_header *
+cpu_ucode_intel_find(char *data, size_t left, uint32_t current)
+{
+ uint64_t platform_id = (rdmsr(MSR_PLATFORM_ID) >> 50) & 0xff;
+ uint32_t sig, dummy1, dummy2, dummy3;
+ uint32_t mask = 1UL << platform_id;
+ struct intel_ucode_header *hdr;
+ uint32_t total_size;
+ int n = 0;
+
+ CPUID(1, sig, dummy1, dummy2, dummy3);
+
+ while (left > 0) {
+ hdr = (struct intel_ucode_header *)data;
+ if (left < sizeof(struct intel_ucode_header)) {
+ DPRINTF(("%s:%d: not enough data for header (%zd)\n",
+ __func__, n, left));
+ break;
+ }
+ /*
+ * Older microcode has an empty length. In that case we
+ * have to use the default length of 2000.
+ */
+ if (hdr->data_size)
+ total_size = hdr->total_size;
+ else
+ total_size = INTEL_UCODE_DEFAULT_DATA_SIZE +
+ sizeof(struct intel_ucode_header);
+ if (total_size > left) {
+ DPRINTF(("%s:%d: size %u out of range (%zd)\n",
+ __func__, n, total_size, left));
+ break;
+ }
+ if (cpu_ucode_intel_verify(hdr)) {
+ DPRINTF(("%s:%d: broken data\n", __func__, n));
+ break;
+ }
+ if (cpu_ucode_intel_match(hdr, sig, mask, current))
+ return hdr;
+ n++;
+ left -= total_size;
+ data += total_size;
+ }
+ DPRINTF(("%s: no update found\n", __func__));
+ return NULL;
+}
+
+int
+cpu_ucode_intel_verify(struct intel_ucode_header *hdr)
+{
+ uint32_t *data = (uint32_t *)hdr;
+ size_t total_size;
+ uint32_t sum;
+ int i;
+
+ CTASSERT(sizeof(struct intel_ucode_header) == 48);
+
+ if ((paddr_t)data % 16 != 0) {
+ DPRINTF(("%s: misaligned microcode update\n", __func__));
+ return 1;
+ }
+ if (hdr->loader_rev != 1) {
+ DPRINTF(("%s: unsupported loader rev\n", __func__));
+ return 1;
+ }
+
+ if (hdr->data_size)
+ total_size = hdr->total_size;
+ else
+ total_size = INTEL_UCODE_DEFAULT_DATA_SIZE +
+ sizeof(struct intel_ucode_header);
+ if (total_size % 4 != 0) {
+ DPRINTF(("%s: inconsistent size\n", __func__));
+ return 1;
+ }
+
+ sum = 0;
+ for (i = 0; i < total_size / 4; i++)
+ sum += data[i];
+ if (sum != 0) {
+ DPRINTF(("%s: wrong checksum (%#x)\n", __func__, sum));
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cpu_ucode_intel_match(struct intel_ucode_header *hdr,
+ uint32_t processor_sig, uint32_t processor_mask,
+ uint32_t ucode_revision)
+{
+ struct intel_ucode_ext_sig_header *ehdr;
+ struct intel_ucode_ext_sig *esig;
+ uint32_t data_size, total_size;
+ unsigned i;
+
+ data_size = hdr->data_size;
+ total_size = hdr->total_size;
+
+ /*
+ * Older microcode has an empty length. In that case we
+ * have to use the default length of 2000.
+ */
+ if (!data_size) {
+ data_size = INTEL_UCODE_DEFAULT_DATA_SIZE;
+ total_size = INTEL_UCODE_DEFAULT_DATA_SIZE +
+ sizeof(struct intel_ucode_header);
+ }
+
+ if (ucode_revision > hdr->update_revision)
+ return 0;
+ if (hdr->processor_sig == processor_sig &&
+ (hdr->processor_flags & processor_mask))
+ return 1;
+ if (total_size <= sizeof(struct intel_ucode_header) +
+ data_size + sizeof(struct intel_ucode_ext_sig_header))
+ return 0;
+
+ ehdr = (void *)((char *)hdr + sizeof(struct intel_ucode_header) +
+ data_size);
+ esig = (void *)&ehdr[1];
+ for (i = 0; i < ehdr->ext_sig_count; i++) {
+ if (esig[i].processor_sig == processor_sig &&
+ (esig[i].processor_flags & processor_mask))
+ return 1;
+ }
+
+ return 0;
+}
+
+uint32_t
+cpu_ucode_intel_rev(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ uint64_t rev;
+
+ wrmsr(MSR_BIOS_SIGN, 0);
+ CPUID(1, eax, ebx, ecx, edx);
+ rev = rdmsr(MSR_BIOS_SIGN);
+ return rev >> 32;
+}
-/* $OpenBSD: exec_i386.c,v 1.44 2016/09/11 17:52:47 jsing Exp $ */
+/* $OpenBSD: exec_i386.c,v 1.45 2018/08/23 14:47:52 jsg Exp $ */
/*
* Copyright (c) 1997-1998 Michael Shalayeff
#include <dev/cons.h>
#include <lib/libsa/loadfile.h>
#include <machine/biosvar.h>
+#include <machine/specialreg.h>
#include <stand/boot/bootarg.h>
+#include "cmd.h"
#include "disk.h"
#include "libsa.h"
typedef void (*startfuncp)(int, int, int, int, int, int, int, int)
__attribute__ ((noreturn));
+void ucode_load(void);
+extern struct cmd_state cmd;
+
char *bootmac = NULL;
void
bcopy(bootdev_dip->disklabel.d_uid, &bootduid.duid, sizeof(bootduid));
addbootarg(BOOTARG_BOOTDUID, sizeof(bootduid), &bootduid);
+ ucode_load();
+
#ifdef SOFTRAID
if (bootdev_dip->sr_vol != NULL) {
bv = bootdev_dip->sr_vol;
/* not reached */
#endif
}
+
+void
+ucode_load(void)
+{
+ uint32_t model, family, stepping;
+ uint32_t dummy, signature;
+ uint32_t vendor[4];
+ bios_ucode_t uc;
+ struct stat sb;
+ char path[128];
+ size_t buflen;
+ char *buf;
+ int fd;
+
+ CPUID(0, dummy, vendor[0], vendor[2], vendor[1]);
+ vendor[3] = 0; /* NULL-terminate */
+ if (strcmp((char *)vendor, "GenuineIntel") != 0)
+ return;
+
+ CPUID(1, signature, dummy, dummy, dummy);
+ family = (signature >> 8) & 0x0f;
+ model = (signature >> 4) & 0x0f;
+ if (family == 0x6 || family == 0xf) {
+ family += (signature >> 20) & 0xff;
+ model += ((signature >> 16) & 0x0f) << 4;
+ }
+ stepping = (signature >> 0) & 0x0f;
+
+ snprintf(path, sizeof(path), "%s:/etc/firmware/intel/%02x-%02x-%02x",
+ cmd.bootdev, family, model, stepping);
+
+ fd = open(path, 0);
+ if (fd == -1)
+ return;
+
+ if (fstat(fd, &sb) == -1)
+ return;
+
+ buflen = sb.st_size;
+ if (buflen > 128*1024) {
+ printf("ucode too large\n");
+ return;
+ }
+
+ buf = (char *)(1*1024*1024);
+
+ if (read(fd, buf, buflen) != buflen) {
+ free(buf, buflen);
+ return;
+ }
+
+ uc.uc_addr = (uint64_t)buf;
+ uc.uc_size = (uint64_t)buflen;
+ addbootarg(BOOTARG_UCODE, sizeof(uc), &uc);
+}