| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. |
| * Copyright (C) 2000 Ronald G. Minnich |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* Microcode update for Intel PIII and later CPUs */ |
| |
| #include <stdint.h> |
| #if !defined(__ROMCC__) |
| #include <console/console.h> |
| #endif |
| #include <cpu/cpu.h> |
| #include <cpu/x86/msr.h> |
| #include <cpu/intel/microcode.h> |
| |
| #ifdef __PRE_RAM__ |
| #if CONFIG_CPU_MICROCODE_IN_CBFS |
| #include <arch/cbfs.h> |
| #endif |
| #else |
| #if CONFIG_CPU_MICROCODE_IN_CBFS |
| #include <cbfs.h> |
| #endif |
| #include <smp/spinlock.h> |
| DECLARE_SPIN_LOCK(microcode_lock) |
| #endif |
| |
| struct microcode { |
| u32 hdrver; /* Header Version */ |
| u32 rev; /* Update Revision */ |
| u32 date; /* Date */ |
| u32 sig; /* Processor Signature */ |
| |
| u32 cksum; /* Checksum */ |
| u32 ldrver; /* Loader Revision */ |
| u32 pf; /* Processor Flags */ |
| |
| u32 data_size; /* Data Size */ |
| u32 total_size; /* Total Size */ |
| |
| u32 reserved[3]; |
| }; |
| |
| static inline u32 read_microcode_rev(void) |
| { |
| /* Some Intel CPUs can be very finicky about the |
| * CPUID sequence used. So this is implemented in |
| * assembly so that it works reliably. |
| */ |
| msr_t msr; |
| asm volatile ( |
| "xorl %%eax, %%eax\n\t" |
| "xorl %%edx, %%edx\n\t" |
| "movl $0x8b, %%ecx\n\t" |
| "wrmsr\n\t" |
| "movl $0x01, %%eax\n\t" |
| "cpuid\n\t" |
| "movl $0x08b, %%ecx\n\t" |
| "rdmsr \n\t" |
| : /* outputs */ |
| "=a" (msr.lo), "=d" (msr.hi) |
| : /* inputs */ |
| : /* trashed */ |
| "ebx", "ecx" |
| ); |
| return msr.hi; |
| } |
| |
| #if CONFIG_CPU_MICROCODE_IN_CBFS |
| |
| #define MICROCODE_CBFS_FILE "cpu_microcode_blob.bin" |
| |
| void intel_microcode_load_unlocked(const void *microcode_patch) |
| { |
| u32 current_rev; |
| msr_t msr; |
| const struct microcode *m = microcode_patch; |
| |
| if (!m) |
| return; |
| |
| current_rev = read_microcode_rev(); |
| |
| /* No use loading the same revision. */ |
| if (current_rev == m->rev) |
| return; |
| |
| msr.lo = (unsigned long)m + sizeof(struct microcode); |
| msr.hi = 0; |
| wrmsr(0x79, msr); |
| |
| #if !defined(__ROMCC__) |
| printk(BIOS_DEBUG, "microcode: updated to revision " |
| "0x%x date=%04x-%02x-%02x\n", read_microcode_rev(), |
| m->date & 0xffff, (m->date >> 24) & 0xff, |
| (m->date >> 16) & 0xff); |
| #endif |
| } |
| |
| const void *intel_microcode_find(void) |
| { |
| void *microcode_updates; |
| u32 eax; |
| u32 pf, rev, sig; |
| unsigned int x86_model, x86_family; |
| const struct microcode *m; |
| const char *c; |
| msr_t msr; |
| |
| #ifdef __PRE_RAM__ |
| microcode_updates = walkcbfs((char *) MICROCODE_CBFS_FILE); |
| #else |
| microcode_updates = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, |
| MICROCODE_CBFS_FILE, |
| CBFS_TYPE_MICROCODE); |
| #endif |
| |
| if (!microcode_updates) |
| return microcode_updates; |
| |
| /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ |
| msr.lo = 0; |
| msr.hi = 0; |
| wrmsr(0x8B, msr); |
| eax = cpuid_eax(1); |
| msr = rdmsr(0x8B); |
| rev = msr.hi; |
| x86_model = (eax >>4) & 0x0f; |
| x86_family = (eax >>8) & 0x0f; |
| sig = eax; |
| |
| pf = 0; |
| if ((x86_model >= 5)||(x86_family>6)) { |
| msr = rdmsr(0x17); |
| pf = 1 << ((msr.hi >> 18) & 7); |
| } |
| #if !defined(__ROMCC__) |
| /* If this code is compiled with ROMCC we're probably in |
| * the bootblock and don't have console output yet. |
| */ |
| printk(BIOS_DEBUG, "microcode: sig=0x%x pf=0x%x revision=0x%x\n", |
| sig, pf, rev); |
| #endif |
| |
| m = microcode_updates; |
| for(c = microcode_updates; m->hdrver; m = (const struct microcode *)c) { |
| if ((m->sig == sig) && (m->pf & pf)) |
| return m; |
| |
| if (m->total_size) { |
| c += m->total_size; |
| } else { |
| #if !defined(__ROMCC__) |
| printk(BIOS_WARNING, "Microcode has no valid size field!\n"); |
| #endif |
| c += 2048; |
| } |
| } |
| |
| /* ROMCC doesn't like NULL. */ |
| return (void *)0; |
| } |
| |
| void intel_update_microcode_from_cbfs(void) |
| { |
| const void *patch = intel_microcode_find(); |
| |
| #if !defined(__ROMCC__) && !defined(__PRE_RAM__) |
| spin_lock(µcode_lock); |
| #endif |
| |
| intel_microcode_load_unlocked(patch); |
| |
| #if !defined(__ROMCC__) && !defined(__PRE_RAM__) |
| spin_unlock(µcode_lock); |
| #endif |
| } |
| |
| #else /* !CONFIG_CPU_MICROCODE_IN_CBFS */ |
| |
| void intel_update_microcode(const void *microcode_updates) |
| { |
| u32 eax; |
| u32 pf, rev, sig; |
| unsigned int x86_model, x86_family; |
| const struct microcode *m; |
| const char *c; |
| msr_t msr; |
| |
| /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ |
| msr.lo = 0; |
| msr.hi = 0; |
| wrmsr(0x8B, msr); |
| eax = cpuid_eax(1); |
| msr = rdmsr(0x8B); |
| rev = msr.hi; |
| x86_model = (eax >>4) & 0x0f; |
| x86_family = (eax >>8) & 0x0f; |
| sig = eax; |
| |
| pf = 0; |
| if ((x86_model >= 5)||(x86_family>6)) { |
| msr = rdmsr(0x17); |
| pf = 1 << ((msr.hi >> 18) & 7); |
| } |
| #if !defined(__ROMCC__) |
| /* If this code is compiled with ROMCC we're probably in |
| * the bootblock and don't have console output yet. |
| */ |
| printk(BIOS_DEBUG, "microcode: sig=0x%x pf=0x%x revision=0x%x\n", |
| sig, pf, rev); |
| #endif |
| #if !defined(__ROMCC__) && !defined(__PRE_RAM__) |
| spin_lock(µcode_lock); |
| #endif |
| |
| m = microcode_updates; |
| for(c = microcode_updates; m->hdrver; m = (const struct microcode *)c) { |
| if ((m->sig == sig) && (m->pf & pf)) { |
| unsigned int new_rev; |
| msr.lo = (unsigned long)c + sizeof(struct microcode); |
| msr.hi = 0; |
| wrmsr(0x79, msr); |
| |
| /* Read back the new microcode version */ |
| new_rev = read_microcode_rev(); |
| |
| #if !defined(__ROMCC__) |
| printk(BIOS_DEBUG, "microcode: updated to revision " |
| "0x%x date=%04x-%02x-%02x\n", new_rev, |
| m->date & 0xffff, (m->date >> 24) & 0xff, |
| (m->date >> 16) & 0xff); |
| #endif |
| break; |
| } |
| |
| if (m->total_size) { |
| c += m->total_size; |
| } else { |
| #if !defined(__ROMCC__) |
| printk(BIOS_WARNING, "Microcode has no valid size field!\n"); |
| #endif |
| c += 2048; |
| } |
| } |
| |
| #if !defined(__ROMCC__) && !defined(__PRE_RAM__) |
| spin_unlock(µcode_lock); |
| #endif |
| } |
| |
| #endif |