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

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

Signed-off-by: Pu Wen <puwen@hygon.cn>
Signed-off-by: Jinliang Zheng <alexjlzheng@tencent.com>
Reviewed-by: Bin Lai <robinlai@tencent.com>
Signed-off-by: Jinliang Zheng <alexjlzheng@tencent.com>
Reviewed-by: caelli <caelli@tencent.com>
Signed-off-by: Jianping Liu <frankjpliu@tencent.com>
This commit is contained in:
Pu Wen 2023-06-08 11:46:30 +08:00 committed by Jianping Liu
parent 602852c638
commit 871c9d1964
5 changed files with 103 additions and 16 deletions

View File

@ -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

View File

@ -1310,15 +1310,15 @@ 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
select FW_LOADER
---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
@ -1350,6 +1350,14 @@ 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
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

View File

@ -79,6 +79,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))
@ -88,6 +94,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))))
@ -114,6 +123,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;
}

View File

@ -466,11 +466,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
@ -487,11 +490,18 @@ 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);
path = (const char *)__pa_nodebug(ucode_path);
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;
path = ucode_path;
if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
path = "kernel/x86/microcode/HygonGenuine.bin";
else
path = ucode_path;
use_pa = false;
}
@ -557,8 +567,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;
cp = find_microcode_in_initrd(ucode_path, false);
if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
path = "kernel/x86/microcode/HygonGenuine.bin";
else
path = ucode_path;
cp = find_microcode_in_initrd(path, false);
if (!(cp.data && cp.size))
return -EINVAL;
@ -901,7 +917,7 @@ static enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t siz
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);
enum ucode_state ret = UCODE_NFOUND;
const struct firmware *fw;
@ -910,8 +926,12 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
if (!refresh_fw)
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);
@ -968,6 +988,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();

View File

@ -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;
@ -134,7 +138,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;
}
@ -186,6 +191,10 @@ void __init load_ucode_bsp(void)
intel = false;
break;
case X86_VENDOR_HYGON:
intel = false;
break;
default:
return;
}
@ -226,6 +235,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;
}
@ -245,6 +257,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;
}
@ -338,6 +353,9 @@ void reload_early_microcode(unsigned int cpu)
if (family >= 0x10)
reload_ucode_amd(cpu);
break;
case X86_VENDOR_HYGON:
reload_ucode_amd(cpu);
break;
default:
break;
}
@ -839,6 +857,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");