Пример #1
0
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;
}
Пример #3
0
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;
}
Пример #4
0
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;
}
Пример #5
0
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;
}