From 65ae8d101f18e9d85b5e8d91f0d164117d811914 Mon Sep 17 00:00:00 2001 From: Arun Ajith S Date: Tue, 6 Jun 2023 07:20:06 -0700 Subject: [PATCH] rpmrc: Fix how x86 models are derived The code to autodetect x86 CPU model runs the cpuid instruction after setting up 1 in eax. This gives the processor signature back in eax which encodes the model number. As per Intel Application Note 485 and AMD publication #25481, when deriving model, we should look at both the base model in eax[7:4] and the extended model in eax[19:16]. However current code is only looking at base model and this causes some newer CPUs like Icelake(Model 0x6a) to be detected wrongly as Pentium 3(Model 0x0a). Note that this code is only exercised when rpm is built as 32-bit binary because all of this is within a `#if defined(__i386__)`. Because of this misdetection on Icelake, `setarch x86_64 rpm --showrc` has `pentium3` as the `installed arch` instead of the expected `x86_64`. It doesn't have `x86_64` as one of the `compatible archs` as expected. Attempting to install a x86_64 rpm with `setarch x86_64 rpm -i` is failing with `is intended for a different architecture` errors. ``` ARCHITECTURE AND OS: build arch : i386 compatible build archs: pentium3 i686 i586 i486 i386 noarch fat build os : Linux compatible build os's : Linux install arch : pentium3 install os : Linux compatible archs : pentium3 i686 i586 i486 i386 noarch fat compatible os's : Linux ``` Fix the code to also consider extended model and extended family when applicable. The implementation is similar to the one in the linuxi kernel. References: https://www.scss.tcd.ie/~jones/CS4021/processor-identification-cpuid-instruction-note.pdf https://www.amd.com/system/files/TechDocs/25481.pdf https://elixir.bootlin.com/linux/v6.3.6/source/arch/x86/lib/cpu.c#L19 Fixes: #2535 --- lib/rpmrc.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/rpmrc.c b/lib/rpmrc.c index e5ddeb0916..8e77ca5458 100644 --- a/lib/rpmrc.c +++ b/lib/rpmrc.c @@ -933,6 +933,31 @@ static int is_athlon(void) return 1; } +static unsigned int x86_family(unsigned int processor_sig) +{ + unsigned int base_family = (processor_sig >> 8) & 0x0f; + unsigned int family; + if (base_family == 0x0f) { + unsigned int extended_family = (processor_sig >> 20) & 0x0ff; + family = base_family + extended_family; + } else + family = base_family; + return family; +} + +static unsigned int x86_model(unsigned int processor_sig) +{ + unsigned int base_model = (processor_sig >> 4) & 0x0f; + unsigned int family = x86_family(processor_sig); + unsigned int model; + if (family >= 0x6) { + unsigned int extended_model = (processor_sig >> 16) & 0x0f; + model = (extended_model << 4) | base_model; + } else + model = base_model; + return model; +} + static int is_pentium3(void) { unsigned int eax, ebx, ecx, edx, family, model; @@ -945,8 +970,8 @@ static int is_pentium3(void) if (!rstreqn(vendor, "GenuineIntel", 12)) return 0; cpuid(1, &eax, &ebx, &ecx, &edx); - family = (eax >> 8) & 0x0f; - model = (eax >> 4) & 0x0f; + family = x86_family(eax); + model = x86_model(eax); if (family == 6) switch (model) { @@ -972,8 +997,8 @@ static int is_pentium4(void) if (!rstreqn(vendor, "GenuineIntel", 12)) return 0; cpuid(1, &eax, &ebx, &ecx, &edx); - family = (eax >> 8) & 0x0f; - model = (eax >> 4) & 0x0f; + family = x86_family(eax); + model = x86_model(eax); if (family == 15) switch (model) { @@ -1002,8 +1027,8 @@ static int is_geode(void) if (!rstreqn(vendor, "AuthenticAMD", 12)) return 0; cpuid(1, &eax, &ebx, &ecx, &edx); - family = (eax >> 8) & 0x0f; - model = (eax >> 4) & 0x0f; + family = x86_family(eax); + model = x86_model(eax); if (family == 5) switch (model) {