mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-27 01:58:19 +00:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			188 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (c) 2014 Google, Inc
 | |
|  * Copyright (C) 2000 Ronald G. Minnich
 | |
|  *
 | |
|  * Microcode update for Intel PIII and later CPUs
 | |
|  */
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <fdtdec.h>
 | |
| #include <log.h>
 | |
| #include <asm/global_data.h>
 | |
| #include <linux/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)
 | |
| 		return -ENOENT;
 | |
| 
 | |
| 	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);
 | |
| 
 | |
| 	return 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;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 	result = cpuid(1);
 | |
| 	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;
 | |
| 	ulong address;
 | |
| 	const void *blob = gd->fdt_blob;
 | |
| 	int skipped;
 | |
| 	int count;
 | |
| 	int node;
 | |
| 	int ret;
 | |
| 	int rev;
 | |
| 
 | |
| 	microcode_read_cpu(&cpu);
 | |
| 	node = 0;
 | |
| 	count = 0;
 | |
| 	skipped = 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;
 | |
| 		}
 | |
| 		address = (ulong)update.data + UCODE_HEADER_LEN;
 | |
| 		wrmsr(MSR_IA32_UCODE_WRITE, address, 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;
 | |
| 		}
 | |
| 		count++;
 | |
| 		if (!ucode_base) {
 | |
| 			ucode_base = (ulong)update.data;
 | |
| 			ucode_size = update.size;
 | |
| 		}
 | |
| 	} while (1);
 | |
| }
 |