Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
Vadim Bendebury | 537b4e0 | 2012-06-19 12:56:57 -0700 | [diff] [blame] | 4 | * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 5 | * Copyright (C) 2000 Ronald G. Minnich |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; version 2 of the License. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 15 | */ |
| 16 | |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 17 | /* Microcode update for Intel PIII and later CPUs */ |
| 18 | |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 19 | #include <stdint.h> |
Vladimir Serbinenko | 2c88cc0 | 2013-03-30 12:04:23 +0100 | [diff] [blame] | 20 | #include <stddef.h> |
Stefan Reinauer | 252111d | 2012-05-01 16:53:22 -0700 | [diff] [blame] | 21 | #if !defined(__ROMCC__) |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 22 | #include <console/console.h> |
Stefan Reinauer | 252111d | 2012-05-01 16:53:22 -0700 | [diff] [blame] | 23 | #endif |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 24 | #include <cpu/cpu.h> |
| 25 | #include <cpu/x86/msr.h> |
| 26 | #include <cpu/intel/microcode.h> |
Rizwan Qureshi | 30b755b | 2015-07-23 22:31:51 +0530 | [diff] [blame] | 27 | #include <rules.h> |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 28 | |
Alexandru Gagniuc | 2c38f50 | 2013-12-06 23:14:54 -0600 | [diff] [blame] | 29 | #if !defined(__PRE_RAM__) |
Vadim Bendebury | 537b4e0 | 2012-06-19 12:56:57 -0700 | [diff] [blame] | 30 | #include <cbfs.h> |
Stefan Reinauer | a42e2f4 | 2012-10-15 13:18:06 -0700 | [diff] [blame] | 31 | #include <smp/spinlock.h> |
| 32 | DECLARE_SPIN_LOCK(microcode_lock) |
Alexandru Gagniuc | 2c38f50 | 2013-12-06 23:14:54 -0600 | [diff] [blame] | 33 | #else |
| 34 | #include <arch/cbfs.h> |
Vadim Bendebury | 537b4e0 | 2012-06-19 12:56:57 -0700 | [diff] [blame] | 35 | #endif |
| 36 | |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 37 | struct microcode { |
Stefan Reinauer | c8f8a6c | 2010-05-26 12:53:43 +0000 | [diff] [blame] | 38 | u32 hdrver; /* Header Version */ |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 39 | u32 rev; /* Update Revision */ |
| 40 | u32 date; /* Date */ |
| 41 | u32 sig; /* Processor Signature */ |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 42 | |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 43 | u32 cksum; /* Checksum */ |
| 44 | u32 ldrver; /* Loader Revision */ |
| 45 | u32 pf; /* Processor Flags */ |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 46 | |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 47 | u32 data_size; /* Data Size */ |
| 48 | u32 total_size; /* Total Size */ |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 49 | |
Stefan Reinauer | c8f8a6c | 2010-05-26 12:53:43 +0000 | [diff] [blame] | 50 | u32 reserved[3]; |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 51 | }; |
| 52 | |
Stefan Reinauer | c8f8a6c | 2010-05-26 12:53:43 +0000 | [diff] [blame] | 53 | static inline u32 read_microcode_rev(void) |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 54 | { |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 55 | /* Some Intel CPUs can be very finicky about the |
Stefan Reinauer | c8f8a6c | 2010-05-26 12:53:43 +0000 | [diff] [blame] | 56 | * CPUID sequence used. So this is implemented in |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 57 | * assembly so that it works reliably. |
| 58 | */ |
| 59 | msr_t msr; |
Stefan Reinauer | 3f8989e | 2012-04-25 22:58:23 +0200 | [diff] [blame] | 60 | asm volatile ( |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 61 | "xorl %%eax, %%eax\n\t" |
| 62 | "xorl %%edx, %%edx\n\t" |
| 63 | "movl $0x8b, %%ecx\n\t" |
| 64 | "wrmsr\n\t" |
| 65 | "movl $0x01, %%eax\n\t" |
| 66 | "cpuid\n\t" |
| 67 | "movl $0x08b, %%ecx\n\t" |
| 68 | "rdmsr \n\t" |
| 69 | : /* outputs */ |
| 70 | "=a" (msr.lo), "=d" (msr.hi) |
| 71 | : /* inputs */ |
| 72 | : /* trashed */ |
Stefan Reinauer | 3b5a9ed | 2012-05-02 16:41:55 -0700 | [diff] [blame] | 73 | "ebx", "ecx" |
Stefan Reinauer | c8f8a6c | 2010-05-26 12:53:43 +0000 | [diff] [blame] | 74 | ); |
Eric Biederman | fcd5ace | 2004-10-14 19:29:29 +0000 | [diff] [blame] | 75 | return msr.hi; |
| 76 | } |
| 77 | |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 78 | #define MICROCODE_CBFS_FILE "cpu_microcode_blob.bin" |
| 79 | |
| 80 | void intel_microcode_load_unlocked(const void *microcode_patch) |
| 81 | { |
| 82 | u32 current_rev; |
| 83 | msr_t msr; |
| 84 | const struct microcode *m = microcode_patch; |
| 85 | |
| 86 | if (!m) |
| 87 | return; |
| 88 | |
| 89 | current_rev = read_microcode_rev(); |
| 90 | |
| 91 | /* No use loading the same revision. */ |
| 92 | if (current_rev == m->rev) |
| 93 | return; |
| 94 | |
Rizwan Qureshi | 30b755b | 2015-07-23 22:31:51 +0530 | [diff] [blame] | 95 | #if ENV_RAMSTAGE |
| 96 | /*SoC specific check to update microcode*/ |
| 97 | if (soc_skip_ucode_update(current_rev, m->rev)) { |
| 98 | printk(BIOS_DEBUG, "Skip microcode update\n"); |
| 99 | return; |
| 100 | } |
| 101 | #endif |
| 102 | |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 103 | msr.lo = (unsigned long)m + sizeof(struct microcode); |
| 104 | msr.hi = 0; |
| 105 | wrmsr(0x79, msr); |
| 106 | |
| 107 | #if !defined(__ROMCC__) |
| 108 | printk(BIOS_DEBUG, "microcode: updated to revision " |
| 109 | "0x%x date=%04x-%02x-%02x\n", read_microcode_rev(), |
| 110 | m->date & 0xffff, (m->date >> 24) & 0xff, |
| 111 | (m->date >> 16) & 0xff); |
Vadim Bendebury | 537b4e0 | 2012-06-19 12:56:57 -0700 | [diff] [blame] | 112 | #endif |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | const void *intel_microcode_find(void) |
| 116 | { |
Aaron Durbin | e676767 | 2014-01-27 15:52:47 -0600 | [diff] [blame] | 117 | const struct microcode *ucode_updates; |
Aaron Durbin | 899d13d | 2015-05-15 23:39:23 -0500 | [diff] [blame] | 118 | size_t microcode_len; |
| 119 | u32 eax; |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 120 | u32 pf, rev, sig, update_size; |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 121 | unsigned int x86_model, x86_family; |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 122 | msr_t msr; |
| 123 | |
| 124 | #ifdef __PRE_RAM__ |
Aaron Durbin | 899d13d | 2015-05-15 23:39:23 -0500 | [diff] [blame] | 125 | struct cbfs_file *microcode_file; |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 126 | |
Aaron Durbin | 899d13d | 2015-05-15 23:39:23 -0500 | [diff] [blame] | 127 | microcode_file = walkcbfs_head((char *) MICROCODE_CBFS_FILE); |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 128 | if (!microcode_file) |
Vladimir Serbinenko | 2c88cc0 | 2013-03-30 12:04:23 +0100 | [diff] [blame] | 129 | return NULL; |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 130 | |
Aaron Durbin | e676767 | 2014-01-27 15:52:47 -0600 | [diff] [blame] | 131 | ucode_updates = CBFS_SUBHEADER(microcode_file); |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 132 | microcode_len = ntohl(microcode_file->len); |
Aaron Durbin | 899d13d | 2015-05-15 23:39:23 -0500 | [diff] [blame] | 133 | #else |
| 134 | ucode_updates = cbfs_boot_map_with_leak(MICROCODE_CBFS_FILE, |
| 135 | CBFS_TYPE_MICROCODE, |
| 136 | µcode_len); |
| 137 | if (ucode_updates == NULL) |
| 138 | return NULL; |
| 139 | #endif |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 140 | |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 141 | /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ |
| 142 | msr.lo = 0; |
| 143 | msr.hi = 0; |
| 144 | wrmsr(0x8B, msr); |
| 145 | eax = cpuid_eax(1); |
| 146 | msr = rdmsr(0x8B); |
| 147 | rev = msr.hi; |
| 148 | x86_model = (eax >>4) & 0x0f; |
| 149 | x86_family = (eax >>8) & 0x0f; |
| 150 | sig = eax; |
| 151 | |
| 152 | pf = 0; |
| 153 | if ((x86_model >= 5)||(x86_family>6)) { |
| 154 | msr = rdmsr(0x17); |
| 155 | pf = 1 << ((msr.hi >> 18) & 7); |
| 156 | } |
| 157 | #if !defined(__ROMCC__) |
| 158 | /* If this code is compiled with ROMCC we're probably in |
| 159 | * the bootblock and don't have console output yet. |
| 160 | */ |
| 161 | printk(BIOS_DEBUG, "microcode: sig=0x%x pf=0x%x revision=0x%x\n", |
| 162 | sig, pf, rev); |
| 163 | #endif |
| 164 | |
Aaron Durbin | e676767 | 2014-01-27 15:52:47 -0600 | [diff] [blame] | 165 | while (microcode_len >= sizeof(*ucode_updates)) { |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 166 | /* Newer microcode updates include a size field, whereas older |
| 167 | * containers set it at 0 and are exactly 2048 bytes long */ |
Aaron Durbin | e676767 | 2014-01-27 15:52:47 -0600 | [diff] [blame] | 168 | if (ucode_updates->total_size) { |
| 169 | update_size = ucode_updates->total_size; |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 170 | } else { |
| 171 | #if !defined(__ROMCC__) |
Alexandru Gagniuc | 2c38f50 | 2013-12-06 23:14:54 -0600 | [diff] [blame] | 172 | printk(BIOS_SPEW, "Microcode size field is 0\n"); |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 173 | #endif |
| 174 | update_size = 2048; |
| 175 | } |
| 176 | |
| 177 | /* Checkpoint 1: The microcode update falls within CBFS */ |
| 178 | if(update_size > microcode_len) { |
| 179 | #if !defined(__ROMCC__) |
| 180 | printk(BIOS_WARNING, "Microcode header corrupted!\n"); |
| 181 | #endif |
| 182 | break; |
| 183 | } |
| 184 | |
Aaron Durbin | e676767 | 2014-01-27 15:52:47 -0600 | [diff] [blame] | 185 | if ((ucode_updates->sig == sig) && (ucode_updates->pf & pf)) |
| 186 | return ucode_updates; |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 187 | |
Aaron Durbin | e676767 | 2014-01-27 15:52:47 -0600 | [diff] [blame] | 188 | ucode_updates = (void *)((char *)ucode_updates + update_size); |
Alexandru Gagniuc | b1ae030 | 2013-12-08 16:30:07 -0600 | [diff] [blame] | 189 | microcode_len -= update_size; |
Aaron Durbin | 98ffb42 | 2013-01-15 15:15:32 -0600 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | /* ROMCC doesn't like NULL. */ |
| 193 | return (void *)0; |
| 194 | } |
| 195 | |
| 196 | void intel_update_microcode_from_cbfs(void) |
| 197 | { |
| 198 | const void *patch = intel_microcode_find(); |
| 199 | |
| 200 | #if !defined(__ROMCC__) && !defined(__PRE_RAM__) |
| 201 | spin_lock(µcode_lock); |
| 202 | #endif |
| 203 | |
| 204 | intel_microcode_load_unlocked(patch); |
| 205 | |
| 206 | #if !defined(__ROMCC__) && !defined(__PRE_RAM__) |
| 207 | spin_unlock(µcode_lock); |
| 208 | #endif |
| 209 | } |
Rizwan Qureshi | 30b755b | 2015-07-23 22:31:51 +0530 | [diff] [blame] | 210 | |
| 211 | #if ENV_RAMSTAGE |
| 212 | __attribute__((weak)) int soc_skip_ucode_update(u32 currrent_patch_id, u32 new_patch_id) |
| 213 | { |
| 214 | return 0; |
| 215 | } |
| 216 | #endif |