* [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h
@ 2026-06-15 12:16 Fu Hao
2026-06-15 12:17 ` [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8 Fu Hao
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:16 UTC (permalink / raw)
To: puwen, tglx, mingo, bp, dave.hansen, x86, peterz, acme, joro,
will, suravee.suthikulpanit, robin.murphy, bhelgaas, perex, tiwai,
namhyung, alexander.shishkin, jolsa, irogers, james.clark, hpa
Cc: linux-kernel, tingyin.duan, Fu Hao
This patch series introduce support for the new-generation Hygon
processor models 0x4–0x8, addressing five key areas:
1. CPU Topology Updates
The new processor introduces a revised CPU topology hierarchy
compared to previous generations. This patch updates the kernel's
topology detection logic to correctly identify core/socket
relationships and cache sharing patterns.
2. L3 Performance Monitoring Updates
The L3 perf registers of model 6h differ from those of the
previous processor.
3. Microcode loading
Add microcode loading support for Hygon processor.
4. Audio Controller Driver
Add initial support for the integrated HD-Audio controller.
5. New SB IOAPIC information support
The southbridge IOAPIC of Hygon's new-generation processors is
located at 00:0b.0.
v2->v3:
- Refine the commit messages for patch x86/cpu/hygon.
- Update the logic for deriving the LLC ID from the APIC ID on Hygon
processors.
- Clean up and refine Hygon microcode support code.
- Refine the code of Hygon HD-Audio device ID.
- The iommu/hygon patch has been accepted and merged into iommu branch.
v1->v2:
- Refine the patch x86/cpu/hygon.
- Do a separate compilation unit on hygon microcode loading support.
- Use "access_sdnctl_in_dword" instead of "hygon_dword_acess" for
accessing the HDA registers.
Fu Hao (7):
x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models
0x4 through 0x8
x86/cpu: Get LLC ID for Hygon processor models 0x6 through 0x8
x86/cpu/hygon: Remove Spectral Chicken for Hygon processors
perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h
x86/microcode/hygon: Add microcode loading support for Hygon
processors
ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio
ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h
arch/x86/Kconfig | 2 +-
arch/x86/events/amd/uncore.c | 48 +-
arch/x86/include/asm/perf_event.h | 8 +
arch/x86/kernel/cpu/cacheinfo.c | 21 +-
arch/x86/kernel/cpu/hygon.c | 16 +-
arch/x86/kernel/cpu/microcode/Makefile | 1 +
arch/x86/kernel/cpu/microcode/core.c | 17 +-
arch/x86/kernel/cpu/microcode/hygon.c | 820 +++++++++++++++++++++++
arch/x86/kernel/cpu/microcode/internal.h | 20 +
sound/hda/controllers/intel.c | 11 +
sound/hda/core/controller.c | 10 +-
sound/hda/core/stream.c | 40 +-
12 files changed, 991 insertions(+), 23 deletions(-)
create mode 100644 arch/x86/kernel/cpu/microcode/hygon.c
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
@ 2026-06-15 12:17 ` Fu Hao
2026-06-15 14:07 ` Dave Hansen
2026-06-15 12:18 ` [PATCH v3 2/7] x86/cpu: Get LLC ID for Hygon processor models 0x6 " Fu Hao
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:17 UTC (permalink / raw)
To: puwen, tglx, mingo, bp, dave.hansen, x86, hpa
Cc: linux-kernel, tingyin.duan, Fu Hao
The die ID should be read from the NodeId field of CPUID leaf
0x8000001e (ECX) for Hygon processor models 0x4–0x8.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
Tested-by: Tingyin Duan <tingyin.duan@gmail.com>
---
arch/x86/kernel/cpu/hygon.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/x86/kernel/cpu/hygon.c b/arch/x86/kernel/cpu/hygon.c
index 7f95a74e4..0bf99072f 100644
--- a/arch/x86/kernel/cpu/hygon.c
+++ b/arch/x86/kernel/cpu/hygon.c
@@ -191,6 +191,16 @@ static void init_hygon(struct cpuinfo_x86 *c)
init_hygon_cacheinfo(c);
+ /*
+ * Adjust the die_id and logical_die_id for Hygon models 0x4-0x8.
+ */
+ if (c->x86_model >= 0x4 && c->x86_model <= 0x8) {
+ c->topo.die_id = cpuid_ecx(0x8000001e) & 0xff;
+ c->topo.logical_die_id = (c->topo.die_id >> 4) *
+ topology_amd_nodes_per_pkg() +
+ (c->topo.die_id & 0xf);
+ }
+
if (cpu_has(c, X86_FEATURE_SVM)) {
rdmsrq(MSR_VM_CR, vm_cr);
if (vm_cr & SVM_VM_CR_SVM_DIS_MASK) {
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 2/7] x86/cpu: Get LLC ID for Hygon processor models 0x6 through 0x8
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
2026-06-15 12:17 ` [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8 Fu Hao
@ 2026-06-15 12:18 ` Fu Hao
2026-06-15 12:19 ` [PATCH v3 3/7] x86/cpu/hygon: Remove Spectral Chicken for Hygon processors Fu Hao
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:18 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa; +Cc: linux-kernel, tingyin.duan, Fu Hao
For Hygon processors, the LLC ID comes from fixed bit of APIC ID
regardless of whether SMT is enabled or not. Add support for
Hygon processor models 0x6–0x8.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
---
arch/x86/kernel/cpu/cacheinfo.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
index 51a95b078..e563e327b 100644
--- a/arch/x86/kernel/cpu/cacheinfo.c
+++ b/arch/x86/kernel/cpu/cacheinfo.c
@@ -342,10 +342,25 @@ void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c)
return;
/*
- * Hygons are similar to AMD Family 17h up to 1F models: LLC is
- * at the core complex level. Core complex ID is ApicId[3].
+ * LLC is at the core complex level. For Hygon processor
+ * models 0x0–0x5, core complex ID is similar to AMD Family
+ * 17h up to 1F models, which is ApicId[3]. For Hygon
+ * processor models 0x6 and 0x7/0x8, core complex ID is
+ * derived from APIC ID bit 4 and bit 5, respectively.
*/
- c->topo.llc_id = c->topo.apicid >> 3;
+
+ switch (c->x86_model) {
+ case 0x6:
+ c->topo.llc_id = c->topo.apicid >> 4;
+ break;
+ case 0x7:
+ case 0x8:
+ c->topo.llc_id = c->topo.apicid >> 5;
+ break;
+ default:
+ c->topo.llc_id = c->topo.apicid >> 3;
+ break;
+ }
}
void init_amd_cacheinfo(struct cpuinfo_x86 *c)
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 3/7] x86/cpu/hygon: Remove Spectral Chicken for Hygon processors
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
2026-06-15 12:17 ` [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8 Fu Hao
2026-06-15 12:18 ` [PATCH v3 2/7] x86/cpu: Get LLC ID for Hygon processor models 0x6 " Fu Hao
@ 2026-06-15 12:19 ` Fu Hao
2026-06-15 12:20 ` [PATCH v3 4/7] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h Fu Hao
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:19 UTC (permalink / raw)
To: puwen, tglx, mingo, bp, dave.hansen, x86, hpa
Cc: linux-kernel, tingyin.duan, Fu Hao
It's no need to set the Spectral chicken bit for all Hygon processors.
Fixes: d7caac991fee ("x86/cpu/amd: Add Spectral Chicken")
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
Tested-by: Tingyin Duan <tingyin.duan@gmail.com>
---
arch/x86/kernel/cpu/hygon.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/arch/x86/kernel/cpu/hygon.c b/arch/x86/kernel/cpu/hygon.c
index 0bf99072f..d5513c0f1 100644
--- a/arch/x86/kernel/cpu/hygon.c
+++ b/arch/x86/kernel/cpu/hygon.c
@@ -176,12 +176,6 @@ static void init_hygon(struct cpuinfo_x86 *c)
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
- /*
- * XXX someone from Hygon needs to confirm this DTRT
- *
- init_spectral_chicken(c);
- */
-
set_cpu_cap(c, X86_FEATURE_ZEN);
set_cpu_cap(c, X86_FEATURE_CPB);
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 4/7] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
` (2 preceding siblings ...)
2026-06-15 12:19 ` [PATCH v3 3/7] x86/cpu/hygon: Remove Spectral Chicken for Hygon processors Fu Hao
@ 2026-06-15 12:20 ` Fu Hao
2026-06-15 12:38 ` sashiko-bot
2026-06-15 12:21 ` [PATCH v3 5/7] x86/microcode/hygon: Add microcode loading support for Hygon processors Fu Hao
` (2 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:20 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung, tglx, bp, dave.hansen, x86,
alexander.shishkin, jolsa, irogers, james.clark, hpa
Cc: linux-perf-users, linux-kernel, tingyin.duan, Fu Hao
Adjust the L3 PMU slicemask and threadmask for Hygon family 18h
model 6h processor.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
Tested-by: Tingyin Duan <tingyin.duan@gmail.com>
---
arch/x86/events/amd/uncore.c | 48 ++++++++++++++++++++++++++++++-
arch/x86/include/asm/perf_event.h | 8 ++++++
2 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
index dd956cfca..e71d9e784 100644
--- a/arch/x86/events/amd/uncore.c
+++ b/arch/x86/events/amd/uncore.c
@@ -308,6 +308,14 @@ amd_f17h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, int i)
attr->mode : 0;
}
+static umode_t
+hygon_f18h_m6h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, int i)
+{
+ return boot_cpu_data.x86 == 0x18 &&
+ boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf ?
+ attr->mode : 0;
+}
+
static umode_t
amd_f19h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, int i)
{
@@ -359,6 +367,8 @@ DEFINE_UNCORE_FORMAT_ATTR(enallslices, enallslices, "config:46"); /* F19h L3
DEFINE_UNCORE_FORMAT_ATTR(enallcores, enallcores, "config:47"); /* F19h L3 */
DEFINE_UNCORE_FORMAT_ATTR(sliceid, sliceid, "config:48-50"); /* F19h L3 */
DEFINE_UNCORE_FORMAT_ATTR(rdwrmask, rdwrmask, "config:8-9"); /* PerfMonV2 UMC */
+DEFINE_UNCORE_FORMAT_ATTR(slicemask4, slicemask, "config:28-31"); /* F18h L3 */
+DEFINE_UNCORE_FORMAT_ATTR(threadmask32, threadmask, "config:32-63"); /* F18h L3 */
/* Common DF and NB attributes */
static struct attribute *amd_uncore_df_format_attr[] = {
@@ -388,6 +398,12 @@ static struct attribute *amd_f17h_uncore_l3_format_attr[] = {
NULL,
};
+/* F18h M06h unique L3 attributes */
+static struct attribute *hygon_f18h_m6h_uncore_l3_format_attr[] = {
+ &format_attr_slicemask4.attr, /* slicemask */
+ NULL,
+};
+
/* F19h unique L3 attributes */
static struct attribute *amd_f19h_uncore_l3_format_attr[] = {
&format_attr_coreid.attr, /* coreid */
@@ -413,6 +429,12 @@ static struct attribute_group amd_f17h_uncore_l3_format_group = {
.is_visible = amd_f17h_uncore_is_visible,
};
+static struct attribute_group hygon_f18h_m6h_uncore_l3_format_group = {
+ .name = "format",
+ .attrs = hygon_f18h_m6h_uncore_l3_format_attr,
+ .is_visible = hygon_f18h_m6h_uncore_is_visible,
+};
+
static struct attribute_group amd_f19h_uncore_l3_format_group = {
.name = "format",
.attrs = amd_f19h_uncore_l3_format_attr,
@@ -442,6 +464,11 @@ static const struct attribute_group *amd_uncore_l3_attr_update[] = {
NULL,
};
+static const struct attribute_group *hygon_uncore_l3_attr_update[] = {
+ &hygon_f18h_m6h_uncore_l3_format_group,
+ NULL,
+};
+
static const struct attribute_group *amd_uncore_umc_attr_groups[] = {
&amd_uncore_attr_group,
&amd_uncore_umc_format_group,
@@ -820,6 +847,12 @@ static int amd_uncore_l3_event_init(struct perf_event *event)
mask = AMD64_L3_F19H_THREAD_MASK | AMD64_L3_EN_ALL_SLICES |
AMD64_L3_EN_ALL_CORES;
+ if (boot_cpu_data.x86 == 0x18 &&
+ boot_cpu_data.x86_model >= 0x6 &&
+ boot_cpu_data.x86_model <= 0xf)
+ mask = ((config & HYGON_L3_SLICE_MASK) ? : HYGON_L3_SLICE_MASK) |
+ ((config & HYGON_L3_THREAD_MASK) ? : HYGON_L3_THREAD_MASK);
+
hwc->config |= mask;
return 0;
@@ -877,7 +910,8 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, unsigned int cpu)
pmu->rdpmc_base = RDPMC_BASE_LLC;
pmu->group = amd_uncore_ctx_gid(uncore, cpu);
- if (boot_cpu_data.x86 >= 0x17) {
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+ boot_cpu_data.x86 >= 0x17) {
*l3_attr++ = &format_attr_event8.attr;
*l3_attr++ = &format_attr_umask8.attr;
*l3_attr++ = boot_cpu_data.x86 >= 0x19 ?
@@ -904,6 +938,18 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, unsigned int cpu)
.module = THIS_MODULE,
};
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON &&
+ boot_cpu_data.x86 == 0x18) {
+ *l3_attr++ = &format_attr_event8.attr;
+ *l3_attr++ = &format_attr_umask8.attr;
+ if (boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf) {
+ *l3_attr++ = &format_attr_threadmask32.attr;
+ pmu->pmu.attr_update = hygon_uncore_l3_attr_update;
+ } else {
+ *l3_attr++ = &format_attr_threadmask8.attr;
+ }
+ }
+
if (perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1)) {
free_percpu(pmu->ctx);
pmu->ctx = NULL;
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index ff5acb8b1..404752601 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -89,6 +89,14 @@
#define AMD64_L3_COREID_MASK \
(0x7ULL << AMD64_L3_COREID_SHIFT)
+#define HYGON_L3_SLICE_SHIFT 28
+#define HYGON_L3_SLICE_MASK \
+ (0xFULL << HYGON_L3_SLICE_SHIFT)
+
+#define HYGON_L3_THREAD_SHIFT 32
+#define HYGON_L3_THREAD_MASK \
+ (0xFFFFFFFFULL << HYGON_L3_THREAD_SHIFT)
+
#define X86_RAW_EVENT_MASK \
(ARCH_PERFMON_EVENTSEL_EVENT | \
ARCH_PERFMON_EVENTSEL_UMASK | \
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 5/7] x86/microcode/hygon: Add microcode loading support for Hygon processors
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
` (3 preceding siblings ...)
2026-06-15 12:20 ` [PATCH v3 4/7] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h Fu Hao
@ 2026-06-15 12:21 ` Fu Hao
2026-06-15 12:22 ` [PATCH v3 6/7] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio Fu Hao
2026-06-15 12:23 ` [PATCH v3 7/7] ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h Fu Hao
6 siblings, 0 replies; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:21 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa; +Cc: linux-kernel, tingyin.duan, Fu Hao
Add support for loading Hygon microcode.
There are two loading methods:
1. Loading from the initrd/builtin.
2. Late loading, which depends on the kernel configuration option
CONFIG_MICROCODE_LATE_LOADING being enabled, and this option
is usually disabled by default, so it's not recommended.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
---
arch/x86/Kconfig | 2 +-
arch/x86/kernel/cpu/microcode/Makefile | 1 +
arch/x86/kernel/cpu/microcode/core.c | 17 +-
arch/x86/kernel/cpu/microcode/hygon.c | 820 +++++++++++++++++++++++
arch/x86/kernel/cpu/microcode/internal.h | 20 +
5 files changed, 858 insertions(+), 2 deletions(-)
create mode 100644 arch/x86/kernel/cpu/microcode/hygon.c
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/Makefile b/arch/x86/kernel/cpu/microcode/Makefile
index 193d98b33..671d9a095 100644
--- a/arch/x86/kernel/cpu/microcode/Makefile
+++ b/arch/x86/kernel/cpu/microcode/Makefile
@@ -3,3 +3,4 @@ microcode-y := core.o
obj-$(CONFIG_MICROCODE) += microcode.o
microcode-$(CONFIG_CPU_SUP_INTEL) += intel.o
microcode-$(CONFIG_CPU_SUP_AMD) += amd.o
+microcode-$(CONFIG_CPU_SUP_HYGON) += hygon.o
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 651202e6f..5f82f2436 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;
@@ -203,7 +206,11 @@ void __init load_ucode_bsp(void)
if (intel)
load_ucode_intel_bsp(&early_data);
else
- load_ucode_amd_bsp(&early_data, cpuid_1_eax);
+ if (x86_cpuid_vendor() == X86_VENDOR_HYGON)
+ load_ucode_hygon_bsp(&early_data, cpuid_1_eax);
+ else
+ load_ucode_amd_bsp(&early_data, cpuid_1_eax);
+
}
void load_ucode_ap(void)
@@ -229,6 +236,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_hygon_ap(cpuid_1_eax);
+ break;
default:
break;
}
@@ -288,6 +298,9 @@ static void reload_early_microcode(unsigned int cpu)
if (family >= 0x10)
reload_ucode_amd(cpu);
break;
+ case X86_VENDOR_HYGON:
+ reload_ucode_hygon(cpu);
+ break;
default:
break;
}
@@ -895,6 +908,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/hygon.c b/arch/x86/kernel/cpu/microcode/hygon.c
new file mode 100644
index 000000000..ce4a60f53
--- /dev/null
+++ b/arch/x86/kernel/cpu/microcode/hygon.c
@@ -0,0 +1,820 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hygon CPU Microcode Update Driver for Linux
+ *
+ * This driver allows to upgrade microcode on Hygon CPUs.
+ *
+ */
+#define pr_fmt(fmt) "microcode: " fmt
+
+#include <linux/earlycpio.h>
+#include <linux/firmware.h>
+#include <linux/bsearch.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/initrd.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <asm/microcode.h>
+#include <asm/processor.h>
+#include <asm/setup.h>
+#include <asm/cpu.h>
+#include <asm/msr.h>
+#include <asm/tlb.h>
+
+#include "internal.h"
+
+#define HYGON_UCODE_MAGIC 0x4859474F
+#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
+#define UCODE_UCODE_TYPE 0x00000001
+
+#define HYGON_CONTAINER_HDR_SIZE 12
+#define HYGON_CONTAINER_HDR_MAGIC_OFFSET 0
+#define HYGON_CONTAINER_HDR_TYPE_OFFSET 4
+#define HYGON_CONTAINER_HDR_LENGTH_OFFSET 8
+
+#define HYGON_SECTION_HDR_SIZE 8
+#define HYGON_SECTION_HDR_TYPE_OFFSET 0
+#define HYGON_SECTION_HDR_SIZE_OFFSET 4
+
+#define HYGON_MSR_PATCH_LEVEL 0x0000008b
+#define HYGON_MSR_PATCH_LOADER 0xc0010020
+
+#define HYGON_PROC_REV_ID_FAMILY_SHIFT 12
+#define HYGON_PROC_REV_ID_BASE_FAMILY 0xF
+
+#define HYGON_UCODE_FW_NAME "hygon-ucode/microcode_hygon_fam18h.bin"
+#define HYGON_UCODE_PATH "kernel/x86/microcode/HygonGenuine.bin"
+
+#define hygon_container_hdr(_buf, _field) \
+ (*(const u32 *)((_buf) + HYGON_CONTAINER_HDR_##_field##_OFFSET))
+
+#define hygon_section_hdr(_buf, _field) \
+ (*(const u32 *)((_buf) + HYGON_SECTION_HDR_##_field##_OFFSET))
+
+#define hygon_extract_proc_family(_proc_rev) \
+ (HYGON_PROC_REV_ID_BASE_FAMILY + \
+ ((_proc_rev) >> HYGON_PROC_REV_ID_FAMILY_SHIFT))
+
+#define hygon_pages_crossed(_start, _size) \
+ (((unsigned long)(_start) ^ ((unsigned long)(_start) + (_size) - 1)) >> PAGE_SHIFT)
+
+#define hygon_container_data(_buf) ((_buf) + HYGON_CONTAINER_HDR_SIZE)
+#define hygon_section_data(_buf) ((_buf) + HYGON_SECTION_HDR_SIZE)
+
+struct hygon_ucode_patch {
+ struct list_head list;
+ void *data;
+ unsigned int patch_size;
+ u32 patch_level;
+ u16 equiv_id;
+};
+
+struct hygon_microcode_header {
+ u32 data;
+ u32 patch_level;
+ u32 reserved0[4];
+ u16 processor_rev_id;
+ u16 reserved1;
+ u32 reserved2[9];
+} __packed;
+
+struct hygon_microcode {
+ struct hygon_microcode_header header;
+ unsigned int payload[];
+};
+
+struct hygon_cpu_equiv_entry {
+ u32 cpu_signature;
+ u16 equiv_id;
+ u16 reserved;
+} __packed;
+
+struct hygon_cpu_equiv_table {
+ unsigned int entry_count;
+ struct hygon_cpu_equiv_entry *entry;
+};
+
+struct hygon_container_desc {
+ struct hygon_microcode *mc;
+ u32 patch_size;
+ u8 *data;
+ size_t size;
+};
+
+static const char ucode_path[] __maybe_unused = HYGON_UCODE_PATH;
+static u32 bsp_cpuid_1_eax __ro_after_init;
+static LIST_HEAD(microcode_cache);
+static struct hygon_cpu_equiv_table equiv_table;
+
+static u32 get_current_patch_level(void)
+{
+ u32 rev, dummy __always_unused;
+
+ native_rdmsr(HYGON_MSR_PATCH_LEVEL, rev, dummy);
+
+ return rev;
+}
+
+static u16 find_equiv_id(struct hygon_cpu_equiv_table *et, u32 sig)
+{
+ unsigned int i;
+
+ if (!et || !et->entry_count)
+ return 0;
+
+ for (i = 0; i < et->entry_count; i++) {
+ if (sig == et->entry[i].cpu_signature)
+ return et->entry[i].equiv_id;
+ }
+ return 0;
+}
+
+static bool is_valid_microcode_container(const u8 *buf, size_t buf_size)
+{
+ if (buf_size <= HYGON_CONTAINER_HDR_SIZE) {
+ ucode_dbg("Truncated microcode container header.\n");
+ return false;
+ }
+
+ if (hygon_container_hdr(buf, MAGIC) != HYGON_UCODE_MAGIC) {
+ ucode_dbg("Invalid magic value (0x%08x).\n",
+ hygon_container_hdr(buf, MAGIC));
+ return false;
+ }
+
+ return true;
+}
+
+static bool verify_cpu_equivalence_table(const u8 *buf, size_t buf_size)
+{
+ u32 cont_type, equiv_tbl_len;
+
+ if (!is_valid_microcode_container(buf, buf_size))
+ return false;
+
+ cont_type = hygon_container_hdr(buf, TYPE);
+ if (cont_type != UCODE_EQUIV_CPU_TABLE_TYPE) {
+ ucode_dbg("Wrong microcode container equivalence table type: %u.\n",
+ cont_type);
+ return false;
+ }
+
+ buf_size -= HYGON_CONTAINER_HDR_SIZE;
+
+ equiv_tbl_len = hygon_container_hdr(buf, LENGTH);
+ if (equiv_tbl_len < sizeof(struct hygon_cpu_equiv_entry) ||
+ buf_size < equiv_tbl_len) {
+ ucode_dbg("Truncated equivalence table.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_valid_patch_section(const u8 *buf, size_t buf_size, u32 *patch_size)
+{
+ if (buf_size < HYGON_SECTION_HDR_SIZE) {
+ ucode_dbg("Truncated patch section.\n");
+ return false;
+ }
+
+ *patch_size = hygon_section_hdr(buf, SIZE);
+
+ if (hygon_section_hdr(buf, TYPE) != UCODE_UCODE_TYPE) {
+ ucode_dbg("Invalid type field (0x%x) in container file section header.\n",
+ hygon_section_hdr(buf, TYPE));
+ return false;
+ }
+
+ if (*patch_size < sizeof(struct hygon_microcode_header)) {
+ ucode_dbg("Patch of size %u too short.\n", *patch_size);
+ return false;
+ }
+
+ return true;
+}
+
+static int validate_patch_section(const u8 *buf, size_t buf_size, u32 *patch_size)
+{
+ u8 family = x86_family(bsp_cpuid_1_eax);
+ struct hygon_microcode_header *mc_hdr;
+ u16 proc_id;
+
+ if (!is_valid_patch_section(buf, buf_size, patch_size))
+ return -1;
+
+ buf_size -= HYGON_SECTION_HDR_SIZE;
+
+ if (buf_size < *patch_size) {
+ ucode_dbg("Patch of size %u truncated.\n", *patch_size);
+ return -1;
+ }
+
+ mc_hdr = (struct hygon_microcode_header *)hygon_section_data(buf);
+
+ proc_id = mc_hdr->processor_rev_id;
+
+ if (hygon_extract_proc_family(proc_id) != family)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Attempt to consume one container from the ucode blob.
+ *
+ * This function processes a single container starting at @ucode. It verifies
+ * the container's CPU equivalence table, locates the equivalence ID for the
+ * current CPU, and then scans all patch sections inside the container.
+ *
+ * If a patch matching the CPU's equivalence ID is found, the function fills
+ * @desc with the patch metadata and the container's bounds, and returns 0.
+ * Otherwise, it returns the total number of bytes consumed (the entire size
+ * of this container) so the caller can skip it and continue scanning.
+ */
+static size_t try_consume_container(u8 *ucode, size_t size, struct hygon_container_desc *desc)
+{
+ struct hygon_cpu_equiv_table table;
+ size_t orig_size = size;
+ u32 equiv_data_len;
+ u16 cpu_equiv_id;
+ u8 *buf;
+
+ if (!verify_cpu_equivalence_table(ucode, size))
+ return 0;
+
+ buf = ucode;
+ equiv_data_len = hygon_container_hdr(buf, LENGTH);
+
+ table.entry = (struct hygon_cpu_equiv_entry *)hygon_container_data(buf);
+ table.entry_count = equiv_data_len / sizeof(struct hygon_cpu_equiv_entry);
+
+ cpu_equiv_id = find_equiv_id(&table, bsp_cpuid_1_eax);
+
+ buf += equiv_data_len + HYGON_CONTAINER_HDR_SIZE;
+ size -= equiv_data_len + HYGON_CONTAINER_HDR_SIZE;
+
+ while (size > 0) {
+ struct hygon_microcode *mc;
+ u32 patch_size;
+ int ret;
+
+ ret = validate_patch_section(buf, size, &patch_size);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ goto skip;
+
+ mc = (struct hygon_microcode *)hygon_section_data(buf);
+
+ if (cpu_equiv_id == mc->header.processor_rev_id) {
+ desc->patch_size = patch_size;
+ desc->mc = mc;
+
+ ucode_dbg(" match: size: %d\n", patch_size);
+ }
+
+skip:
+ buf += patch_size + HYGON_SECTION_HDR_SIZE;
+ size -= patch_size + HYGON_SECTION_HDR_SIZE;
+ }
+
+out:
+ /*
+ * If we have found a matching patch, record the container's base pointer
+ * and its total size, then return 0 to indicate that @ucode already points
+ * to the correct container.
+ *
+ * Otherwise, return the total number of bytes scanned, allowing the caller
+ * to skip this entire container and try the next one.
+ */
+ if (desc->mc) {
+ desc->data = ucode;
+ desc->size = orig_size - size;
+
+ return 0;
+ }
+
+ return orig_size - size;
+}
+
+/*
+ * Find the container that contains a patch for the current CPU
+ *
+ * The ucode blob may consist of multiple containers concatenated together.
+ * This function iterates through them by repeatedly calling try_consume_container().
+ * Once a container with a matching patch is found, it fills @desc and returns.
+ * If no matching container exists in the entire blob, @desc remains unchanged.
+ */
+static void locate_patch_container(u8 *ucode, size_t size, struct hygon_container_desc *desc)
+{
+ while (size) {
+ size_t consumed = try_consume_container(ucode, size, desc);
+
+ if (!consumed)
+ return;
+
+ if (size >= consumed) {
+ ucode += consumed;
+ size -= consumed;
+ } else {
+ return;
+ }
+ }
+}
+
+static bool apply_hygon_patch(struct hygon_microcode *mc, u32 *cur_rev,
+ unsigned int patch_size)
+{
+ unsigned long patch_start = (unsigned long)&mc->header.data;
+ unsigned long patch_end = patch_start + patch_size - 1;
+
+ native_wrmsrq(HYGON_MSR_PATCH_LOADER, patch_start);
+ invlpg(patch_start);
+
+ /*
+ * Invalidate the next page if the patch image crosses a page boundary.
+ */
+ if (hygon_pages_crossed(patch_start, patch_size))
+ invlpg(patch_end);
+
+ if (IS_ENABLED(CONFIG_MICROCODE_DBG) && hypervisor_present)
+ microcode_rev[smp_processor_id()] = mc->header.patch_level;
+
+ *cur_rev = get_current_patch_level();
+
+ ucode_dbg("updated rev: 0x%x\n", *cur_rev);
+
+ return *cur_rev == mc->header.patch_level;
+}
+
+static bool load_builtin_microcode(struct cpio_data *cp)
+{
+ struct firmware fw;
+
+ if (IS_ENABLED(CONFIG_X86_32))
+ return false;
+
+ if (firmware_request_builtin(&fw, HYGON_UCODE_FW_NAME)) {
+ cp->size = fw.size;
+ cp->data = (void *)fw.data;
+ return true;
+ }
+
+ return false;
+}
+
+static bool __init find_microcode_blob(struct cpio_data *ret)
+{
+ struct cpio_data cp;
+
+ if (!load_builtin_microcode(&cp))
+ cp = find_microcode_in_initrd(ucode_path);
+
+ if (cp.data && cp.size) {
+ *ret = cp;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * This function handles microcode loading during the VERY EARLY boot stage
+ * (before vmalloc is available). The entire operation must be done in-place
+ * within the initrd memory.
+ *
+ * Flow:
+ * 1. Read current microcode revision.
+ * 2. Locate the microcode container blob from initrd.
+ * 3. Traverse the Equivalent CPU Table to find the matching microcode patch.
+ * 4. Apply the patch if it is newer than the current revision.
+ */
+void __init load_ucode_hygon_bsp(struct early_load_data *ed, unsigned int cpuid_1_eax)
+{
+ struct hygon_container_desc desc = { };
+ struct hygon_microcode *mc;
+ struct cpio_data cp = { };
+ u32 rev;
+
+ bsp_cpuid_1_eax = cpuid_1_eax;
+
+ rev = get_current_patch_level();
+ ed->old_rev = rev;
+
+ /* Needed in hygon_load_microcode() */
+ ucode_cpu_info[0].cpu_sig.sig = cpuid_1_eax;
+
+ if (!find_microcode_blob(&cp))
+ return;
+
+ locate_patch_container(cp.data, cp.size, &desc);
+
+ mc = desc.mc;
+
+ if (!mc || (ed->old_rev > mc->header.patch_level))
+ return;
+
+ if (apply_hygon_patch(mc, &rev, desc.patch_size))
+ ed->new_rev = rev;
+}
+
+/*
+ * a small, trivial cache of per-family ucode patches
+ */
+static struct hygon_ucode_patch *find_cached_patch(u16 equiv_id)
+{
+ struct hygon_ucode_patch *p;
+
+ list_for_each_entry(p, µcode_cache, list)
+ if (p->equiv_id == equiv_id)
+ return p;
+
+ return NULL;
+}
+
+static void add_to_cache(struct hygon_ucode_patch *new_patch)
+{
+ struct hygon_ucode_patch *p;
+
+ list_for_each_entry(p, µcode_cache, list) {
+ if (p->equiv_id == new_patch->equiv_id) {
+ if (p->patch_level >= new_patch->patch_level) {
+ /* we already have the latest patch */
+ kfree(new_patch->data);
+ kfree(new_patch);
+ return;
+ }
+
+ list_replace(&p->list, &new_patch->list);
+ kfree(p->data);
+ kfree(p);
+ return;
+ }
+ }
+ /* no patch found, add it */
+ list_add_tail(&new_patch->list, µcode_cache);
+}
+
+static void clear_microcode_cache(void)
+{
+ struct hygon_ucode_patch *p, *tmp;
+
+ list_for_each_entry_safe(p, tmp, µcode_cache, list) {
+ __list_del(p->list.prev, p->list.next);
+ kfree(p->data);
+ kfree(p);
+ }
+}
+
+static struct hygon_ucode_patch *find_hygon_patch(unsigned int cpu)
+{
+ struct ucode_cpu_info *u_cpu_info = ucode_cpu_info + cpu;
+ u16 equiv_id;
+
+ u_cpu_info->cpu_sig.rev = get_current_patch_level();
+
+ equiv_id = find_equiv_id(&equiv_table, u_cpu_info->cpu_sig.sig);
+
+ return equiv_id ? find_cached_patch(equiv_id) : NULL;
+}
+
+void reload_ucode_hygon(unsigned int cpu)
+{
+ struct hygon_microcode *mc;
+ struct hygon_ucode_patch *p;
+ u32 rev;
+
+ p = find_hygon_patch(cpu);
+ if (!p)
+ return;
+
+ mc = p->data;
+
+ rev = get_current_patch_level();
+ if (rev < mc->header.patch_level) {
+ if (apply_hygon_patch(mc, &rev, p->patch_size))
+ pr_info_once("reload revision: 0x%08x\n", rev);
+ }
+}
+
+static int hygon_collect_cpu_info(int cpu, struct cpu_signature *csig)
+{
+ struct ucode_cpu_info *u_cpu_info = ucode_cpu_info + cpu;
+ struct hygon_ucode_patch *p;
+
+ csig->sig = cpuid_eax(0x00000001);
+ csig->rev = get_current_patch_level();
+
+ p = find_hygon_patch(cpu);
+ if (p && (p->patch_level == csig->rev))
+ u_cpu_info->mc = p->data;
+
+ return 0;
+}
+
+static enum ucode_state hygon_apply_microcode(int cpu)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ struct hygon_microcode *mc_hygon;
+ struct ucode_cpu_info *u_cpu_info;
+ struct hygon_ucode_patch *p;
+ enum ucode_state ret;
+ u32 rev;
+
+ if (raw_smp_processor_id() != cpu) {
+ WARN_ON_ONCE(1);
+ return UCODE_ERROR;
+ }
+
+ u_cpu_info = ucode_cpu_info + cpu;
+
+ p = find_hygon_patch(cpu);
+ if (!p)
+ return UCODE_NFOUND;
+
+ rev = u_cpu_info->cpu_sig.rev;
+
+ mc_hygon = p->data;
+ u_cpu_info->mc = p->data;
+
+ /* Skip update if current revision is already newer than the patch */
+ if (rev > mc_hygon->header.patch_level) {
+ ret = UCODE_OK;
+ goto out;
+ }
+
+ if (!apply_hygon_patch(mc_hygon, &rev, p->patch_size)) {
+ pr_err("CPU%d: update failed for patch_level=0x%08x\n",
+ cpu, mc_hygon->header.patch_level);
+ return UCODE_ERROR;
+ }
+
+ rev = mc_hygon->header.patch_level;
+ ret = UCODE_UPDATED;
+
+out:
+ u_cpu_info->cpu_sig.rev = rev;
+ c->microcode = rev;
+
+ /* Update boot CPU's microcode revision as well */
+ if (c->cpu_index == boot_cpu_data.cpu_index)
+ boot_cpu_data.microcode = rev;
+
+ return ret;
+}
+
+void load_ucode_hygon_ap(unsigned int cpuid_1_eax)
+{
+ unsigned int cpu = smp_processor_id();
+
+ ucode_cpu_info[cpu].cpu_sig.sig = cpuid_1_eax;
+ hygon_apply_microcode(cpu);
+}
+
+static size_t load_equiv_cpu_table(const u8 *buf, size_t buf_size)
+{
+ u32 equiv_tbl_len;
+
+ if (!verify_cpu_equivalence_table(buf, buf_size))
+ return 0;
+
+ equiv_tbl_len = hygon_container_hdr(buf, LENGTH);
+
+ equiv_table.entry = vmalloc(equiv_tbl_len);
+ if (!equiv_table.entry) {
+ pr_err("failed to allocate equivalent CPU table\n");
+ return 0;
+ }
+
+ memcpy(equiv_table.entry, hygon_container_data(buf), equiv_tbl_len);
+ equiv_table.entry_count = equiv_tbl_len / sizeof(struct hygon_cpu_equiv_entry);
+
+ return equiv_tbl_len + HYGON_CONTAINER_HDR_SIZE;
+}
+
+static void clear_equiv_cpu_table(void)
+{
+ vfree(equiv_table.entry);
+ memset(&equiv_table, 0, sizeof(equiv_table));
+}
+
+static void hygon_cleanup_microcode(void)
+{
+ clear_equiv_cpu_table();
+ clear_microcode_cache();
+}
+
+/*
+ * Return a non-negative value even if some of the checks failed so that
+ * we can skip over the next patch. If we return a negative value, we
+ * signal a grave error like a memory allocation has failed and the
+ * driver cannot continue functioning normally. In such cases, we tear
+ * down everything we've used up so far and exit.
+ */
+static int try_verify_and_cache_patch(u8 family, u8 *fw, unsigned int leftover,
+ unsigned int *patch_size)
+{
+ struct hygon_microcode_header *mc_hdr;
+ struct hygon_ucode_patch *patch;
+ int ret;
+
+ ret = validate_patch_section(fw, leftover, patch_size);
+ if (ret)
+ return ret;
+
+ patch = kzalloc_obj(*patch);
+ if (!patch) {
+ pr_err("Patch allocation failure.\n");
+ return -EINVAL;
+ }
+
+ patch->data = kmemdup(hygon_section_data(fw), *patch_size, GFP_KERNEL);
+ if (!patch->data) {
+ pr_err("Patch data allocation failure.\n");
+ kfree(patch);
+ return -EINVAL;
+ }
+ patch->patch_size = *patch_size;
+
+ mc_hdr = (struct hygon_microcode_header *)hygon_section_data(fw);
+
+ INIT_LIST_HEAD(&patch->list);
+ patch->patch_level = mc_hdr->patch_level;
+ patch->equiv_id = mc_hdr->processor_rev_id;
+
+ ucode_dbg("%s: Adding patch_level: 0x%08x, proc_id: 0x%04x\n",
+ __func__, patch->patch_level, patch->equiv_id);
+
+ add_to_cache(patch);
+
+ return 0;
+}
+
+/* Scan the blob in @data and add microcode patches to the cache. */
+static enum ucode_state parse_and_cache_patches(u8 family, const u8 *data, size_t size)
+{
+ u8 *fw = (u8 *)data;
+ size_t offset;
+
+ offset = load_equiv_cpu_table(data, size);
+ if (!offset)
+ return UCODE_ERROR;
+
+ fw += offset;
+ size -= offset;
+
+ if (hygon_section_hdr(fw, TYPE) != UCODE_UCODE_TYPE) {
+ pr_err("invalid type field in container file section header\n");
+ clear_equiv_cpu_table();
+ return UCODE_ERROR;
+ }
+
+ while (size > 0) {
+ unsigned int crnt_size = 0;
+ int ret;
+
+ ret = try_verify_and_cache_patch(family, fw, size, &crnt_size);
+ if (ret < 0)
+ return UCODE_ERROR;
+
+ fw += crnt_size + HYGON_SECTION_HDR_SIZE;
+ size -= (crnt_size + HYGON_SECTION_HDR_SIZE);
+ }
+
+ return UCODE_OK;
+}
+
+static enum ucode_state prepare_microcode_blob(u8 family, const u8 *data, size_t size)
+{
+ enum ucode_state ret;
+
+ clear_equiv_cpu_table();
+
+ ret = parse_and_cache_patches(family, data, size);
+ if (ret != UCODE_OK)
+ hygon_cleanup_microcode();
+
+ return ret;
+}
+
+static enum ucode_state hygon_load_microcode(u8 family, const u8 *data, size_t size)
+{
+ struct cpuinfo_x86 *c;
+ unsigned int nid, cpu;
+ struct hygon_ucode_patch *p;
+ enum ucode_state ret;
+
+ ret = prepare_microcode_blob(family, data, size);
+ if (ret != UCODE_OK)
+ return ret;
+
+ for_each_node_with_cpus(nid) {
+ cpu = cpumask_first(cpumask_of_node(nid));
+ c = &cpu_data(cpu);
+
+ p = find_hygon_patch(cpu);
+ if (!p)
+ continue;
+
+ if (c->microcode >= p->patch_level)
+ continue;
+
+ ret = UCODE_NEW;
+ }
+
+ return ret;
+}
+
+static int __init save_microcode_in_initrd(void)
+{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+ struct hygon_container_desc desc = { 0 };
+ unsigned int cpuid_1_eax;
+ enum ucode_state ret;
+ struct cpio_data cp;
+
+ if (microcode_loader_disabled() || (c->x86_vendor != X86_VENDOR_HYGON))
+ return 0;
+
+ cpuid_1_eax = native_cpuid_eax(1);
+
+ if (!find_microcode_blob(&cp))
+ return -EINVAL;
+
+ locate_patch_container(cp.data, cp.size, &desc);
+ if (!desc.mc)
+ return -EINVAL;
+
+ ret = prepare_microcode_blob(x86_family(cpuid_1_eax), desc.data, desc.size);
+ return (ret > UCODE_UPDATED) ? -EINVAL : 0;
+}
+early_initcall(save_microcode_in_initrd);
+
+/*
+ * Hygon microcode firmware naming are in family-specific firmware files:
+ * hygon-ucode/microcode_hygon_fam18h.bin
+ */
+static enum ucode_state hygon_request_microcode(int cpu, struct device *device)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ enum ucode_state ret = UCODE_NFOUND;
+ const struct firmware *fw;
+
+ if (force_minrev)
+ return UCODE_NFOUND;
+
+ if (request_firmware_direct(&fw, (const char *)HYGON_UCODE_FW_NAME, device)) {
+ ucode_dbg("failed to load file %s\n", HYGON_UCODE_FW_NAME);
+ goto out;
+ }
+
+ ret = UCODE_ERROR;
+ if (!is_valid_microcode_container(fw->data, fw->size))
+ goto fw_release;
+
+ ret = hygon_load_microcode(c->x86, fw->data, fw->size);
+
+ fw_release:
+ release_firmware(fw);
+
+ out:
+ return ret;
+}
+
+static void hygon_microcode_fini_cpu(int cpu)
+{
+ struct ucode_cpu_info *u_cpu_info = ucode_cpu_info + cpu;
+
+ u_cpu_info->mc = NULL;
+}
+
+static void hygon_finalize_late_load(int result)
+{
+ if (result)
+ hygon_cleanup_microcode();
+}
+
+static struct microcode_ops microcode_hygon_ops = {
+ .request_microcode_fw = hygon_request_microcode,
+ .collect_cpu_info = hygon_collect_cpu_info,
+ .apply_microcode = hygon_apply_microcode,
+ .microcode_fini_cpu = hygon_microcode_fini_cpu,
+ .finalize_late_load = hygon_finalize_late_load,
+ .nmi_safe = true,
+};
+
+struct microcode_ops * __init init_hygon_microcode(void)
+{
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
+ return NULL;
+
+ return µcode_hygon_ops;
+}
+
+void __exit exit_hygon_microcode(void)
+{
+ hygon_cleanup_microcode();
+}
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index 3b93c0676..1dbb48651 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,20 @@ 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
+void load_ucode_hygon_bsp(struct early_load_data *ed, unsigned int family);
+void load_ucode_hygon_ap(unsigned int family);
+void reload_ucode_hygon(unsigned int cpu);
+struct microcode_ops *init_hygon_microcode(void);
+void exit_hygon_microcode(void);
+#else /* CONFIG_CPU_SUP_HYGON */
+static inline void load_ucode_hygon_bsp(struct early_load_data *ed, unsigned int family) { }
+static inline void load_ucode_hygon_ap(unsigned int family) { }
+static inline void reload_ucode_hygon(unsigned int cpu) { }
+static inline struct microcode_ops *init_hygon_microcode(void) { return NULL; }
+static inline void exit_hygon_microcode(void) { }
+#endif /* !CONFIG_CPU_SUP_HYGON */
+
#define ucode_dbg(fmt, ...) \
({ \
if (IS_ENABLED(CONFIG_MICROCODE_DBG)) \
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 6/7] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
` (4 preceding siblings ...)
2026-06-15 12:21 ` [PATCH v3 5/7] x86/microcode/hygon: Add microcode loading support for Hygon processors Fu Hao
@ 2026-06-15 12:22 ` Fu Hao
2026-06-15 12:37 ` sashiko-bot
2026-06-15 12:23 ` [PATCH v3 7/7] ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h Fu Hao
6 siblings, 1 reply; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:22 UTC (permalink / raw)
To: bhelgaas, perex, tiwai
Cc: linux-kernel, linux-sound, linux-pci, tingyin.duan, Fu Hao
Add the new PCI ID 0x1d94 0x14a9 for Hygon family 18h model 5h
HDA controller.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
---
sound/hda/controllers/intel.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
index 3f434994c..0c3f72170 100644
--- a/sound/hda/controllers/intel.c
+++ b/sound/hda/controllers/intel.c
@@ -100,6 +100,8 @@ enum {
#define ATIHDMI_NUM_CAPTURE 0
#define ATIHDMI_NUM_PLAYBACK 8
+/* Hygon HD Audio controller */
+#define PCI_DEVICE_ID_HYGON_18H_M05H_HDA 0x14a9
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -241,6 +243,7 @@ enum {
AZX_DRIVER_ZHAOXIN,
AZX_DRIVER_ZHAOXINHDMI,
AZX_DRIVER_LOONGSON,
+ AZX_DRIVER_HYGON,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -357,6 +360,7 @@ static const char * const driver_short_names[] = {
[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
[AZX_DRIVER_ZHAOXINHDMI] = "HDA Zhaoxin HDMI",
[AZX_DRIVER_LOONGSON] = "HDA Loongson",
+ [AZX_DRIVER_HYGON] = "HDA Hygon",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
@@ -2818,6 +2822,9 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
{ PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
.driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
+ /* Hygon HDAudio */
+ { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_HDA),
+ .driver_data = AZX_DRIVER_HYGON | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_NO_MSI },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 7/7] ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
` (5 preceding siblings ...)
2026-06-15 12:22 ` [PATCH v3 6/7] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio Fu Hao
@ 2026-06-15 12:23 ` Fu Hao
6 siblings, 0 replies; 11+ messages in thread
From: Fu Hao @ 2026-06-15 12:23 UTC (permalink / raw)
To: perex, tiwai; +Cc: linux-kernel, linux-sound, tingyin.duan, Fu Hao
On Hygon family 18h model 5h controller, some registers such as
GCTL, SD_CTL and SD_CTL_3B should be accessed in dword, or the
writing will fail.
Signed-off-by: Fu Hao <fuhao@open-hieco.net>
---
sound/hda/controllers/intel.c | 4 ++++
sound/hda/core/controller.c | 10 +++++++--
sound/hda/core/stream.c | 40 +++++++++++++++++++++++++++--------
3 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
index 0c3f72170..8afa6b1d6 100644
--- a/sound/hda/controllers/intel.c
+++ b/sound/hda/controllers/intel.c
@@ -1887,6 +1887,10 @@ static int azx_first_init(struct azx *chip)
if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI)
bus->polling_mode = 1;
+ if (chip->driver_type == AZX_DRIVER_HYGON &&
+ chip->pci->device == PCI_DEVICE_ID_HYGON_18H_M05H_HDA)
+ bus->access_sdnctl_in_dword = 1;
+
bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio");
if (IS_ERR(bus->remap_addr))
return PTR_ERR(bus->remap_addr);
diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c
index 69e11d62b..6312ad7af 100644
--- a/sound/hda/core/controller.c
+++ b/sound/hda/core/controller.c
@@ -511,7 +511,10 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
{
unsigned long timeout;
- snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
+ else
+ snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
timeout = jiffies + msecs_to_jiffies(100);
while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
@@ -576,7 +579,10 @@ static void azx_int_disable(struct hdac_bus *bus)
/* disable interrupts in stream descriptor */
list_for_each_entry(azx_dev, &bus->stream_list, list)
- snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_INT_MASK, 0);
+ else
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
/* disable SIE for all streams & disable controller CIE and GIE */
snd_hdac_chip_writel(bus, INTCTL, 0);
diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c
index b471a038b..0091c4ef6 100644
--- a/sound/hda/core/stream.c
+++ b/sound/hda/core/stream.c
@@ -146,8 +146,12 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev)
stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
else
stripe_ctl = 0;
- snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
- stripe_ctl);
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+ stripe_ctl);
+ else
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+ stripe_ctl);
}
/* set DMA start and interrupt mask */
if (bus->access_sdnctl_in_dword)
@@ -166,11 +170,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
*/
static void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
{
- snd_hdac_stream_updateb(azx_dev, SD_CTL,
- SD_CTL_DMA_START | SD_INT_MASK, 0);
- snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
- if (azx_dev->stripe)
- snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+ struct hdac_bus *bus = azx_dev->bus;
+
+ if (bus->access_sdnctl_in_dword) {
+ snd_hdac_stream_updatel(azx_dev, SD_CTL,
+ SD_CTL_DMA_START | SD_INT_MASK, 0);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ if (azx_dev->stripe)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+ } else {
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ SD_CTL_DMA_START | SD_INT_MASK, 0);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ if (azx_dev->stripe)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+ }
+
azx_dev->running = false;
}
@@ -225,12 +240,16 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
{
unsigned char val;
int dma_run_state;
+ struct hdac_bus *bus = azx_dev->bus;
snd_hdac_stream_clear(azx_dev);
dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;
- snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+ else
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
/* wait for hardware to report that the stream entered reset */
snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300);
@@ -238,7 +257,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
if (azx_dev->bus->dma_stop_delay && dma_run_state)
udelay(azx_dev->bus->dma_stop_delay);
- snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
+ else
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
/* wait for hardware to report that the stream is out of reset */
snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300);
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 6/7] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio
2026-06-15 12:22 ` [PATCH v3 6/7] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio Fu Hao
@ 2026-06-15 12:37 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-06-15 12:37 UTC (permalink / raw)
To: Fu Hao; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The new Hygon HD-Audio controller support misses critical AMD/Zen architectural quirks, specifically 40-bit DMA mask clamping and the AZX_DCAPS_NO_TCSEL capability flag.
--
commit b159eff305dbdd80947b5dde567df2ff7e67de70
Author: Fu Hao <fuhao@open-hieco.net>
ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio
This commit adds the PCI ID 0x1d94 0x14a9 to support the Hygon family 18h
model 5h HDA controller.
> diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
> index 1e8e3d61291a1..e706d38d7fd6c 100644
> --- a/sound/hda/controllers/intel.c
> +++ b/sound/hda/controllers/intel.c
[ ... ]
> @@ -2815,6 +2819,9 @@ static const struct pci_device_id azx_ids[] = {
> .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
> { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
> .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
> + /* Hygon HDAudio */
> + { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_HDA),
> + .driver_data = AZX_DRIVER_HYGON | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_NO_MSI },
[Severity: High]
Should this capabilities bitmask include the AZX_DCAPS_NO_TCSEL flag?
Hygon processors are based on the AMD Zen architecture. Without this flag,
azx_init_pci() will inadvertently write to the Intel-specific Traffic Class
Select register at PCI offset 0x44:
sound/hda/controllers/intel.c:azx_init_pci() {
...
if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) {
dev_dbg(chip->card->dev, "Clearing TCSEL\n");
update_pci_byte(chip->pci, AZX_PCIREG_TCSEL, 0x07, 0);
}
...
}
On AMD/Hygon hardware, offset 0x44 is not TCSEL and modifying it could cause
unintended behavior or system instability.
[Severity: High]
Does this new Hygon device also need the 40-bit DMA mask clamping that is
applied to AMD devices?
In azx_first_init(), AMD devices have their DMA bits explicitly restricted:
sound/hda/controllers/intel.c:azx_first_init() {
...
if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
dma_bits = 40;
...
}
Since Hygon shares hardware constraints with AMD Zen, leaving the DMA bits
at 64 could lead to physical address truncation or IOMMU DMAR faults on
systems with sufficient memory.
> { 0, }
> };
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1781523812.git.fuhao@open-hieco.net?part=6
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 4/7] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h
2026-06-15 12:20 ` [PATCH v3 4/7] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h Fu Hao
@ 2026-06-15 12:38 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-06-15 12:38 UTC (permalink / raw)
To: Fu Hao; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Registered PMUs are not unregistered if `cpuhp_setup_state()` fails during module initialization, leading to a Use-After-Free.
--
commit 98d69c16b51701dce640f5ac92cee0daa35f2cfb
Author: Fu Hao <fuhao@open-hieco.net>
perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h
This patch adds L3 PMU support for the Hygon family 18h model 6h processor
by adjusting the slicemask and threadmask in the uncore driver.
> diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
> index 9293ce50574da..21f00edd3caa6 100644
> --- a/arch/x86/events/amd/uncore.c
> +++ b/arch/x86/events/amd/uncore.c
[ ... ]
> @@ -877,7 +910,8 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, unsigned int cpu)
> pmu->rdpmc_base = RDPMC_BASE_LLC;
> pmu->group = amd_uncore_ctx_gid(uncore, cpu);
>
> - if (boot_cpu_data.x86 >= 0x17) {
> + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
> + boot_cpu_data.x86 >= 0x17) {
> *l3_attr++ = &format_attr_event8.attr;
> *l3_attr++ = &format_attr_umask8.attr;
[ ... ]
> + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON &&
> + boot_cpu_data.x86 == 0x18) {
> + *l3_attr++ = &format_attr_event8.attr;
> + *l3_attr++ = &format_attr_umask8.attr;
> + if (boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf) {
> + *l3_attr++ = &format_attr_threadmask32.attr;
> + pmu->pmu.attr_update = hygon_uncore_l3_attr_update;
> + } else {
> + *l3_attr++ = &format_attr_threadmask8.attr;
> + }
> + }
> +
> if (perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1)) {
> free_percpu(pmu->ctx);
> pmu->ctx = NULL;
[Severity: High]
This is a pre-existing issue, but registering the PMU here exposes a teardown
issue in the module initialization error path.
If cpuhp_setup_state() fails for the online state in amd_uncore_init(), the
error path jumps to fail_start and removes the hotplug states. This triggers
the amd_uncore_cpu_dead() teardown callback, which correctly frees the
per-CPU context by calling amd_uncore_ctx_free().
However, the error path does not unregister the PMU or free the uncore->pmus
array:
arch/x86/events/amd/uncore.c:amd_uncore_init() {
...
fail:
for (i = 0; i < UNCORE_TYPE_MAX; i++) {
uncore = &uncores[i];
if (uncore->info) {
free_percpu(uncore->info);
uncore->info = NULL;
}
}
return ret;
}
Since the initialization failed, the module will be unloaded, but the perf
subsystem retains references to the registered PMUs.
Could this lead to a use-after-free if the perf subsystem later accesses
these dangling PMUs (for instance, via perf stat) after the module memory
has been freed?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1781523812.git.fuhao@open-hieco.net?part=4
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8
2026-06-15 12:17 ` [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8 Fu Hao
@ 2026-06-15 14:07 ` Dave Hansen
0 siblings, 0 replies; 11+ messages in thread
From: Dave Hansen @ 2026-06-15 14:07 UTC (permalink / raw)
To: Fu Hao, puwen, tglx, mingo, bp, dave.hansen, x86, hpa
Cc: linux-kernel, tingyin.duan
On 6/15/26 05:17, Fu Hao wrote:
> + /*
> + * Adjust the die_id and logical_die_id for Hygon models 0x4-0x8.
> + */
> + if (c->x86_model >= 0x4 && c->x86_model <= 0x8) {
> + c->topo.die_id = cpuid_ecx(0x8000001e) & 0xff;
> + c->topo.logical_die_id = (c->topo.die_id >> 4) *
> + topology_amd_nodes_per_pkg() +
> + (c->topo.die_id & 0xf);
> + }
My assumption for Hygon stuff is that there's AMD code _somewhere_ for
99% of it. It's just a matter of using it.
Why doesn't this use parse_8000_001e()?
I think that kind of justification needs to be the baseline for Hygon
work when it adds code. It needs to explain why the code is being added
and why the existing (AMD usually) code doesn't work.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-15 14:07 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-15 12:16 [PATCH v3 0/7] Add support for Hygon family 18h models 4h-8h Fu Hao
2026-06-15 12:17 ` [PATCH v3 1/7] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8 Fu Hao
2026-06-15 14:07 ` Dave Hansen
2026-06-15 12:18 ` [PATCH v3 2/7] x86/cpu: Get LLC ID for Hygon processor models 0x6 " Fu Hao
2026-06-15 12:19 ` [PATCH v3 3/7] x86/cpu/hygon: Remove Spectral Chicken for Hygon processors Fu Hao
2026-06-15 12:20 ` [PATCH v3 4/7] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h Fu Hao
2026-06-15 12:38 ` sashiko-bot
2026-06-15 12:21 ` [PATCH v3 5/7] x86/microcode/hygon: Add microcode loading support for Hygon processors Fu Hao
2026-06-15 12:22 ` [PATCH v3 6/7] ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio Fu Hao
2026-06-15 12:37 ` sashiko-bot
2026-06-15 12:23 ` [PATCH v3 7/7] ALSA: hda: Fix single byte writing issue for Hygon family 18h model 5h Fu Hao
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.