[PATCH] x86/microcode: Fix crashes on early 486 CPUs due to usage of 'cpuid'.

Oerg866 posted 1 patch 1 month, 1 week ago
arch/x86/kernel/cpu/microcode/amd.c | 8 ++++++--
arch/x86/kernel/head32.c            | 2 +-
2 files changed, 7 insertions(+), 3 deletions(-)
[PATCH] x86/microcode: Fix crashes on early 486 CPUs due to usage of 'cpuid'.
Posted by Oerg866 1 month, 1 week ago
Starting with v6.7-rc1, the kernel was no longer able to boot on early
i486-class CPUs.

To clarify, this was caused by the comprehensive microcode rework, from

0a23fb262d17f("Merge tag 'x86_microcode_for_v6.7_rc1'
of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip").

The breaking changes were introduced with these specific commits:

4c585af7180c1("x86/boot/32: Temporarily map initrd for microcode loading")

...causes immediate reboot.

a7939f0167203("x86/microcode/amd: Cache builtin/initrd microcode early")

...causes kernel panic early on.

They assume the host CPU supports the CPUID instruction, which
the pre-"enhanced" 486 type ones do not.

These changes make every kernel version from 6.7 to 6.11.4 bootable on
them.
---
 arch/x86/kernel/cpu/microcode/amd.c | 8 ++++++--
 arch/x86/kernel/head32.c            | 2 +-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kernel/cpu/microcode/amd.c
b/arch/x86/kernel/cpu/microcode/amd.c
index c0d56c02b8da..71fa388573ac 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -516,14 +516,18 @@ static enum ucode_state load_microcode_amd(u8
family, const u8 *data, size_t siz

 static int __init save_microcode_in_initrd(void)
 {
- unsigned int cpuid_1_eax = native_cpuid_eax(1);
+ unsigned int cpuid_1_eax;
  struct cpuinfo_x86 *c = &boot_cpu_data;
  struct cont_desc desc = { 0 };
  enum ucode_state ret;
  struct cpio_data cp;

- if (dis_ucode_ldr || c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10)
+ if (!have_cpuid_p() || dis_ucode_ldr ||
+        c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
         return 0;
+ }
+
+ cpuid_1_eax = native_cpuid_eax(1);

  find_blobs_in_containers(cpuid_1_eax, &cp);
  if (!(cp.data && cp.size))
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index de001b2146ab..79fd9f11dbcb 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -146,7 +146,7 @@ void __init __no_stack_protector mk_early_pgtbl_32(void)

 #ifdef CONFIG_MICROCODE_INITRD32
  /* Running on a hypervisor? */
- if (native_cpuid_ecx(1) & BIT(31))
+ if (have_cpuid_p() && (native_cpuid_ecx(1) & BIT(31)))
         return;

  params = (struct boot_params *)__pa_nodebug(&boot_params);
--
2.34.1
Re: [PATCH] x86/microcode: Fix crashes on early 486 CPUs due to usage of 'cpuid'.
Posted by Borislav Petkov 1 month, 1 week ago
On Sat, Oct 19, 2024 at 08:29:04AM +0200, Oerg866 wrote:
> They assume the host CPU supports the CPUID instruction, which
> the pre-"enhanced" 486 type ones do not.

"host"?

How exactly are you triggering this?

Are you running some weird guest or is it real hardware?

Any chance you can share details so that I can try to reproduce here in a VM?

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH] x86/microcode: Fix crashes on early 486 CPUs due to usage of 'cpuid'.
Posted by Oerg866 1 month, 1 week ago
Hello,

apologies, I had forgotten to switch to plain text in my previous reply.

This is my first kernel patch submission, please bear with me :-)

> Are you running some weird guest or is it real hardware?

Host in this case means the CPU the kernel is running on, so real hardware.

As far as I'm aware, common emulators used for kernel testing, such as
QEMU, do not exhibit this problem.

There are however emulators that can somewhat precisely emulate the
affected CPUs, such as 86Box, that can reproduce this behaviour.

> Any chance you can share details so that I can try to reproduce here in a VM?

I have prepared a small archive to help with near effortless reproduction:

wget https://kext.de/dl/486patchtest.tar.gz
tar -zxvf 486patchtest.tar.gz

- Use the included .config file to compile a minimal kernel for 486DX,
  in this example the kernel has been cloned to ./linux (I used tag v6.11):

cp .config linux/.config
pushd linux
make -j16
popd

- Then, proceed with emulation:

wget https://github.com/86Box/86Box/releases/download/v4.2.1/86Box-Linux-x86_64-b6130.AppImage
chmod +x ./86Box-Linux-x86_64-b6130.AppImage
git clone https://github.com/86Box/roms
./86Box-Linux-x86_64-b6130.AppImage --config 86box.cfg

- Click the little CD-ROM Icon on the bottom left
- select "Folder"
- select linux/arch/x86/boot

The machine will then boot into FreeDOS, load the kernel via LOADLIN
and you should see a kernel panic.

The patch is included in the archive (486.patch) for convenience.
After applying it, the kernel will boot all the way to init (which
expectedly fails, as it is missing in this case).

I hope this information is of use to you!

Best regards
Eric Voirin