static enum ucode_state generic_load_microcode(int cpu, const u8 *data, size_t size) { struct ucode_cpu_info *uci = ucode_cpu_info + cpu; const u8 *ucode_ptr = data; void *new_mc = NULL; void *mc; int new_rev = uci->cpu_sig.rev; unsigned int leftover; unsigned long offset; enum ucode_state state = UCODE_OK; offset = install_equiv_cpu_table(ucode_ptr); if (!offset) { printk(KERN_ERR "microcode: failed to create " "equivalent cpu table\n"); return UCODE_ERROR; } ucode_ptr += offset; leftover = size - offset; while (leftover) { unsigned int uninitialized_var(mc_size); struct microcode_header_amd *mc_header; mc = get_next_ucode(cpu, ucode_ptr, leftover, &mc_size); if (!mc) break; mc_header = (struct microcode_header_amd *)mc; if (get_matching_microcode(cpu, mc, new_rev)) { vfree(new_mc); new_rev = mc_header->patch_id; new_mc = mc; } else vfree(mc); ucode_ptr += mc_size; leftover -= mc_size; } if (new_mc) { if (!leftover) { vfree(uci->mc); uci->mc = new_mc; pr_debug("microcode: CPU%d found a matching microcode " "update with version 0x%x (current=0x%x)\n", cpu, new_rev, uci->cpu_sig.rev); } else { vfree(new_mc); state = UCODE_ERROR; } } else state = UCODE_NFOUND; free_equiv_cpu_table(); return state; }
static enum ucode_state generic_load_microcode(int cpu, const u8 *data, size_t size) { struct ucode_cpu_info *uci = ucode_cpu_info + cpu; struct microcode_header_amd *mc_hdr = NULL; unsigned int mc_size, leftover; int offset; const u8 *ucode_ptr = data; void *new_mc = NULL; unsigned int new_rev = uci->cpu_sig.rev; enum ucode_state state = UCODE_OK; offset = install_equiv_cpu_table(ucode_ptr); if (offset < 0) { pr_err("failed to create equivalent cpu table\n"); return UCODE_ERROR; } ucode_ptr += offset; leftover = size - offset; while (leftover) { mc_hdr = get_next_ucode(cpu, ucode_ptr, leftover, &mc_size); if (!mc_hdr) break; if (get_matching_microcode(cpu, mc_hdr, new_rev)) { vfree(new_mc); new_rev = mc_hdr->patch_id; new_mc = mc_hdr; } else vfree(mc_hdr); ucode_ptr += mc_size; leftover -= mc_size; } if (!new_mc) { state = UCODE_NFOUND; goto free_table; } if (!leftover) { vfree(uci->mc); uci->mc = new_mc; pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n", cpu, uci->cpu_sig.rev, new_rev); } else { vfree(new_mc); state = UCODE_ERROR; } free_table: free_equiv_cpu_table(); return state; }
static int cpu_request_microcode(int cpu, const void *buf, size_t bufsize) { struct microcode_amd *mc_amd, *mc_old; size_t offset = 0; size_t last_offset, applied_offset = 0; int error = 0, save_error = 1; struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); unsigned int current_cpu_id; unsigned int equiv_cpu_id; /* We should bind the task to the CPU */ BUG_ON(cpu != raw_smp_processor_id()); current_cpu_id = cpuid_eax(0x00000001); if ( *(const uint32_t *)buf != UCODE_MAGIC ) { printk(KERN_ERR "microcode: Wrong microcode patch file magic\n"); error = -EINVAL; goto out; } mc_amd = xmalloc(struct microcode_amd); if ( !mc_amd ) { printk(KERN_ERR "microcode: Cannot allocate memory for microcode patch\n"); error = -ENOMEM; goto out; } /* * Multiple container file support: * 1. check if this container file has equiv_cpu_id match * 2. If not, fast-fwd to next container file */ while ( offset < bufsize ) { error = install_equiv_cpu_table(mc_amd, buf, &offset); if ( error ) { printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); break; } /* * Could happen as we advance 'offset' early * in install_equiv_cpu_table */ if ( offset > bufsize ) { printk(KERN_ERR "microcode: Microcode buffer overrun\n"); error = -EINVAL; break; } if ( find_equiv_cpu_id(mc_amd->equiv_cpu_table, current_cpu_id, &equiv_cpu_id) ) break; error = container_fast_forward(buf, bufsize - offset, &offset); if ( error == -ENODATA ) { ASSERT(offset == bufsize); break; } if ( error ) { printk(KERN_ERR "microcode: CPU%d incorrect or corrupt container file\n" "microcode: Failed to update patch level. " "Current lvl:%#x\n", cpu, uci->cpu_sig.rev); break; } } if ( error ) { xfree(mc_amd); goto out; } mc_old = uci->mc.mc_amd; /* implicitely validates uci->mc.mc_valid */ uci->mc.mc_amd = mc_amd; /* * It's possible the data file has multiple matching ucode, * lets keep searching till the latest version */ mc_amd->mpb = NULL; mc_amd->mpb_size = 0; last_offset = offset; while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize, &offset)) == 0 ) { if ( microcode_fits(mc_amd, cpu) ) { error = apply_microcode(cpu); if ( error ) break; applied_offset = last_offset; } last_offset = offset; if ( offset >= bufsize ) break; /* * 1. Given a situation where multiple containers exist and correct * patch lives on a container that is not the last container. * 2. We match equivalent ids using find_equiv_cpu_id() from the * earlier while() (On this case, matches on earlier container * file and we break) * 3. Proceed to while ( (error = get_ucode_from_buffer_amd(mc_amd, * buf, bufsize,&offset)) == 0 ) * 4. Find correct patch using microcode_fits() and apply the patch * (Assume: apply_microcode() is successful) * 5. The while() loop from (3) continues to parse the binary as * there is a subsequent container file, but... * 6. ...a correct patch can only be on one container and not on any * subsequent ones. (Refer docs for more info) Therefore, we * don't have to parse a subsequent container. So, we can abort * the process here. * 7. This ensures that we retain a success value (= 0) to 'error' * before if ( mpbuf->type != UCODE_UCODE_TYPE ) evaluates to * false and returns -EINVAL. */ if ( offset + SECTION_HDR_SIZE <= bufsize && *(const uint32_t *)(buf + offset) == UCODE_MAGIC ) break; } /* On success keep the microcode patch for * re-apply on resume. */ if ( applied_offset ) { save_error = get_ucode_from_buffer_amd( mc_amd, buf, bufsize, &applied_offset); if ( save_error ) error = save_error; } if ( save_error ) { xfree(mc_amd); uci->mc.mc_amd = mc_old; } else xfree(mc_old); out: svm_host_osvw_init(); /* * In some cases we may return an error even if processor's microcode has * been updated. For example, the first patch in a container file is loaded * successfully but subsequent container file processing encounters a * failure. */ return error; }
static int cpu_request_microcode(int cpu, const void *buf, size_t bufsize) { struct microcode_amd *mc_amd, *mc_old; size_t offset = bufsize; size_t last_offset, applied_offset = 0; int error = 0; struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); /* We should bind the task to the CPU */ BUG_ON(cpu != raw_smp_processor_id()); if ( *(const uint32_t *)buf != UCODE_MAGIC ) { printk(KERN_ERR "microcode: Wrong microcode patch file magic\n"); error = -EINVAL; goto out; } mc_amd = xmalloc(struct microcode_amd); if ( !mc_amd ) { printk(KERN_ERR "microcode: Cannot allocate memory for microcode patch\n"); error = -ENOMEM; goto out; } error = install_equiv_cpu_table(mc_amd, buf, &offset); if ( error ) { xfree(mc_amd); printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); error = -EINVAL; goto out; } mc_old = uci->mc.mc_amd; /* implicitely validates uci->mc.mc_valid */ uci->mc.mc_amd = mc_amd; /* * It's possible the data file has multiple matching ucode, * lets keep searching till the latest version */ mc_amd->mpb = NULL; mc_amd->mpb_size = 0; last_offset = offset; while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize, &offset)) == 0 ) { if ( microcode_fits(mc_amd, cpu) ) { error = apply_microcode(cpu); if ( error ) break; applied_offset = last_offset; } last_offset = offset; if ( offset >= bufsize ) break; } /* On success keep the microcode patch for * re-apply on resume. */ if ( applied_offset ) { int ret = get_ucode_from_buffer_amd(mc_amd, buf, bufsize, &applied_offset); if ( ret == 0 ) xfree(mc_old); else error = ret; } if ( !applied_offset || error ) { xfree(mc_amd); uci->mc.mc_amd = mc_old; } out: svm_host_osvw_init(); /* * In some cases we may return an error even if processor's microcode has * been updated. For example, the first patch in a container file is loaded * successfully but subsequent container file processing encounters a * failure. */ return error; }
static int cpu_request_microcode(int cpu, const void *buf, size_t size) { const uint32_t *buf_pos; unsigned long offset = 0; int error; struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); void *mc; size_t mc_size; /* We should bind the task to the CPU */ BUG_ON(cpu != raw_smp_processor_id()); buf_pos = (const uint32_t *)buf; if ( buf_pos[0] != UCODE_MAGIC ) { printk(KERN_ERR "microcode: error! Wrong " "microcode patch file magic\n"); return -EINVAL; } error = install_equiv_cpu_table(buf, (uint32_t)(buf_pos[2]), &offset); if ( error ) { printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); return -EINVAL; } /* Size of 1st microcode patch in bytes */ mc_size = buf_pos[offset / sizeof(*buf_pos) + 1]; mc = xmalloc_bytes(mc_size); if ( mc == NULL ) { printk(KERN_ERR "microcode: error! " "Can not allocate memory for microcode patch\n"); error = -ENOMEM; goto out; } /* implicitely validates uci->mc.mc_valid */ uci->mc.mc_amd = mc; /* * It's possible the data file has multiple matching ucode, * lets keep searching till the latest version */ while ( (error = get_next_ucode_from_buffer_amd(&mc, &mc_size, buf, size, &offset)) == 0 ) { uci->mc.mc_amd = mc; error = microcode_fits(mc, cpu); if (error <= 0) continue; error = apply_microcode(cpu); if (error == 0) { error = 1; break; } } /* On success keep the microcode patch for * re-apply on resume. */ if ( error <= 0 ) { xfree(mc); mc = NULL; } else error = 0; uci->mc.mc_amd = mc; out: xfree(equiv_cpu_table); equiv_cpu_table = NULL; return error; }