1045 lines · 23.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Intel CPU Microcode Update Driver for Linux * * Copyright (C) 2000-2006 Tigran Aivazian <aivazian.tigran@gmail.com> *		 2006 Shaohua Li <shaohua.li@intel.com> * * Intel CPU microcode early update for Linux * * Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com> *		      H Peter Anvin" <hpa@zytor.com> */ /* * This needs to be before all headers so that pr_debug in printk.h doesn't turn * printk calls into no_printk(). * *#define DEBUG */#define pr_fmt(fmt) "microcode: " fmt #include <linux/earlycpio.h>#include <linux/firmware.h>#include <linux/uaccess.h>#include <linux/vmalloc.h>#include <linux/initrd.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/cpu.h>#include <linux/uio.h>#include <linux/mm.h> #include <asm/microcode_intel.h>#include <asm/intel-family.h>#include <asm/processor.h>#include <asm/tlbflush.h>#include <asm/setup.h>#include <asm/msr.h> static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin"; /* Current microcode patch used in early patching on the APs. */static struct microcode_intel *intel_ucode_patch; /* last level cache size per core */static int llc_size_per_core; static inline bool cpu_signatures_match(unsigned int s1, unsigned int p1,					unsigned int s2, unsigned int p2){	if (s1 != s2)		return false; 	/* Processor flags are either both 0 ... */	if (!p1 && !p2)		return true; 	/* ... or they intersect. */	return p1 & p2;} /* * Returns 1 if update has been found, 0 otherwise. */static int find_matching_signature(void *mc, unsigned int csig, int cpf){	struct microcode_header_intel *mc_hdr = mc;	struct extended_sigtable *ext_hdr;	struct extended_signature *ext_sig;	int i; 	if (cpu_signatures_match(csig, cpf, mc_hdr->sig, mc_hdr->pf))		return 1; 	/* Look for ext. headers: */	if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE)		return 0; 	ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE;	ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE; 	for (i = 0; i < ext_hdr->count; i++) {		if (cpu_signatures_match(csig, cpf, ext_sig->sig, ext_sig->pf))			return 1;		ext_sig++;	}	return 0;} /* * Returns 1 if update has been found, 0 otherwise. */static int has_newer_microcode(void *mc, unsigned int csig, int cpf, int new_rev){	struct microcode_header_intel *mc_hdr = mc; 	if (mc_hdr->rev <= new_rev)		return 0; 	return find_matching_signature(mc, csig, cpf);} /* * Given CPU signature and a microcode patch, this function finds if the * microcode patch has matching family and model with the CPU. * * %true - if there's a match * %false - otherwise */static bool microcode_matches(struct microcode_header_intel *mc_header,			      unsigned long sig){	unsigned long total_size = get_totalsize(mc_header);	unsigned long data_size = get_datasize(mc_header);	struct extended_sigtable *ext_header;	unsigned int fam_ucode, model_ucode;	struct extended_signature *ext_sig;	unsigned int fam, model;	int ext_sigcount, i; 	fam   = x86_family(sig);	model = x86_model(sig); 	fam_ucode   = x86_family(mc_header->sig);	model_ucode = x86_model(mc_header->sig); 	if (fam == fam_ucode && model == model_ucode)		return true; 	/* Look for ext. headers: */	if (total_size <= data_size + MC_HEADER_SIZE)		return false; 	ext_header   = (void *) mc_header + data_size + MC_HEADER_SIZE;	ext_sig      = (void *)ext_header + EXT_HEADER_SIZE;	ext_sigcount = ext_header->count; 	for (i = 0; i < ext_sigcount; i++) {		fam_ucode   = x86_family(ext_sig->sig);		model_ucode = x86_model(ext_sig->sig); 		if (fam == fam_ucode && model == model_ucode)			return true; 		ext_sig++;	}	return false;} static struct ucode_patch *memdup_patch(void *data, unsigned int size){	struct ucode_patch *p; 	p = kzalloc(sizeof(struct ucode_patch), GFP_KERNEL);	if (!p)		return NULL; 	p->data = kmemdup(data, size, GFP_KERNEL);	if (!p->data) {		kfree(p);		return NULL;	} 	return p;} static void save_microcode_patch(void *data, unsigned int size){	struct microcode_header_intel *mc_hdr, *mc_saved_hdr;	struct ucode_patch *iter, *tmp, *p = NULL;	bool prev_found = false;	unsigned int sig, pf; 	mc_hdr = (struct microcode_header_intel *)data; 	list_for_each_entry_safe(iter, tmp, &microcode_cache, plist) {		mc_saved_hdr = (struct microcode_header_intel *)iter->data;		sig	     = mc_saved_hdr->sig;		pf	     = mc_saved_hdr->pf; 		if (find_matching_signature(data, sig, pf)) {			prev_found = true; 			if (mc_hdr->rev <= mc_saved_hdr->rev)				continue; 			p = memdup_patch(data, size);			if (!p)				pr_err("Error allocating buffer %p\n", data);			else {				list_replace(&iter->plist, &p->plist);				kfree(iter->data);				kfree(iter);			}		}	} 	/*	 * There weren't any previous patches found in the list cache; save the	 * newly found.	 */	if (!prev_found) {		p = memdup_patch(data, size);		if (!p)			pr_err("Error allocating buffer for %p\n", data);		else			list_add_tail(&p->plist, &microcode_cache);	} 	if (!p)		return; 	/*	 * Save for early loading. On 32-bit, that needs to be a physical	 * address as the APs are running from physical addresses, before	 * paging has been enabled.	 */	if (IS_ENABLED(CONFIG_X86_32))		intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p->data);	else		intel_ucode_patch = p->data;} static int microcode_sanity_check(void *mc, int print_err){	unsigned long total_size, data_size, ext_table_size;	struct microcode_header_intel *mc_header = mc;	struct extended_sigtable *ext_header = NULL;	u32 sum, orig_sum, ext_sigcount = 0, i;	struct extended_signature *ext_sig; 	total_size = get_totalsize(mc_header);	data_size = get_datasize(mc_header); 	if (data_size + MC_HEADER_SIZE > total_size) {		if (print_err)			pr_err("Error: bad microcode data file size.\n");		return -EINVAL;	} 	if (mc_header->ldrver != 1 || mc_header->hdrver != 1) {		if (print_err)			pr_err("Error: invalid/unknown microcode update format.\n");		return -EINVAL;	} 	ext_table_size = total_size - (MC_HEADER_SIZE + data_size);	if (ext_table_size) {		u32 ext_table_sum = 0;		u32 *ext_tablep; 		if ((ext_table_size < EXT_HEADER_SIZE)		 || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {			if (print_err)				pr_err("Error: truncated extended signature table.\n");			return -EINVAL;		} 		ext_header = mc + MC_HEADER_SIZE + data_size;		if (ext_table_size != exttable_size(ext_header)) {			if (print_err)				pr_err("Error: extended signature table size mismatch.\n");			return -EFAULT;		} 		ext_sigcount = ext_header->count; 		/*		 * Check extended table checksum: the sum of all dwords that		 * comprise a valid table must be 0.		 */		ext_tablep = (u32 *)ext_header; 		i = ext_table_size / sizeof(u32);		while (i--)			ext_table_sum += ext_tablep[i]; 		if (ext_table_sum) {			if (print_err)				pr_warn("Bad extended signature table checksum, aborting.\n");			return -EINVAL;		}	} 	/*	 * Calculate the checksum of update data and header. The checksum of	 * valid update data and header including the extended signature table	 * must be 0.	 */	orig_sum = 0;	i = (MC_HEADER_SIZE + data_size) / sizeof(u32);	while (i--)		orig_sum += ((u32 *)mc)[i]; 	if (orig_sum) {		if (print_err)			pr_err("Bad microcode data checksum, aborting.\n");		return -EINVAL;	} 	if (!ext_table_size)		return 0; 	/*	 * Check extended signature checksum: 0 => valid.	 */	for (i = 0; i < ext_sigcount; i++) {		ext_sig = (void *)ext_header + EXT_HEADER_SIZE +			  EXT_SIGNATURE_SIZE * i; 		sum = (mc_header->sig + mc_header->pf + mc_header->cksum) -		      (ext_sig->sig + ext_sig->pf + ext_sig->cksum);		if (sum) {			if (print_err)				pr_err("Bad extended signature checksum, aborting.\n");			return -EINVAL;		}	}	return 0;} /* * Get microcode matching with BSP's model. Only CPUs with the same model as * BSP can stay in the platform. */static struct microcode_intel *scan_microcode(void *data, size_t size, struct ucode_cpu_info *uci, bool save){	struct microcode_header_intel *mc_header;	struct microcode_intel *patch = NULL;	unsigned int mc_size; 	while (size) {		if (size < sizeof(struct microcode_header_intel))			break; 		mc_header = (struct microcode_header_intel *)data; 		mc_size = get_totalsize(mc_header);		if (!mc_size ||		    mc_size > size ||		    microcode_sanity_check(data, 0) < 0)			break; 		size -= mc_size; 		if (!microcode_matches(mc_header, uci->cpu_sig.sig)) {			data += mc_size;			continue;		} 		if (save) {			save_microcode_patch(data, mc_size);			goto next;		}  		if (!patch) {			if (!has_newer_microcode(data,						 uci->cpu_sig.sig,						 uci->cpu_sig.pf,						 uci->cpu_sig.rev))				goto next; 		} else {			struct microcode_header_intel *phdr = &patch->hdr; 			if (!has_newer_microcode(data,						 phdr->sig,						 phdr->pf,						 phdr->rev))				goto next;		} 		/* We have a newer patch, save it. */		patch = data; next:		data += mc_size;	} 	if (size)		return NULL; 	return patch;} static int collect_cpu_info_early(struct ucode_cpu_info *uci){	unsigned int val[2];	unsigned int family, model;	struct cpu_signature csig = { 0 };	unsigned int eax, ebx, ecx, edx; 	memset(uci, 0, sizeof(*uci)); 	eax = 0x00000001;	ecx = 0;	native_cpuid(&eax, &ebx, &ecx, &edx);	csig.sig = eax; 	family = x86_family(eax);	model  = x86_model(eax); 	if ((model >= 5) || (family > 6)) {		/* get processor flags from MSR 0x17 */		native_rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);		csig.pf = 1 << ((val[1] >> 18) & 7);	} 	csig.rev = intel_get_microcode_revision(); 	uci->cpu_sig = csig;	uci->valid = 1; 	return 0;} static void show_saved_mc(void){#ifdef DEBUG	int i = 0, j;	unsigned int sig, pf, rev, total_size, data_size, date;	struct ucode_cpu_info uci;	struct ucode_patch *p; 	if (list_empty(&microcode_cache)) {		pr_debug("no microcode data saved.\n");		return;	} 	collect_cpu_info_early(&uci); 	sig	= uci.cpu_sig.sig;	pf	= uci.cpu_sig.pf;	rev	= uci.cpu_sig.rev;	pr_debug("CPU: sig=0x%x, pf=0x%x, rev=0x%x\n", sig, pf, rev); 	list_for_each_entry(p, &microcode_cache, plist) {		struct microcode_header_intel *mc_saved_header;		struct extended_sigtable *ext_header;		struct extended_signature *ext_sig;		int ext_sigcount; 		mc_saved_header = (struct microcode_header_intel *)p->data; 		sig	= mc_saved_header->sig;		pf	= mc_saved_header->pf;		rev	= mc_saved_header->rev;		date	= mc_saved_header->date; 		total_size	= get_totalsize(mc_saved_header);		data_size	= get_datasize(mc_saved_header); 		pr_debug("mc_saved[%d]: sig=0x%x, pf=0x%x, rev=0x%x, total size=0x%x, date = %04x-%02x-%02x\n",			 i++, sig, pf, rev, total_size,			 date & 0xffff,			 date >> 24,			 (date >> 16) & 0xff); 		/* Look for ext. headers: */		if (total_size <= data_size + MC_HEADER_SIZE)			continue; 		ext_header = (void *)mc_saved_header + data_size + MC_HEADER_SIZE;		ext_sigcount = ext_header->count;		ext_sig = (void *)ext_header + EXT_HEADER_SIZE; 		for (j = 0; j < ext_sigcount; j++) {			sig = ext_sig->sig;			pf = ext_sig->pf; 			pr_debug("\tExtended[%d]: sig=0x%x, pf=0x%x\n",				 j, sig, pf); 			ext_sig++;		}	}#endif} /* * Save this microcode patch. It will be loaded early when a CPU is * hot-added or resumes. */static void save_mc_for_early(u8 *mc, unsigned int size){	/* Synchronization during CPU hotplug. */	static DEFINE_MUTEX(x86_cpu_microcode_mutex); 	mutex_lock(&x86_cpu_microcode_mutex); 	save_microcode_patch(mc, size);	show_saved_mc(); 	mutex_unlock(&x86_cpu_microcode_mutex);} static bool load_builtin_intel_microcode(struct cpio_data *cp){

Lines 1–500 of 1045.