Home | History | Annotate | Line # | Download | only in x86
      1 /* $NetBSD: cpu_ucode_amd.c,v 1.11 2020/04/25 15:26:18 bouyer Exp $ */
      2 /*
      3  * Copyright (c) 2012 The NetBSD Foundation, Inc.
      4  * All rights reserved.
      5  *
      6  * This code is derived from software contributed to The NetBSD Foundation
      7  * by Christoph Egger.
      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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28  * POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: cpu_ucode_amd.c,v 1.11 2020/04/25 15:26:18 bouyer Exp $");
     33 
     34 #ifdef _KERNEL_OPT
     35 #include "opt_xen.h"
     36 #include "opt_cpu_ucode.h"
     37 #endif
     38 
     39 #include <sys/param.h>
     40 #include <sys/conf.h>
     41 #include <sys/cpuio.h>
     42 #include <sys/cpu.h>
     43 #include <sys/kmem.h>
     44 #include <sys/xcall.h>
     45 
     46 #include <machine/cpufunc.h>
     47 #include <machine/specialreg.h>
     48 #include <x86/cpu_ucode.h>
     49 
     50 struct microcode_amd_header {
     51 	uint32_t ah_data_code;
     52 	uint32_t ah_patch_id;
     53 	uint8_t ah_patch_data_id[2];
     54 	uint8_t ah_patch_data_len;
     55 	uint8_t ah_init_flag;
     56 	uint32_t ah_patch_data_checksum;
     57 	uint32_t ah_nb_dev_id;
     58 	uint32_t ah_sb_dev_id;
     59 	uint16_t ah_processor_rev_id;
     60 	uint8_t ah_nb_rev_id;
     61 	uint8_t ah_sb_rev_id;
     62 	uint8_t ah_bios_api_rev;
     63 	uint8_t ah_reserved[3];
     64 	uint32_t ah_match_reg[8];
     65 } __packed;
     66 
     67 /* equivalence cpu table */
     68 struct microcode_amd_equiv_cpu_table {
     69 	uint32_t ect_installed_cpu;
     70 	uint32_t ect_fixed_errata_mask;
     71 	uint32_t ect_fixed_errata_compare;
     72 	uint16_t ect_equiv_cpu;
     73 	uint16_t ect_reserved;
     74 };
     75 
     76 #define UCODE_MAGIC	0x00414d44
     77 
     78 struct microcode_amd {
     79 	uint8_t *mpb; /* microcode patch block */
     80 	size_t mpb_size;
     81 	struct microcode_amd_equiv_cpu_table *ect;
     82 	size_t ect_size;
     83 };
     84 
     85 struct mpbhdr {
     86 #define UCODE_TYPE_EQUIV	0
     87 #define UCODE_TYPE_PATCH	1
     88 	uint32_t mpb_type;
     89 	uint32_t mpb_len;
     90 	uint32_t mpb_data[];
     91 };
     92 
     93 static uint32_t
     94 amd_cpufamily(void)
     95 {
     96 	uint32_t family;
     97 	struct cpu_info *ci = curcpu();
     98 
     99 	family = CPUID_TO_FAMILY(ci->ci_signature);
    100 
    101 	return family;
    102 }
    103 
    104 int
    105 cpu_ucode_amd_get_version(struct cpu_ucode_version *ucode, void *ptr,
    106     size_t len)
    107 {
    108 	struct cpu_ucode_version_amd *data = ptr;
    109 
    110 	if (ucode->loader_version != CPU_UCODE_LOADER_AMD
    111 	    || amd_cpufamily() < 0x10)
    112 		return EOPNOTSUPP;
    113 
    114 	if (len < sizeof(*data))
    115 		return ENOSPC;
    116 
    117 	data->version = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
    118 	return 0;
    119 }
    120 
    121 int
    122 cpu_ucode_amd_firmware_open(firmware_handle_t *fwh, const char *fwname)
    123 {
    124 	const char *fw_path = "x86/amd";
    125 	char _fwname[32];
    126 	int error;
    127 
    128 	if (fwname != NULL && fwname[0] != '\0')
    129 		return firmware_open(fw_path, fwname, fwh);
    130 
    131 	snprintf(_fwname, sizeof(_fwname), "microcode_amd_fam%xh.bin",
    132 	    amd_cpufamily());
    133 
    134 	error = firmware_open(fw_path, _fwname, fwh);
    135 	if (error == 0)
    136 		return 0;
    137 
    138 	return firmware_open(fw_path, "microcode_amd.bin", fwh);
    139 }
    140 
    141 #ifndef XENPV
    142 struct mc_buf {
    143 	uint8_t *mc_buf;
    144 	uint32_t mc_equiv_cpuid;
    145 	struct mpbhdr *mc_mpbuf;
    146 	struct microcode_amd *mc_amd;
    147 	int mc_error;
    148 };
    149 
    150 static void
    151 cpu_apply_cb(void *arg0, void *arg1)
    152 {
    153 	int error = 0;
    154 	const struct cpu_ucode_softc *sc = arg0;
    155 	struct microcode_amd mc_amd;
    156 	struct mc_buf mc;
    157 	device_t dev;
    158 	int s;
    159 
    160 	memcpy(&mc, arg1, sizeof(mc));
    161 	mc_amd.mpb = mc.mc_amd->mpb;
    162 	mc_amd.mpb_size = mc.mc_amd->mpb_size;
    163 
    164 	dev = curcpu()->ci_dev;
    165 	s = splhigh();
    166 
    167 	do {
    168 		uint64_t patchlevel;
    169 		struct microcode_amd_header *hdr;
    170 
    171 		if (mc.mc_mpbuf->mpb_type != UCODE_TYPE_PATCH) {
    172 			aprint_debug_dev(dev, "ucode: patch type expected\n");
    173 			goto next;
    174 		}
    175 
    176 		hdr = (struct microcode_amd_header *)mc_amd.mpb;
    177 		if (hdr->ah_processor_rev_id != mc.mc_equiv_cpuid) {
    178 			aprint_debug_dev(dev, "ucode: patch does not "
    179 			    "match this cpu "
    180 			    "(patch is for cpu id %x, cpu id is %x)\n",
    181 			    hdr->ah_processor_rev_id, mc.mc_equiv_cpuid);
    182 			goto next;
    183 		}
    184 
    185 		patchlevel = rdmsr(MSR_UCODE_AMD_PATCHLEVEL);
    186 		if (hdr->ah_patch_id <= patchlevel)
    187 			goto next;
    188 
    189 		/* found matching microcode update */
    190 		wrmsr(MSR_UCODE_AMD_PATCHLOADER, (u_long)hdr);
    191 
    192 		/* check current patch id and patch's id for match */
    193 		if (patchlevel == rdmsr(MSR_UCODE_AMD_PATCHLEVEL)) {
    194 			aprint_debug_dev(dev, "ucode: update from revision "
    195 			    "0x%"PRIx64" to 0x%x failed\n",
    196 			    patchlevel, hdr->ah_patch_id);
    197 			error = EIO;
    198 			goto out;
    199 		} else {
    200 			/* Success */
    201 			error = 0;
    202 			goto out;
    203 		}
    204 
    205 next:
    206 		/* Check for race:
    207 		 * When we booted with -x a cpu might already finished
    208 		 * (why doesn't xc_wait() wait for *all* cpus?)
    209 		 * and sc->sc_blob is already freed.
    210 		 * In this case the calculation below touches
    211 		 * non-allocated memory and results in a crash.
    212 		 */
    213 		if (sc->sc_blob == NULL)
    214 			break;
    215 
    216 		mc.mc_buf += mc.mc_mpbuf->mpb_len +
    217 		    sizeof(mc.mc_mpbuf->mpb_type) +
    218 		    sizeof(mc.mc_mpbuf->mpb_len);
    219 		mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
    220 		mc_amd.mpb = (uint8_t *)mc.mc_mpbuf->mpb_data;
    221 		mc_amd.mpb_size = mc.mc_mpbuf->mpb_len;
    222 
    223 	} while ((uintptr_t)((mc.mc_buf) - (uint8_t *)sc->sc_blob) < sc->sc_blobsize);
    224 	aprint_error_dev(dev, "ucode: No newer patch available "
    225 	    "for this cpu than is already installed.\n");
    226 	error = ENOENT;
    227 
    228 out:
    229 	if (error)
    230 		((struct mc_buf *)(arg1))->mc_error = error;
    231 	splx(s);
    232 }
    233 
    234 int
    235 cpu_ucode_amd_apply(struct cpu_ucode_softc *sc, int cpuno)
    236 {
    237 	int i, error = 0;
    238 	uint32_t *magic;
    239 	uint32_t cpu_signature;
    240 	uint32_t equiv_cpuid = 0;
    241 	struct mc_buf mc;
    242 	int where;
    243 
    244 	if (sc->loader_version != CPU_UCODE_LOADER_AMD
    245 	    || cpuno != CPU_UCODE_ALL_CPUS)
    246 		return EINVAL;
    247 
    248 	cpu_signature = curcpu()->ci_signature;
    249 
    250 	KASSERT(sc->sc_blob != NULL);
    251 	magic = (uint32_t *)sc->sc_blob;
    252 	if (*magic != UCODE_MAGIC) {
    253 		aprint_error("ucode: wrong file magic\n");
    254 		return EINVAL;
    255 	}
    256 
    257 	mc.mc_buf = &sc->sc_blob[sizeof(*magic)];
    258 	mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
    259 
    260 	/* equivalence table is expected to come first */
    261 	if (mc.mc_mpbuf->mpb_type != UCODE_TYPE_EQUIV) {
    262 		aprint_error("ucode: missing equivalence table\n");
    263 		return EINVAL;
    264 	}
    265 
    266 	mc.mc_amd = kmem_zalloc(sizeof(*mc.mc_amd), KM_SLEEP);
    267 	mc.mc_amd->ect = kmem_alloc(mc.mc_mpbuf->mpb_len, KM_SLEEP);
    268 	memcpy(mc.mc_amd->ect, mc.mc_mpbuf->mpb_data, mc.mc_mpbuf->mpb_len);
    269 	mc.mc_amd->ect_size = mc.mc_mpbuf->mpb_len;
    270 
    271 	/* check if there is a patch for this cpu present */
    272 	for (i = 0; mc.mc_amd->ect[i].ect_installed_cpu != 0; i++) {
    273 		if (cpu_signature == mc.mc_amd->ect[i].ect_installed_cpu) {
    274 			/* keep extended family and extended model */
    275 			equiv_cpuid = mc.mc_amd->ect[i].ect_equiv_cpu & 0xffff;
    276 			break;
    277 		}
    278 	}
    279 	if (equiv_cpuid == 0) {
    280 		aprint_error("ucode: No patch available for this cpu\n");
    281 		error = ENOENT;
    282 		goto err1;
    283 	}
    284 
    285 	mc.mc_equiv_cpuid = equiv_cpuid;
    286 	mc.mc_buf += mc.mc_mpbuf->mpb_len + sizeof(mc.mc_mpbuf->mpb_type) +
    287 	    sizeof(mc.mc_mpbuf->mpb_len);
    288 	mc.mc_mpbuf = (struct mpbhdr *)mc.mc_buf;
    289 	mc.mc_amd->mpb = (uint8_t *)mc.mc_mpbuf->mpb_data;
    290 	mc.mc_amd->mpb_size = mc.mc_mpbuf->mpb_len;
    291 
    292 	/* Apply it on all cpus */
    293 	mc.mc_error = 0;
    294 	where = xc_broadcast(0, cpu_apply_cb, sc, &mc);
    295 
    296 	/* Wait for completion */
    297 	xc_wait(where);
    298 	error = mc.mc_error;
    299 
    300 err1:
    301 	kmem_free(mc.mc_amd->ect, mc.mc_amd->ect_size);
    302 	kmem_free(mc.mc_amd, sizeof(*mc.mc_amd));
    303 	return error;
    304 }
    305 #endif /* ! XENPV */
    306