Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102057528
D43318.id132566.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D43318.id132566.diff
View Options
diff --git a/sys/x86/include/ucode.h b/sys/x86/include/ucode.h
--- a/sys/x86/include/ucode.h
+++ b/sys/x86/include/ucode.h
@@ -58,6 +58,8 @@
int ucode_intel_load(void *data, bool unsafe,
uint64_t *nrevp, uint64_t *orevp);
+int ucode_amd_load(void *data, bool unsafe,
+ uint64_t *nrevp, uint64_t *orevp);
size_t ucode_load_bsp(uintptr_t free);
void ucode_load_ap(int cpu);
void ucode_reload(void);
diff --git a/sys/x86/x86/ucode.c b/sys/x86/x86/ucode.c
--- a/sys/x86/x86/ucode.c
+++ b/sys/x86/x86/ucode.c
@@ -50,10 +50,53 @@
#include <vm/vm_kern.h>
#include <vm/vm_param.h>
+/*
+ * AMD family 10h and later.
+ */
+typedef struct amd_10h_fw_header {
+ uint32_t data_code;
+ uint32_t patch_id;
+ uint16_t mc_patch_data_id;
+ uint8_t mc_patch_data_len;
+ uint8_t init_flag;
+ uint32_t mc_patch_data_checksum;
+ uint32_t nb_dev_id;
+ uint32_t sb_dev_id;
+ uint16_t processor_rev_id;
+ uint8_t nb_rev_id;
+ uint8_t sb_rev_id;
+ uint8_t bios_api_rev;
+ uint8_t reserved1[3];
+ uint32_t match_reg[8];
+} amd_10h_fw_header_t;
+
+typedef struct equiv_cpu_entry {
+ uint32_t installed_cpu;
+ uint32_t fixed_errata_mask;
+ uint32_t fixed_errata_compare;
+ uint16_t equiv_cpu;
+ uint16_t res;
+} equiv_cpu_entry_t;
+
+typedef struct section_header {
+ uint32_t type;
+ uint32_t size;
+} section_header_t;
+
+typedef struct container_header {
+ uint32_t magic;
+} container_header_t;
+
+#define AMD_10H_MAGIC 0x414d44
+#define AMD_10H_EQUIV_TABLE_TYPE 0
+#define AMD_10H_uCODE_TYPE 1
+
static void *ucode_intel_match(uint8_t *data, size_t *len);
static int ucode_intel_verify(struct ucode_intel_header *hdr,
size_t resid);
+static void *ucode_amd_match(uint8_t *data, size_t *len);
+
static struct ucode_ops {
const char *vendor;
int (*load)(void *, bool, uint64_t *, uint64_t *);
@@ -64,6 +107,11 @@
.load = ucode_intel_load,
.match = ucode_intel_match,
},
+ {
+ .vendor = AMD_VENDOR_ID,
+ .load = ucode_amd_load,
+ .match = ucode_amd_match,
+ },
};
/* Selected microcode update data. */
@@ -224,6 +272,210 @@
return (NULL);
}
+int
+ucode_amd_load(void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
+{
+ uint64_t nrev, orev;
+ uint32_t cpuid[4];
+
+ orev = rdmsr(MSR_BIOS_SIGN);
+
+ /*
+ * Perform update.
+ */
+ if (unsafe)
+ wrmsr_safe(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data);
+ else
+ wrmsr(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data);
+
+ /*
+ * Serialize instruction flow.
+ */
+ do_cpuid(0, cpuid);
+
+ /*
+ * Verify that the microcode revision changed.
+ */
+ nrev = rdmsr(MSR_BIOS_SIGN);
+ if (nrevp != NULL)
+ *nrevp = nrev;
+ if (orevp != NULL)
+ *orevp = orev;
+ if (nrev <= orev)
+ return (EEXIST);
+ return (0);
+
+}
+
+static void *
+ucode_amd_match(uint8_t *data, size_t *len)
+{
+ const amd_10h_fw_header_t *fw_header;
+ const amd_10h_fw_header_t *selected_fw;
+ const equiv_cpu_entry_t *equiv_cpu_table;
+ const section_header_t *section_header;
+ const container_header_t *container_header;
+ const uint8_t *fw_data;
+ size_t fw_size;
+ size_t selected_size;
+ uint32_t revision;
+ uint32_t signature;
+ uint16_t equiv_id;
+ uint32_t regs[4];
+ int i;
+
+ const char *path = "loader blob";
+
+ fw_data = data;
+ fw_size = *len;
+
+ do_cpuid(1, regs);
+ signature = regs[0];
+
+ revision = rdmsr(MSR_BIOS_SIGN);
+
+#define WARNX(level, ...) \
+{ \
+ printf(__VA_ARGS__); \
+ printf("\n"); \
+}
+
+ WARNX(1, "found cpu family %#x model %#x "
+ "stepping %#x extfamily %#x extmodel %#x.",
+ ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff),
+ (signature >> 4) & 0x0f,
+ (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
+ (signature >> 16) & 0x0f);
+ WARNX(1, "microcode revision %#x", revision);
+
+nextfile:
+ WARNX(3, "processing next container file");
+ if (fw_size <
+ (sizeof(*container_header) + sizeof(*section_header))) {
+ WARNX(2, "file too short: %s", path);
+ goto done;
+ }
+
+ container_header = (const container_header_t *)fw_data;
+ if (container_header->magic != AMD_10H_MAGIC) {
+ WARNX(2, "%s is not a valid amd firmware: bad magic", path);
+ goto done;
+ }
+ fw_data += sizeof(*container_header);
+ fw_size -= sizeof(*container_header);
+
+ section_header = (const section_header_t *)fw_data;
+ if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "first section is not CPU equivalence table", path);
+ goto done;
+ }
+ if (section_header->size == 0) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "first section is empty", path);
+ goto done;
+ }
+ fw_data += sizeof(*section_header);
+ fw_size -= sizeof(*section_header);
+
+ if (section_header->size > fw_size) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "file is truncated", path);
+ goto done;
+ }
+ if (section_header->size < sizeof(*equiv_cpu_table)) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "first section is too short", path);
+ goto done;
+ }
+ equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
+ fw_data += section_header->size;
+ fw_size -= section_header->size;
+
+ equiv_id = 0;
+ for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
+ WARNX(3, "signature 0x%x i %d installed_cpu 0x%x equiv 0x%x",
+ signature, i, equiv_cpu_table[i].installed_cpu,
+ equiv_cpu_table[i].equiv_cpu);
+ if (signature == equiv_cpu_table[i].installed_cpu) {
+ equiv_id = equiv_cpu_table[i].equiv_cpu;
+ WARNX(3, "equiv_id: %x, signature %8x,"
+ " equiv_cpu_table[%d] %8x", equiv_id, signature,
+ i, equiv_cpu_table[i].installed_cpu);
+ break;
+ }
+ }
+ if (equiv_id == 0) {
+ WARNX(2, "CPU is not found in the equivalence table");
+ }
+
+ selected_fw = NULL;
+ selected_size = 0;
+ while (fw_size >= sizeof(*section_header)) {
+ section_header = (const section_header_t *)fw_data;
+ if (section_header->type == AMD_10H_MAGIC) {
+ WARNX(2, "%s next section is actually a new container",
+ path);
+ if (selected_fw != NULL)
+ goto found;
+ else
+ goto nextfile;
+ }
+ fw_data += sizeof(*section_header);
+ fw_size -= sizeof(*section_header);
+ if (section_header->type != AMD_10H_uCODE_TYPE) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "section has incorrect type", path);
+ goto done;
+ }
+ if (section_header->size > fw_size) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "file is truncated", path);
+ goto done;
+ }
+ if (section_header->size < sizeof(*fw_header)) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "section is too short", path);
+ goto done;
+ }
+ fw_header = (const amd_10h_fw_header_t *)fw_data;
+ fw_data += section_header->size;
+ fw_size -= section_header->size;
+
+ if (fw_header->processor_rev_id != equiv_id) {
+ WARNX(1, "firmware processor_rev_id %x, equiv_id %x",
+ fw_header->processor_rev_id, equiv_id);
+ continue; /* different cpu */
+ }
+ if (fw_header->patch_id <= revision) {
+ WARNX(1, "patch_id %x, revision %x",
+ fw_header->patch_id, revision);
+ continue; /* not newer revision */
+ }
+ if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
+ WARNX(2, "Chipset-specific microcode is not supported");
+ }
+
+ WARNX(3, "selecting revision: %x", fw_header->patch_id);
+ revision = fw_header->patch_id;
+ selected_fw = fw_header;
+ selected_size = section_header->size;
+ }
+
+ if (fw_size != 0) {
+ WARNX(2, "%s is not a valid amd firmware: "
+ "file is truncated", path);
+ goto done;
+ }
+
+found:
+ *len = selected_size;
+ return (__DECONST(void *, selected_fw));
+
+done:
+ return (NULL);
+}
+
/*
* Release any memory backing unused microcode blobs back to the system.
* We copy the selected update and free the entire microcode file.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 8, 2:20 AM (18 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14525650
Default Alt Text
D43318.id132566.diff (7 KB)
Attached To
Mode
D43318: x86/ucode: add support for early loading of CPU ucode on AMD.
Attached
Detach File
Event Timeline
Log In to Comment