Index | Thread | Search

From:
Jonathan Gray <jsg@jsg.id.au>
Subject:
check minimum microcode versions
To:
tech@openbsd.org
Date:
Sun, 26 Oct 2025 11:45:09 +1100

Download raw body.

Thread
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;
 	}