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 kernel/x86/microcode/GenuineIntel.bin
on AMD : on AMD :
kernel/x86/microcode/AuthenticAMD.bin kernel/x86/microcode/AuthenticAMD.bin
on Hygon:
kernel/x86/microcode/HygonGenuine.bin
During BSP (BootStrapping Processor) boot (pre-SMP), the kernel During BSP (BootStrapping Processor) boot (pre-SMP), the kernel
scans the microcode file in the initrd. If microcode matching the scans the microcode file in the initrd. If microcode matching the
@ -68,6 +70,10 @@ here for future reference only).
cd $TMPDIR cd $TMPDIR
mkdir -p $DSTDIR 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 if [ -d /lib/firmware/amd-ucode ]; then
cat /lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin cat /lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin
fi fi
@ -119,7 +125,8 @@ currently supported.
Here's an example:: 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" CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"
This basically means, you have the following tree structure locally:: 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 | |-- microcode_amd_fam15h.bin
... ...
|-- hygon-ucode
...
| |-- microcode_hygon_fam18h.bin
...
|-- intel-ucode |-- intel-ucode
... ...
| |-- 06-3a-09 | |-- 06-3a-09

View File

@ -1310,15 +1310,15 @@ config X86_REBOOTFIXUPS
config MICROCODE config MICROCODE
bool "CPU microcode loading support" bool "CPU microcode loading support"
default y default y
depends on CPU_SUP_AMD || CPU_SUP_INTEL depends on CPU_SUP_AMD || CPU_SUP_INTEL || CPU_SUP_HYGON
select FW_LOADER select FW_LOADER
---help--- ---help---
If you say Y here, you will be able to update the microcode on If you say Y here, you will be able to update the microcode on Intel,
Intel and AMD processors. The Intel support is for the IA32 family, 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 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 AMD support is for families 0x10 and later. The Hygon support is for
the actual microcode binary data itself which is not shipped with families 0x18 and later. You will obviously need the actual microcode
the Linux kernel. binary data itself which is not shipped with the Linux kernel.
The preferred method to load microcode from a detached initrd is described The preferred method to load microcode from a detached initrd is described
in Documentation/x86/microcode.rst. For that you need to enable 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 If you select this option, microcode patch loading support for AMD
processors will be enabled. 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 config MICROCODE_OLD_INTERFACE
bool "Ancient loading interface (DEPRECATED)" bool "Ancient loading interface (DEPRECATED)"
default n 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) {} static inline void __exit exit_amd_microcode(void) {}
#endif #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 MAX_UCODE_COUNT 128
#define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24)) #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_AMD1 QCHAR('A', 'u', 't', 'h')
#define CPUID_AMD2 QCHAR('e', 'n', 't', 'i') #define CPUID_AMD2 QCHAR('e', 'n', 't', 'i')
#define CPUID_AMD3 QCHAR('c', 'A', 'M', 'D') #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) \ #define CPUID_IS(a, b, c, ebx, ecx, edx) \
(!((ebx ^ (a))|(edx ^ (b))|(ecx ^ (c)))) (!((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)) if (CPUID_IS(CPUID_AMD1, CPUID_AMD2, CPUID_AMD3, ebx, ecx, edx))
return X86_VENDOR_AMD; return X86_VENDOR_AMD;
if (CPUID_IS(CPUID_HYGON1, CPUID_HYGON2, CPUID_HYGON3, ebx, ecx, edx))
return X86_VENDOR_HYGON;
return X86_VENDOR_UNKNOWN; 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) static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family)
{ {
#ifdef CONFIG_X86_64 #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), snprintf(fw_name, sizeof(fw_name),
"amd-ucode/microcode_amd_fam%.2xh.bin", family); "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); return get_builtin_firmware(cp, fw_name);
#else #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)) { if (IS_ENABLED(CONFIG_X86_32)) {
uci = (struct ucode_cpu_info *)__pa_nodebug(ucode_cpu_info); 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; use_pa = true;
} else { } else {
uci = ucode_cpu_info; 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; use_pa = false;
} }
@ -557,8 +567,14 @@ int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax)
struct cont_desc desc = { 0 }; struct cont_desc desc = { 0 };
enum ucode_state ret; enum ucode_state ret;
struct cpio_data cp; 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)) if (!(cp.data && cp.size))
return -EINVAL; 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, static enum ucode_state request_microcode_amd(int cpu, struct device *device,
bool refresh_fw) 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); struct cpuinfo_x86 *c = &cpu_data(cpu);
enum ucode_state ret = UCODE_NFOUND; enum ucode_state ret = UCODE_NFOUND;
const struct firmware *fw; const struct firmware *fw;
@ -910,8 +926,12 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
if (!refresh_fw) if (!refresh_fw)
return UCODE_OK; return UCODE_OK;
if (c->x86 >= 0x15) 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); 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)) { if (request_firmware_direct(&fw, (const char *)fw_name, device)) {
pr_debug("failed to load file %s\n", fw_name); 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; 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) void __exit exit_amd_microcode(void)
{ {
cleanup(); cleanup();

View File

@ -42,7 +42,11 @@
#define DRIVER_VERSION "2.2" #define DRIVER_VERSION "2.2"
#ifdef CONFIG_MICROCODE_HYGON
static const struct microcode_ops *microcode_ops;
#else
static struct microcode_ops *microcode_ops; static struct microcode_ops *microcode_ops;
#endif
static bool dis_ucode_ldr = true; static bool dis_ucode_ldr = true;
bool initrd_gone; bool initrd_gone;
@ -134,7 +138,8 @@ static bool __init check_loader_disabled_bsp(void)
if (native_cpuid_ecx(1) & BIT(31)) if (native_cpuid_ecx(1) & BIT(31))
return *res; 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()) if (amd_check_current_patch_level())
return *res; return *res;
} }
@ -186,6 +191,10 @@ void __init load_ucode_bsp(void)
intel = false; intel = false;
break; break;
case X86_VENDOR_HYGON:
intel = false;
break;
default: default:
return; return;
} }
@ -226,6 +235,9 @@ void load_ucode_ap(void)
if (x86_family(cpuid_1_eax) >= 0x10) if (x86_family(cpuid_1_eax) >= 0x10)
load_ucode_amd_ap(cpuid_1_eax); load_ucode_amd_ap(cpuid_1_eax);
break; break;
case X86_VENDOR_HYGON:
load_ucode_amd_ap(cpuid_1_eax);
break;
default: default:
break; break;
} }
@ -245,6 +257,9 @@ static int __init save_microcode_in_initrd(void)
if (c->x86 >= 0x10) if (c->x86 >= 0x10)
ret = save_microcode_in_initrd_amd(cpuid_eax(1)); ret = save_microcode_in_initrd_amd(cpuid_eax(1));
break; break;
case X86_VENDOR_HYGON:
ret = save_microcode_in_initrd_amd(cpuid_eax(1));
break;
default: default:
break; break;
} }
@ -338,6 +353,9 @@ void reload_early_microcode(unsigned int cpu)
if (family >= 0x10) if (family >= 0x10)
reload_ucode_amd(cpu); reload_ucode_amd(cpu);
break; break;
case X86_VENDOR_HYGON:
reload_ucode_amd(cpu);
break;
default: default:
break; break;
} }
@ -839,6 +857,8 @@ int __init microcode_init(void)
microcode_ops = init_intel_microcode(); microcode_ops = init_intel_microcode();
else if (c->x86_vendor == X86_VENDOR_AMD) else if (c->x86_vendor == X86_VENDOR_AMD)
microcode_ops = init_amd_microcode(); microcode_ops = init_amd_microcode();
else if (c->x86_vendor == X86_VENDOR_HYGON)
microcode_ops = init_hygon_microcode();
else else
pr_err("no support for this CPU vendor\n"); pr_err("no support for this CPU vendor\n");