aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/microcode_amd.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/microcode_amd.c')
-rw-r--r--arch/x86/kernel/microcode_amd.c208
1 files changed, 108 insertions, 100 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index e1af7c055c7d..c5610384ab16 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -66,7 +66,6 @@ struct microcode_amd {
66 unsigned int mpb[0]; 66 unsigned int mpb[0];
67}; 67};
68 68
69#define UCODE_MAX_SIZE 2048
70#define UCODE_CONTAINER_SECTION_HDR 8 69#define UCODE_CONTAINER_SECTION_HDR 8
71#define UCODE_CONTAINER_HEADER_SIZE 12 70#define UCODE_CONTAINER_HEADER_SIZE 12
72 71
@@ -77,20 +76,20 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
77 struct cpuinfo_x86 *c = &cpu_data(cpu); 76 struct cpuinfo_x86 *c = &cpu_data(cpu);
78 u32 dummy; 77 u32 dummy;
79 78
80 memset(csig, 0, sizeof(*csig));
81 if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { 79 if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
82 pr_warning("microcode: CPU%d: AMD CPU family 0x%x not " 80 pr_warning("CPU%d: family %d not supported\n", cpu, c->x86);
83 "supported\n", cpu, c->x86);
84 return -1; 81 return -1;
85 } 82 }
83
86 rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy); 84 rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy);
87 pr_info("CPU%d: patch_level=0x%x\n", cpu, csig->rev); 85 pr_info("CPU%d: patch_level=0x%08x\n", cpu, csig->rev);
86
88 return 0; 87 return 0;
89} 88}
90 89
91static int get_matching_microcode(int cpu, void *mc, int rev) 90static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr,
91 int rev)
92{ 92{
93 struct microcode_header_amd *mc_header = mc;
94 unsigned int current_cpu_id; 93 unsigned int current_cpu_id;
95 u16 equiv_cpu_id = 0; 94 u16 equiv_cpu_id = 0;
96 unsigned int i = 0; 95 unsigned int i = 0;
@@ -109,17 +108,17 @@ static int get_matching_microcode(int cpu, void *mc, int rev)
109 if (!equiv_cpu_id) 108 if (!equiv_cpu_id)
110 return 0; 109 return 0;
111 110
112 if (mc_header->processor_rev_id != equiv_cpu_id) 111 if (mc_hdr->processor_rev_id != equiv_cpu_id)
113 return 0; 112 return 0;
114 113
115 /* ucode might be chipset specific -- currently we don't support this */ 114 /* ucode might be chipset specific -- currently we don't support this */
116 if (mc_header->nb_dev_id || mc_header->sb_dev_id) { 115 if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
117 pr_err("CPU%d: loading of chipset specific code not yet supported\n", 116 pr_err("CPU%d: chipset specific code not yet supported\n",
118 cpu); 117 cpu);
119 return 0; 118 return 0;
120 } 119 }
121 120
122 if (mc_header->patch_id <= rev) 121 if (mc_hdr->patch_id <= rev)
123 return 0; 122 return 0;
124 123
125 return 1; 124 return 1;
@@ -144,85 +143,93 @@ static int apply_microcode_amd(int cpu)
144 143
145 /* check current patch id and patch's id for match */ 144 /* check current patch id and patch's id for match */
146 if (rev != mc_amd->hdr.patch_id) { 145 if (rev != mc_amd->hdr.patch_id) {
147 pr_err("CPU%d: update failed (for patch_level=0x%x)\n", 146 pr_err("CPU%d: update failed for patch_level=0x%08x\n",
148 cpu, mc_amd->hdr.patch_id); 147 cpu, mc_amd->hdr.patch_id);
149 return -1; 148 return -1;
150 } 149 }
151 150
152 pr_info("CPU%d: updated (new patch_level=0x%x)\n", cpu, rev); 151 pr_info("CPU%d: new patch_level=0x%08x\n", cpu, rev);
153 uci->cpu_sig.rev = rev; 152 uci->cpu_sig.rev = rev;
154 153
155 return 0; 154 return 0;
156} 155}
157 156
158static int get_ucode_data(void *to, const u8 *from, size_t n) 157static unsigned int verify_ucode_size(int cpu, const u8 *buf, unsigned int size)
159{ 158{
160 memcpy(to, from, n); 159 struct cpuinfo_x86 *c = &cpu_data(cpu);
161 return 0; 160 unsigned int max_size, actual_size;
161
162#define F1XH_MPB_MAX_SIZE 2048
163#define F14H_MPB_MAX_SIZE 1824
164#define F15H_MPB_MAX_SIZE 4096
165
166 switch (c->x86) {
167 case 0x14:
168 max_size = F14H_MPB_MAX_SIZE;
169 break;
170 case 0x15:
171 max_size = F15H_MPB_MAX_SIZE;
172 break;
173 default:
174 max_size = F1XH_MPB_MAX_SIZE;
175 break;
176 }
177
178 actual_size = buf[4] + (buf[5] << 8);
179
180 if (actual_size > size || actual_size > max_size) {
181 pr_err("section size mismatch\n");
182 return 0;
183 }
184
185 return actual_size;
162} 186}
163 187
164static void * 188static struct microcode_header_amd *
165get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size) 189get_next_ucode(int cpu, const u8 *buf, unsigned int size, unsigned int *mc_size)
166{ 190{
167 unsigned int total_size; 191 struct microcode_header_amd *mc = NULL;
168 u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; 192 unsigned int actual_size = 0;
169 void *mc;
170 193
171 if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR)) 194 if (buf[0] != UCODE_UCODE_TYPE) {
172 return NULL; 195 pr_err("invalid type field in container file section header\n");
173 196 goto out;
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 } 197 }
178 198
179 total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); 199 actual_size = verify_ucode_size(cpu, buf, size);
200 if (!actual_size)
201 goto out;
180 202
181 if (total_size > size || total_size > UCODE_MAX_SIZE) { 203 mc = vzalloc(actual_size);
182 pr_err("error: size mismatch\n"); 204 if (!mc)
183 return NULL; 205 goto out;
184 }
185 206
186 mc = vmalloc(UCODE_MAX_SIZE); 207 get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, actual_size);
187 if (mc) { 208 *mc_size = actual_size + UCODE_CONTAINER_SECTION_HDR;
188 memset(mc, 0, UCODE_MAX_SIZE); 209
189 if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, 210out:
190 total_size)) {
191 vfree(mc);
192 mc = NULL;
193 } else
194 *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR;
195 }
196 return mc; 211 return mc;
197} 212}
198 213
199static int install_equiv_cpu_table(const u8 *buf) 214static int install_equiv_cpu_table(const u8 *buf)
200{ 215{
201 u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; 216 unsigned int *ibuf = (unsigned int *)buf;
202 unsigned int *buf_pos = (unsigned int *)container_hdr; 217 unsigned int type = ibuf[1];
203 unsigned long size; 218 unsigned int size = ibuf[2];
204 219
205 if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE)) 220 if (type != UCODE_EQUIV_CPU_TABLE_TYPE || !size) {
206 return 0; 221 pr_err("empty section/"
207 222 "invalid type field in container file section header\n");
208 size = buf_pos[2]; 223 return -EINVAL;
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 } 224 }
214 225
215 equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); 226 equiv_cpu_table = vmalloc(size);
216 if (!equiv_cpu_table) { 227 if (!equiv_cpu_table) {
217 pr_err("failed to allocate equivalent CPU table\n"); 228 pr_err("failed to allocate equivalent CPU table\n");
218 return 0; 229 return -ENOMEM;
219 } 230 }
220 231
221 buf += UCODE_CONTAINER_HEADER_SIZE; 232 get_ucode_data(equiv_cpu_table, buf + UCODE_CONTAINER_HEADER_SIZE, size);
222 if (get_ucode_data(equiv_cpu_table, buf, size)) {
223 vfree(equiv_cpu_table);
224 return 0;
225 }
226 233
227 return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */ 234 return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */
228} 235}
@@ -237,16 +244,16 @@ static enum ucode_state
237generic_load_microcode(int cpu, const u8 *data, size_t size) 244generic_load_microcode(int cpu, const u8 *data, size_t size)
238{ 245{
239 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 246 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
247 struct microcode_header_amd *mc_hdr = NULL;
248 unsigned int mc_size, leftover;
249 int offset;
240 const u8 *ucode_ptr = data; 250 const u8 *ucode_ptr = data;
241 void *new_mc = NULL; 251 void *new_mc = NULL;
242 void *mc; 252 unsigned int new_rev = uci->cpu_sig.rev;
243 int new_rev = uci->cpu_sig.rev;
244 unsigned int leftover;
245 unsigned long offset;
246 enum ucode_state state = UCODE_OK; 253 enum ucode_state state = UCODE_OK;
247 254
248 offset = install_equiv_cpu_table(ucode_ptr); 255 offset = install_equiv_cpu_table(ucode_ptr);
249 if (!offset) { 256 if (offset < 0) {
250 pr_err("failed to create equivalent cpu table\n"); 257 pr_err("failed to create equivalent cpu table\n");
251 return UCODE_ERROR; 258 return UCODE_ERROR;
252 } 259 }
@@ -255,64 +262,65 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
255 leftover = size - offset; 262 leftover = size - offset;
256 263
257 while (leftover) { 264 while (leftover) {
258 unsigned int uninitialized_var(mc_size); 265 mc_hdr = get_next_ucode(cpu, ucode_ptr, leftover, &mc_size);
259 struct microcode_header_amd *mc_header; 266 if (!mc_hdr)
260
261 mc = get_next_ucode(ucode_ptr, leftover, &mc_size);
262 if (!mc)
263 break; 267 break;
264 268
265 mc_header = (struct microcode_header_amd *)mc; 269 if (get_matching_microcode(cpu, mc_hdr, new_rev)) {
266 if (get_matching_microcode(cpu, mc, new_rev)) {
267 vfree(new_mc); 270 vfree(new_mc);
268 new_rev = mc_header->patch_id; 271 new_rev = mc_hdr->patch_id;
269 new_mc = mc; 272 new_mc = mc_hdr;
270 } else 273 } else
271 vfree(mc); 274 vfree(mc_hdr);
272 275
273 ucode_ptr += mc_size; 276 ucode_ptr += mc_size;
274 leftover -= mc_size; 277 leftover -= mc_size;
275 } 278 }
276 279
277 if (new_mc) { 280 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; 281 state = UCODE_NFOUND;
282 goto free_table;
283 }
289 284
285 if (!leftover) {
286 vfree(uci->mc);
287 uci->mc = new_mc;
288 pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
289 cpu, uci->cpu_sig.rev, new_rev);
290 } else {
291 vfree(new_mc);
292 state = UCODE_ERROR;
293 }
294
295free_table:
290 free_equiv_cpu_table(); 296 free_equiv_cpu_table();
291 297
292 return state; 298 return state;
293} 299}
294 300
295static enum ucode_state request_microcode_fw(int cpu, struct device *device) 301static enum ucode_state request_microcode_amd(int cpu, struct device *device)
296{ 302{
297 const char *fw_name = "amd-ucode/microcode_amd.bin"; 303 const char *fw_name = "amd-ucode/microcode_amd.bin";
298 const struct firmware *firmware; 304 const struct firmware *fw;
299 enum ucode_state ret; 305 enum ucode_state ret = UCODE_NFOUND;
300 306
301 if (request_firmware(&firmware, fw_name, device)) { 307 if (request_firmware(&fw, fw_name, device)) {
302 printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); 308 pr_err("failed to load file %s\n", fw_name);
303 return UCODE_NFOUND; 309 goto out;
304 } 310 }
305 311
306 if (*(u32 *)firmware->data != UCODE_MAGIC) { 312 ret = UCODE_ERROR;
307 pr_err("invalid UCODE_MAGIC (0x%08x)\n", 313 if (*(u32 *)fw->data != UCODE_MAGIC) {
308 *(u32 *)firmware->data); 314 pr_err("invalid magic value (0x%08x)\n", *(u32 *)fw->data);
309 return UCODE_ERROR; 315 goto fw_release;
310 } 316 }
311 317
312 ret = generic_load_microcode(cpu, firmware->data, firmware->size); 318 ret = generic_load_microcode(cpu, fw->data, fw->size);
313 319
314 release_firmware(firmware); 320fw_release:
321 release_firmware(fw);
315 322
323out:
316 return ret; 324 return ret;
317} 325}
318 326
@@ -333,7 +341,7 @@ static void microcode_fini_cpu_amd(int cpu)
333 341
334static struct microcode_ops microcode_amd_ops = { 342static struct microcode_ops microcode_amd_ops = {
335 .request_microcode_user = request_microcode_user, 343 .request_microcode_user = request_microcode_user,
336 .request_microcode_fw = request_microcode_fw, 344 .request_microcode_fw = request_microcode_amd,
337 .collect_cpu_info = collect_cpu_info_amd, 345 .collect_cpu_info = collect_cpu_info_amd,
338 .apply_microcode = apply_microcode_amd, 346 .apply_microcode = apply_microcode_amd,
339 .microcode_fini_cpu = microcode_fini_cpu_amd, 347 .microcode_fini_cpu = microcode_fini_cpu_amd,