aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/microcode_amd.c179
1 files changed, 93 insertions, 86 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 9129c6981c5..384990d2c54 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -89,27 +89,76 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
89 return 0; 89 return 0;
90} 90}
91 91
92static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr, 92static unsigned int verify_ucode_size(int cpu, u32 patch_size,
93 int rev) 93 unsigned int size)
94{ 94{
95 unsigned int current_cpu_id; 95 struct cpuinfo_x86 *c = &cpu_data(cpu);
96 u16 equiv_cpu_id = 0; 96 u32 max_size;
97 unsigned int i = 0; 97
98#define F1XH_MPB_MAX_SIZE 2048
99#define F14H_MPB_MAX_SIZE 1824
100#define F15H_MPB_MAX_SIZE 4096
101
102 switch (c->x86) {
103 case 0x14:
104 max_size = F14H_MPB_MAX_SIZE;
105 break;
106 case 0x15:
107 max_size = F15H_MPB_MAX_SIZE;
108 break;
109 default:
110 max_size = F1XH_MPB_MAX_SIZE;
111 break;
112 }
113
114 if (patch_size > min_t(u32, size, max_size)) {
115 pr_err("patch size mismatch\n");
116 return 0;
117 }
118
119 return patch_size;
120}
121
122static u16 find_equiv_id(void)
123{
124 unsigned int current_cpu_id, i = 0;
98 125
99 BUG_ON(equiv_cpu_table == NULL); 126 BUG_ON(equiv_cpu_table == NULL);
127
100 current_cpu_id = cpuid_eax(0x00000001); 128 current_cpu_id = cpuid_eax(0x00000001);
101 129
102 while (equiv_cpu_table[i].installed_cpu != 0) { 130 while (equiv_cpu_table[i].installed_cpu != 0) {
103 if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { 131 if (current_cpu_id == equiv_cpu_table[i].installed_cpu)
104 equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; 132 return equiv_cpu_table[i].equiv_cpu;
105 break; 133
106 }
107 i++; 134 i++;
108 } 135 }
136 return 0;
137}
109 138
139/*
140 * we signal a good patch is found by returning its size > 0
141 */
142static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
143 unsigned int leftover_size, int rev,
144 unsigned int *current_size)
145{
146 struct microcode_header_amd *mc_hdr;
147 unsigned int actual_size;
148 u16 equiv_cpu_id;
149
150 /* size of the current patch we're staring at */
151 *current_size = *(u32 *)(ucode_ptr + 4) + SECTION_HDR_SIZE;
152
153 equiv_cpu_id = find_equiv_id();
110 if (!equiv_cpu_id) 154 if (!equiv_cpu_id)
111 return 0; 155 return 0;
112 156
157 /*
158 * let's look at the patch header itself now
159 */
160 mc_hdr = (struct microcode_header_amd *)(ucode_ptr + SECTION_HDR_SIZE);
161
113 if (mc_hdr->processor_rev_id != equiv_cpu_id) 162 if (mc_hdr->processor_rev_id != equiv_cpu_id)
114 return 0; 163 return 0;
115 164
@@ -123,7 +172,20 @@ static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr,
123 if (mc_hdr->patch_id <= rev) 172 if (mc_hdr->patch_id <= rev)
124 return 0; 173 return 0;
125 174
126 return 1; 175 /*
176 * now that the header looks sane, verify its size
177 */
178 actual_size = verify_ucode_size(cpu, *current_size, leftover_size);
179 if (!actual_size)
180 return 0;
181
182 /* clear the patch buffer */
183 memset(patch, 0, PAGE_SIZE);
184
185 /* all looks ok, get the binary patch */
186 get_ucode_data(patch, ucode_ptr + SECTION_HDR_SIZE, actual_size);
187
188 return actual_size;
127} 189}
128 190
129static int apply_microcode_amd(int cpu) 191static int apply_microcode_amd(int cpu)
@@ -158,63 +220,6 @@ static int apply_microcode_amd(int cpu)
158 return 0; 220 return 0;
159} 221}
160 222
161static unsigned int verify_ucode_size(int cpu, const u8 *buf, unsigned int size)
162{
163 struct cpuinfo_x86 *c = &cpu_data(cpu);
164 u32 max_size, actual_size;
165
166#define F1XH_MPB_MAX_SIZE 2048
167#define F14H_MPB_MAX_SIZE 1824
168#define F15H_MPB_MAX_SIZE 4096
169
170 switch (c->x86) {
171 case 0x14:
172 max_size = F14H_MPB_MAX_SIZE;
173 break;
174 case 0x15:
175 max_size = F15H_MPB_MAX_SIZE;
176 break;
177 default:
178 max_size = F1XH_MPB_MAX_SIZE;
179 break;
180 }
181
182 actual_size = *(u32 *)(buf + 4);
183
184 if (actual_size + SECTION_HDR_SIZE > size || actual_size > max_size) {
185 pr_err("section size mismatch\n");
186 return 0;
187 }
188
189 return actual_size;
190}
191
192static struct microcode_header_amd *
193get_next_ucode(int cpu, const u8 *buf, unsigned int size, unsigned int *mc_size)
194{
195 struct microcode_header_amd *mc = NULL;
196 unsigned int actual_size = 0;
197
198 if (*(u32 *)buf != UCODE_UCODE_TYPE) {
199 pr_err("invalid type field in container file section header\n");
200 goto out;
201 }
202
203 actual_size = verify_ucode_size(cpu, buf, size);
204 if (!actual_size)
205 goto out;
206
207 mc = vzalloc(actual_size);
208 if (!mc)
209 goto out;
210
211 get_ucode_data(mc, buf + SECTION_HDR_SIZE, actual_size);
212 *mc_size = actual_size + SECTION_HDR_SIZE;
213
214out:
215 return mc;
216}
217
218static int install_equiv_cpu_table(const u8 *buf) 223static int install_equiv_cpu_table(const u8 *buf)
219{ 224{
220 unsigned int *ibuf = (unsigned int *)buf; 225 unsigned int *ibuf = (unsigned int *)buf;
@@ -250,36 +255,38 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
250{ 255{
251 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 256 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
252 struct microcode_header_amd *mc_hdr = NULL; 257 struct microcode_header_amd *mc_hdr = NULL;
253 unsigned int mc_size, leftover; 258 unsigned int mc_size, leftover, current_size = 0;
254 int offset; 259 int offset;
255 const u8 *ucode_ptr = data; 260 const u8 *ucode_ptr = data;
256 void *new_mc = NULL; 261 void *new_mc = NULL;
257 unsigned int new_rev = uci->cpu_sig.rev; 262 unsigned int new_rev = uci->cpu_sig.rev;
258 enum ucode_state state = UCODE_OK; 263 enum ucode_state state = UCODE_ERROR;
259 264
260 offset = install_equiv_cpu_table(ucode_ptr); 265 offset = install_equiv_cpu_table(ucode_ptr);
261 if (offset < 0) { 266 if (offset < 0) {
262 pr_err("failed to create equivalent cpu table\n"); 267 pr_err("failed to create equivalent cpu table\n");
263 return UCODE_ERROR; 268 goto out;
264 } 269 }
265
266 ucode_ptr += offset; 270 ucode_ptr += offset;
267 leftover = size - offset; 271 leftover = size - offset;
268 272
269 while (leftover) { 273 if (*(u32 *)ucode_ptr != UCODE_UCODE_TYPE) {
270 mc_hdr = get_next_ucode(cpu, ucode_ptr, leftover, &mc_size); 274 pr_err("invalid type field in container file section header\n");
271 if (!mc_hdr) 275 goto free_table;
272 break; 276 }
273 277
274 if (get_matching_microcode(cpu, mc_hdr, new_rev)) { 278 while (leftover) {
275 vfree(new_mc); 279 mc_size = get_matching_microcode(cpu, ucode_ptr, leftover,
280 new_rev, &current_size);
281 if (mc_size) {
282 mc_hdr = patch;
283 new_mc = patch;
276 new_rev = mc_hdr->patch_id; 284 new_rev = mc_hdr->patch_id;
277 new_mc = mc_hdr; 285 leftover -= mc_size;
278 } else 286 } else {
279 vfree(mc_hdr); 287 ucode_ptr += current_size;
280 288 leftover -= current_size;
281 ucode_ptr += mc_size; 289 }
282 leftover -= mc_size;
283 } 290 }
284 291
285 if (!new_mc) { 292 if (!new_mc) {
@@ -288,18 +295,19 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
288 } 295 }
289 296
290 if (!leftover) { 297 if (!leftover) {
291 vfree(uci->mc);
292 uci->mc = new_mc; 298 uci->mc = new_mc;
299 state = UCODE_OK;
293 pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n", 300 pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
294 cpu, uci->cpu_sig.rev, new_rev); 301 cpu, uci->cpu_sig.rev, new_rev);
295 } else { 302 } else {
296 vfree(new_mc); 303 new_mc = NULL;
297 state = UCODE_ERROR; 304 state = UCODE_ERROR;
298 } 305 }
299 306
300free_table: 307free_table:
301 free_equiv_cpu_table(); 308 free_equiv_cpu_table();
302 309
310out:
303 return state; 311 return state;
304} 312}
305 313
@@ -340,7 +348,6 @@ static void microcode_fini_cpu_amd(int cpu)
340{ 348{
341 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 349 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
342 350
343 vfree(uci->mc);
344 uci->mc = NULL; 351 uci->mc = NULL;
345} 352}
346 353