at v2.6.38-rc2 331 lines 7.9 kB view raw
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 void * 159get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size) 160{ 161 unsigned int total_size; 162 u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; 163 void *mc; 164 165 get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR); 166 167 if (section_hdr[0] != UCODE_UCODE_TYPE) { 168 pr_err("error: invalid type field in container file section header\n"); 169 return NULL; 170 } 171 172 total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); 173 174 if (total_size > size || total_size > UCODE_MAX_SIZE) { 175 pr_err("error: size mismatch\n"); 176 return NULL; 177 } 178 179 mc = vzalloc(UCODE_MAX_SIZE); 180 if (!mc) 181 return NULL; 182 183 get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, total_size); 184 *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR; 185 186 return mc; 187} 188 189static int install_equiv_cpu_table(const u8 *buf) 190{ 191 u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; 192 unsigned int *buf_pos = (unsigned int *)container_hdr; 193 unsigned long size; 194 195 get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE); 196 197 size = buf_pos[2]; 198 199 if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { 200 pr_err("error: invalid type field in container file section header\n"); 201 return 0; 202 } 203 204 equiv_cpu_table = vmalloc(size); 205 if (!equiv_cpu_table) { 206 pr_err("failed to allocate equivalent CPU table\n"); 207 return 0; 208 } 209 210 buf += UCODE_CONTAINER_HEADER_SIZE; 211 get_ucode_data(equiv_cpu_table, buf, size); 212 213 return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */ 214} 215 216static void free_equiv_cpu_table(void) 217{ 218 vfree(equiv_cpu_table); 219 equiv_cpu_table = NULL; 220} 221 222static enum ucode_state 223generic_load_microcode(int cpu, const u8 *data, size_t size) 224{ 225 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 226 const u8 *ucode_ptr = data; 227 void *new_mc = NULL; 228 void *mc; 229 int new_rev = uci->cpu_sig.rev; 230 unsigned int leftover; 231 unsigned long offset; 232 enum ucode_state state = UCODE_OK; 233 234 offset = install_equiv_cpu_table(ucode_ptr); 235 if (!offset) { 236 pr_err("failed to create equivalent cpu table\n"); 237 return UCODE_ERROR; 238 } 239 240 ucode_ptr += offset; 241 leftover = size - offset; 242 243 while (leftover) { 244 unsigned int uninitialized_var(mc_size); 245 struct microcode_header_amd *mc_header; 246 247 mc = get_next_ucode(ucode_ptr, leftover, &mc_size); 248 if (!mc) 249 break; 250 251 mc_header = (struct microcode_header_amd *)mc; 252 if (get_matching_microcode(cpu, mc, new_rev)) { 253 vfree(new_mc); 254 new_rev = mc_header->patch_id; 255 new_mc = mc; 256 } else 257 vfree(mc); 258 259 ucode_ptr += mc_size; 260 leftover -= mc_size; 261 } 262 263 if (new_mc) { 264 if (!leftover) { 265 vfree(uci->mc); 266 uci->mc = new_mc; 267 pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n", 268 cpu, new_rev, uci->cpu_sig.rev); 269 } else { 270 vfree(new_mc); 271 state = UCODE_ERROR; 272 } 273 } else 274 state = UCODE_NFOUND; 275 276 free_equiv_cpu_table(); 277 278 return state; 279} 280 281static enum ucode_state request_microcode_fw(int cpu, struct device *device) 282{ 283 const char *fw_name = "amd-ucode/microcode_amd.bin"; 284 const struct firmware *firmware; 285 enum ucode_state ret; 286 287 if (request_firmware(&firmware, fw_name, device)) { 288 printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); 289 return UCODE_NFOUND; 290 } 291 292 if (*(u32 *)firmware->data != UCODE_MAGIC) { 293 pr_err("invalid UCODE_MAGIC (0x%08x)\n", 294 *(u32 *)firmware->data); 295 return UCODE_ERROR; 296 } 297 298 ret = generic_load_microcode(cpu, firmware->data, firmware->size); 299 300 release_firmware(firmware); 301 302 return ret; 303} 304 305static enum ucode_state 306request_microcode_user(int cpu, const void __user *buf, size_t size) 307{ 308 pr_info("AMD microcode update via /dev/cpu/microcode not supported\n"); 309 return UCODE_ERROR; 310} 311 312static void microcode_fini_cpu_amd(int cpu) 313{ 314 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 315 316 vfree(uci->mc); 317 uci->mc = NULL; 318} 319 320static struct microcode_ops microcode_amd_ops = { 321 .request_microcode_user = request_microcode_user, 322 .request_microcode_fw = request_microcode_fw, 323 .collect_cpu_info = collect_cpu_info_amd, 324 .apply_microcode = apply_microcode_amd, 325 .microcode_fini_cpu = microcode_fini_cpu_amd, 326}; 327 328struct microcode_ops * __init init_amd_microcode(void) 329{ 330 return &microcode_amd_ops; 331}