/* * This file is part of the coreboot project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #define UCODE_DEBUG(fmt, args...) \ do { printk(BIOS_DEBUG, "[microcode] "fmt, ##args); } while (0) #define UCODE_MAGIC 0x00414d44 #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 #define UCODE_SECTION_START_ID 0x00000001 #define UCODE_MAGIC 0x00414d44 #define F1XH_MPB_MAX_SIZE 2048 #define F15H_MPB_MAX_SIZE 4096 #define CONT_HDR 12 #define SECT_HDR 8 /* * STRUCTURE OF A MICROCODE (UCODE) FILE * Container Header * Section Header * Microcode Header * Microcode "Blob" * Section Header * Microcode Header * Microcode "Blob" * ... * ... * (end of file) * * * CONTAINER HEADER (offset 0 bytes from start of file) * Total size = fixed size (12 bytes) + variable size * [0:3] 32-bit unique ID * [4:7] don't-care * [8-11] Size (n) in bytes of variable portion of container header * [12-n] don't-care * * SECTION HEADER (offset += 12+n) * Total size = 8 bytes * [0:3] Unique identifier signaling start of section (0x00000001) * [4:7] Total size (m) of following microcode section, including microcode header * * MICROCODE HEADER (offset += 8) * Total size = 64 bytes * [0:3] Data code (32 bits) * [4:7] Patch ID (32 bits) * [8:9] Microcode patch data ID (16 bits) * [10] c patch data length (8 bits) * [11] init flag (8 bits) * [12:15] ucode patch data cksum (32 bits) * [16:19] nb dev ID (32 bits) * [20:23] sb dev ID (32 bits) * [24:25] Processor rev ID (16 bits) * [26] nb revision ID (8 bits) * [27] sb revision ID (8 bits) * [28] BIOS API revision (8 bits) * [29-31] Reserved 1 (array of three 8-bit values) * [32-63] Match reg (array of eight 32-bit values) * * MICROCODE BLOB (offset += 64) * Total size = m bytes * */ struct microcode { 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]; uint8_t m_patch_data[896]; uint8_t resv2[896]; uint8_t x86_code_present; uint8_t x86_code_entry[191]; }; static void apply_microcode_patch(const struct microcode *m) { uint32_t new_patch_id; msr_t msr; /* apply patch */ msr.hi = 0; msr.lo = (uint32_t)m; wrmsr(MSR_PATCH_LOADER, msr); UCODE_DEBUG("patch id to apply = 0x%08x\n", m->patch_id); /* read the patch_id again */ msr = rdmsr(IA32_BIOS_SIGN_ID); new_patch_id = msr.lo; UCODE_DEBUG("updated to patch id = 0x%08x %s\n", new_patch_id, (new_patch_id == m->patch_id) ? "success" : "fail"); } static void amd_update_microcode(const void *ucode, size_t ucode_len, uint32_t equivalent_processor_rev_id) { const struct microcode *m; const uint8_t *c = ucode; const uint8_t *ucode_end = (uint8_t*)ucode + ucode_len; const uint8_t *cur_section_hdr; uint32_t container_hdr_id; uint32_t container_hdr_size; uint32_t blob_size; uint32_t sec_hdr_id; /* Container Header */ container_hdr_id = read32(c); if (container_hdr_id != UCODE_MAGIC) { UCODE_DEBUG("Invalid container header ID\n"); return; } container_hdr_size = read32(c + 8); cur_section_hdr = c + CONT_HDR + container_hdr_size; /* Read in first section header ID */ sec_hdr_id = read32(cur_section_hdr); c = cur_section_hdr + 4; /* Loop through sections */ while (sec_hdr_id == UCODE_SECTION_START_ID && c <= (ucode_end - F15H_MPB_MAX_SIZE)) { blob_size = read32(c); m = (struct microcode *)(c + 4); if (m->processor_rev_id == equivalent_processor_rev_id) { apply_microcode_patch(m); break; } cur_section_hdr = c + 4 + blob_size; sec_hdr_id = read32(cur_section_hdr); c = cur_section_hdr + 4; } } static const char *microcode_cbfs_file[] = { "microcode_amd.bin", "microcode_amd_fam15h.bin", }; void amd_update_microcode_from_cbfs(uint32_t equivalent_processor_rev_id) { const void *ucode; size_t ucode_len; uint32_t i; for (i = 0; i < ARRAY_SIZE(microcode_cbfs_file); i++) { if (equivalent_processor_rev_id == 0) { UCODE_DEBUG("rev id not found. Skipping microcode patch!\n"); return; } #ifdef __PRE_RAM__ #if CONFIG(HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK) spin_lock(romstage_microcode_cbfs_lock()); #endif #endif ucode = cbfs_boot_map_with_leak(microcode_cbfs_file[i], CBFS_TYPE_MICROCODE, &ucode_len); if (!ucode) { UCODE_DEBUG("microcode file not found. Skipping updates.\n"); #ifdef __PRE_RAM__ #if CONFIG(HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK) spin_unlock(romstage_microcode_cbfs_lock()); #endif #endif return; } amd_update_microcode(ucode, ucode_len, equivalent_processor_rev_id); #ifdef __PRE_RAM__ #if CONFIG(HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK) spin_unlock(romstage_microcode_cbfs_lock()); #endif #endif } }