/* * AMD CPU Microcode Update Driver for Linux * Copyright (C) 2008 Advanced Micro Devices Inc. * * Author: Peter Oruba * * Based on work by: * Tigran Aivazian * * This driver allows to upgrade microcode on AMD * family 0x10 and later. * * Licensed unter the terms of the GNU General Public * License version 2. See file COPYING for details. */ #include #include #include /* TODO: Fix asm/tlbflush.h breakage */ #include #include "private.h" #define pr_debug(x...) ((void)0) struct equiv_cpu_entry { uint32_t installed_cpu; uint32_t fixed_errata_mask; uint32_t fixed_errata_compare; uint16_t equiv_cpu; uint16_t reserved; }; struct microcode_patch { uint16_t year; uint8_t day; uint8_t month; uint32_t patch_id; uint8_t mc_patch_data_id[2]; 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]; }; #define UCODE_MAGIC 0x00414d44 #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 #define UCODE_UCODE_TYPE 0x00000001 struct container_equiv_table { uint32_t type; /* UCODE_EQUIV_CPU_TABLE_TYPE */ uint32_t len; struct equiv_cpu_entry eq[]; }; struct container_microcode { uint32_t type; /* UCODE_UCODE_TYPE */ uint32_t len; struct microcode_patch patch[]; }; /* * Microcode updates for different CPUs are distinguished by their * processor_rev_id in the header. This denotes the format of the internals * of the microcode engine, and is fixed for an individual CPU. * * There is a mapping from the CPU signature (CPUID.1.EAX - * family/model/stepping) to the "equivalent CPU identifier" which is * similarly fixed. In some cases, multiple different CPU signatures map to * the same equiv_id for processor lines which share identical microcode * facilities. * * This mapping can't be calculated in the general case, but is provided in * the microcode container, so the correct piece of microcode for the CPU can * be identified. We cache it the first time we encounter the correct mapping * for this system. * * Note: for now, we assume a fully homogeneous setup, meaning that there is * exactly one equiv_id we need to worry about for microcode blob * identification. This may need revisiting in due course. */ static struct { uint32_t sig; uint16_t id; } equiv __read_mostly; static void cf_check collect_cpu_info(void) { struct cpu_signature *csig = &this_cpu(cpu_sig); memset(csig, 0, sizeof(*csig)); csig->sig = cpuid_eax(1); rdmsrl(MSR_AMD_PATCHLEVEL, csig->rev); pr_debug("microcode: CPU%d collect_cpu_info: patch_id=%#x\n", smp_processor_id(), csig->rev); } static bool verify_patch_size(uint32_t patch_size) { uint32_t max_size; #define F1XH_MPB_MAX_SIZE 2048 #define F14H_MPB_MAX_SIZE 1824 #define F15H_MPB_MAX_SIZE 4096 #define F16H_MPB_MAX_SIZE 3458 #define F17H_MPB_MAX_SIZE 3200 #define F19H_MPB_MAX_SIZE 5568 switch ( boot_cpu_data.x86 ) { case 0x14: max_size = F14H_MPB_MAX_SIZE; break; case 0x15: max_size = F15H_MPB_MAX_SIZE; break; case 0x16: max_size = F16H_MPB_MAX_SIZE; break; case 0x17: max_size = F17H_MPB_MAX_SIZE; break; case 0x19: max_size = F19H_MPB_MAX_SIZE; break; default: max_size = F1XH_MPB_MAX_SIZE; break; } return patch_size <= max_size; } static bool check_final_patch_levels(const struct cpu_signature *sig) { /* * The 'final_levels' of patch ids have been obtained empirically. * Refer bug https://bugzilla.suse.com/show_bug.cgi?id=913996 * for details of the issue. The short version is that people * using certain Fam10h systems noticed system hang issues when * trying to update microcode levels beyond the patch IDs below. * From internal discussions, we gathered that OS/hypervisor * cannot reliably perform microcode updates beyond these levels * due to hardware issues. Therefore, we need to abort microcode * update process if we hit any of these levels. */ static const unsigned int final_levels[] = { 0x01000098, 0x0100009f, 0x010000af, }; unsigned int i; if ( boot_cpu_data.x86 != 0x10 ) return false; for ( i = 0; i < ARRAY_SIZE(final_levels); i++ ) if ( sig->rev == final_levels[i] ) return true; return false; } static enum microcode_match_result compare_revisions( uint32_t old_rev, uint32_t new_rev) { if ( new_rev > old_rev ) return NEW_UCODE; if ( new_rev == old_rev ) return SAME_UCODE; return OLD_UCODE; } static enum microcode_match_result microcode_fits( const struct microcode_patch *patch) { unsigned int cpu = smp_processor_id(); const struct cpu_signature *sig = &per_cpu(cpu_sig, cpu); if ( equiv.sig != sig->sig || equiv.id != patch->processor_rev_id ) return MIS_UCODE; return compare_revisions(sig->rev, patch->patch_id); } static enum microcode_match_result compare_header( const struct microcode_patch *new, const struct microcode_patch *old) { if ( new->processor_rev_id != old->processor_rev_id ) return MIS_UCODE; return compare_revisions(old->patch_id, new->patch_id); } static enum microcode_match_result cf_check compare_patch( const struct microcode_patch *new, const struct microcode_patch *old) { /* Both patches to compare are supposed to be applicable to local CPU. */ ASSERT(microcode_fits(new) != MIS_UCODE); ASSERT(microcode_fits(old) != MIS_UCODE); return compare_header(new, old); } static int cf_check apply_microcode(const struct microcode_patch *patch) { int hw_err; unsigned int cpu = smp_processor_id(); struct cpu_signature *sig = &per_cpu(cpu_sig, cpu); uint32_t rev, old_rev = sig->rev; enum microcode_match_result result = microcode_fits(patch); /* * Allow application of the same revision to pick up SMT-specific changes * even if the revision of the other SMT thread is already up-to-date. */ if ( result != NEW_UCODE && result != SAME_UCODE ) return -EINVAL; if ( check_final_patch_levels(sig) ) { printk(XENLOG_ERR "microcode: CPU%u current rev %#x unsafe to update\n", cpu, sig->rev); return -ENXIO; } hw_err = wrmsr_safe(MSR_AMD_PATCHLOADER, (unsigned long)patch); /* get patch id after patching */ rdmsrl(MSR_AMD_PATCHLEVEL, rev); sig->rev = rev; /* * Some processors leave the ucode blob mapping as UC after the update. * Flush the mapping to regain normal cacheability. */ flush_area_local(patch, FLUSH_TLB_GLOBAL | FLUSH_ORDER(0)); /* check current patch id and patch's id for match */ if ( hw_err || (rev != patch->patch_id) ) { printk(XENLOG_ERR "microcode: CPU%u update rev %#x to %#x failed, result %#x\n", cpu, old_rev, patch->patch_id, rev); return -EIO; } printk(XENLOG_WARNING "microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n", cpu, old_rev, rev, patch->year, patch->month, patch->day); return 0; } static int scan_equiv_cpu_table(const struct container_equiv_table *et) { const struct cpu_signature *sig = &this_cpu(cpu_sig); unsigned int i, nr = et->len / sizeof(et->eq[0]); /* Search the equiv_cpu_table for the current CPU. */ for ( i = 0; i < nr && et->eq[i].installed_cpu; ++i ) { if ( et->eq[i].installed_cpu != sig->sig ) continue; if ( !equiv.sig ) /* Cache details on first find. */ { equiv.sig = sig->sig; equiv.id = et->eq[i].equiv_cpu; return 0; } if ( equiv.sig != sig->sig || equiv.id != et->eq[i].equiv_cpu ) { /* * This can only occur if two equiv tables have been seen with * different mappings for the same CPU. The mapping is fixed, so * one of the tables is wrong. As we can't calculate the mapping, * we trusted the first table we saw. */ printk(XENLOG_ERR "microcode: Equiv mismatch: cpu %08x, got %04x, cached %04x\n", sig->sig, et->eq[i].equiv_cpu, equiv.id); return -EINVAL; } return 0; } /* equiv_cpu_table was fine, but nothing found for the current CPU. */ return -ESRCH; } static struct microcode_patch *cf_check cpu_request_microcode( const void *buf, size_t size, bool make_copy) { const struct microcode_patch *saved = NULL; struct microcode_patch *patch = NULL; size_t saved_size = 0; int error = 0; while ( size ) { const struct container_equiv_table *et; bool skip_ucode; if ( size < 4 || *(const uint32_t *)buf != UCODE_MAGIC ) { printk(XENLOG_ERR "microcode: Wrong microcode patch file magic\n"); error = -EINVAL; break; } /* Move over UCODE_MAGIC. */ buf += 4; size -= 4; if ( size < sizeof(*et) || (et = buf)->type != UCODE_EQUIV_CPU_TABLE_TYPE || size - sizeof(*et) < et->len || et->len % sizeof(et->eq[0]) || et->eq[(et->len / sizeof(et->eq[0])) - 1].installed_cpu ) { printk(XENLOG_ERR "microcode: Bad equivalent cpu table\n"); error = -EINVAL; break; } /* Move over the Equiv table. */ buf += sizeof(*et) + et->len; size -= sizeof(*et) + et->len; error = scan_equiv_cpu_table(et); /* -ESRCH means no applicable microcode in this container. */ if ( error && error != -ESRCH ) break; skip_ucode = error; error = 0; while ( size ) { const struct container_microcode *mc; if ( size < sizeof(*mc) || (mc = buf)->type != UCODE_UCODE_TYPE || size - sizeof(*mc) < mc->len || mc->len < sizeof(struct microcode_patch) ) { printk(XENLOG_ERR "microcode: Bad microcode data\n"); error = -EINVAL; break; } if ( skip_ucode ) goto skip; if ( !verify_patch_size(mc->len) ) { printk(XENLOG_WARNING "microcode: Bad microcode length 0x%08x for cpu 0x%04x\n", mc->len, mc->patch->processor_rev_id); /* * If the blob size sanity check fails, trust the container * length which has already been checked to be at least * plausible at this point. */ goto skip; } /* * If the new ucode covers current CPU, compare ucodes and store the * one with higher revision. */ if ( (microcode_fits(mc->patch) != MIS_UCODE) && (!saved || (compare_header(mc->patch, saved) == NEW_UCODE)) ) { saved = mc->patch; saved_size = mc->len; } /* Move over the microcode blob. */ skip: buf += sizeof(*mc) + mc->len; size -= sizeof(*mc) + mc->len; /* * Peek ahead. If we see the start of another container, we've * exhaused all microcode blobs in this container. Exit cleanly. */ if ( size >= 4 && *(const uint32_t *)buf == UCODE_MAGIC ) break; } /* * Any error means we didn't get cleanly to the end of the microcode * container. There isn't an overall length field, so we've got no * way of skipping to the next container in the stream. */ if ( error ) break; } if ( saved ) { if ( make_copy ) { patch = xmemdup_bytes(saved, saved_size); if ( !patch ) error = -ENOMEM; } else patch = (struct microcode_patch *)saved; } if ( error && !patch ) patch = ERR_PTR(error); return patch; } const struct microcode_ops __initconst_cf_clobber amd_ucode_ops = { .cpu_request_microcode = cpu_request_microcode, .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode, .compare_patch = compare_patch, };