Download raw body.
check minimum microcode versions
On Sun, Oct 26, 2025 at 11:45:09AM +1100, Jonathan Gray wrote:
> 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
The latest family 19h microcode now has multiple patches for some parts.
A patch with no minimum version and a patch with a minimum version.
Specifically for:
19-01-01 Milan-B1
19-01-02 Milan-X B2
19-11-01 Genoa-B1
19-11-02 Genoa-X B2
19-a0-02 Bergamo A1
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 31 Oct 2025 02:23:10 -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,21 +129,26 @@ 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;
void
cpu_ucode_amd_apply(struct cpu_info *ci)
{
- uint64_t level;
+ uint64_t cur_level;
uint32_t magic, tlen, i;
uint16_t eid = 0;
uint32_t sig, ebx, ecx, edx;
uint64_t start = 0;
- uint32_t patch_len = 0;
+ uint32_t patch_len = 0, new_level = 0;
if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
DPRINTF(("%s: no microcode provided\n", __func__));
@@ -156,8 +163,8 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
CPUID(1, sig, ebx, ecx, edx);
- level = rdmsr(MSR_PATCH_LEVEL);
- DPRINTF(("%s: cur patch level 0x%llx\n", __func__, level));
+ cur_level = rdmsr(MSR_PATCH_LEVEL);
+ DPRINTF(("%s: cur patch level 0x%llx\n", __func__, cur_level));
memcpy(&magic, cpu_ucode_data, 4);
if (magic != AMD_MAGIC) {
@@ -180,7 +187,10 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
eid = ae.eid;
}
- /* look for newer patch with the equivalence id */
+ /*
+ * Look for newer patch with the equivalence id. There can be
+ * multiple patches for a given equivalence id.
+ */
while (i < cpu_ucode_size) {
struct amd_patch ap;
if (i + sizeof(ap) > cpu_ucode_size) {
@@ -188,9 +198,18 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
goto out;
}
memcpy(&ap, &cpu_ucode_data[i], sizeof(ap));
- if (ap.type == 1 && ap.eid == eid && ap.level > level) {
- start = (uint64_t)&cpu_ucode_data[i + 8];
- patch_len = ap.len;
+ if (ap.type == 1 && ap.eid == eid && ap.level > cur_level &&
+ ap.level > new_level) {
+
+ /* check minimum version for fam >= 0x19 */
+ if (ci->ci_family < 0x19 || cur_level >= ap.data_val) {
+ start = (uint64_t)&cpu_ucode_data[i + 8];
+ new_level = ap.level;
+ patch_len = ap.len;
+ } else {
+ DPRINTF(("%s: ucode %#llx < min %#x\n",
+ __func__, cur_level, ap.data_val));
+ }
}
if (i + ap.len + 8 > cpu_ucode_size) {
DPRINTF(("%s: truncated patch\n", __func__));
@@ -207,8 +226,8 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
memcpy(p, (uint8_t *)start, patch_len);
start = (uint64_t)p;
wrmsr(MSR_PATCH_LOADER, start);
- level = rdmsr(MSR_PATCH_LEVEL);
- DPRINTF(("%s: new patch level 0x%llx\n", __func__, level));
+ cur_level = rdmsr(MSR_PATCH_LEVEL);
+ DPRINTF(("%s: new patch level 0x%llx\n", __func__, cur_level));
free(p, M_TEMP, patch_len);
}
out:
@@ -244,6 +263,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 31 Oct 2025 02:23:21 -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,21 +152,26 @@ 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;
void
cpu_ucode_amd_apply(struct cpu_info *ci)
{
- uint64_t level;
+ uint64_t cur_level;
uint32_t magic, tlen, i;
uint16_t eid = 0;
uint32_t sig, ebx, ecx, edx;
uint64_t start = 0;
- uint32_t patch_len = 0;
+ uint32_t patch_len = 0, new_level = 0;
if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
DPRINTF(("%s: no microcode provided\n", __func__));
@@ -179,8 +186,8 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
CPUID(1, sig, ebx, ecx, edx);
- level = rdmsr(MSR_PATCH_LEVEL);
- DPRINTF(("%s: cur patch level 0x%llx\n", __func__, level));
+ cur_level = rdmsr(MSR_PATCH_LEVEL);
+ DPRINTF(("%s: cur patch level 0x%llx\n", __func__, cur_level));
memcpy(&magic, cpu_ucode_data, 4);
if (magic != AMD_MAGIC) {
@@ -203,7 +210,10 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
eid = ae.eid;
}
- /* look for newer patch with the equivalence id */
+ /*
+ * Look for newer patch with the equivalence id. There can be
+ * multiple patches for a given equivalence id.
+ */
while (i < cpu_ucode_size) {
struct amd_patch ap;
if (i + sizeof(ap) > cpu_ucode_size) {
@@ -211,9 +221,18 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
goto out;
}
memcpy(&ap, &cpu_ucode_data[i], sizeof(ap));
- if (ap.type == 1 && ap.eid == eid && ap.level > level) {
- start = (uint64_t)&cpu_ucode_data[i + 8];
- patch_len = ap.len;
+ if (ap.type == 1 && ap.eid == eid && ap.level > cur_level &&
+ ap.level > new_level) {
+
+ /* check minimum version for fam >= 0x19 */
+ if (ci->ci_family < 0x19 || cur_level >= ap.data_val) {
+ start = (uint64_t)&cpu_ucode_data[i + 8];
+ new_level = ap.level;
+ patch_len = ap.len;
+ } else {
+ DPRINTF(("%s: ucode %#llx < min %#x\n",
+ __func__, cur_level, ap.data_val));
+ }
}
if (i + ap.len + 8 > cpu_ucode_size) {
DPRINTF(("%s: truncated patch\n", __func__));
@@ -230,8 +249,8 @@ cpu_ucode_amd_apply(struct cpu_info *ci)
memcpy(p, (uint8_t *)start, patch_len);
start = (uint64_t)p;
wrmsr(MSR_PATCH_LOADER, start);
- level = rdmsr(MSR_PATCH_LEVEL);
- DPRINTF(("%s: new patch level 0x%llx\n", __func__, level));
+ cur_level = rdmsr(MSR_PATCH_LEVEL);
+ DPRINTF(("%s: new patch level 0x%llx\n", __func__, cur_level));
free(p, M_TEMP, patch_len);
}
out:
@@ -267,6 +286,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;
}
check minimum microcode versions