From nobody Fri Sep 4 05:19:44 2026 Received: from out28-50.mail.aliyun.com (out28-50.mail.aliyun.com [115.124.28.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6E16F3AA51E for ; Fri, 4 Sep 2026 03:10:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.28.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491425; cv=none; b=bdxnHBk1u5pwQsn5JjspVB2Xr3RIBGJ3aN6JOWEXoV8yLR0aFZHsbJKVx/NZPtUoM3pQGF5n7iL2jtZMilNGGMBHd7zGOsBUG5zwJbgzGmvX7F6Wu8BGl+WCZYG0BjwuNGOnp9mOOgXiJjJvOF5Op1P21XRHh6Rx6fe/MrXBo8s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491425; c=relaxed/simple; bh=TR1DU6xKSXoHFIgeof3CUzjvhLtuKnOEJ2ZTvUWg4oU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=mZHVC71WhurBh+zvcMu4KxxvLmqbyGCRNe/O4UXzkuNCRXpFzRBBsrHxfFCBtXBHSA3TkkjR7iaOdWorAulfWzKxqeBDhQxJY9bnlHT1Dyp/4s6rOOSI/HHAE+T6Hd/rVh8TCMOy6oQ69bOUzQ1jleGi1JrIU2vpDH8PTJF3DDQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net; spf=pass smtp.mailfrom=open-hieco.net; arc=none smtp.client-ip=115.124.28.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=open-hieco.net X-Alimail-AntiSpam: AC=CONTINUE;BC=0.1738966|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_alarm|0.00528743-8.67872e-05-0.994626;FP=14259654310229575967|1|1|1|0|-1|-1|-1;HT=maildocker-contentspam033023018039;MF=fuhao@open-hieco.net;NM=1;PH=DS;RN=10;RT=10;SR=0;TI=SMTPD_---.j54tlGi_1788491405; Received: from higon..(mailfrom:fuhao@open-hieco.net fp:SMTPD_---.j54tlGi_1788491405 cluster:ay29) by smtp.aliyun-inc.com; Fri, 04 Sep 2026 11:10:11 +0800 From: Fu Hao To: puwen@hygon.cn, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com Cc: linux-kernel@vger.kernel.org, tingyin.duan@gmail.com, Fu Hao Subject: [PATCH v5 1/5] x86/cpu/hygon: Adjust the die_id and logical_die_id for Hygon models 0x4 through 0x8 Date: Fri, 4 Sep 2026 11:09:57 +0800 Message-Id: <1d19f337f055b74d3044ec347e5fa6d728067f7d.1788404433.git.fuhao@open-hieco.net> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable For Hygon processor models 0x4=E2=80=930x8, the die ID should be obtained from the NodeId field in ECX of CPUID leaf 0x8000001e. Starting with these models, Hygon CPUs support CPUID leaf 0xb but do not support leaf 0x80000026. Consequently, the die ID and logical die ID cannot be derived from the APIC ID space. CPUID leaf 0xb only supports core and package levels, it cannot enumerate die IDs within a socket. However, we found that CPUID leaf 0x8000001e (ECX) provides the necessary information. In Hygon CPU design, the NodeId field [7:0] encodes both Socket and Die IDs in the format: Bits [7:4]: Socket ID Bits [3:0]: Die ID We can calculate the correct IDs(die_id and logical_die_id) by parsing this field. Signed-off-by: Fu Hao --- arch/x86/kernel/cpu/hygon.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/x86/kernel/cpu/hygon.c b/arch/x86/kernel/cpu/hygon.c index 3e8891a9c..64f9a64c3 100644 --- a/arch/x86/kernel/cpu/hygon.c +++ b/arch/x86/kernel/cpu/hygon.c @@ -192,6 +192,20 @@ static void init_hygon(struct cpuinfo_x86 *c) =20 init_hygon_cacheinfo(c); =20 + /* + * Adjust the die_id and logical_die_id for Hygon models 0x4-0x8. + * In Hygon CPU design, the NodeId field of CPUID leaf 0x8000001e (ECX) + * is now fixed to represent the socket ID and die ID, allocated as + * 4 bits each: socket ID =3D NodeId[7:4], die ID =3D NodeId[3:0]. + */ + if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && + c->x86_model >=3D 0x4 && c->x86_model <=3D 0x8) { + c->topo.die_id =3D cpuid_ecx(0x8000001e) & 0xff; + c->topo.logical_die_id =3D (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) { --=20 2.34.1 From nobody Fri Sep 4 05:19:44 2026 Received: from out28-145.mail.aliyun.com (out28-145.mail.aliyun.com [115.124.28.145]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C4843AC0D3 for ; Fri, 4 Sep 2026 03:26:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.28.145 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788492404; cv=none; b=ZY54COeOF+yBB2LsCztiV1eqIk/KCX/eonnECRBDi2wNMB8WnCot2rbBY6w6GbxHTsbX3TTQRRWhROrp/SzXftiy3rbUqD2TERtjRoqz25nk2N9f73oeSUtVifbY6jVgtGShpA8uRPcdsI7prMKRLT8yYCxO4aaDROB0f0+rhzI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788492404; c=relaxed/simple; bh=sb1O+9+IjENpDmkhqgDYScQLJV4P1UBv/1Wg/PHtc1o=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=XwZrwU1/+RiXyrtL0Q4EQ/MsXjw0+dAdm354zPG30n9tuDhSsvgGnZYs1LQJ/a/TEnUzqXrAUhTZ8Nvgm/8s8OxG5QW14DIJwBXc5l2oi0wMcUKgs4SE4U4fNhsPsW+dJK46brGp4V2VRoX4DYZblciATXrhreAEVeDnWeULEzE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net; spf=pass smtp.mailfrom=open-hieco.net; arc=none smtp.client-ip=115.124.28.145 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=open-hieco.net X-Alimail-AntiSpam: AC=CONTINUE;BC=0.176391|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.0191211-0.000379474-0.980499;FP=6296111688724483326|1|1|1|0|-1|-1|-1;HT=maildocker-contentspam033037006180;MF=fuhao@open-hieco.net;NM=1;PH=DS;RN=9;RT=9;SR=0;TI=SMTPD_---.j52FmRD_1788491453; Received: from higon..(mailfrom:fuhao@open-hieco.net fp:SMTPD_---.j52FmRD_1788491453 cluster:ay29) by smtp.aliyun-inc.com; Fri, 04 Sep 2026 11:10:58 +0800 From: Fu Hao To: tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com Cc: linux-kernel@vger.kernel.org, tingyin.duan@gmail.com, Fu Hao Subject: [PATCH v5 2/5] x86/cpu: Get LLC ID for Hygon processor models 0x6 through 0x8 Date: Fri, 4 Sep 2026 11:10:34 +0800 Message-Id: <941337751e95765a33d0d1daf4042831bb429133.1788404433.git.fuhao@open-hieco.net> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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=E2=80=930x8. Signed-off-by: Fu Hao --- 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/cacheinf= o.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; =20 /* - * 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=E2=80=930x5, 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 =3D c->topo.apicid >> 3; + + switch (c->x86_model) { + case 0x6: + c->topo.llc_id =3D c->topo.apicid >> 4; + break; + case 0x7: + case 0x8: + c->topo.llc_id =3D c->topo.apicid >> 5; + break; + default: + c->topo.llc_id =3D c->topo.apicid >> 3; + break; + } } =20 void init_amd_cacheinfo(struct cpuinfo_x86 *c) --=20 2.34.1 From nobody Fri Sep 4 05:19:44 2026 Received: from out28-4.mail.aliyun.com (out28-4.mail.aliyun.com [115.124.28.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D614F3ADBA7 for ; Fri, 4 Sep 2026 03:11:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.28.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491509; cv=none; b=VwyJHzboaiR4hmWiRSgIEM2AyjTtpvHQQXZ+Qx0IE34MxDVARIRtRkXwdRth9vMALsdUJSq7xPINZFzF+vUlFhP7r/EgCxgv4pkRwpba9tTGepf5geX3KveL++QJof+3zXTsF0YNsYn2WUsZGOsxV5tqyyF5jQJKZCs4MSqUe+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491509; c=relaxed/simple; bh=5LfwsCJPtyahMaZAzJf/H7V3qhyO6tYux98c8XU4RPE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=f1j8bpuWXhqJUNv7RauiewpfnQ6jNPQBUrVLzoJI0v/RhkXPB5VM+SY4oD3xZrT/+ZsF0qVoCI8RZW+X25ITDYwAl58Eufhy1EhIwn3NJfhu0Nhsk/p34FvMIURjp12BbDNWR+ikrECu4qyMKIHZ4GHyH2oT9rITsoesD7htfJE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net; spf=pass smtp.mailfrom=open-hieco.net; arc=none smtp.client-ip=115.124.28.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=open-hieco.net X-Alimail-AntiSpam: AC=CONTINUE;BC=0.1299369|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.0134151-0.000775871-0.985809;FP=12090291487539561935|1|1|1|0|-1|-1|-1;HT=maildocker-contentspam011083013073;MF=fuhao@open-hieco.net;NM=1;PH=DS;RN=10;RT=10;SR=0;TI=SMTPD_---.j54tmqc_1788491490; Received: from higon..(mailfrom:fuhao@open-hieco.net fp:SMTPD_---.j54tmqc_1788491490 cluster:ay29) by smtp.aliyun-inc.com; Fri, 04 Sep 2026 11:11:36 +0800 From: Fu Hao To: puwen@hygon.cn, tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com Cc: linux-kernel@vger.kernel.org, tingyin.duan@gmail.com, Fu Hao Subject: [PATCH v5 3/5] x86/cpu/hygon: Remove Spectral Chicken for Hygon processors Date: Fri, 4 Sep 2026 11:11:23 +0800 Message-Id: <538d6bb1809d8b9c63f1be93dd5e5600c17cbc41.1788404433.git.fuhao@open-hieco.net> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" It's no need to set the Spectral chicken bit for all Hygon processors. Commit d7caac991fee ("x86/cpu/amd: Add Spectral Chicken") added the spectral chicken bit for AMD platforms. There was an inquiry about whether Hygon platforms also require this chicken bit. Hygon has confirmed that it is not needed. Therefore, the related comment is removed in this patch. Signed-off-by: Fu Hao --- 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 64f9a64c3..eea9e36a8 100644 --- a/arch/x86/kernel/cpu/hygon.c +++ b/arch/x86/kernel/cpu/hygon.c @@ -177,12 +177,6 @@ static void init_hygon(struct cpuinfo_x86 *c) =20 set_cpu_cap(c, X86_FEATURE_REP_GOOD); =20 - /* - * 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); =20 --=20 2.34.1 From nobody Fri Sep 4 05:19:44 2026 Received: from out198-12.us.a.mail.aliyun.com (out198-12.us.a.mail.aliyun.com [47.90.198.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B01857080D; Fri, 4 Sep 2026 03:12:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=47.90.198.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491566; cv=none; b=PFKtXf/ZT36iecNGPldn8iwzmBhjyliVJdAIR3qNQhcrRF2HGQ5KJr8XhsnTFoLXt7VHa4WgF1/o29VsJwqP6/K91p75mW49hKR98EL5DCR4k6fv81k8y0Wsq94yCjtK3MumWmr2nZMH11PvnN1zly8TDyTEJWpaUA5O4aDSzyQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491566; c=relaxed/simple; bh=7VDLthI2uZIfqh43XHAT4dnHqFIJAeZHKF+9HPx/EiU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=u6ZCl/lkgAqXYwYRQ2JDE6SpQu246QogmFgl8lehZzaTXBOTMK/mbwW+D5Kb2p+OIxI7mVbIl6jvnGxkQ8HBOe708WUeHmVOxdtyGv5F+x5SQfRKBJFx+GdFdKvH0178DqIwEE26EZ2oxpo+MmKYCuQzOpmJ/u36ZeAPr1cxFLM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net; spf=pass smtp.mailfrom=open-hieco.net; arc=none smtp.client-ip=47.90.198.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=open-hieco.net X-Alimail-AntiSpam: AC=CONTINUE;BC=0.07436402|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_alarm|0.00867014-0.000596182-0.990734;FP=5927377541413799312|1|1|2|0|-1|-1|-1;HT=maildocker-contentspam033037028158;MF=fuhao@open-hieco.net;NM=1;PH=DS;RN=17;RT=17;SR=0;TI=SMTPD_---.j531Mf2_1788491537; Received: from higon..(mailfrom:fuhao@open-hieco.net fp:SMTPD_---.j531Mf2_1788491537 cluster:ay29) by smtp.aliyun-inc.com; Fri, 04 Sep 2026 11:12:25 +0800 From: Fu Hao To: peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, tglx@kernel.org, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, james.clark@linaro.org, hpa@zytor.com Cc: linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, tingyin.duan@gmail.com, Fu Hao Subject: [PATCH v5 4/5] perf/x86/uncore: Add L3 PMU support for Hygon family 18h model 6h Date: Fri, 4 Sep 2026 11:12:12 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Adjust the L3 PMU slicemask and threadmask for Hygon family 18h model 6h processor. Signed-off-by: Fu Hao Tested-by: Tingyin Duan --- 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 dbc00b6dd..fa7a1c70e 100644 --- a/arch/x86/events/amd/uncore.c +++ b/arch/x86/events/amd/uncore.c @@ -309,6 +309,14 @@ amd_f17h_uncore_is_visible(struct kobject *kobj, struc= t attribute *attr, int i) attr->mode : 0; } =20 +static umode_t +hygon_f18h_m6h_uncore_is_visible(struct kobject *kobj, struct attribute *a= ttr, int i) +{ + return boot_cpu_data.x86 =3D=3D 0x18 && + boot_cpu_data.x86_model >=3D 0x6 && boot_cpu_data.x86_model <=3D 0= xf ? + attr->mode : 0; +} + static umode_t amd_f19h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, i= nt i) { @@ -360,6 +368,8 @@ DEFINE_UNCORE_FORMAT_ATTR(enallslices, enallslices, "co= nfig:46"); /* F19h L3 DEFINE_UNCORE_FORMAT_ATTR(enallcores, enallcores, "config:47"); /* F19= h L3 */ DEFINE_UNCORE_FORMAT_ATTR(sliceid, sliceid, "config:48-50"); /* F19h L3= */ DEFINE_UNCORE_FORMAT_ATTR(rdwrmask, rdwrmask, "config:8-9"); /* PerfMo= nV2 UMC */ +DEFINE_UNCORE_FORMAT_ATTR(slicemask4, slicemask, "config:28-31"); /* F1= 8h L3 */ +DEFINE_UNCORE_FORMAT_ATTR(threadmask32, threadmask, "config:32-63"); /*= F18h L3 */ =20 /* Common DF and NB attributes */ static struct attribute *amd_uncore_df_format_attr[] =3D { @@ -389,6 +399,12 @@ static struct attribute *amd_f17h_uncore_l3_format_att= r[] =3D { NULL, }; =20 +/* F18h M06h unique L3 attributes */ +static struct attribute *hygon_f18h_m6h_uncore_l3_format_attr[] =3D { + &format_attr_slicemask4.attr, /* slicemask */ + NULL, +}; + /* F19h unique L3 attributes */ static struct attribute *amd_f19h_uncore_l3_format_attr[] =3D { &format_attr_coreid.attr, /* coreid */ @@ -414,6 +430,12 @@ static struct attribute_group amd_f17h_uncore_l3_forma= t_group =3D { .is_visible =3D amd_f17h_uncore_is_visible, }; =20 +static struct attribute_group hygon_f18h_m6h_uncore_l3_format_group =3D { + .name =3D "format", + .attrs =3D hygon_f18h_m6h_uncore_l3_format_attr, + .is_visible =3D hygon_f18h_m6h_uncore_is_visible, +}; + static struct attribute_group amd_f19h_uncore_l3_format_group =3D { .name =3D "format", .attrs =3D amd_f19h_uncore_l3_format_attr, @@ -443,6 +465,11 @@ static const struct attribute_group *amd_uncore_l3_att= r_update[] =3D { NULL, }; =20 +static const struct attribute_group *hygon_uncore_l3_attr_update[] =3D { + &hygon_f18h_m6h_uncore_l3_format_group, + NULL, +}; + static const struct attribute_group *amd_uncore_umc_attr_groups[] =3D { &amd_uncore_attr_group, &amd_uncore_umc_format_group, @@ -821,6 +848,12 @@ static int amd_uncore_l3_event_init(struct perf_event = *event) mask =3D AMD64_L3_F19H_THREAD_MASK | AMD64_L3_EN_ALL_SLICES | AMD64_L3_EN_ALL_CORES; =20 + if (boot_cpu_data.x86 =3D=3D 0x18 && + boot_cpu_data.x86_model >=3D 0x6 && + boot_cpu_data.x86_model <=3D 0xf) + mask =3D ((config & HYGON_L3_SLICE_MASK) ? : HYGON_L3_SLICE_MASK) | + ((config & HYGON_L3_THREAD_MASK) ? : HYGON_L3_THREAD_MASK); + hwc->config |=3D mask; =20 return 0; @@ -878,7 +911,8 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, u= nsigned int cpu) pmu->rdpmc_base =3D RDPMC_BASE_LLC; pmu->group =3D amd_uncore_ctx_gid(uncore, cpu); =20 - if (boot_cpu_data.x86 >=3D 0x17) { + if (boot_cpu_data.x86_vendor =3D=3D X86_VENDOR_AMD && + boot_cpu_data.x86 >=3D 0x17) { *l3_attr++ =3D &format_attr_event8.attr; *l3_attr++ =3D &format_attr_umask8.attr; *l3_attr++ =3D boot_cpu_data.x86 >=3D 0x19 ? @@ -905,6 +939,18 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, = unsigned int cpu) .module =3D THIS_MODULE, }; =20 + if (boot_cpu_data.x86_vendor =3D=3D X86_VENDOR_HYGON && + boot_cpu_data.x86 =3D=3D 0x18) { + *l3_attr++ =3D &format_attr_event8.attr; + *l3_attr++ =3D &format_attr_umask8.attr; + if (boot_cpu_data.x86_model >=3D 0x6 && boot_cpu_data.x86_model <=3D 0xf= ) { + *l3_attr++ =3D &format_attr_threadmask32.attr; + pmu->pmu.attr_update =3D hygon_uncore_l3_attr_update; + } else { + *l3_attr++ =3D &format_attr_threadmask8.attr; + } + } + if (perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1)) { free_percpu(pmu->ctx); pmu->ctx =3D NULL; diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_= event.h index 1eb13673e..f448e93ec 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -91,6 +91,14 @@ #define AMD64_L3_COREID_MASK \ (0x7ULL << AMD64_L3_COREID_SHIFT) =20 +#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 | \ --=20 2.34.1 From nobody Fri Sep 4 05:19:44 2026 Received: from out28-53.mail.aliyun.com (out28-53.mail.aliyun.com [115.124.28.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 81C9C36DA1D for ; Fri, 4 Sep 2026 03:18:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.28.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491914; cv=none; b=JQ9zngQ2mCS2C8fnvNYoGEsxB88eCxzv4Gv8Mw7mug0UyKxugKTe0xMlq6DaI7U8D/VfeaQxhPZ56y0M7IsS5ZRSwW21GxpfFNlpz//HY/WJl90V91RrMZnIwDHsh0tWt3FjftiReTJGo/JTzq74jMFb08INNdlEfQBPXAatEkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788491914; c=relaxed/simple; bh=1cOsLHTuYiT5w2PWk7M4CCsVr2YUw2DqBYXtCjY3IK8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dZN2Ag7a8EiKhwfk51hAJPZfHv0cKQOns3Y//UwaTDFcKogaU8t+8oDWsl+zk9TitDDsGk5Re3IEOJHHSDAMHfmEqSEkWTOdnPOJpcfKf3qloc+1LB88D7k36J9J9HVMKlJ3NtXdwjcq4Rd6XWuY+c6B6RlEGuvEtPuBInplOJQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net; spf=pass smtp.mailfrom=open-hieco.net; arc=none smtp.client-ip=115.124.28.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=open-hieco.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=open-hieco.net X-Alimail-AntiSpam: AC=CONTINUE;BC=0.07436259|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_alarm|0.0179578-0.00442053-0.977622;FP=18194379066821919994|1|1|1|0|-1|-1|-1;HT=maildocker-contentspam033045220102;MF=fuhao@open-hieco.net;NM=1;PH=DS;RN=9;RT=9;SR=0;TI=SMTPD_---.j54BOuI_1788491585; Received: from higon..(mailfrom:fuhao@open-hieco.net fp:SMTPD_---.j54BOuI_1788491585 cluster:ay29) by smtp.aliyun-inc.com; Fri, 04 Sep 2026 11:13:10 +0800 From: Fu Hao To: tglx@kernel.org, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com Cc: linux-kernel@vger.kernel.org, tingyin.duan@gmail.com, Fu Hao Subject: [PATCH v5 5/5] x86/microcode/hygon: Add microcode loading support for Hygon processors Date: Fri, 4 Sep 2026 11:12:59 +0800 Message-Id: <8fb38ca273c0020d59f99adb6b33707e80dcf8b4.1788404433.git.fuhao@open-hieco.net> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 | 851 +++++++++++++++++++++++ arch/x86/kernel/cpu/microcode/internal.h | 20 + 5 files changed, 889 insertions(+), 2 deletions(-) create mode 100644 arch/x86/kernel/cpu/microcode/hygon.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index bdad90f21..a9245fa47 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1313,7 +1313,7 @@ config X86_REBOOTFIXUPS =20 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 =20 config MICROCODE_INITRD32 diff --git a/arch/x86/kernel/cpu/microcode/Makefile b/arch/x86/kernel/cpu/m= icrocode/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 :=3D core.o obj-$(CONFIG_MICROCODE) +=3D microcode.o microcode-$(CONFIG_CPU_SUP_INTEL) +=3D intel.o microcode-$(CONFIG_CPU_SUP_AMD) +=3D amd.o +microcode-$(CONFIG_CPU_SUP_HYGON) +=3D hygon.o diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/mic= rocode/core.c index 0dd0c7241..ed10266af 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -194,6 +194,9 @@ void __init load_ucode_bsp(void) return; intel =3D false; break; + case X86_VENDOR_HYGON: + intel =3D false; + break; =20 default: return; @@ -202,7 +205,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() =3D=3D X86_VENDOR_HYGON) + load_ucode_hygon_bsp(&early_data, cpuid_1_eax); + else + load_ucode_amd_bsp(&early_data, cpuid_1_eax); + } =20 void load_ucode_ap(void) @@ -228,6 +235,9 @@ void load_ucode_ap(void) if (x86_family(cpuid_1_eax) >=3D 0x10) load_ucode_amd_ap(cpuid_1_eax); break; + case X86_VENDOR_HYGON: + load_ucode_hygon_ap(cpuid_1_eax); + break; default: break; } @@ -287,6 +297,9 @@ static void reload_early_microcode(unsigned int cpu) if (family >=3D 0x10) reload_ucode_amd(cpu); break; + case X86_VENDOR_HYGON: + reload_ucode_hygon(cpu); + break; default: break; } @@ -894,6 +907,8 @@ static int __init microcode_init(void) microcode_ops =3D init_intel_microcode(); else if (c->x86_vendor =3D=3D X86_VENDOR_AMD) microcode_ops =3D init_amd_microcode(); + else if (c->x86_vendor =3D=3D X86_VENDOR_HYGON) + microcode_ops =3D init_hygon_microcode(); else pr_err("no support for this CPU vendor\n"); =20 diff --git a/arch/x86/kernel/cpu/microcode/hygon.c b/arch/x86/kernel/cpu/mi= crocode/hygon.c new file mode 100644 index 000000000..10b84752d --- /dev/null +++ b/arch/x86/kernel/cpu/microcode/hygon.c @@ -0,0 +1,851 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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)) >> P= AGE_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 =3D 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 int get_patch_level_cpu(unsigned int cpu, u32 *rev) +{ + u64 val; + int ret =3D 0; + + if (cpu =3D=3D smp_processor_id()) { + u32 dummy; + native_rdmsr(HYGON_MSR_PATCH_LEVEL, *rev, dummy); + } else { + ret =3D rdmsrq_safe_on_cpu(cpu, HYGON_MSR_PATCH_LEVEL, &va= l); + if (!ret) + *rev =3D (u32)val; + } + + return ret; +} + +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 =3D 0; i < et->entry_count; i++) { + if (sig =3D=3D 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 <=3D HYGON_CONTAINER_HDR_SIZE) { + ucode_dbg("Truncated microcode container header.\n"); + return false; + } + + if (hygon_container_hdr(buf, MAGIC) !=3D 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 =3D hygon_container_hdr(buf, TYPE); + if (cont_type !=3D UCODE_EQUIV_CPU_TABLE_TYPE) { + ucode_dbg("Wrong microcode container equivalence table type: %u.\n", + cont_type); + return false; + } + + buf_size -=3D HYGON_CONTAINER_HDR_SIZE; + + equiv_tbl_len =3D 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 *pa= tch_size) +{ + if (buf_size < HYGON_SECTION_HDR_SIZE) { + ucode_dbg("Truncated patch section.\n"); + return false; + } + + *patch_size =3D hygon_section_hdr(buf, SIZE); + + if (hygon_section_hdr(buf, TYPE) !=3D 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 *pat= ch_size) +{ + u8 family =3D 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 -=3D HYGON_SECTION_HDR_SIZE; + + if (buf_size < *patch_size) { + ucode_dbg("Patch of size %u truncated.\n", *patch_size); + return -1; + } + + mc_hdr =3D (struct hygon_microcode_header *)hygon_section_data(buf); + + proc_id =3D mc_hdr->processor_rev_id; + + if (hygon_extract_proc_family(proc_id) !=3D family) + return 1; + + return 0; +} + +/* + * Attempt to consume one container from the ucode blob. + * + * This function processes a single container starting at @ucode. It verif= ies + * the container's CPU equivalence table, locates the equivalence ID for t= he + * current CPU, and then scans all patch sections inside the container. + * + * If a patch matching the CPU's equivalence ID is found, the function fil= ls + * @desc with the patch metadata and the container's bounds, and returns 0. + * Otherwise, it returns the total number of bytes consumed (the entire si= ze + * 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_c= ontainer_desc *desc) +{ + struct hygon_cpu_equiv_table table; + size_t orig_size =3D size; + u32 equiv_data_len; + u16 cpu_equiv_id; + u8 *buf; + + if (!verify_cpu_equivalence_table(ucode, size)) + return 0; + + buf =3D ucode; + equiv_data_len =3D hygon_container_hdr(buf, LENGTH); + + table.entry =3D (struct hygon_cpu_equiv_entry *)hygon_container_data(buf); + table.entry_count =3D equiv_data_len / sizeof(struct hygon_cpu_equiv_entr= y); + + cpu_equiv_id =3D find_equiv_id(&table, bsp_cpuid_1_eax); + + buf +=3D equiv_data_len + HYGON_CONTAINER_HDR_SIZE; + size -=3D equiv_data_len + HYGON_CONTAINER_HDR_SIZE; + + while (size > 0) { + struct hygon_microcode *mc; + u32 patch_size; + int ret; + + ret =3D validate_patch_section(buf, size, &patch_size); + if (ret < 0) + goto out; + else if (ret > 0) + goto skip; + + mc =3D (struct hygon_microcode *)hygon_section_data(buf); + + if (cpu_equiv_id =3D=3D mc->header.processor_rev_id) { + desc->patch_size =3D patch_size; + desc->mc =3D mc; + + ucode_dbg(" match: size: %d\n", patch_size); + } + +skip: + buf +=3D patch_size + HYGON_SECTION_HDR_SIZE; + size -=3D 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 poin= ts + * to the correct container. + * + * Otherwise, return the total number of bytes scanned, allowing the call= er + * to skip this entire container and try the next one. + */ + if (desc->mc) { + desc->data =3D ucode; + desc->size =3D 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_c= ontainer(). + * Once a container with a matching patch is found, it fills @desc and ret= urns. + * If no matching container exists in the entire blob, @desc remains uncha= nged. + */ +static void locate_patch_container(u8 *ucode, size_t size, struct hygon_co= ntainer_desc *desc) +{ + while (size) { + size_t consumed =3D try_consume_container(ucode, size, desc); + + if (!consumed) + return; + + if (size >=3D consumed) { + ucode +=3D consumed; + size -=3D consumed; + } else { + return; + } + } +} + +static bool apply_hygon_patch(struct hygon_microcode *mc, u32 *cur_rev, + unsigned int patch_size) +{ + unsigned long patch_start =3D (unsigned long)&mc->header.data; + unsigned long patch_end =3D patch_start + patch_size - 1; + unsigned int cpu =3D smp_processor_id(); + + 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) && x86_hypervisor_present) + microcode_rev[cpu] =3D mc->header.patch_level; + + if (get_patch_level_cpu(cpu, cur_rev) !=3D 0) { + pr_err("Failed to read microcode revision for CPU%d\n", cp= u); + return false; + } + ucode_dbg("updated rev: 0x%x\n", *cur_rev); + + return *cur_rev =3D=3D 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 =3D fw.size; + cp->data =3D (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 =3D find_microcode_in_initrd(ucode_path); + + if (cp.data && cp.size) { + *ret =3D 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-pla= ce + * 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 pat= ch. + * 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 =3D { }; + struct hygon_microcode *mc; + struct cpio_data cp =3D { }; + u32 rev; + + bsp_cpuid_1_eax =3D cpuid_1_eax; + + if (get_patch_level_cpu(0, &rev) !=3D 0) { + pr_err("Failed to read BSP microcode revision\n"); + return; + } + ed->old_rev =3D rev; + + /* Needed in hygon_load_microcode() */ + ucode_cpu_info[0].cpu_sig.sig =3D cpuid_1_eax; + + if (!find_microcode_blob(&cp)) + return; + + locate_patch_container(cp.data, cp.size, &desc); + + mc =3D desc.mc; + + if (!mc || (ed->old_rev > mc->header.patch_level)) + return; + + if (apply_hygon_patch(mc, &rev, desc.patch_size)) + ed->new_rev =3D 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 =3D=3D 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 =3D=3D new_patch->equiv_id) { + if (p->patch_level >=3D 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 =3D ucode_cpu_info + cpu; + u16 equiv_id; + u32 rev; + + if (get_patch_level_cpu(cpu, &rev) !=3D 0) { + pr_err("Failed to read microcode revision for CPU%d\n", cp= u); + return NULL; + } + + u_cpu_info->cpu_sig.rev =3D rev; + + equiv_id =3D 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 =3D find_hygon_patch(cpu); + if (!p) + return; + + mc =3D p->data; + if (get_patch_level_cpu(cpu, &rev) !=3D 0) { + pr_err("Failed to read microcode revision for CPU%d\n", cp= u); + return; + } + 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 =3D ucode_cpu_info + cpu; + struct hygon_ucode_patch *p; + u32 rev; + + csig->sig =3D cpuid_eax(0x00000001); + if (get_patch_level_cpu(cpu, &rev) !=3D 0) { + pr_err("Failed to read microcode revision for CPU%d\n", cpu); + return -EIO; + } + csig->rev =3D rev; + + p =3D find_hygon_patch(cpu); + if (p && (p->patch_level =3D=3D csig->rev)) + u_cpu_info->mc =3D p->data; + + return 0; +} + +static enum ucode_state hygon_apply_microcode(int cpu) +{ + struct cpuinfo_x86 *c =3D &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() !=3D cpu) { + WARN_ON_ONCE(1); + return UCODE_ERROR; + } + + u_cpu_info =3D ucode_cpu_info + cpu; + + p =3D find_hygon_patch(cpu); + if (!p) + return UCODE_NFOUND; + + rev =3D u_cpu_info->cpu_sig.rev; + + mc_hygon =3D p->data; + u_cpu_info->mc =3D p->data; + + /* Skip update if current revision is already newer than the patch */ + if (rev > mc_hygon->header.patch_level) { + ret =3D UCODE_OK; + goto out; + } + + if (!apply_hygon_patch(mc_hygon, &rev, p->patch_size)) { + pr_err("CPU%d: update failed for patch_level=3D0x%08x\n", + cpu, mc_hygon->header.patch_level); + return UCODE_ERROR; + } + + rev =3D mc_hygon->header.patch_level; + ret =3D UCODE_UPDATED; + +out: + u_cpu_info->cpu_sig.rev =3D rev; + c->microcode =3D rev; + + /* Update boot CPU's microcode revision as well */ + if (c->cpu_index =3D=3D boot_cpu_data.cpu_index) + boot_cpu_data.microcode =3D rev; + + return ret; +} + +void load_ucode_hygon_ap(unsigned int cpuid_1_eax) +{ + unsigned int cpu =3D smp_processor_id(); + + ucode_cpu_info[cpu].cpu_sig.sig =3D 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 =3D hygon_container_hdr(buf, LENGTH); + + equiv_table.entry =3D 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 =3D 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 left= over, + unsigned int *patch_size) +{ + struct hygon_microcode_header *mc_hdr; + struct hygon_ucode_patch *patch; + int ret; + + ret =3D validate_patch_section(fw, leftover, patch_size); + if (ret) + return ret; + + patch =3D kzalloc_obj(*patch); + if (!patch) { + pr_err("Patch allocation failure.\n"); + return -EINVAL; + } + + patch->data =3D 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 =3D *patch_size; + + mc_hdr =3D (struct hygon_microcode_header *)hygon_section_data(fw); + + INIT_LIST_HEAD(&patch->list); + patch->patch_level =3D mc_hdr->patch_level; + patch->equiv_id =3D 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 =3D (u8 *)data; + size_t offset; + + offset =3D load_equiv_cpu_table(data, size); + if (!offset) + return UCODE_ERROR; + + fw +=3D offset; + size -=3D offset; + if (size < HYGON_SECTION_HDR_SIZE) { + pr_err("no patch sections in container file\n"); + return UCODE_ERROR; + } + + if (hygon_section_hdr(fw, TYPE) !=3D 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 =3D 0; + int ret; + + ret =3D try_verify_and_cache_patch(family, fw, size, &crnt_size); + if (ret < 0) + return UCODE_ERROR; + + fw +=3D crnt_size + HYGON_SECTION_HDR_SIZE; + size -=3D (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 =3D parse_and_cache_patches(family, data, size); + if (ret !=3D UCODE_OK) + hygon_cleanup_microcode(); + + return ret; +} + +static enum ucode_state hygon_load_microcode(u8 family, const u8 *data, si= ze_t size) +{ + struct cpuinfo_x86 *c; + unsigned int nid, cpu; + struct hygon_ucode_patch *p; + enum ucode_state ret; + + ret =3D prepare_microcode_blob(family, data, size); + if (ret !=3D UCODE_OK) + return ret; + + for_each_node_with_cpus(nid) { + cpu =3D cpumask_first(cpumask_of_node(nid)); + c =3D &cpu_data(cpu); + + p =3D find_hygon_patch(cpu); + if (!p) + continue; + + if (c->microcode >=3D p->patch_level) + continue; + + ret =3D UCODE_NEW; + } + + return ret; +} + +static int __init save_microcode_in_initrd(void) +{ + struct cpuinfo_x86 *c =3D &boot_cpu_data; + struct hygon_container_desc desc =3D { 0 }; + unsigned int cpuid_1_eax; + enum ucode_state ret; + struct cpio_data cp; + + if (microcode_loader_disabled() || (c->x86_vendor !=3D X86_VENDOR_HYGON)) + return 0; + + cpuid_1_eax =3D 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 =3D prepare_microcode_blob(x86_family(cpuid_1_eax), desc.data, desc.s= ize); + 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 *de= vice) +{ + struct cpuinfo_x86 *c =3D &cpu_data(cpu); + enum ucode_state ret =3D UCODE_NFOUND; + const struct firmware *fw; + + if (force_minrev) + return UCODE_NFOUND; + + if (request_firmware_direct(&fw, (const char *)HYGON_UCODE_FW_NAME, devic= e)) { + ucode_dbg("failed to load file %s\n", HYGON_UCODE_FW_NAME); + goto out; + } + + ret =3D UCODE_ERROR; + if (!is_valid_microcode_container(fw->data, fw->size)) + goto fw_release; + + ret =3D 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 =3D ucode_cpu_info + cpu; + + u_cpu_info->mc =3D NULL; +} + +static void hygon_finalize_late_load(int result) +{ + if (result) + hygon_cleanup_microcode(); +} + +static struct microcode_ops microcode_hygon_ops =3D { + .request_microcode_fw =3D hygon_request_microcode, + .collect_cpu_info =3D hygon_collect_cpu_info, + .apply_microcode =3D hygon_apply_microcode, + .microcode_fini_cpu =3D hygon_microcode_fini_cpu, + .finalize_late_load =3D hygon_finalize_late_load, + .nmi_safe =3D true, +}; + +struct microcode_ops * __init init_hygon_microcode(void) +{ + if (boot_cpu_data.x86_vendor !=3D 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 a10b547ed..f8b624255 100644 --- a/arch/x86/kernel/cpu/microcode/internal.h +++ b/arch/x86/kernel/cpu/microcode/internal.h @@ -60,6 +60,9 @@ struct cpio_data find_microcode_in_initrd(const char *pat= h); #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') =20 #define CPUID_IS(a, b, c, ebx, ecx, edx) \ (!(((ebx) ^ (a)) | ((edx) ^ (b)) | ((ecx) ^ (c)))) @@ -86,6 +89,9 @@ static inline int x86_cpuid_vendor(void) if (CPUID_IS(CPUID_AMD1, CPUID_AMD2, CPUID_AMD3, ebx, ecx, edx)) return X86_VENDOR_AMD; =20 + if (CPUID_IS(CPUID_HYGON1, CPUID_HYGON2, CPUID_HYGON3, ebx, ecx, edx)) + return X86_VENDOR_HYGON; + return X86_VENDOR_UNKNOWN; } =20 @@ -127,6 +133,20 @@ static inline void reload_ucode_intel(void) { } static inline struct microcode_ops *init_intel_microcode(void) { return NU= LL; } #endif /* !CONFIG_CPU_SUP_INTEL */ =20 +#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, unsign= ed 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 NU= LL; } +static inline void exit_hygon_microcode(void) { } +#endif /* !CONFIG_CPU_SUP_HYGON */ + #define ucode_dbg(fmt, ...) \ ({ \ if (IS_ENABLED(CONFIG_MICROCODE_DBG)) \ --=20 2.34.1