Add support for loading Hygon microcode, which is compatible with AMD one.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
---
arch/x86/Kconfig | 2 +-
arch/x86/kernel/cpu/microcode/amd.c | 63 ++++++++++++++++++------
arch/x86/kernel/cpu/microcode/core.c | 11 +++++
arch/x86/kernel/cpu/microcode/internal.h | 12 +++++
4 files changed, 73 insertions(+), 15 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index e2df1b147..b94f3dbf1 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1330,7 +1330,7 @@ config X86_REBOOTFIXUPS
config MICROCODE
def_bool y
- depends on CPU_SUP_AMD || CPU_SUP_INTEL
+ depends on CPU_SUP_AMD || CPU_SUP_INTEL || CPU_SUP_HYGON
select CRYPTO_LIB_SHA256 if CPU_SUP_AMD
config MICROCODE_INITRD32
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index e53388128..0f2d4829c 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -347,7 +347,8 @@ static u16 find_equiv_id(struct equiv_cpu_table *et, u32 sig)
unsigned int i;
/* Zen and newer do not need an equivalence table. */
- if (x86_family(bsp_cpuid_1_eax) >= 0x17)
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17)
return 0;
if (!et || !et->num_entries)
@@ -397,7 +398,8 @@ static bool verify_equivalence_table(const u8 *buf, size_t buf_size)
return false;
/* Zen and newer do not need an equivalence table. */
- if (x86_family(bsp_cpuid_1_eax) >= 0x17)
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17)
return true;
cont_type = hdr[1];
@@ -577,7 +579,8 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
static bool mc_patch_matches(struct microcode_amd *mc, u16 eq_id)
{
/* Zen and newer do not need an equivalence table. */
- if (x86_family(bsp_cpuid_1_eax) >= 0x17)
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17)
return ucode_rev_to_cpuid(mc->hdr.patch_id).full == bsp_cpuid_1_eax;
else
return eq_id == mc->hdr.processor_rev_id;
@@ -701,7 +704,9 @@ static bool __apply_microcode_amd(struct microcode_amd *mc, u32 *cur_rev,
native_wrmsrq(MSR_AMD64_PATCH_LOADER, p_addr);
- if (x86_family(bsp_cpuid_1_eax) == 0x17) {
+ if ((x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) == 0x17) ||
+ x86_cpuid_vendor() == X86_VENDOR_HYGON) {
unsigned long p_addr_end = p_addr + psize - 1;
invlpg(p_addr);
@@ -730,16 +735,19 @@ static bool __apply_microcode_amd(struct microcode_amd *mc, u32 *cur_rev,
static bool get_builtin_microcode(struct cpio_data *cp)
{
- char fw_name[36] = "amd-ucode/microcode_amd.bin";
+ char fw_name[40] = "amd-ucode/microcode_amd.bin";
u8 family = x86_family(bsp_cpuid_1_eax);
struct firmware fw;
if (IS_ENABLED(CONFIG_X86_32))
return false;
- if (family >= 0x15)
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD && family >= 0x15)
snprintf(fw_name, sizeof(fw_name),
"amd-ucode/microcode_amd_fam%02hhxh.bin", family);
+ else if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
+ snprintf(fw_name, sizeof(fw_name),
+ "hygon-ucode/microcode_hygon_fam%.2xh.bin", family);
if (firmware_request_builtin(&fw, fw_name)) {
cp->size = fw.size;
@@ -824,7 +832,8 @@ static inline bool patch_cpus_equivalent(struct ucode_patch *p,
bool ignore_stepping)
{
/* Zen and newer hardcode the f/m/s in the patch ID */
- if (x86_family(bsp_cpuid_1_eax) >= 0x17) {
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17) {
union cpuid_1_eax p_cid = ucode_rev_to_cpuid(p->patch_id);
union cpuid_1_eax n_cid = ucode_rev_to_cpuid(n->patch_id);
@@ -860,7 +869,8 @@ static struct ucode_patch *cache_find_patch(struct ucode_cpu_info *uci, u16 equi
static inline int patch_newer(struct ucode_patch *p, struct ucode_patch *n)
{
/* Zen and newer hardcode the f/m/s in the patch ID */
- if (x86_family(bsp_cpuid_1_eax) >= 0x17) {
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17) {
union zen_patch_rev zp, zn;
zp.ucode_rev = p->patch_id;
@@ -920,7 +930,9 @@ static struct ucode_patch *find_patch(unsigned int cpu)
uci->cpu_sig.rev = get_patch_level();
- if (x86_family(bsp_cpuid_1_eax) < 0x17) {
+ if ((x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) < 0x17) ||
+ x86_cpuid_vendor() == X86_VENDOR_HYGON) {
equiv_id = find_equiv_id(&equiv_table, uci->cpu_sig.sig);
if (!equiv_id)
return NULL;
@@ -1035,7 +1047,8 @@ static size_t install_equiv_cpu_table(const u8 *buf, size_t buf_size)
equiv_tbl_len = hdr[2];
/* Zen and newer do not need an equivalence table. */
- if (x86_family(bsp_cpuid_1_eax) >= 0x17)
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17)
goto out;
equiv_table.entry = vmalloc(equiv_tbl_len);
@@ -1054,7 +1067,8 @@ static size_t install_equiv_cpu_table(const u8 *buf, size_t buf_size)
static void free_equiv_cpu_table(void)
{
- if (x86_family(bsp_cpuid_1_eax) >= 0x17)
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD &&
+ x86_family(bsp_cpuid_1_eax) >= 0x17)
return;
vfree(equiv_table.entry);
@@ -1200,7 +1214,9 @@ static int __init save_microcode_in_initrd(void)
enum ucode_state ret;
struct cpio_data cp;
- if (microcode_loader_disabled() || c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10)
+ if (microcode_loader_disabled() ||
+ ((c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) &&
+ (c->x86_vendor != X86_VENDOR_HYGON)))
return 0;
cpuid_1_eax = native_cpuid_eax(1);
@@ -1238,7 +1254,7 @@ early_initcall(save_microcode_in_initrd);
*/
static enum ucode_state request_microcode_amd(int cpu, struct device *device)
{
- 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;
@@ -1246,8 +1262,11 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device)
if (force_minrev)
return UCODE_NFOUND;
- 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);
+ 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)) {
ucode_dbg("failed to load file %s\n", fw_name);
@@ -1297,8 +1316,24 @@ struct microcode_ops * __init init_amd_microcode(void)
pr_warn("AMD CPU family 0x%x not supported\n", c->x86);
return NULL;
}
+
+ return µcode_amd_ops;
+}
+
+#ifdef CONFIG_CPU_SUP_HYGON
+struct microcode_ops * __init init_hygon_microcode(void)
+{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+
+ if (c->x86_vendor != X86_VENDOR_HYGON)
+ return NULL;
+
+ strscpy((char *)ucode_path, "kernel/x86/microcode/HygonGenuine.bin",
+ sizeof(ucode_path));
+
return µcode_amd_ops;
}
+#endif
void __exit exit_amd_microcode(void)
{
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 651202e6f..813c5c157 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -195,6 +195,9 @@ void __init load_ucode_bsp(void)
return;
intel = false;
break;
+ case X86_VENDOR_HYGON:
+ intel = false;
+ break;
default:
return;
@@ -229,6 +232,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;
}
@@ -288,6 +294,9 @@ static 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;
}
@@ -895,6 +904,8 @@ static 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");
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index 3b93c0676..75591afae 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -61,6 +61,9 @@ struct cpio_data find_microcode_in_initrd(const char *path);
#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))))
@@ -87,6 +90,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;
}
@@ -128,6 +134,12 @@ static inline void reload_ucode_intel(void) { }
static inline struct microcode_ops *init_intel_microcode(void) { return NULL; }
#endif /* !CONFIG_CPU_SUP_INTEL */
+#ifdef CONFIG_CPU_SUP_HYGON
+struct microcode_ops *init_hygon_microcode(void);
+#else /* CONFIG_CPU_SUP_HYGON */
+static inline struct microcode_ops *init_hygon_microcode(void) { return NULL; }
+#endif /* !CONFIG_CPU_SUP_HYGON */
+
#define ucode_dbg(fmt, ...) \
({ \
if (IS_ENABLED(CONFIG_MICROCODE_DBG)) \
--
2.34.1
On Fri, Mar 27, 2026 at 04:10:09PM +0800, Fu Hao wrote:
> Add support for loading Hygon microcode, which is compatible with AMD one.
>
> Signed-off-by: Fu Hao <fuhao@open-hieco.net>
> ---
> arch/x86/Kconfig | 2 +-
> arch/x86/kernel/cpu/microcode/amd.c | 63 ++++++++++++++++++------
No.
arch/x86/kernel/cpu/microcode/hygon.c
please.
Do a separate compilation unit where all the Hygon stuff can live instead of
sprinkling "if HYGON" everywhere in the AMD loader.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
On 2026/3/27 16:13, Borislav Petkov wrote: > On Fri, Mar 27, 2026 at 04:10:09PM +0800, Fu Hao wrote: >> Add support for loading Hygon microcode, which is compatible with AMD one. >> >> Signed-off-by: Fu Hao <fuhao@open-hieco.net> >> --- >> arch/x86/Kconfig | 2 +- >> arch/x86/kernel/cpu/microcode/amd.c | 63 ++++++++++++++++++------ > > No. > > arch/x86/kernel/cpu/microcode/hygon.c > > please. > > Do a separate compilation unit where all the Hygon stuff can live instead of > sprinkling "if HYGON" everywhere in the AMD loader. > > Thx. > OK, thanks! I will create arch/x86/kernel/cpu/microcode/hygon.c in next patch series. -- Regards, Fu Hao
© 2016 - 2026 Red Hat, Inc.