2008-07-28 18:44:22 +02:00
/*
* AMD CPU Microcode Update Driver for Linux
* Copyright ( C ) 2008 Advanced Micro Devices Inc .
*
* Author : Peter Oruba < peter . oruba @ amd . com >
*
* Based on work by :
* Tigran Aivazian < tigran @ aivazian . fsnet . co . uk >
*
* This driver allows to upgrade microcode on AMD
* family 0x10 and 0x11 processors .
*
2008-12-16 19:08:53 +01:00
* Licensed under the terms of the GNU General Public
2008-07-28 18:44:22 +02:00
* License version 2. See file COPYING for details .
2009-03-11 11:19:46 +01:00
*/
2009-12-08 22:30:50 -08:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2009-03-11 11:19:46 +01:00
# include <linux/firmware.h>
# include <linux/pci_ids.h>
# include <linux/uaccess.h>
# include <linux/vmalloc.h>
# include <linux/kernel.h>
# include <linux/module.h>
2008-07-28 18:44:22 +02:00
# include <linux/pci.h>
# include <asm/microcode.h>
2009-03-11 11:19:46 +01:00
# include <asm/processor.h>
# include <asm/msr.h>
2008-07-28 18:44:22 +02:00
MODULE_DESCRIPTION ( " AMD Microcode Update Driver " ) ;
2008-10-17 15:30:38 +02:00
MODULE_AUTHOR ( " Peter Oruba " ) ;
2008-07-29 10:07:36 +02:00
MODULE_LICENSE ( " GPL v2 " ) ;
2008-07-28 18:44:22 +02:00
# define UCODE_MAGIC 0x00414d44
# define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
# define UCODE_UCODE_TYPE 0x00000001
2008-09-23 12:08:44 +02:00
struct equiv_cpu_entry {
2008-12-16 19:21:30 +01:00
u32 installed_cpu ;
u32 fixed_errata_mask ;
u32 fixed_errata_compare ;
u16 equiv_cpu ;
u16 res ;
} __attribute__ ( ( packed ) ) ;
2008-09-23 12:08:44 +02:00
struct microcode_header_amd {
2008-12-16 19:21:30 +01:00
u32 data_code ;
u32 patch_id ;
u16 mc_patch_data_id ;
u8 mc_patch_data_len ;
u8 init_flag ;
u32 mc_patch_data_checksum ;
u32 nb_dev_id ;
u32 sb_dev_id ;
u16 processor_rev_id ;
u8 nb_rev_id ;
u8 sb_rev_id ;
u8 bios_api_rev ;
u8 reserved1 [ 3 ] ;
u32 match_reg [ 8 ] ;
} __attribute__ ( ( packed ) ) ;
2008-09-23 12:08:44 +02:00
struct microcode_amd {
2009-03-11 11:19:46 +01:00
struct microcode_header_amd hdr ;
unsigned int mpb [ 0 ] ;
2008-09-23 12:08:44 +02:00
} ;
2011-06-15 15:34:57 +02:00
# define SECTION_HDR_SIZE 8
# define CONTAINER_HDR_SZ 12
2008-07-28 18:44:22 +02:00
2008-09-11 23:27:52 +02:00
static struct equiv_cpu_entry * equiv_cpu_table ;
2008-07-28 18:44:22 +02:00
2008-08-20 00:22:26 +02:00
static int collect_cpu_info_amd ( int cpu , struct cpu_signature * csig )
2008-07-28 18:44:22 +02:00
{
2010-01-22 21:34:56 +01:00
struct cpuinfo_x86 * c = & cpu_data ( cpu ) ;
2008-12-16 19:16:34 +01:00
u32 dummy ;
2008-07-28 18:44:22 +02:00
2010-01-22 21:34:56 +01:00
if ( c - > x86_vendor ! = X86_VENDOR_AMD | | c - > x86 < 0x10 ) {
2011-01-05 18:13:19 +01:00
pr_warning ( " CPU%d: family %d not supported \n " , cpu , c - > x86 ) ;
2010-01-22 21:34:56 +01:00
return - 1 ;
}
2011-01-05 18:13:19 +01:00
2008-12-16 19:16:34 +01:00
rdmsr ( MSR_AMD64_PATCH_LEVEL , csig - > rev , dummy ) ;
2011-01-05 18:13:19 +01:00
pr_info ( " CPU%d: patch_level=0x%08x \n " , cpu , csig - > rev ) ;
2008-08-20 00:22:26 +02:00
return 0 ;
2008-07-28 18:44:22 +02:00
}
2010-12-31 16:57:48 +01:00
static int get_matching_microcode ( int cpu , struct microcode_header_amd * mc_hdr ,
int rev )
2008-07-28 18:44:22 +02:00
{
unsigned int current_cpu_id ;
2008-12-16 19:21:30 +01:00
u16 equiv_cpu_id = 0 ;
2008-07-28 18:44:22 +02:00
unsigned int i = 0 ;
2008-09-11 23:27:52 +02:00
BUG_ON ( equiv_cpu_table = = NULL ) ;
2008-07-28 18:44:22 +02:00
current_cpu_id = cpuid_eax ( 0x00000001 ) ;
while ( equiv_cpu_table [ i ] . installed_cpu ! = 0 ) {
if ( current_cpu_id = = equiv_cpu_table [ i ] . installed_cpu ) {
2008-12-16 19:21:30 +01:00
equiv_cpu_id = equiv_cpu_table [ i ] . equiv_cpu ;
2008-07-28 18:44:22 +02:00
break ;
}
i + + ;
}
2009-11-10 12:08:25 +01:00
if ( ! equiv_cpu_id )
2008-07-28 18:44:22 +02:00
return 0 ;
2010-12-31 16:57:48 +01:00
if ( mc_hdr - > processor_rev_id ! = equiv_cpu_id )
2008-07-28 18:44:22 +02:00
return 0 ;
2008-12-16 19:20:21 +01:00
/* ucode might be chipset specific -- currently we don't support this */
2010-12-31 16:57:48 +01:00
if ( mc_hdr - > nb_dev_id | | mc_hdr - > sb_dev_id ) {
2011-01-05 18:13:19 +01:00
pr_err ( " CPU%d: chipset specific code not yet supported \n " ,
2009-12-08 22:30:50 -08:00
cpu ) ;
2008-12-16 19:20:21 +01:00
return 0 ;
2008-07-28 18:44:22 +02:00
}
2010-12-31 16:57:48 +01:00
if ( mc_hdr - > patch_id < = rev )
2008-07-28 18:44:22 +02:00
return 0 ;
return 1 ;
}
2009-05-11 23:48:27 +02:00
static int apply_microcode_amd ( int cpu )
2008-07-28 18:44:22 +02:00
{
2008-12-16 19:16:34 +01:00
u32 rev , dummy ;
2008-07-28 18:44:22 +02:00
int cpu_num = raw_smp_processor_id ( ) ;
struct ucode_cpu_info * uci = ucode_cpu_info + cpu_num ;
2008-09-23 12:08:44 +02:00
struct microcode_amd * mc_amd = uci - > mc ;
2008-07-28 18:44:22 +02:00
/* We should bind the task to the CPU */
BUG_ON ( cpu_num ! = cpu ) ;
2008-09-23 12:08:44 +02:00
if ( mc_amd = = NULL )
2009-05-11 23:48:27 +02:00
return 0 ;
2008-07-28 18:44:22 +02:00
2008-12-19 01:36:14 +01:00
wrmsrl ( MSR_AMD64_PATCH_LOADER , ( u64 ) ( long ) & mc_amd - > hdr . data_code ) ;
2008-07-28 18:44:22 +02:00
/* get patch id after patching */
2008-12-16 19:16:34 +01:00
rdmsr ( MSR_AMD64_PATCH_LEVEL , rev , dummy ) ;
2008-07-28 18:44:22 +02:00
/* check current patch id and patch's id for match */
2008-09-23 12:08:44 +02:00
if ( rev ! = mc_amd - > hdr . patch_id ) {
2011-01-05 18:13:19 +01:00
pr_err ( " CPU%d: update failed for patch_level=0x%08x \n " ,
2009-12-08 22:30:50 -08:00
cpu , mc_amd - > hdr . patch_id ) ;
2009-05-11 23:48:27 +02:00
return - 1 ;
2008-07-28 18:44:22 +02:00
}
2011-01-05 18:13:19 +01:00
pr_info ( " CPU%d: new patch_level=0x%08x \n " , cpu , rev ) ;
2008-08-20 00:22:26 +02:00
uci - > cpu_sig . rev = rev ;
2009-05-11 23:48:27 +02:00
return 0 ;
2008-07-28 18:44:22 +02:00
}
2011-02-10 12:19:47 +01:00
static unsigned int verify_ucode_size ( int cpu , const u8 * buf , unsigned int size )
{
struct cpuinfo_x86 * c = & cpu_data ( cpu ) ;
2011-02-22 18:41:48 +01:00
u32 max_size , actual_size ;
2011-02-10 12:19:47 +01:00
# define F1XH_MPB_MAX_SIZE 2048
# define F14H_MPB_MAX_SIZE 1824
# define F15H_MPB_MAX_SIZE 4096
switch ( c - > x86 ) {
case 0x14 :
max_size = F14H_MPB_MAX_SIZE ;
break ;
case 0x15 :
max_size = F15H_MPB_MAX_SIZE ;
break ;
default :
max_size = F1XH_MPB_MAX_SIZE ;
break ;
}
2011-02-22 18:41:48 +01:00
actual_size = * ( u32 * ) ( buf + 4 ) ;
2011-02-10 12:19:47 +01:00
2011-06-15 15:34:57 +02:00
if ( actual_size + SECTION_HDR_SIZE > size | | actual_size > max_size ) {
2011-02-10 12:19:47 +01:00
pr_err ( " section size mismatch \n " ) ;
return 0 ;
}
return actual_size ;
}
2010-12-31 16:57:48 +01:00
static struct microcode_header_amd *
2011-02-10 12:19:47 +01:00
get_next_ucode ( int cpu , const u8 * buf , unsigned int size , unsigned int * mc_size )
2008-07-28 18:44:22 +02:00
{
2011-02-10 12:19:47 +01:00
struct microcode_header_amd * mc = NULL ;
unsigned int actual_size = 0 ;
2008-07-28 18:44:22 +02:00
2011-02-22 18:41:48 +01:00
if ( * ( u32 * ) buf ! = UCODE_UCODE_TYPE ) {
2011-01-05 18:13:19 +01:00
pr_err ( " invalid type field in container file section header \n " ) ;
2011-02-10 12:19:47 +01:00
goto out ;
2008-07-28 18:44:22 +02:00
}
2011-02-10 12:19:47 +01:00
actual_size = verify_ucode_size ( cpu , buf , size ) ;
if ( ! actual_size )
goto out ;
2008-07-28 18:44:22 +02:00
2011-02-10 12:19:47 +01:00
mc = vzalloc ( actual_size ) ;
2010-11-01 22:44:34 +01:00
if ( ! mc )
2011-02-10 12:19:47 +01:00
goto out ;
2010-11-01 22:44:34 +01:00
2011-06-15 15:34:57 +02:00
get_ucode_data ( mc , buf + SECTION_HDR_SIZE , actual_size ) ;
* mc_size = actual_size + SECTION_HDR_SIZE ;
2010-11-01 22:44:34 +01:00
2011-02-10 12:19:47 +01:00
out :
2008-09-11 23:27:52 +02:00
return mc ;
2008-07-28 18:44:22 +02:00
}
2008-12-16 19:14:05 +01:00
static int install_equiv_cpu_table ( const u8 * buf )
2008-07-28 18:44:22 +02:00
{
2010-12-30 22:10:12 +01:00
unsigned int * ibuf = ( unsigned int * ) buf ;
unsigned int type = ibuf [ 1 ] ;
unsigned int size = ibuf [ 2 ] ;
2008-07-28 18:44:22 +02:00
2010-12-30 22:10:12 +01:00
if ( type ! = UCODE_EQUIV_CPU_TABLE_TYPE | | ! size ) {
2011-01-05 18:13:19 +01:00
pr_err ( " empty section/ "
" invalid type field in container file section header \n " ) ;
2010-12-30 22:10:12 +01:00
return - EINVAL ;
2008-07-28 18:44:22 +02:00
}
2010-11-09 00:08:11 +01:00
equiv_cpu_table = vmalloc ( size ) ;
2008-07-28 18:44:22 +02:00
if ( ! equiv_cpu_table ) {
2009-12-08 22:30:50 -08:00
pr_err ( " failed to allocate equivalent CPU table \n " ) ;
2010-12-30 22:10:12 +01:00
return - ENOMEM ;
2008-07-28 18:44:22 +02:00
}
2011-06-15 15:34:57 +02:00
get_ucode_data ( equiv_cpu_table , buf + CONTAINER_HDR_SZ , size ) ;
2008-07-28 18:44:22 +02:00
2011-06-15 15:34:57 +02:00
/* add header length */
return size + CONTAINER_HDR_SZ ;
2008-07-28 18:44:22 +02:00
}
2008-09-11 23:27:52 +02:00
static void free_equiv_cpu_table ( void )
2008-07-28 18:44:22 +02:00
{
2009-06-07 22:30:36 +08:00
vfree ( equiv_cpu_table ) ;
equiv_cpu_table = NULL ;
2008-09-11 23:27:52 +02:00
}
2008-07-28 18:44:22 +02:00
2009-05-11 23:48:27 +02:00
static enum ucode_state
generic_load_microcode ( int cpu , const u8 * data , size_t size )
2008-09-11 23:27:52 +02:00
{
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
2010-12-31 16:57:48 +01:00
struct microcode_header_amd * mc_hdr = NULL ;
unsigned int mc_size , leftover ;
2011-02-18 12:17:16 +03:00
int offset ;
2008-12-16 19:13:00 +01:00
const u8 * ucode_ptr = data ;
void * new_mc = NULL ;
2011-01-05 18:13:19 +01:00
unsigned int new_rev = uci - > cpu_sig . rev ;
2009-05-11 23:48:27 +02:00
enum ucode_state state = UCODE_OK ;
2008-07-28 18:44:22 +02:00
2008-12-16 19:14:05 +01:00
offset = install_equiv_cpu_table ( ucode_ptr ) ;
2010-12-30 22:10:12 +01:00
if ( offset < 0 ) {
2009-12-08 22:30:50 -08:00
pr_err ( " failed to create equivalent cpu table \n " ) ;
2009-05-11 23:48:27 +02:00
return UCODE_ERROR ;
2008-07-28 18:44:22 +02:00
}
2008-09-11 23:27:52 +02:00
ucode_ptr + = offset ;
leftover = size - offset ;
while ( leftover ) {
2011-02-10 12:19:47 +01:00
mc_hdr = get_next_ucode ( cpu , ucode_ptr , leftover , & mc_size ) ;
2010-12-31 16:57:48 +01:00
if ( ! mc_hdr )
2008-07-28 18:44:22 +02:00
break ;
2008-09-11 23:27:52 +02:00
2010-12-31 16:57:48 +01:00
if ( get_matching_microcode ( cpu , mc_hdr , new_rev ) ) {
2009-06-07 22:30:36 +08:00
vfree ( new_mc ) ;
2010-12-31 16:57:48 +01:00
new_rev = mc_hdr - > patch_id ;
new_mc = mc_hdr ;
2008-12-16 19:11:23 +01:00
} else
2010-12-31 16:57:48 +01:00
vfree ( mc_hdr ) ;
2008-09-11 23:27:52 +02:00
ucode_ptr + = mc_size ;
leftover - = mc_size ;
2008-07-28 18:44:22 +02:00
}
2008-09-11 23:27:52 +02:00
2010-12-31 16:57:48 +01:00
if ( ! new_mc ) {
2009-05-11 23:48:27 +02:00
state = UCODE_NFOUND ;
2010-12-31 16:57:48 +01:00
goto free_table ;
}
if ( ! leftover ) {
vfree ( uci - > mc ) ;
uci - > mc = new_mc ;
2011-01-05 18:13:19 +01:00
pr_debug ( " CPU%d update ucode (0x%08x -> 0x%08x) \n " ,
cpu , uci - > cpu_sig . rev , new_rev ) ;
2010-12-31 16:57:48 +01:00
} else {
vfree ( new_mc ) ;
state = UCODE_ERROR ;
}
2008-09-11 23:27:52 +02:00
2010-12-31 16:57:48 +01:00
free_table :
2008-09-11 23:27:52 +02:00
free_equiv_cpu_table ( ) ;
2009-05-11 23:48:27 +02:00
return state ;
2008-09-11 23:27:52 +02:00
}
2010-12-30 21:06:01 +01:00
static enum ucode_state request_microcode_amd ( int cpu , struct device * device )
2008-09-11 23:27:52 +02:00
{
2010-01-22 21:34:56 +01:00
const char * fw_name = " amd-ucode/microcode_amd.bin " ;
2010-12-30 21:06:01 +01:00
const struct firmware * fw ;
enum ucode_state ret = UCODE_NFOUND ;
2008-09-11 23:27:52 +02:00
2010-12-30 21:06:01 +01:00
if ( request_firmware ( & fw , fw_name , device ) ) {
2011-01-05 18:13:19 +01:00
pr_err ( " failed to load file %s \n " , fw_name ) ;
2010-12-30 21:06:01 +01:00
goto out ;
2010-01-22 21:34:56 +01:00
}
2008-09-11 23:27:52 +02:00
2010-12-30 21:06:01 +01:00
ret = UCODE_ERROR ;
if ( * ( u32 * ) fw - > data ! = UCODE_MAGIC ) {
2011-01-05 18:13:19 +01:00
pr_err ( " invalid magic value (0x%08x) \n " , * ( u32 * ) fw - > data ) ;
2010-12-30 21:06:01 +01:00
goto fw_release ;
2009-10-29 14:45:52 +01:00
}
2010-12-30 21:06:01 +01:00
ret = generic_load_microcode ( cpu , fw - > data , fw - > size ) ;
2008-09-11 23:27:52 +02:00
2010-12-30 21:06:01 +01:00
fw_release :
release_firmware ( fw ) ;
2010-01-22 21:34:56 +01:00
2010-12-30 21:06:01 +01:00
out :
2008-09-11 23:27:52 +02:00
return ret ;
}
2009-05-11 23:48:27 +02:00
static enum ucode_state
request_microcode_user ( int cpu , const void __user * buf , size_t size )
2008-09-11 23:27:52 +02:00
{
2009-12-08 22:30:50 -08:00
pr_info ( " AMD microcode update via /dev/cpu/microcode not supported \n " ) ;
2009-05-11 23:48:27 +02:00
return UCODE_ERROR ;
2008-07-28 18:44:22 +02:00
}
static void microcode_fini_cpu_amd ( int cpu )
{
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
2008-09-23 12:08:44 +02:00
vfree ( uci - > mc ) ;
uci - > mc = NULL ;
2008-07-28 18:44:22 +02:00
}
static struct microcode_ops microcode_amd_ops = {
2008-09-11 23:27:52 +02:00
. request_microcode_user = request_microcode_user ,
2010-12-30 21:06:01 +01:00
. request_microcode_fw = request_microcode_amd ,
2008-07-28 18:44:22 +02:00
. collect_cpu_info = collect_cpu_info_amd ,
. apply_microcode = apply_microcode_amd ,
. microcode_fini_cpu = microcode_fini_cpu_amd ,
} ;
2008-09-23 12:08:44 +02:00
struct microcode_ops * __init init_amd_microcode ( void )
2008-07-28 18:44:22 +02:00
{
2008-09-23 12:08:44 +02:00
return & microcode_amd_ops ;
2008-07-28 18:44:22 +02:00
}