1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>. 5 * All rights reserved. 6 * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>. 7 * All rights reserved. 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 ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 33 #include <x86/ucode.h> 34 35 #ifndef _KERNEL 36 #include <err.h> 37 38 #include "cpucontrol.h" 39 #endif 40 41 42 #ifdef _KERNEL 43 #define WARNX(level, ...) \ 44 if (bootverbose) { \ 45 printf(__VA_ARGS__); \ 46 printf("\n"); \ 47 } 48 #endif 49 50 /* 51 * AMD family 10h and later. 52 */ 53 typedef struct amd_10h_fw_header { 54 uint32_t data_code; 55 uint32_t patch_id; 56 uint16_t mc_patch_data_id; 57 uint8_t mc_patch_data_len; 58 uint8_t init_flag; 59 uint32_t mc_patch_data_checksum; 60 uint32_t nb_dev_id; 61 uint32_t sb_dev_id; 62 uint16_t processor_rev_id; 63 uint8_t nb_rev_id; 64 uint8_t sb_rev_id; 65 uint8_t bios_api_rev; 66 uint8_t reserved1[3]; 67 uint32_t match_reg[8]; 68 } amd_10h_fw_header_t; 69 70 typedef struct equiv_cpu_entry { 71 uint32_t installed_cpu; 72 uint32_t fixed_errata_mask; 73 uint32_t fixed_errata_compare; 74 uint16_t equiv_cpu; 75 uint16_t res; 76 } equiv_cpu_entry_t; 77 78 typedef struct section_header { 79 uint32_t type; 80 uint32_t size; 81 } section_header_t; 82 83 typedef struct container_header { 84 uint32_t magic; 85 } container_header_t; 86 87 #define AMD_10H_MAGIC 0x414d44 88 #define AMD_10H_EQUIV_TABLE_TYPE 0 89 #define AMD_10H_uCODE_TYPE 1 90 91 /* 92 * NB: the format of microcode update files is not documented by AMD. 93 * It has been reverse engineered from studying Coreboot, illumos and Linux 94 * source code. 95 */ 96 const void * 97 ucode_amd_find(const char *path, uint32_t signature, uint32_t revision, 98 const uint8_t *fw_data, size_t fw_size, size_t *selected_sizep) 99 { 100 const amd_10h_fw_header_t *fw_header; 101 const amd_10h_fw_header_t *selected_fw; 102 const equiv_cpu_entry_t *equiv_cpu_table; 103 const section_header_t *section_header; 104 const container_header_t *container_header; 105 size_t selected_size; 106 uint16_t equiv_id; 107 int i; 108 109 WARNX(1, "found cpu family %#x model %#x " 110 "stepping %#x extfamily %#x extmodel %#x.", 111 ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff), 112 (signature >> 4) & 0x0f, 113 (signature >> 0) & 0x0f, (signature >> 20) & 0xff, 114 (signature >> 16) & 0x0f); 115 WARNX(1, "microcode revision %#x", revision); 116 117 nextfile: 118 WARNX(1, "checking %s for update.", path); 119 WARNX(3, "processing next container file"); 120 if (fw_size < 121 (sizeof(*container_header) + sizeof(*section_header))) { 122 WARNX(2, "file too short: %s", path); 123 return (NULL); 124 } 125 126 container_header = (const container_header_t *)fw_data; 127 if (container_header->magic != AMD_10H_MAGIC) { 128 WARNX(2, "%s is not a valid amd firmware: bad magic", path); 129 return (NULL); 130 } 131 fw_data += sizeof(*container_header); 132 fw_size -= sizeof(*container_header); 133 134 section_header = (const section_header_t *)fw_data; 135 if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { 136 WARNX(2, "%s is not a valid amd firmware: " 137 "first section is not CPU equivalence table", path); 138 return (NULL); 139 } 140 if (section_header->size == 0) { 141 WARNX(2, "%s is not a valid amd firmware: " 142 "first section is empty", path); 143 return (NULL); 144 } 145 fw_data += sizeof(*section_header); 146 fw_size -= sizeof(*section_header); 147 148 if (section_header->size > fw_size) { 149 WARNX(2, "%s is not a valid amd firmware: " 150 "file is truncated", path); 151 return (NULL); 152 } 153 if (section_header->size < sizeof(*equiv_cpu_table)) { 154 WARNX(2, "%s is not a valid amd firmware: " 155 "first section is too short", path); 156 return (NULL); 157 } 158 equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; 159 fw_data += section_header->size; 160 fw_size -= section_header->size; 161 162 equiv_id = 0; 163 for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { 164 WARNX(3, "signature 0x%x i %d installed_cpu 0x%x equiv 0x%x", 165 signature, i, equiv_cpu_table[i].installed_cpu, 166 equiv_cpu_table[i].equiv_cpu); 167 if (signature == equiv_cpu_table[i].installed_cpu) { 168 equiv_id = equiv_cpu_table[i].equiv_cpu; 169 WARNX(3, "equiv_id: %x, signature %8x," 170 " equiv_cpu_table[%d] %8x", equiv_id, signature, 171 i, equiv_cpu_table[i].installed_cpu); 172 break; 173 } 174 } 175 if (equiv_id == 0) { 176 WARNX(2, "CPU is not found in the equivalence table"); 177 } 178 179 while (fw_size >= sizeof(*section_header)) { 180 section_header = (const section_header_t *)fw_data; 181 if (section_header->type == AMD_10H_MAGIC) { 182 WARNX(2, "%s next section is actually a new container", 183 path); 184 if (selected_fw != NULL) 185 goto found; 186 else 187 goto nextfile; 188 } 189 fw_data += sizeof(*section_header); 190 fw_size -= sizeof(*section_header); 191 if (section_header->type != AMD_10H_uCODE_TYPE) { 192 WARNX(2, "%s is not a valid amd firmware: " 193 "section has incorrect type", path); 194 break; 195 } 196 if (section_header->size > fw_size) { 197 WARNX(2, "%s is not a valid amd firmware: " 198 "file is truncated", path); 199 break; 200 } 201 if (section_header->size < sizeof(*fw_header)) { 202 WARNX(2, "%s is not a valid amd firmware: " 203 "section is too short", path); 204 break; 205 } 206 fw_header = (const amd_10h_fw_header_t *)fw_data; 207 fw_data += section_header->size; 208 fw_size -= section_header->size; 209 210 if (fw_header->processor_rev_id != equiv_id) { 211 WARNX(1, "firmware processor_rev_id %x, equiv_id %x", 212 fw_header->processor_rev_id, equiv_id); 213 continue; /* different cpu */ 214 } 215 if (fw_header->patch_id <= revision) { 216 WARNX(1, "patch_id %x, revision %x", 217 fw_header->patch_id, revision); 218 continue; /* not newer revision */ 219 } 220 if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) { 221 WARNX(2, "Chipset-specific microcode is not supported"); 222 } 223 224 WARNX(3, "selecting revision: %x", fw_header->patch_id); 225 revision = fw_header->patch_id; 226 selected_fw = fw_header; 227 selected_size = section_header->size; 228 } 229 230 if (fw_size != 0) { 231 WARNX(2, "%s is not a valid amd firmware: " 232 "file is truncated", path); 233 selected_fw = NULL; 234 } 235 236 found: 237 *selected_sizep = selected_size; 238 return (selected_fw); 239 } 240