301 lines · 7.1 KB · C
RawDownload
/* * Copyright (C) 2013 Advanced Micro Devices, Inc. * * Author: Jacob Shin <jacob.shin@amd.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/earlycpio.h>#include <linux/initrd.h> #include <asm/cpu.h>#include <asm/setup.h>#include <asm/microcode_amd.h> static bool ucode_loaded;static u32 ucode_new_rev;static unsigned long ucode_offset;static size_t ucode_size; /* * Microcode patch container file is prepended to the initrd in cpio format. * See Documentation/x86/early-microcode.txt */static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin"; static struct cpio_data __init find_ucode_in_initrd(void){	long offset = 0;	char *path;	void *start;	size_t size;	unsigned long *uoffset;	size_t *usize;	struct cpio_data cd; #ifdef CONFIG_X86_32	struct boot_params *p; 	/*	 * On 32-bit, early load occurs before paging is turned on so we need	 * to use physical addresses.	 */	p       = (struct boot_params *)__pa_nodebug(&boot_params);	path    = (char *)__pa_nodebug(ucode_path);	start   = (void *)p->hdr.ramdisk_image;	size    = p->hdr.ramdisk_size;	uoffset = (unsigned long *)__pa_nodebug(&ucode_offset);	usize   = (size_t *)__pa_nodebug(&ucode_size);#else	path    = ucode_path;	start   = (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET);	size    = boot_params.hdr.ramdisk_size;	uoffset = &ucode_offset;	usize   = &ucode_size;#endif 	cd = find_cpio_data(path, start, size, &offset);	if (!cd.data)		return cd; 	if (*(u32 *)cd.data != UCODE_MAGIC) {		cd.data = NULL;		cd.size = 0;		return cd;	} 	*uoffset = (u8 *)cd.data - (u8 *)start;	*usize   = cd.size; 	return cd;} /* * Early load occurs before we can vmalloc(). So we look for the microcode * patch container file in initrd, traverse equivalent cpu table, look for a * matching microcode patch, and update, all in initrd memory in place. * When vmalloc() is available for use later -- on 64-bit during first AP load, * and on 32-bit during save_microcode_in_initrd_amd() -- we can call * load_microcode_amd() to save equivalent cpu table and microcode patches in * kernel heap memory. */static void apply_ucode_in_initrd(void *ucode, size_t size){	struct equiv_cpu_entry *eq;	u32 *header;	u8  *data;	u16 eq_id = 0;	int offset, left;	u32 rev, eax;	u32 *new_rev;	unsigned long *uoffset;	size_t *usize; #ifdef CONFIG_X86_32	new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);	uoffset = (unsigned long *)__pa_nodebug(&ucode_offset);	usize   = (size_t *)__pa_nodebug(&ucode_size);#else	new_rev = &ucode_new_rev;	uoffset = &ucode_offset;	usize   = &ucode_size;#endif 	data   = ucode;	left   = size;	header = (u32 *)data; 	/* find equiv cpu table */ 	if (header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */	    header[2] == 0)                            /* size */		return; 	eax = cpuid_eax(0x00000001); 	while (left > 0) {		eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ); 		offset = header[2] + CONTAINER_HDR_SZ;		data  += offset;		left  -= offset; 		eq_id = find_equiv_id(eq, eax);		if (eq_id)			break; 		/*		 * support multiple container files appended together. if this		 * one does not have a matching equivalent cpu entry, we fast		 * forward to the next container file.		 */		while (left > 0) {			header = (u32 *)data;			if (header[0] == UCODE_MAGIC &&			    header[1] == UCODE_EQUIV_CPU_TABLE_TYPE)				break; 			offset = header[1] + SECTION_HDR_SIZE;			data  += offset;			left  -= offset;		} 		/* mark where the next microcode container file starts */		offset    = data - (u8 *)ucode;		*uoffset += offset;		*usize   -= offset;		ucode     = data;	} 	if (!eq_id) {		*usize = 0;		return;	} 	/* find ucode and update if needed */ 	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax); 	while (left > 0) {		struct microcode_amd *mc; 		header = (u32 *)data;		if (header[0] != UCODE_UCODE_TYPE || /* type */		    header[1] == 0)                  /* size */			break; 		mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE);		if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id)			if (__apply_microcode_amd(mc) == 0) {				rev = mc->hdr.patch_id;				*new_rev = rev;			} 		offset  = header[1] + SECTION_HDR_SIZE;		data   += offset;		left   -= offset;	} 	/* mark where this microcode container file ends */	offset  = *usize - (data - (u8 *)ucode);	*usize -= offset; 	if (!(*new_rev))		*usize = 0;} void __init load_ucode_amd_bsp(void){	struct cpio_data cd = find_ucode_in_initrd();	if (!cd.data)		return; 	apply_ucode_in_initrd(cd.data, cd.size);} #ifdef CONFIG_X86_32u8 amd_bsp_mpb[MPB_MAX_SIZE]; /* * On 32-bit, since AP's early load occurs before paging is turned on, we * cannot traverse cpu_equiv_table and pcache in kernel heap memory. So during * cold boot, AP will apply_ucode_in_initrd() just like the BSP. During * save_microcode_in_initrd_amd() BSP's patch is copied to amd_bsp_mpb, which * is used upon resume from suspend. */void load_ucode_amd_ap(void){	struct microcode_amd *mc;	unsigned long *initrd;	unsigned long *uoffset;	size_t *usize;	void *ucode; 	mc = (struct microcode_amd *)__pa(amd_bsp_mpb);	if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {		__apply_microcode_amd(mc);		return;	} 	initrd  = (unsigned long *)__pa(&initrd_start);	uoffset = (unsigned long *)__pa(&ucode_offset);	usize   = (size_t *)__pa(&ucode_size); 	if (!*usize || !*initrd)		return; 	ucode = (void *)((unsigned long)__pa(*initrd) + *uoffset);	apply_ucode_in_initrd(ucode, *usize);} static void __init collect_cpu_sig_on_bsp(void *arg){	unsigned int cpu = smp_processor_id();	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;	uci->cpu_sig.sig = cpuid_eax(0x00000001);}#elsevoid load_ucode_amd_ap(void){	unsigned int cpu = smp_processor_id();	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;	u32 rev, eax; 	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);	eax = cpuid_eax(0x00000001); 	uci->cpu_sig.rev = rev;	uci->cpu_sig.sig = eax; 	if (cpu && !ucode_loaded) {		void *ucode; 		if (!ucode_size || !initrd_start)			return; 		ucode = (void *)(initrd_start + ucode_offset);		eax   = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);		if (load_microcode_amd(eax, ucode, ucode_size) != UCODE_OK)			return; 		ucode_loaded = true;	} 	apply_microcode_amd(cpu);}#endif int __init save_microcode_in_initrd_amd(void){	enum ucode_state ret;	void *ucode;	u32 eax; #ifdef CONFIG_X86_32	unsigned int bsp = boot_cpu_data.cpu_index;	struct ucode_cpu_info *uci = ucode_cpu_info + bsp; 	if (!uci->cpu_sig.sig)		smp_call_function_single(bsp, collect_cpu_sig_on_bsp, NULL, 1);#endif	if (ucode_new_rev)		pr_info("microcode: updated early to new patch_level=0x%08x\n",			ucode_new_rev); 	if (ucode_loaded || !ucode_size || !initrd_start)		return 0; 	ucode = (void *)(initrd_start + ucode_offset);	eax   = cpuid_eax(0x00000001);	eax   = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); 	ret = load_microcode_amd(eax, ucode, ucode_size);	if (ret != UCODE_OK)		return -EINVAL; 	ucode_loaded = true;	return 0;}