1/* 2 * AMD CPU Microcode Update Driver for Linux 3 * Copyright (C) 2008 Advanced Micro Devices Inc. 4 * 5 * Author: Peter Oruba <peter.oruba@amd.com> 6 * 7 * Based on work by: 8 * Tigran Aivazian <tigran@aivazian.fsnet.co.uk> 9 * 10 * This driver allows to upgrade microcode on AMD 11 * family 0x10 and 0x11 processors. 12 * 13 * Licensed under the terms of the GNU General Public 14 * License version 2. See file COPYING for details. 15 */ 16 17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 19#include <linux/firmware.h> 20#include <linux/pci_ids.h> 21#include <linux/uaccess.h> 22#include <linux/vmalloc.h> 23#include <linux/kernel.h> 24#include <linux/module.h> 25#include <linux/pci.h> 26 27#include <asm/microcode.h> 28#include <asm/processor.h> 29#include <asm/msr.h> 30 31MODULE_DESCRIPTION("AMD Microcode Update Driver"); 32MODULE_AUTHOR("Peter Oruba"); 33MODULE_LICENSE("GPL v2"); 34 35#define UCODE_MAGIC 0x00414d44 36#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 37#define UCODE_UCODE_TYPE 0x00000001 38 39struct equiv_cpu_entry { 40 u32 installed_cpu; 41 u32 fixed_errata_mask; 42 u32 fixed_errata_compare; 43 u16 equiv_cpu; 44 u16 res; 45} __attribute__((packed)); 46 47struct microcode_header_amd { 48 u32 data_code; 49 u32 patch_id; 50 u16 mc_patch_data_id; 51 u8 mc_patch_data_len; 52 u8 init_flag; 53 u32 mc_patch_data_checksum; 54 u32 nb_dev_id; 55 u32 sb_dev_id; 56 u16 processor_rev_id; 57 u8 nb_rev_id; 58 u8 sb_rev_id; 59 u8 bios_api_rev; 60 u8 reserved1[3]; 61 u32 match_reg[8]; 62} __attribute__((packed)); 63 64struct microcode_amd { 65 struct microcode_header_amd hdr; 66 unsigned int mpb[0]; 67}; 68 69#define UCODE_MAX_SIZE 2048 70#define UCODE_CONTAINER_SECTION_HDR 8 71#define UCODE_CONTAINER_HEADER_SIZE 12 72 73static struct equiv_cpu_entry *equiv_cpu_table; 74 75static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) 76{ 77 struct cpuinfo_x86 *c = &cpu_data(cpu); 78 u32 dummy; 79 80 memset(csig, 0, sizeof(*csig)); 81 if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { 82 pr_warning("microcode: CPU%d: AMD CPU family 0x%x not " 83 "supported\n", cpu, c->x86); 84 return -1; 85 } 86 rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy); 87 pr_info("CPU%d: patch_level=0x%x\n", cpu, csig->rev); 88 return 0; 89} 90 91static int get_matching_microcode(int cpu, void *mc, int rev) 92{ 93 struct microcode_header_amd *mc_header = mc; 94 unsigned int current_cpu_id; 95 u16 equiv_cpu_id = 0; 96 unsigned int i = 0; 97 98 BUG_ON(equiv_cpu_table == NULL); 99 current_cpu_id = cpuid_eax(0x00000001); 100 101 while (equiv_cpu_table[i].installed_cpu != 0) { 102 if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { 103 equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; 104 break; 105 } 106 i++; 107 } 108 109 if (!equiv_cpu_id) 110 return 0; 111 112 if (mc_header->processor_rev_id != equiv_cpu_id) 113 return 0; 114 115 /* ucode might be chipset specific -- currently we don't support this */ 116 if (mc_header->nb_dev_id || mc_header->sb_dev_id) { 117 pr_err("CPU%d: loading of chipset specific code not yet supported\n", 118 cpu); 119 return 0; 120 } 121 122 if (mc_header->patch_id <= rev) 123 return 0; 124 125 return 1; 126} 127 128static int apply_microcode_amd(int cpu) 129{ 130 u32 rev, dummy; 131 int cpu_num = raw_smp_processor_id(); 132 struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; 133 struct microcode_amd *mc_amd = uci->mc; 134 135 /* We should bind the task to the CPU */ 136 BUG_ON(cpu_num != cpu); 137 138 if (mc_amd == NULL) 139 return 0; 140 141 wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code); 142 /* get patch id after patching */ 143 rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); 144 145 /* check current patch id and patch's id for match */ 146 if (rev != mc_amd->hdr.patch_id) { 147 pr_err("CPU%d: update failed (for patch_level=0x%x)\n", 148 cpu, mc_amd->hdr.patch_id); 149 return -1; 150 } 151 152 pr_info("CPU%d: updated (new patch_level=0x%x)\n", cpu, rev); 153 uci->cpu_sig.rev = rev; 154 155 return 0; 156} 157 158static int get_ucode_data(void *to, const u8 *from, size_t n) 159{ 160 memcpy(to, from, n); 161 return 0; 162} 163 164static void * 165get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size) 166{ 167 unsigned int total_size; 168 u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; 169 void *mc; 170 171 if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR)) 172 return NULL; 173 174 if (section_hdr[0] != UCODE_UCODE_TYPE) { 175 pr_err("error: invalid type field in container file section header\n"); 176 return NULL; 177 } 178 179 total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); 180 181 if (total_size > size || total_size > UCODE_MAX_SIZE) { 182 pr_err("error: size mismatch\n"); 183 return NULL; 184 } 185 186 mc = vmalloc(UCODE_MAX_SIZE); 187 if (mc) { 188 memset(mc, 0, UCODE_MAX_SIZE); 189 if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, 190 total_size)) { 191 vfree(mc); 192 mc = NULL; 193 } else 194 *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR; 195 } 196 return mc; 197} 198 199static int install_equiv_cpu_table(const u8 *buf) 200{ 201 u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; 202 unsigned int *buf_pos = (unsigned int *)container_hdr; 203 unsigned long size; 204 205 if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE)) 206 return 0; 207 208 size = buf_pos[2]; 209 210 if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { 211 pr_err("error: invalid type field in container file section header\n"); 212 return 0; 213 } 214 215 equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); 216 if (!equiv_cpu_table) { 217 pr_err("failed to allocate equivalent CPU table\n"); 218 return 0; 219 } 220 221 buf += UCODE_CONTAINER_HEADER_SIZE; 222 if (get_ucode_data(equiv_cpu_table, buf, size)) { 223 vfree(equiv_cpu_table); 224 return 0; 225 } 226 227 return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */ 228} 229 230static void free_equiv_cpu_table(void) 231{ 232 vfree(equiv_cpu_table); 233 equiv_cpu_table = NULL; 234} 235 236static enum ucode_state 237generic_load_microcode(int cpu, const u8 *data, size_t size) 238{ 239 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 240 const u8 *ucode_ptr = data; 241 void *new_mc = NULL; 242 void *mc; 243 int new_rev = uci->cpu_sig.rev; 244 unsigned int leftover; 245 unsigned long offset; 246 enum ucode_state state = UCODE_OK; 247 248 offset = install_equiv_cpu_table(ucode_ptr); 249 if (!offset) { 250 pr_err("failed to create equivalent cpu table\n"); 251 return UCODE_ERROR; 252 } 253 254 ucode_ptr += offset; 255 leftover = size - offset; 256 257 while (leftover) { 258 unsigned int uninitialized_var(mc_size); 259 struct microcode_header_amd *mc_header; 260 261 mc = get_next_ucode(ucode_ptr, leftover, &mc_size); 262 if (!mc) 263 break; 264 265 mc_header = (struct microcode_header_amd *)mc; 266 if (get_matching_microcode(cpu, mc, new_rev)) { 267 vfree(new_mc); 268 new_rev = mc_header->patch_id; 269 new_mc = mc; 270 } else 271 vfree(mc); 272 273 ucode_ptr += mc_size; 274 leftover -= mc_size; 275 } 276 277 if (new_mc) { 278 if (!leftover) { 279 vfree(uci->mc); 280 uci->mc = new_mc; 281 pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n", 282 cpu, new_rev, uci->cpu_sig.rev); 283 } else { 284 vfree(new_mc); 285 state = UCODE_ERROR; 286 } 287 } else 288 state = UCODE_NFOUND; 289 290 free_equiv_cpu_table(); 291 292 return state; 293} 294 295static enum ucode_state request_microcode_fw(int cpu, struct device *device) 296{ 297 const char *fw_name = "amd-ucode/microcode_amd.bin"; 298 const struct firmware *firmware; 299 enum ucode_state ret; 300 301 if (request_firmware(&firmware, fw_name, device)) { 302 printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); 303 return UCODE_NFOUND; 304 } 305 306 if (*(u32 *)firmware->data != UCODE_MAGIC) { 307 pr_err("invalid UCODE_MAGIC (0x%08x)\n", 308 *(u32 *)firmware->data); 309 return UCODE_ERROR; 310 } 311 312 ret = generic_load_microcode(cpu, firmware->data, firmware->size); 313 314 release_firmware(firmware); 315 316 return ret; 317} 318 319static enum ucode_state 320request_microcode_user(int cpu, const void __user *buf, size_t size) 321{ 322 pr_info("AMD microcode update via /dev/cpu/microcode not supported\n"); 323 return UCODE_ERROR; 324} 325 326static void microcode_fini_cpu_amd(int cpu) 327{ 328 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 329 330 vfree(uci->mc); 331 uci->mc = NULL; 332} 333 334static struct microcode_ops microcode_amd_ops = { 335 .request_microcode_user = request_microcode_user, 336 .request_microcode_fw = request_microcode_fw, 337 .collect_cpu_info = collect_cpu_info_amd, 338 .apply_microcode = apply_microcode_amd, 339 .microcode_fini_cpu = microcode_fini_cpu_amd, 340}; 341 342struct microcode_ops * __init init_amd_microcode(void) 343{ 344 return µcode_amd_ops; 345} 346