Commit 3f893ab5 authored by Pu Wen's avatar Pu Wen
Browse files

x86/microcode/hygon: Add microcode loading support for Hygon processors

hygon inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I7DX6V


CVE: NA

---------------------------

Add support for loading Hygon microcode, which is compatible with AMD one.

Signed-off-by: default avatarPu Wen <puwen@hygon.cn>
parent 0f1ef985
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ on Intel:
  kernel/x86/microcode/GenuineIntel.bin
on AMD  :
  kernel/x86/microcode/AuthenticAMD.bin
on Hygon:
  kernel/x86/microcode/HygonGenuine.bin

During BSP (BootStrapping Processor) boot (pre-SMP), the kernel
scans the microcode file in the initrd. If microcode matching the
@@ -68,6 +70,10 @@ here for future reference only).
  cd $TMPDIR
  mkdir -p $DSTDIR

  if [ -d /lib/firmware/hygon-ucode ]; then
          cat /lib/firmware/hygon-ucode/microcode_hygon*.bin > $DSTDIR/HygonGenuine.bin
  fi

  if [ -d /lib/firmware/amd-ucode ]; then
          cat /lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin
  fi
@@ -119,7 +125,8 @@ currently supported.

Here's an example::

  CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3a-09 amd-ucode/microcode_amd_fam15h.bin"
  CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3a-09 \
    amd-ucode/microcode_amd_fam15h.bin hygon-ucode/microcode_hygon_fam18h.bin"
  CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"

This basically means, you have the following tree structure locally::
@@ -129,6 +136,10 @@ This basically means, you have the following tree structure locally::
  ...
  |   |-- microcode_amd_fam15h.bin
  ...
  |-- hygon-ucode
  ...
  |   |-- microcode_hygon_fam18h.bin
  ...
  |-- intel-ucode
  ...
  |   |-- 06-3a-09
+15 −6
Original line number Diff line number Diff line
@@ -1299,14 +1299,14 @@ config X86_REBOOTFIXUPS
config MICROCODE
	bool "CPU microcode loading support"
	default y
	depends on CPU_SUP_AMD || CPU_SUP_INTEL
	depends on CPU_SUP_AMD || CPU_SUP_INTEL || CPU_SUP_HYGON
	help
	  If you say Y here, you will be able to update the microcode on
	  Intel and AMD processors. The Intel support is for the IA32 family,
	  If you say Y here, you will be able to update the microcode on Intel,
	  AMD and Hygon processors. The Intel support is for the IA32 family,
	  e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, Xeon etc. The
	  AMD support is for families 0x10 and later. You will obviously need
	  the actual microcode binary data itself which is not shipped with
	  the Linux kernel.
	  AMD support is for families 0x10 and later. The Hygon support is for
	  families 0x18 and later. You will obviously need the actual microcode
	  binary data itself which is not shipped with the Linux kernel.

	  The preferred method to load microcode from a detached initrd is described
	  in Documentation/x86/microcode.rst. For that you need to enable
@@ -1336,6 +1336,15 @@ config MICROCODE_AMD
	  If you select this option, microcode patch loading support for AMD
	  processors will be enabled.

config MICROCODE_HYGON
	bool "Hygon microcode loading support"
	depends on CPU_SUP_HYGON && MICROCODE
	default MICROCODE
	select MICROCODE_AMD
	help
	  If you select this option, microcode patch loading support for Hygon
	  processors will be enabled.

config MICROCODE_OLD_INTERFACE
	bool "Ancient loading interface (DEPRECATED)"
	default n
+12 −0
Original line number Diff line number Diff line
@@ -80,6 +80,12 @@ static inline struct microcode_ops * __init init_amd_microcode(void)
static inline void __exit exit_amd_microcode(void) {}
#endif

#ifdef CONFIG_MICROCODE_HYGON
extern const struct microcode_ops * __init init_hygon_microcode(void);
#else
#define init_hygon_microcode()	NULL
#endif

#define MAX_UCODE_COUNT 128

#define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
@@ -89,6 +95,9 @@ static inline void __exit exit_amd_microcode(void) {}
#define CPUID_AMD1 QCHAR('A', 'u', 't', 'h')
#define CPUID_AMD2 QCHAR('e', 'n', 't', 'i')
#define CPUID_AMD3 QCHAR('c', 'A', 'M', 'D')
#define CPUID_HYGON1 QCHAR('H', 'y', 'g', 'o')
#define CPUID_HYGON2 QCHAR('n', 'G', 'e', 'n')
#define CPUID_HYGON3 QCHAR('u', 'i', 'n', 'e')

#define CPUID_IS(a, b, c, ebx, ecx, edx)	\
		(!((ebx ^ (a))|(edx ^ (b))|(ecx ^ (c))))
@@ -115,6 +124,9 @@ static inline int x86_cpuid_vendor(void)
	if (CPUID_IS(CPUID_AMD1, CPUID_AMD2, CPUID_AMD3, ebx, ecx, edx))
		return X86_VENDOR_AMD;

	if (CPUID_IS(CPUID_HYGON1, CPUID_HYGON2, CPUID_HYGON3, ebx, ecx, edx))
		return X86_VENDOR_HYGON;

	return X86_VENDOR_UNKNOWN;
}

+44 −8
Original line number Diff line number Diff line
@@ -464,11 +464,14 @@ apply_microcode_early_amd(u32 cpuid_1_eax, void *ucode, size_t size, bool save_p
static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family)
{
#ifdef CONFIG_X86_64
	char fw_name[36] = "amd-ucode/microcode_amd.bin";
	char fw_name[40] = "amd-ucode/microcode_amd.bin";

	if (family >= 0x15)
	if (x86_cpuid_vendor() == X86_VENDOR_AMD && family >= 0x15)
		snprintf(fw_name, sizeof(fw_name),
			 "amd-ucode/microcode_amd_fam%.2xh.bin", family);
	else if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
		snprintf(fw_name, sizeof(fw_name),
			 "hygon-ucode/microcode_hygon_fam%.2xh.bin", family);

	return get_builtin_firmware(cp, fw_name);
#else
@@ -485,10 +488,17 @@ static void __load_ucode_amd(unsigned int cpuid_1_eax, struct cpio_data *ret)

	if (IS_ENABLED(CONFIG_X86_32)) {
		uci	= (struct ucode_cpu_info *)__pa_nodebug(ucode_cpu_info);
		if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
			path = (const char *)__pa_nodebug(
				"kernel/x86/microcode/HygonGenuine.bin");
		else
			path	= (const char *)__pa_nodebug(ucode_path);
		use_pa	= true;
	} else {
		uci     = ucode_cpu_info;
		if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
			path = "kernel/x86/microcode/HygonGenuine.bin";
		else
			path	= ucode_path;
		use_pa	= false;
	}
@@ -556,8 +566,14 @@ int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax)
	struct cont_desc desc = { 0 };
	enum ucode_state ret;
	struct cpio_data cp;
	const char *path;

	if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
		path = "kernel/x86/microcode/HygonGenuine.bin";
	else
		path = ucode_path;

	cp = find_microcode_in_initrd(ucode_path, false);
	cp = find_microcode_in_initrd(path, false);
	if (!(cp.data && cp.size))
		return -EINVAL;

@@ -899,7 +915,7 @@ load_microcode_amd(bool save, u8 family, const u8 *data, size_t size)
static enum ucode_state request_microcode_amd(int cpu, struct device *device,
					      bool refresh_fw)
{
	char fw_name[36] = "amd-ucode/microcode_amd.bin";
	char fw_name[40] = "amd-ucode/microcode_amd.bin";
	struct cpuinfo_x86 *c = &cpu_data(cpu);
	bool bsp = c->cpu_index == boot_cpu_data.cpu_index;
	enum ucode_state ret = UCODE_NFOUND;
@@ -909,8 +925,12 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
	if (!refresh_fw || !bsp)
		return UCODE_OK;

	if (c->x86 >= 0x15)
		snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
	if (x86_cpuid_vendor() == X86_VENDOR_AMD && c->x86 >= 0x15)
		snprintf(fw_name, sizeof(fw_name),
			"amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
	else if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
		snprintf(fw_name, sizeof(fw_name),
			"hygon-ucode/microcode_hygon_fam%.2xh.bin", c->x86);

	if (request_firmware_direct(&fw, (const char *)fw_name, device)) {
		pr_debug("failed to load file %s\n", fw_name);
@@ -967,6 +987,22 @@ struct microcode_ops * __init init_amd_microcode(void)
	return &microcode_amd_ops;
}

#ifdef CONFIG_MICROCODE_HYGON
const struct microcode_ops * __init init_hygon_microcode(void)
{
	struct cpuinfo_x86 *c = &boot_cpu_data;

	if (c->x86_vendor != X86_VENDOR_HYGON)
		return NULL;

	if (ucode_new_rev)
		pr_info_once("microcode updated early to new patch_level=0x%08x\n",
			     ucode_new_rev);

	return &microcode_amd_ops;
}
#endif

void __exit exit_amd_microcode(void)
{
	cleanup();
+21 −1
Original line number Diff line number Diff line
@@ -42,7 +42,11 @@

#define DRIVER_VERSION	"2.2"

#ifdef CONFIG_MICROCODE_HYGON
static const struct microcode_ops	*microcode_ops;
#else
static struct microcode_ops	*microcode_ops;
#endif
static bool dis_ucode_ldr = true;

bool initrd_gone;
@@ -129,7 +133,8 @@ static bool __init check_loader_disabled_bsp(void)
	if (native_cpuid_ecx(1) & BIT(31))
		return *res;

	if (x86_cpuid_vendor() == X86_VENDOR_AMD) {
	if (x86_cpuid_vendor() == X86_VENDOR_AMD ||
	    x86_cpuid_vendor() == X86_VENDOR_HYGON) {
		if (amd_check_current_patch_level())
			return *res;
	}
@@ -179,6 +184,10 @@ void __init load_ucode_bsp(void)
		intel = false;
		break;

	case X86_VENDOR_HYGON:
		intel = false;
		break;

	default:
		return;
	}
@@ -219,6 +228,9 @@ void load_ucode_ap(void)
		if (x86_family(cpuid_1_eax) >= 0x10)
			load_ucode_amd_ap(cpuid_1_eax);
		break;
	case X86_VENDOR_HYGON:
		load_ucode_amd_ap(cpuid_1_eax);
		break;
	default:
		break;
	}
@@ -238,6 +250,9 @@ static int __init save_microcode_in_initrd(void)
		if (c->x86 >= 0x10)
			ret = save_microcode_in_initrd_amd(cpuid_eax(1));
		break;
	case X86_VENDOR_HYGON:
		ret = save_microcode_in_initrd_amd(cpuid_eax(1));
		break;
	default:
		break;
	}
@@ -331,6 +346,9 @@ void reload_early_microcode(void)
		if (family >= 0x10)
			reload_ucode_amd();
		break;
	case X86_VENDOR_HYGON:
		reload_ucode_amd();
		break;
	default:
		break;
	}
@@ -842,6 +860,8 @@ int __init microcode_init(void)
		microcode_ops = init_intel_microcode();
	else if (c->x86_vendor == X86_VENDOR_AMD)
		microcode_ops = init_amd_microcode();
	else if (c->x86_vendor == X86_VENDOR_HYGON)
		microcode_ops = init_hygon_microcode();
	else
		pr_err("no support for this CPU vendor\n");