From: Jonathan Gray Subject: check minimum microcode versions To: tech@openbsd.org Date: Sun, 26 Oct 2025 11:45:09 +1100 Some AMD microcode now lists a minimum version for performing an MSR based update. If the microcode is not at that level it seems a protection fault will be generated. Only some server (Epyc) parts have a minimum version at the moment. And not in the version currently in ports. https://gitlab.com/kernel-firmware/linux-firmware/-/blob/main/amd-ucode/README?ref_type=heads "Minimum base ucode version for loading" "#GP fault will occur if attempting to hot load microcode on older BIOS." https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7033.html Fam-Model-Step Codename Minimum microcode patch 19-01-01 Milan-B1 0x0a0011d9 19-01-02 Milan-X B2 0x0a001242 19-11-01 Genoa-B1 0x0a101153 19-11-02 Genoa-X B2 0x0a10124e 19-a0-02 Bergamo A2 0x0aa00218 1a-02-01 Turin 0x0b002140 1a-11-00 Turin Dense 0x0b101040 Some Intel microcode also has a non-zero minimum version. From the latest 20250812 release: fam-model-step patch level minimum 06-55-03 01000191 0100015b 06-55-04 02007006 02006b06 06-55-07 05003901 05003102 06-55-0b 07002b01 07002402 06-56-05 0e000015 0e00000f 06-5f-01 0000003e 0000002e 06-6a-06 0d000410 0d000311 06-6c-01 010002e0 010001d1 06-7e-05 000000ca 000000a8 06-8c-01 000000bc 000000a4 06-8c-02 0000003c 00000022 06-8d-01 00000056 0000003c 06-8e-09 000000f6 000000ea 06-8e-0a 000000f6 000000ea 06-8e-0b 000000f6 000000ea 06-8e-0c 00000100 000000ea 06-8f-07 2b000643 2b000161 06-8f-08 2c000401 2c0001d0 06-97-02 0000003a 0000001f 06-97-05 0000003a 0000001f 06-9a-03 00000437 00000419 06-9a-04 00000437 00000419 06-9e-09 000000f8 000000ea 06-9e-0a 000000fa 000000ea 06-9e-0b 000000f6 000000ea 06-9e-0c 000000f8 000000ea 06-9e-0d 00000104 000000ea 06-a5-02 00000100 000000ea 06-a5-03 00000100 000000ea 06-a5-05 00000100 000000ea 06-a6-00 00000102 000000ea 06-a6-01 00000100 000000ea 06-a7-01 00000064 00000051 06-ad-01 010003d0 00000001 06-af-03 03000362 03000132 06-b7-01 0000012f 0000010b 06-ba-02 00004129 0000410c 06-ba-03 00004129 0000410c 06-be-00 0000001d 0000000f 06-bf-02 0000003a 0000001f 06-bf-05 0000003a 0000001f 06-cf-02 210002b3 21000200 Index: sys/arch/amd64/amd64/ucode.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/ucode.c,v diff -u -p -r1.9 ucode.c --- sys/arch/amd64/amd64/ucode.c 3 Apr 2024 02:01:21 -0000 1.9 +++ sys/arch/amd64/amd64/ucode.c 26 Oct 2025 00:06:11 -0000 @@ -48,7 +48,9 @@ struct intel_ucode_header { uint32_t processor_flags; uint32_t data_size; uint32_t total_size; - uint32_t reserved[3]; + uint32_t metadata_size; + uint32_t min_revision; + uint32_t reserved; }; struct intel_ucode_ext_sig_header { @@ -127,9 +129,14 @@ struct amd_equiv { struct amd_patch { uint32_t type; uint32_t len; - uint32_t a; + uint32_t date; uint32_t level; - uint8_t c[16]; + uint16_t data_id; + uint8_t data_len; + uint8_t flag; + uint32_t data_val; + uint32_t nb_dev; + uint32_t sb_dev; uint16_t eid; } __packed; @@ -191,6 +198,13 @@ cpu_ucode_amd_apply(struct cpu_info *ci) if (ap.type == 1 && ap.eid == eid && ap.level > level) { start = (uint64_t)&cpu_ucode_data[i + 8]; patch_len = ap.len; + + /* check minimum version */ + if (ci->ci_family >= 0x19 && level < ap.data_val) { + DPRINTF(("%s: ucode %#llx < min %#x\n", + __func__, level, ap.data_val)); + goto out; + } } if (i + ap.len + 8 > cpu_ucode_size) { DPRINTF(("%s: truncated patch\n", __func__)); @@ -244,6 +258,11 @@ cpu_ucode_intel_apply(struct cpu_info *c } if (update->update_revision == old_rev) { DPRINTF(("%s: microcode already up-to-date\n", __func__)); + goto out; + } + if (old_rev < update->min_revision) { + DPRINTF(("%s: cur %#x < min %#x\n", __func__, old_rev, + update->min_revision)); goto out; } Index: sys/arch/i386/i386/ucode.c =================================================================== RCS file: /cvs/src/sys/arch/i386/i386/ucode.c,v diff -u -p -r1.6 ucode.c --- sys/arch/i386/i386/ucode.c 10 Sep 2023 09:32:31 -0000 1.6 +++ sys/arch/i386/i386/ucode.c 25 Oct 2025 23:53:12 -0000 @@ -48,7 +48,9 @@ struct intel_ucode_header { uint32_t processor_flags; uint32_t data_size; uint32_t total_size; - uint32_t reserved[3]; + uint32_t metadata_size; + uint32_t min_revision; + uint32_t reserved; }; struct intel_ucode_ext_sig_header { @@ -150,9 +152,14 @@ struct amd_equiv { struct amd_patch { uint32_t type; uint32_t len; - uint32_t a; + uint32_t date; uint32_t level; - uint8_t c[16]; + uint16_t data_id; + uint8_t data_len; + uint8_t flag; + uint32_t data_val; + uint32_t nb_dev; + uint32_t sb_dev; uint16_t eid; } __packed; @@ -214,6 +221,13 @@ cpu_ucode_amd_apply(struct cpu_info *ci) if (ap.type == 1 && ap.eid == eid && ap.level > level) { start = (uint64_t)&cpu_ucode_data[i + 8]; patch_len = ap.len; + + /* check minimum version */ + if (ci->ci_family >= 0x19 && level < ap.data_val) { + DPRINTF(("%s: ucode %#llx < min %#x\n", + __func__, level, ap.data_val)); + goto out; + } } if (i + ap.len + 8 > cpu_ucode_size) { DPRINTF(("%s: truncated patch\n", __func__)); @@ -267,6 +281,11 @@ cpu_ucode_intel_apply(struct cpu_info *c } if (update->update_revision == old_rev) { DPRINTF(("%s: microcode already up-to-date\n", __func__)); + goto out; + } + if (old_rev < update->min_revision) { + DPRINTF(("%s: cur %#x < min %#x\n", __func__, old_rev, + update->min_revision)); goto out; }