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