Skip to content
Snippets Groups Projects
microcode.c 4.65 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright (c) 2014 Google, Inc
     * Copyright (C) 2000 Ronald G. Minnich
     *
     * Microcode update for Intel PIII and later CPUs
     *
     * SPDX-License-Identifier:	GPL-2.0
     */
    
    #include <common.h>
    #include <errno.h>
    #include <fdtdec.h>
    #include <libfdt.h>
    #include <asm/cpu.h>
    
    #include <asm/microcode.h>
    
    #include <asm/msr.h>
    
    #include <asm/msr-index.h>
    
    #include <asm/processor.h>
    
    
    DECLARE_GLOBAL_DATA_PTR;
    
    
    /**
     * struct microcode_update - standard microcode header from Intel
     *
     * We read this information out of the device tree and use it to determine
     * whether the update is applicable or not. We also use the same structure
     * to read information from the CPU.
     */
    struct microcode_update {
    	uint header_version;
    	uint update_revision;
    	uint date_code;
    	uint processor_signature;
    	uint checksum;
    	uint loader_revision;
    	uint processor_flags;
    	const void *data;
    	int size;
    };
    
    static int microcode_decode_node(const void *blob, int node,
    				 struct microcode_update *update)
    {
    	update->data = fdt_getprop(blob, node, "data", &update->size);
    	if (!update->data)
    
    	update->data += UCODE_HEADER_LEN;
    	update->size -= UCODE_HEADER_LEN;
    
    
    	update->header_version = fdtdec_get_int(blob, node,
    						"intel,header-version", 0);
    	update->update_revision = fdtdec_get_int(blob, node,
    						 "intel,update-revision", 0);
    	update->date_code = fdtdec_get_int(blob, node,
    					   "intel,date-code", 0);
    	update->processor_signature = fdtdec_get_int(blob, node,
    
    					"intel,processor-signature", 0);
    
    	update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0);
    	update->loader_revision = fdtdec_get_int(blob, node,
    
    						 "intel,loader-revision", 0);
    
    	update->processor_flags = fdtdec_get_int(blob, node,
    
    						 "intel,processor-flags", 0);
    
    int microcode_read_rev(void)
    
    	/* Quark does not have microcode MSRs */
    #ifdef CONFIG_INTEL_QUARK
    	return 0;
    #else
    
    	/*
    	 * Some Intel CPUs can be very finicky about the CPUID sequence used.
    	 * So this is implemented in assembly so that it works reliably.
    	 */
    	uint32_t low, high;
    
    	asm volatile (
    		"xorl %%eax, %%eax\n"
    		"xorl %%edx, %%edx\n"
    
    		"movl %2, %%ecx\n"
    
    		"wrmsr\n"
    		"movl $0x01, %%eax\n"
    		"cpuid\n"
    
    		"movl %2, %%ecx\n"
    
    		"rdmsr\n"
    		: /* outputs */
    		"=a" (low), "=d" (high)
    		: /* inputs */
    
    		"i" (MSR_IA32_UCODE_REV)
    
    		: /* clobbers */
    		 "ebx", "ecx"
    	);
    
    	return high;
    
    }
    
    static void microcode_read_cpu(struct microcode_update *cpu)
    {
    	/* CPUID sets MSR 0x8B iff a microcode update has been loaded. */
    	unsigned int x86_model, x86_family;
    	struct cpuid_result result;
    	uint32_t low, high;
    
    
    	wrmsr(MSR_IA32_UCODE_REV, 0, 0);
    
    	rdmsr(MSR_IA32_UCODE_REV, low, cpu->update_revision);
    
    	x86_model = (result.eax >> 4) & 0x0f;
    	x86_family = (result.eax >> 8) & 0x0f;
    	cpu->processor_signature = result.eax;
    
    	cpu->processor_flags = 0;
    	if ((x86_model >= 5) || (x86_family > 6)) {
    		rdmsr(0x17, low, high);
    		cpu->processor_flags = 1 << ((high >> 18) & 7);
    	}
    	debug("microcode: sig=%#x pf=%#x revision=%#x\n",
    	      cpu->processor_signature, cpu->processor_flags,
    	      cpu->update_revision);
    }
    
    /* Get a microcode update from the device tree and apply it */
    int microcode_update_intel(void)
    {
    	struct microcode_update cpu, update;
    	const void *blob = gd->fdt_blob;
    
    
    	microcode_read_cpu(&cpu);
    	node = 0;
    	count = 0;
    
    	do {
    		node = fdtdec_next_compatible(blob, node,
    					      COMPAT_INTEL_MICROCODE);
    		if (node < 0) {
    			debug("%s: Found %d updates\n", __func__, count);
    
    			return count ? 0 : skipped ? -EEXIST : -ENOENT;
    
    		}
    
    		ret = microcode_decode_node(blob, node, &update);
    
    		if (ret == -ENOENT && ucode_base) {
    			/*
    			 * The microcode has been removed from the device tree
    			 * in the build system. In that case it will have
    			 * already been updated in car_init().
    			 */
    			debug("%s: Microcode data not available\n", __func__);
    			skipped++;
    			continue;
    		}
    
    		if (ret) {
    			debug("%s: Unable to decode update: %d\n", __func__,
    			      ret);
    			return ret;
    		}
    
    		if (!(update.processor_signature == cpu.processor_signature &&
    		      (update.processor_flags & cpu.processor_flags))) {
    			debug("%s: Skipping non-matching update, sig=%x, pf=%x\n",
    			      __func__, update.processor_signature,
    			      update.processor_flags);
    			skipped++;
    			continue;
    
    		wrmsr(MSR_IA32_UCODE_WRITE, (ulong)update.data, 0);
    		rev = microcode_read_rev();
    
    		debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n",
    
    		      rev, update.date_code & 0xffff,
    
    		      (update.date_code >> 24) & 0xff,
    		      (update.date_code >> 16) & 0xff);
    
    		if (update.update_revision != rev) {
    			printf("Microcode update failed\n");
    			return -EFAULT;
    		}