diff options
Diffstat (limited to 'arch/x86/kernel/cpu/microcode/intel.c')
| -rw-r--r-- | arch/x86/kernel/cpu/microcode/intel.c | 834 | 
1 files changed, 349 insertions, 485 deletions
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index cdc0deab00c9..54d50c3694d8 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -39,125 +39,83 @@  #include <asm/setup.h>  #include <asm/msr.h> -/* - * Temporary microcode blobs pointers storage. We note here during early load - * the pointers to microcode blobs we've got from whatever storage (detached - * initrd, builtin). Later on, we put those into final storage - * mc_saved_data.mc_saved. - * - * Important: those are offsets from the beginning of initrd or absolute - * addresses within the kernel image when built-in. - */ -static unsigned long mc_tmp_ptrs[MAX_UCODE_COUNT]; - -static struct mc_saved_data { -	unsigned int num_saved; -	struct microcode_intel **mc_saved; -} mc_saved_data; +static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin"; -/* Microcode blobs within the initrd. 0 if builtin. */ -static struct ucode_blobs { -	unsigned long start; -	bool valid; -} blobs; +/* Current microcode patch used in early patching */ +struct microcode_intel *intel_ucode_patch; -/* Go through saved patches and find the one suitable for the current CPU. */ -static enum ucode_state -find_microcode_patch(struct microcode_intel **saved, -		     unsigned int num_saved, struct ucode_cpu_info *uci) +static inline bool cpu_signatures_match(unsigned int s1, unsigned int p1, +					unsigned int s2, unsigned int p2)  { -	struct microcode_intel *ucode_ptr, *new_mc = NULL; -	struct microcode_header_intel *mc_hdr; -	int new_rev, ret, i; - -	new_rev = uci->cpu_sig.rev; - -	for (i = 0; i < num_saved; i++) { -		ucode_ptr = saved[i]; -		mc_hdr	  = (struct microcode_header_intel *)ucode_ptr; - -		ret = has_newer_microcode(ucode_ptr, -					  uci->cpu_sig.sig, -					  uci->cpu_sig.pf, -					  new_rev); -		if (!ret) -			continue; - -		new_rev = mc_hdr->rev; -		new_mc  = ucode_ptr; -	} +	if (s1 != s2) +		return false; -	if (!new_mc) -		return UCODE_NFOUND; +	/* Processor flags are either both 0 ... */ +	if (!p1 && !p2) +		return true; -	uci->mc = (struct microcode_intel *)new_mc; -	return UCODE_OK; +	/* ... or they intersect. */ +	return p1 & p2;  } -static inline void -copy_ptrs(struct microcode_intel **mc_saved, unsigned long *mc_ptrs, -	  unsigned long off, int num_saved) +/* + * 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; -	for (i = 0; i < num_saved; i++) -		mc_saved[i] = (struct microcode_intel *)(mc_ptrs[i] + off); -} - -#ifdef CONFIG_X86_32 -static void -microcode_phys(struct microcode_intel **mc_saved_tmp, struct mc_saved_data *mcs) -{ -	int i; -	struct microcode_intel ***mc_saved; +	if (cpu_signatures_match(csig, cpf, mc_hdr->sig, mc_hdr->pf)) +		return 1; -	mc_saved = (struct microcode_intel ***)__pa_nodebug(&mcs->mc_saved); +	/* Look for ext. headers: */ +	if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE) +		return 0; -	for (i = 0; i < mcs->num_saved; i++) { -		struct microcode_intel *p; +	ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE; +	ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE; -		p = *(struct microcode_intel **)__pa_nodebug(mcs->mc_saved + i); -		mc_saved_tmp[i] = (struct microcode_intel *)__pa_nodebug(p); +	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;  } -#endif -static enum ucode_state -load_microcode(struct mc_saved_data *mcs, unsigned long *mc_ptrs, -	       unsigned long offset, struct ucode_cpu_info *uci) +/* + * 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_intel *mc_saved_tmp[MAX_UCODE_COUNT]; -	unsigned int count = mcs->num_saved; +	struct microcode_header_intel *mc_hdr = mc; -	if (!mcs->mc_saved) { -		copy_ptrs(mc_saved_tmp, mc_ptrs, offset, count); +	if (mc_hdr->rev <= new_rev) +		return 0; -		return find_microcode_patch(mc_saved_tmp, count, uci); -	} else { -#ifdef CONFIG_X86_32 -		microcode_phys(mc_saved_tmp, mcs); -		return find_microcode_patch(mc_saved_tmp, count, uci); -#else -		return find_microcode_patch(mcs->mc_saved, count, uci); -#endif -	} +	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 enum ucode_state -matching_model_microcode(struct microcode_header_intel *mc_header, -			unsigned long sig) +static bool microcode_matches(struct microcode_header_intel *mc_header, +			      unsigned long sig)  { -	unsigned int fam, model; -	unsigned int fam_ucode, model_ucode; -	struct extended_sigtable *ext_header;  	unsigned long total_size = get_totalsize(mc_header);  	unsigned long data_size = get_datasize(mc_header); -	int ext_sigcount, i; +	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); @@ -166,11 +124,11 @@ matching_model_microcode(struct microcode_header_intel *mc_header,  	model_ucode = x86_model(mc_header->sig);  	if (fam == fam_ucode && model == model_ucode) -		return UCODE_OK; +		return true;  	/* Look for ext. headers: */  	if (total_size <= data_size + MC_HEADER_SIZE) -		return UCODE_NFOUND; +		return false;  	ext_header   = (void *) mc_header + data_size + MC_HEADER_SIZE;  	ext_sig      = (void *)ext_header + EXT_HEADER_SIZE; @@ -181,192 +139,242 @@ matching_model_microcode(struct microcode_header_intel *mc_header,  		model_ucode = x86_model(ext_sig->sig);  		if (fam == fam_ucode && model == model_ucode) -			return UCODE_OK; +			return true;  		ext_sig++;  	} -	return UCODE_NFOUND; +	return false;  } -static int -save_microcode(struct mc_saved_data *mcs, -	       struct microcode_intel **mc_saved_src, -	       unsigned int num_saved) +static struct ucode_patch *__alloc_microcode_buf(void *data, unsigned int size)  { -	int i, j; -	struct microcode_intel **saved_ptr; -	int ret; +	struct ucode_patch *p; -	if (!num_saved) -		return -EINVAL; +	p = kzalloc(size, GFP_KERNEL); +	if (!p) +		return ERR_PTR(-ENOMEM); -	/* -	 * Copy new microcode data. -	 */ -	saved_ptr = kcalloc(num_saved, sizeof(struct microcode_intel *), GFP_KERNEL); -	if (!saved_ptr) -		return -ENOMEM; - -	for (i = 0; i < num_saved; i++) { -		struct microcode_header_intel *mc_hdr; -		struct microcode_intel *mc; -		unsigned long size; - -		if (!mc_saved_src[i]) { -			ret = -EINVAL; -			goto err; -		} +	p->data = kmemdup(data, size, GFP_KERNEL); +	if (!p->data) { +		kfree(p); +		return ERR_PTR(-ENOMEM); +	} -		mc     = mc_saved_src[i]; -		mc_hdr = &mc->hdr; -		size   = get_totalsize(mc_hdr); +	return p; +} -		saved_ptr[i] = kmemdup(mc, size, GFP_KERNEL); -		if (!saved_ptr[i]) { -			ret = -ENOMEM; -			goto err; +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; +	bool prev_found = false; +	unsigned int sig, pf; + +	mc_hdr = (struct microcode_header_intel *)data; + +	list_for_each_entry_safe(iter, tmp, µcode_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 = __alloc_microcode_buf(data, size); +			if (IS_ERR(p)) +				pr_err("Error allocating buffer %p\n", data); +			else +				list_replace(&iter->plist, &p->plist);  		}  	}  	/* -	 * Point to newly saved microcode. +	 * There weren't any previous patches found in the list cache; save the +	 * newly found.  	 */ -	mcs->mc_saved  = saved_ptr; -	mcs->num_saved = num_saved; - -	return 0; - -err: -	for (j = 0; j <= i; j++) -		kfree(saved_ptr[j]); -	kfree(saved_ptr); - -	return ret; +	if (!prev_found) { +		p = __alloc_microcode_buf(data, size); +		if (IS_ERR(p)) +			pr_err("Error allocating buffer for %p\n", data); +		else +			list_add_tail(&p->plist, µcode_cache); +	}  } -/* - * A microcode patch in ucode_ptr is saved into mc_saved - * - if it has matching signature and newer revision compared to an existing - *   patch mc_saved. - * - or if it is a newly discovered microcode patch. - * - * The microcode patch should have matching model with CPU. - * - * Returns: The updated number @num_saved of saved microcode patches. - */ -static unsigned int _save_mc(struct microcode_intel **mc_saved, -			     u8 *ucode_ptr, unsigned int num_saved) +static int microcode_sanity_check(void *mc, int print_err)  { -	struct microcode_header_intel *mc_hdr, *mc_saved_hdr; -	unsigned int sig, pf; -	int found = 0, i; +	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; -	mc_hdr = (struct microcode_header_intel *)ucode_ptr; +	total_size = get_totalsize(mc_header); +	data_size = get_datasize(mc_header); -	for (i = 0; i < num_saved; i++) { -		mc_saved_hdr = (struct microcode_header_intel *)mc_saved[i]; -		sig	     = mc_saved_hdr->sig; -		pf	     = mc_saved_hdr->pf; +	if (data_size + MC_HEADER_SIZE > total_size) { +		if (print_err) +			pr_err("Error: bad microcode data file size.\n"); +		return -EINVAL; +	} -		if (!find_matching_signature(ucode_ptr, sig, pf)) -			continue; +	if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { +		if (print_err) +			pr_err("Error: invalid/unknown microcode update format.\n"); +		return -EINVAL; +	} -		found = 1; +	ext_table_size = total_size - (MC_HEADER_SIZE + data_size); +	if (ext_table_size) { +		u32 ext_table_sum = 0; +		u32 *ext_tablep; -		if (mc_hdr->rev <= mc_saved_hdr->rev) -			continue; +		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;  		/* -		 * Found an older ucode saved earlier. Replace it with -		 * this newer one. +		 * Check extended table checksum: the sum of all dwords that +		 * comprise a valid table must be 0.  		 */ -		mc_saved[i] = (struct microcode_intel *)ucode_ptr; -		break; +		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;  	} -	/* Newly detected microcode, save it to memory. */ -	if (i >= num_saved && !found) -		mc_saved[num_saved++] = (struct microcode_intel *)ucode_ptr; +	if (!ext_table_size) +		return 0; -	return num_saved; +	/* +	 * 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 enum ucode_state __init -get_matching_model_microcode(unsigned long start, void *data, size_t size, -			     struct mc_saved_data *mcs, unsigned long *mc_ptrs, -			     struct ucode_cpu_info *uci) +static struct microcode_intel * +scan_microcode(void *data, size_t size, struct ucode_cpu_info *uci, bool save)  { -	struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];  	struct microcode_header_intel *mc_header; -	unsigned int num_saved = mcs->num_saved; -	enum ucode_state state = UCODE_OK; -	unsigned int leftover = size; -	u8 *ucode_ptr = data; +	struct microcode_intel *patch = NULL;  	unsigned int mc_size; -	int i; - -	while (leftover && num_saved < ARRAY_SIZE(mc_saved_tmp)) { -		if (leftover < sizeof(mc_header)) +	while (size) { +		if (size < sizeof(struct microcode_header_intel))  			break; -		mc_header = (struct microcode_header_intel *)ucode_ptr; +		mc_header = (struct microcode_header_intel *)data;  		mc_size = get_totalsize(mc_header); -		if (!mc_size || mc_size > leftover || -			microcode_sanity_check(ucode_ptr, 0) < 0) +		if (!mc_size || +		    mc_size > size || +		    microcode_sanity_check(data, 0) < 0)  			break; -		leftover -= mc_size; +		size -= mc_size; -		/* -		 * Since APs with same family and model as the BSP may boot in -		 * the platform, we need to find and save microcode patches -		 * with the same family and model as the BSP. -		 */ -		if (matching_model_microcode(mc_header, uci->cpu_sig.sig) != UCODE_OK) { -			ucode_ptr += mc_size; +		if (!microcode_matches(mc_header, uci->cpu_sig.sig)) { +			data += mc_size;  			continue;  		} -		num_saved = _save_mc(mc_saved_tmp, ucode_ptr, num_saved); +		if (save) { +			save_microcode_patch(data, mc_size); +			goto next; +		} -		ucode_ptr += mc_size; -	} -	if (leftover) { -		state = UCODE_ERROR; -		return state; -	} +		if (!patch) { +			if (!has_newer_microcode(data, +						 uci->cpu_sig.sig, +						 uci->cpu_sig.pf, +						 uci->cpu_sig.rev)) +				goto next; -	if (!num_saved) { -		state = UCODE_NFOUND; -		return state; -	} +		} else { +			struct microcode_header_intel *phdr = &patch->hdr; -	for (i = 0; i < num_saved; i++) -		mc_ptrs[i] = (unsigned long)mc_saved_tmp[i] - start; +			if (!has_newer_microcode(data, +						 phdr->sig, +						 phdr->pf, +						 phdr->rev)) +				goto next; +		} + +		/* We have a newer patch, save it. */ +		patch = data; -	mcs->num_saved = num_saved; +next: +		data += mc_size; +	} -	return state; +	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; +	struct cpu_signature csig = { 0 };  	unsigned int eax, ebx, ecx, edx; -	csig.sig = 0; -	csig.pf = 0; -	csig.rev = 0; -  	memset(uci, 0, sizeof(*uci));  	eax = 0x00000001; @@ -374,8 +382,8 @@ static int collect_cpu_info_early(struct ucode_cpu_info *uci)  	native_cpuid(&eax, &ebx, &ecx, &edx);  	csig.sig = eax; -	family = x86_family(csig.sig); -	model  = x86_model(csig.sig); +	family = x86_family(eax); +	model  = x86_model(eax);  	if ((model >= 5) || (family > 6)) {  		/* get processor flags from MSR 0x17 */ @@ -401,40 +409,41 @@ static int collect_cpu_info_early(struct ucode_cpu_info *uci)  static void show_saved_mc(void)  {  #ifdef DEBUG -	int i, j; +	int i = 0, j;  	unsigned int sig, pf, rev, total_size, data_size, date;  	struct ucode_cpu_info uci; +	struct ucode_patch *p; -	if (!mc_saved_data.num_saved) { +	if (list_empty(µcode_cache)) {  		pr_debug("no microcode data saved.\n");  		return;  	} -	pr_debug("Total microcode saved: %d\n", mc_saved_data.num_saved);  	collect_cpu_info_early(&uci); -	sig = uci.cpu_sig.sig; -	pf = uci.cpu_sig.pf; -	rev = uci.cpu_sig.rev; +	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); -	for (i = 0; i < mc_saved_data.num_saved; i++) { +	list_for_each_entry(p, µcode_cache, plist) {  		struct microcode_header_intel *mc_saved_header;  		struct extended_sigtable *ext_header; -		int ext_sigcount;  		struct extended_signature *ext_sig; +		int ext_sigcount; + +		mc_saved_header = (struct microcode_header_intel *)p->data; -		mc_saved_header = (struct microcode_header_intel *) -				  mc_saved_data.mc_saved[i]; -		sig = mc_saved_header->sig; -		pf = mc_saved_header->pf; -		rev = mc_saved_header->rev; -		total_size = get_totalsize(mc_saved_header); -		data_size = get_datasize(mc_saved_header); -		date = mc_saved_header->date; +		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, +			 i++, sig, pf, rev, total_size,  			 date & 0xffff,  			 date >> 24,  			 (date >> 16) & 0xff); @@ -443,7 +452,7 @@ static void show_saved_mc(void)  		if (total_size <= data_size + MC_HEADER_SIZE)  			continue; -		ext_header = (void *) mc_saved_header + data_size + MC_HEADER_SIZE; +		ext_header = (void *)mc_saved_header + data_size + MC_HEADER_SIZE;  		ext_sigcount = ext_header->count;  		ext_sig = (void *)ext_header + EXT_HEADER_SIZE; @@ -456,85 +465,43 @@ static void show_saved_mc(void)  			ext_sig++;  		} -  	}  #endif  }  /* - * Save this mc into mc_saved_data. So it will be loaded early when a CPU is - * hot added or resumes. - * - * Please make sure this mc should be a valid microcode patch before calling - * this function. + * 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) +static void save_mc_for_early(u8 *mc, unsigned int size)  {  #ifdef CONFIG_HOTPLUG_CPU  	/* Synchronization during CPU hotplug. */  	static DEFINE_MUTEX(x86_cpu_microcode_mutex); -	struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT]; -	unsigned int mc_saved_count_init; -	unsigned int num_saved; -	struct microcode_intel **mc_saved; -	int ret, i; -  	mutex_lock(&x86_cpu_microcode_mutex); -	mc_saved_count_init = mc_saved_data.num_saved; -	num_saved = mc_saved_data.num_saved; -	mc_saved = mc_saved_data.mc_saved; - -	if (mc_saved && num_saved) -		memcpy(mc_saved_tmp, mc_saved, -		       num_saved * sizeof(struct microcode_intel *)); -	/* -	 * Save the microcode patch mc in mc_save_tmp structure if it's a newer -	 * version. -	 */ -	num_saved = _save_mc(mc_saved_tmp, mc, num_saved); - -	/* -	 * Save the mc_save_tmp in global mc_saved_data. -	 */ -	ret = save_microcode(&mc_saved_data, mc_saved_tmp, num_saved); -	if (ret) { -		pr_err("Cannot save microcode patch.\n"); -		goto out; -	} - +	save_microcode_patch(mc, size);  	show_saved_mc(); -	/* -	 * Free old saved microcode data. -	 */ -	if (mc_saved) { -		for (i = 0; i < mc_saved_count_init; i++) -			kfree(mc_saved[i]); -		kfree(mc_saved); -	} - -out:  	mutex_unlock(&x86_cpu_microcode_mutex);  #endif  } -static bool __init load_builtin_intel_microcode(struct cpio_data *cp) +static bool load_builtin_intel_microcode(struct cpio_data *cp)  { -#ifdef CONFIG_X86_64 -	unsigned int eax = 0x00000001, ebx, ecx = 0, edx; +	unsigned int eax = 1, ebx, ecx = 0, edx;  	char name[30]; +	if (IS_ENABLED(CONFIG_X86_32)) +		return false; +  	native_cpuid(&eax, &ebx, &ecx, &edx);  	sprintf(name, "intel-ucode/%02x-%02x-%02x",  		      x86_family(eax), x86_model(eax), x86_stepping(eax));  	return get_builtin_firmware(cp, name); -#else -	return false; -#endif  }  /* @@ -570,8 +537,7 @@ void show_ucode_info_early(void)  }  /* - * At this point, we can not call printk() yet. Keep microcode patch number in - * mc_saved_data.mc_saved and delay printing microcode info in + * At this point, we can not call printk() yet. Delay printing microcode info in   * show_ucode_info_early() until printk() works.   */  static void print_ucode(struct ucode_cpu_info *uci) @@ -648,206 +614,140 @@ static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)  	return 0;  } -/* - * This function converts microcode patch offsets previously stored in - * mc_tmp_ptrs to pointers and stores the pointers in mc_saved_data. - */  int __init save_microcode_in_initrd_intel(void)  { -	struct microcode_intel *mc_saved[MAX_UCODE_COUNT]; -	unsigned int count = mc_saved_data.num_saved; -	unsigned long offset = 0; -	int ret; - -	if (!count) -		return 0; +	struct ucode_cpu_info uci; +	struct cpio_data cp;  	/* -	 * We have found a valid initrd but it might've been relocated in the -	 * meantime so get its updated address. +	 * AP loading didn't find any microcode patch, no need to save anything.  	 */ -	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && blobs.valid) -		offset = initrd_start; - -	copy_ptrs(mc_saved, mc_tmp_ptrs, offset, count); +	if (!intel_ucode_patch || IS_ERR(intel_ucode_patch)) +		return 0; -	ret = save_microcode(&mc_saved_data, mc_saved, count); -	if (ret) -		pr_err("Cannot save microcode patches from initrd.\n"); -	else -		show_saved_mc(); +	if (!load_builtin_intel_microcode(&cp)) +		cp = find_microcode_in_initrd(ucode_path, false); -	return ret; -} +	if (!(cp.data && cp.size)) +		return 0; -static __init enum ucode_state -__scan_microcode_initrd(struct cpio_data *cd, struct ucode_blobs *blbp) -{ -#ifdef CONFIG_BLK_DEV_INITRD -	static __initdata char ucode_name[] = "kernel/x86/microcode/GenuineIntel.bin"; -	char *p = IS_ENABLED(CONFIG_X86_32) ? (char *)__pa_nodebug(ucode_name) -						    : ucode_name; -# ifdef CONFIG_X86_32 -	unsigned long start = 0, size; -	struct boot_params *params; +	collect_cpu_info_early(&uci); -	params = (struct boot_params *)__pa_nodebug(&boot_params); -	size   = params->hdr.ramdisk_size; +	scan_microcode(cp.data, cp.size, &uci, true); -	/* -	 * Set start only if we have an initrd image. We cannot use initrd_start -	 * because it is not set that early yet. -	 */ -	start = (size ? params->hdr.ramdisk_image : 0); +	show_saved_mc(); -# else /* CONFIG_X86_64 */ -	unsigned long start = 0, size; +	return 0; +} -	size  = (u64)boot_params.ext_ramdisk_size << 32; -	size |= boot_params.hdr.ramdisk_size; -	if (size) { -		start  = (u64)boot_params.ext_ramdisk_image << 32; -		start |= boot_params.hdr.ramdisk_image; +/* + * @res_patch, output: a pointer to the patch we found. + */ +static struct microcode_intel *__load_ucode_intel(struct ucode_cpu_info *uci) +{ +	static const char *path; +	struct cpio_data cp; +	bool use_pa; -		start += PAGE_OFFSET; +	if (IS_ENABLED(CONFIG_X86_32)) { +		path	  = (const char *)__pa_nodebug(ucode_path); +		use_pa	  = true; +	} else { +		path	  = ucode_path; +		use_pa	  = false;  	} -# endif - -	*cd = find_cpio_data(p, (void *)start, size, NULL); -	if (cd->data) { -		blbp->start = start; -		blbp->valid = true; -		return UCODE_OK; -	} else -#endif /* CONFIG_BLK_DEV_INITRD */ -		return UCODE_ERROR; -} +	/* try built-in microcode first */ +	if (!load_builtin_intel_microcode(&cp)) +		cp = find_microcode_in_initrd(path, use_pa); -static __init enum ucode_state -scan_microcode(struct mc_saved_data *mcs, unsigned long *mc_ptrs, -	       struct ucode_cpu_info *uci, struct ucode_blobs *blbp) -{ -	struct cpio_data cd = { NULL, 0, "" }; -	enum ucode_state ret; +	if (!(cp.data && cp.size)) +		return NULL; -	/* try built-in microcode first */ -	if (load_builtin_intel_microcode(&cd)) -		/* -		 * Invalidate blobs as we might've gotten an initrd too, -		 * supplied by the boot loader, by mistake or simply forgotten -		 * there. That's fine, we ignore it since we've found builtin -		 * microcode already. -		 */ -		blbp->valid = false; -	else { -		ret = __scan_microcode_initrd(&cd, blbp); -		if (ret != UCODE_OK) -			return ret; -	} +	collect_cpu_info_early(uci); -	return get_matching_model_microcode(blbp->start, cd.data, cd.size, -					    mcs, mc_ptrs, uci); +	return scan_microcode(cp.data, cp.size, uci, false);  } -static void __init -_load_ucode_intel_bsp(struct mc_saved_data *mcs, unsigned long *mc_ptrs, -		      struct ucode_blobs *blbp) +void __init load_ucode_intel_bsp(void)  { +	struct microcode_intel *patch;  	struct ucode_cpu_info uci; -	enum ucode_state ret; - -	collect_cpu_info_early(&uci); -	ret = scan_microcode(mcs, mc_ptrs, &uci, blbp); -	if (ret != UCODE_OK) +	patch = __load_ucode_intel(&uci); +	if (!patch)  		return; -	ret = load_microcode(mcs, mc_ptrs, blbp->start, &uci); -	if (ret != UCODE_OK) -		return; +	uci.mc = patch;  	apply_microcode_early(&uci, true);  } -void __init load_ucode_intel_bsp(void) +void load_ucode_intel_ap(void)  { -	struct ucode_blobs *blobs_p; -	struct mc_saved_data *mcs; -	unsigned long *ptrs; +	struct microcode_intel *patch, **iup; +	struct ucode_cpu_info uci; -#ifdef CONFIG_X86_32 -	mcs	= (struct mc_saved_data *)__pa_nodebug(&mc_saved_data); -	ptrs	= (unsigned long *)__pa_nodebug(&mc_tmp_ptrs); -	blobs_p	= (struct ucode_blobs *)__pa_nodebug(&blobs); -#else -	mcs	= &mc_saved_data; -	ptrs	= mc_tmp_ptrs; -	blobs_p = &blobs; -#endif +	if (IS_ENABLED(CONFIG_X86_32)) +		iup = (struct microcode_intel **) __pa_nodebug(&intel_ucode_patch); +	else +		iup = &intel_ucode_patch; + +reget: +	if (!*iup) { +		patch = __load_ucode_intel(&uci); +		if (!patch) +			return; + +		*iup = patch; +	} -	_load_ucode_intel_bsp(mcs, ptrs, blobs_p); +	uci.mc = *iup; + +	if (apply_microcode_early(&uci, true)) { +		/* Mixed-silicon system? Try to refetch the proper patch: */ +		*iup = NULL; + +		goto reget; +	}  } -void load_ucode_intel_ap(void) +static struct microcode_intel *find_patch(struct ucode_cpu_info *uci)  { -	struct ucode_blobs *blobs_p; -	unsigned long *ptrs, start = 0; -	struct mc_saved_data *mcs; -	struct ucode_cpu_info uci; -	enum ucode_state ret; +	struct microcode_header_intel *phdr; +	struct ucode_patch *iter, *tmp; -#ifdef CONFIG_X86_32 -	mcs	= (struct mc_saved_data *)__pa_nodebug(&mc_saved_data); -	ptrs	= (unsigned long *)__pa_nodebug(mc_tmp_ptrs); -	blobs_p	= (struct ucode_blobs *)__pa_nodebug(&blobs); -#else -	mcs	= &mc_saved_data; -	ptrs	= mc_tmp_ptrs; -	blobs_p = &blobs; -#endif - -	/* -	 * If there is no valid ucode previously saved in memory, no need to -	 * update ucode on this AP. -	 */ -	if (!mcs->num_saved) -		return; +	list_for_each_entry_safe(iter, tmp, µcode_cache, plist) { -	if (blobs_p->valid) { -		start = blobs_p->start; +		phdr = (struct microcode_header_intel *)iter->data; -		/* -		 * Pay attention to CONFIG_RANDOMIZE_MEMORY=y as it shuffles -		 * physmem mapping too and there we have the initrd. -		 */ -		start += PAGE_OFFSET - __PAGE_OFFSET_BASE; -	} +		if (phdr->rev <= uci->cpu_sig.rev) +			continue; -	collect_cpu_info_early(&uci); -	ret = load_microcode(mcs, ptrs, start, &uci); -	if (ret != UCODE_OK) -		return; +		if (!find_matching_signature(phdr, +					     uci->cpu_sig.sig, +					     uci->cpu_sig.pf)) +			continue; -	apply_microcode_early(&uci, true); +		return iter->data; +	} +	return NULL;  }  void reload_ucode_intel(void)  { +	struct microcode_intel *p;  	struct ucode_cpu_info uci; -	enum ucode_state ret; - -	if (!mc_saved_data.num_saved) -		return;  	collect_cpu_info_early(&uci); -	ret = find_microcode_patch(mc_saved_data.mc_saved, -				   mc_saved_data.num_saved, &uci); -	if (ret != UCODE_OK) +	p = find_patch(&uci); +	if (!p)  		return; +	uci.mc = p; +  	apply_microcode_early(&uci, false);  } @@ -879,24 +779,6 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)  	return 0;  } -/* - * return 0 - no update found - * return 1 - found update - */ -static int get_matching_mc(struct microcode_intel *mc, int cpu) -{ -	struct cpu_signature cpu_sig; -	unsigned int csig, cpf, crev; - -	collect_cpu_info(cpu, &cpu_sig); - -	csig = cpu_sig.sig; -	cpf = cpu_sig.pf; -	crev = cpu_sig.rev; - -	return has_newer_microcode(mc, csig, cpf, crev); -} -  static int apply_microcode_intel(int cpu)  {  	struct microcode_intel *mc; @@ -911,16 +793,12 @@ static int apply_microcode_intel(int cpu)  	uci = ucode_cpu_info + cpu;  	mc = uci->mc; -	if (!mc) -		return 0; - -	/* -	 * Microcode on this CPU could be updated earlier. Only apply the -	 * microcode patch in mc when it is newer than the one on this -	 * CPU. -	 */ -	if (!get_matching_mc(mc, cpu)) -		return 0; +	if (!mc) { +		/* Look for a newer patch in our cache: */ +		mc = find_patch(uci); +		if (!mc) +			return 0; +	}  	/* write microcode via MSR 0x79 */  	wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)mc->bits); @@ -962,7 +840,6 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,  	u8 *ucode_ptr = data, *new_mc = NULL, *mc = NULL;  	int new_rev = uci->cpu_sig.rev;  	unsigned int leftover = size; -	enum ucode_state state = UCODE_OK;  	unsigned int curr_mc_size = 0;  	unsigned int csig, cpf; @@ -1015,14 +892,11 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,  	if (leftover) {  		vfree(new_mc); -		state = UCODE_ERROR; -		goto out; +		return UCODE_ERROR;  	} -	if (!new_mc) { -		state = UCODE_NFOUND; -		goto out; -	} +	if (!new_mc) +		return UCODE_NFOUND;  	vfree(uci->mc);  	uci->mc = (struct microcode_intel *)new_mc; @@ -1032,12 +906,12 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,  	 * permanent memory. So it will be loaded early when a CPU is hot added  	 * or resumes.  	 */ -	save_mc_for_early(new_mc); +	save_mc_for_early(new_mc, curr_mc_size);  	pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",  		 cpu, new_rev, uci->cpu_sig.rev); -out: -	return state; + +	return UCODE_OK;  }  static int get_ucode_fw(void *to, const void *from, size_t n) @@ -1081,20 +955,11 @@ request_microcode_user(int cpu, const void __user *buf, size_t size)  	return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user);  } -static void microcode_fini_cpu(int cpu) -{ -	struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - -	vfree(uci->mc); -	uci->mc = NULL; -} -  static struct microcode_ops microcode_intel_ops = {  	.request_microcode_user		  = request_microcode_user,  	.request_microcode_fw             = request_microcode_fw,  	.collect_cpu_info                 = collect_cpu_info,  	.apply_microcode                  = apply_microcode_intel, -	.microcode_fini_cpu               = microcode_fini_cpu,  };  struct microcode_ops * __init init_intel_microcode(void) @@ -1109,4 +974,3 @@ struct microcode_ops * __init init_intel_microcode(void)  	return µcode_intel_ops;  } -  |