untrusted comment: verify with openbsd-72-base.pub
RWQTKNnK3CZZ8Nu9ll6vyAa7XuKWKaRDgPfRjhN5yLLDlbfalJC3gtASzdexlpzfdMQDfF4eY38acsyc9LX6d1ALRhxasb0qsws=

OpenBSD 7.2 errata 035, July 24, 2023:

Install firmware updates for AMD cpus, to repair past or potential future bugs.
For i386 and amd64 platforms.

Apply by doing:
    signify -Vep /etc/signify/openbsd-72-base.pub -x 035_amdcpufirmware.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install a new kernel:
    KK=`sysctl -n kern.osversion | cut -d# -f1`
    cd /usr/src/sys/arch/`machine`/compile/$KK
    make obj
    make config
    make
    make install

New firmwares can be downloaded using
    fw_update

New bootblocks will need to be installed, using a command similar to this:
    installboot -v sd0

Index: sys/arch/amd64/amd64/ucode.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/ucode.c,v
diff -u -p -u -r1.5 ucode.c
--- sys/arch/amd64/amd64/ucode.c	28 Jun 2019 21:54:05 -0000	1.5
+++ sys/arch/amd64/amd64/ucode.c	23 Jul 2023 02:44:39 -0000
@@ -72,6 +72,8 @@ size_t	 cpu_ucode_size;
 void	 cpu_ucode_setup(void);
 void	 cpu_ucode_apply(struct cpu_info *);
 
+struct mutex	cpu_ucode_mtx = MUTEX_INITIALIZER(IPL_HIGH);
+
 /* Intel */
 void	 cpu_ucode_intel_apply(struct cpu_info *);
 struct intel_ucode_header *
@@ -82,7 +84,8 @@ int	 cpu_ucode_intel_match(struct intel_
 uint32_t cpu_ucode_intel_rev(void);
 
 struct intel_ucode_header	*cpu_ucode_intel_applied;
-struct mutex			 cpu_ucode_intel_mtx = MUTEX_INITIALIZER(IPL_HIGH);
+
+void cpu_ucode_amd_apply(struct cpu_info *);
 
 void
 cpu_ucode_setup(void)
@@ -107,6 +110,99 @@ cpu_ucode_apply(struct cpu_info *ci)
 {
 	if (strcmp(cpu_vendor, "GenuineIntel") == 0)
 		cpu_ucode_intel_apply(ci);
+	else if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
+		cpu_ucode_amd_apply(ci);
+}
+
+#define AMD_MAGIC 0x00414d44
+
+struct amd_equiv {
+	uint32_t id;
+	uint32_t a;
+	uint32_t b;
+	uint16_t eid;
+	uint16_t c;
+} __packed;
+
+struct amd_patch {
+	uint32_t type;
+	uint32_t len;
+	uint32_t a;
+	uint32_t level;
+	uint8_t c[16];
+	uint16_t eid;
+} __packed;
+
+void
+cpu_ucode_amd_apply(struct cpu_info *ci)
+{
+	uint64_t level;
+	uint32_t magic, tlen, i;
+	uint16_t eid = 0;
+	uint32_t sig, ebx, ecx, edx;
+	uint64_t start = 0;
+
+	if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
+		DPRINTF(("%s: no microcode provided\n", __func__));
+		return;
+	}
+
+	/*
+	 * Grab a mutex, because we are not allowed to run updates
+	 * simultaneously on HT siblings.
+	 */
+	mtx_enter(&cpu_ucode_mtx);
+
+	CPUID(1, sig, ebx, ecx, edx);
+
+	level = rdmsr(MSR_PATCH_LEVEL);
+	DPRINTF(("%s: cur patch level 0x%llx\n", __func__, level));
+
+	memcpy(&magic, cpu_ucode_data, 4);
+	if (magic != AMD_MAGIC) {
+		DPRINTF(("%s: bad magic %x\n", __func__, magic));
+		goto out;
+	}
+
+	memcpy(&tlen, &cpu_ucode_data[8], 4);
+
+	/* find equivalence id matching our cpu signature */
+	for (i = 12; i < 12 + tlen;) {
+		struct amd_equiv ae;
+		if (i + sizeof(ae) > cpu_ucode_size) {
+			DPRINTF(("%s: truncated etable\n", __func__));
+			goto out;
+		}
+		memcpy(&ae, &cpu_ucode_data[i], sizeof(ae));
+		i += sizeof(ae);
+		if (ae.id == sig)
+			eid = ae.eid;
+	}
+
+	/* look for newer patch with the equivalence id */
+	while (i < cpu_ucode_size) {
+		struct amd_patch ap;
+		if (i + sizeof(ap) > cpu_ucode_size) {
+			DPRINTF(("%s: truncated ptable\n", __func__));
+			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];
+		if (i + ap.len + 8 > cpu_ucode_size) {
+			DPRINTF(("%s: truncated patch\n", __func__));
+			goto out;
+		}
+		i += ap.len + 8;
+	}
+
+	if (start != 0) {
+		wrmsr(MSR_PATCH_LOADER, start);
+		level = rdmsr(MSR_PATCH_LEVEL);
+		DPRINTF(("%s: new patch level 0x%llx\n", __func__, level));
+	}
+out:
+	mtx_leave(&cpu_ucode_mtx);
 }
 
 void
@@ -125,7 +221,7 @@ cpu_ucode_intel_apply(struct cpu_info *c
 	 * Grab a mutex, because we are not allowed to run updates
 	 * simultaneously on HT siblings.
 	 */
-	mtx_enter(&cpu_ucode_intel_mtx);
+	mtx_enter(&cpu_ucode_mtx);
 
 	old_rev = cpu_ucode_intel_rev();
 	update = cpu_ucode_intel_applied;
@@ -159,7 +255,7 @@ cpu_ucode_intel_apply(struct cpu_info *c
 	}
 
 out:
-	mtx_leave(&cpu_ucode_intel_mtx);
+	mtx_leave(&cpu_ucode_mtx);
 }
 
 struct intel_ucode_header *
Index: sys/arch/i386/i386/ucode.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/ucode.c,v
diff -u -p -u -r1.3 ucode.c
--- sys/arch/i386/i386/ucode.c	15 Dec 2020 22:51:34 -0000	1.3
+++ sys/arch/i386/i386/ucode.c	23 Jul 2023 02:56:42 -0000
@@ -72,6 +72,8 @@ size_t	 cpu_ucode_size;
 void	 cpu_ucode_setup(void);
 void	 cpu_ucode_apply(struct cpu_info *);
 
+struct mutex	cpu_ucode_mtx = MUTEX_INITIALIZER(IPL_HIGH);
+
 /* Intel */
 void	 cpu_ucode_intel_apply(struct cpu_info *);
 struct intel_ucode_header *
@@ -82,7 +84,8 @@ int	 cpu_ucode_intel_match(struct intel_
 uint32_t cpu_ucode_intel_rev(void);
 
 struct intel_ucode_header	*cpu_ucode_intel_applied;
-struct mutex			 cpu_ucode_intel_mtx = MUTEX_INITIALIZER(IPL_HIGH);
+
+void cpu_ucode_amd_apply(struct cpu_info *);
 
 void
 cpu_ucode_setup(void)
@@ -130,6 +133,99 @@ cpu_ucode_apply(struct cpu_info *ci)
 {
 	if (strcmp(cpu_vendor, "GenuineIntel") == 0)
 		cpu_ucode_intel_apply(ci);
+	else if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
+		cpu_ucode_amd_apply(ci);
+}
+
+#define AMD_MAGIC 0x00414d44
+
+struct amd_equiv {
+	uint32_t id;
+	uint32_t a;
+	uint32_t b;
+	uint16_t eid;
+	uint16_t c;
+} __packed;
+
+struct amd_patch {
+	uint32_t type;
+	uint32_t len;
+	uint32_t a;
+	uint32_t level;
+	uint8_t c[16];
+	uint16_t eid;
+} __packed;
+
+void
+cpu_ucode_amd_apply(struct cpu_info *ci)
+{
+	uint64_t level;
+	uint32_t magic, tlen, i;
+	uint16_t eid = 0;
+	uint32_t sig, ebx, ecx, edx;
+	uint64_t start = 0;
+
+	if (cpu_ucode_data == NULL || cpu_ucode_size == 0) {
+		DPRINTF(("%s: no microcode provided\n", __func__));
+		return;
+	}
+
+	/*
+	 * Grab a mutex, because we are not allowed to run updates
+	 * simultaneously on HT siblings.
+	 */
+	mtx_enter(&cpu_ucode_mtx);
+
+	CPUID(1, sig, ebx, ecx, edx);
+
+	level = rdmsr(MSR_PATCH_LEVEL);
+	DPRINTF(("%s: cur patch level 0x%llx\n", __func__, level));
+
+	memcpy(&magic, cpu_ucode_data, 4);
+	if (magic != AMD_MAGIC) {
+		DPRINTF(("%s: bad magic %x\n", __func__, magic));
+		goto out;
+	}
+
+	memcpy(&tlen, &cpu_ucode_data[8], 4);
+
+	/* find equivalence id matching our cpu signature */
+	for (i = 12; i < 12 + tlen;) {
+		struct amd_equiv ae;
+		if (i + sizeof(ae) > cpu_ucode_size) {
+			DPRINTF(("%s: truncated etable\n", __func__));
+			goto out;
+		}
+		memcpy(&ae, &cpu_ucode_data[i], sizeof(ae));
+		i += sizeof(ae);
+		if (ae.id == sig)
+			eid = ae.eid;
+	}
+
+	/* look for newer patch with the equivalence id */
+	while (i < cpu_ucode_size) {
+		struct amd_patch ap;
+		if (i + sizeof(ap) > cpu_ucode_size) {
+			DPRINTF(("%s: truncated ptable\n", __func__));
+			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];
+		if (i + ap.len + 8 > cpu_ucode_size) {
+			DPRINTF(("%s: truncated patch\n", __func__));
+			goto out;
+		}
+		i += ap.len + 8;
+	}
+
+	if (start != 0) {
+		wrmsr(MSR_PATCH_LOADER, start);
+		level = rdmsr(MSR_PATCH_LEVEL);
+		DPRINTF(("%s: new patch level 0x%llx\n", __func__, level));
+	}
+out:
+	mtx_leave(&cpu_ucode_mtx);
 }
 
 void
@@ -148,7 +244,7 @@ cpu_ucode_intel_apply(struct cpu_info *c
 	 * Grab a mutex, because we are not allowed to run updates
 	 * simultaneously on HT siblings.
 	 */
-	mtx_enter(&cpu_ucode_intel_mtx);
+	mtx_enter(&cpu_ucode_mtx);
 
 	old_rev = cpu_ucode_intel_rev();
 	update = cpu_ucode_intel_applied;
@@ -182,7 +278,7 @@ cpu_ucode_intel_apply(struct cpu_info *c
 	}
 
 out:
-	mtx_leave(&cpu_ucode_intel_mtx);
+	mtx_leave(&cpu_ucode_mtx);
 }
 
 struct intel_ucode_header *
