AMD Microcode Hack & Patch for Linux

The whole story is here on RealWorldTech.com; the worst thing about this is the security risk:



Interestingly, this does have serious implications for system security. If one is able to get root access on a machine even once, it is hypothetically possible to install a microcode update specifically to help compromise security from userspace at a later time. Such an update could be flashed into the BIOS to make it persistent across reboots.


For instance, by patching the appropriate microcode lines, it may be possible to catch an opcode that would normally be illegal, and instead handle it by tricking the TLB into thinking we're in kernel mode when in fact the attacker has only compromised a userspace process. From there, the attacker could control the entire machine, all without altering a single bit of "software".


Here is a summary and the code:


Summary

~~~~~~~


This document details the procedure for performing microcode updates on the AMD K8 processors. It also gives background information on the K8 microcode design and provides information on altering the microcode and loading the altered update for those who are interested in microcode hacking.




Source code is included for a simple Linux microcode update driver for those who want to update their K8's microcode without waiting for the motherboard vendor to add it to the BIOS. The latest microcode update blocks are included in the driver.




Background

~~~~~~~~~~

Modern x86 microprocessors from Intel and AMD contain a feature known as "microcode update", or as the vendors prefer to call it, "BIOS update". Essentially the processor can reconfigure parts of its own hardware to fix bugs ("errata") in the silicon that would normally require a recall.




This is done by loading a block of "patch data" created by the CPU vendor into the processor using special control registers. Microcode updates essentially override hardware features with sequences of the internal RISC-like micro-ops (uops) actually executed by the processor. They can also replace the implementations of microcoded instructions already handled by hard-wired sequences in an on-die microcode ROM.


AMD's U.S. Patent 6438664 ("Microcode patch device and method for patching microcode using match registers and patch routines") goes into substantial detail on this.



/*
* AMD K8 (Athlon 64 / Opteron) Microcode Update Driver
*
* This code has been tested on a 64-bit Linux 2.6 kernel
* running on an Athlon 64. It has not been tested on
* other K8 cores. It may or may not work on 32-bit
* Linux kernels (but it should).
*
* This program is free software; it is licensed under
* the GNU General Public License, version 2.
*
* Version: 2004 Jul 20
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/mm.h>

#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/io.h>

MODULE_DESCRIPTION("AMD K8 microcode update driver");
MODULE_LICENSE("GPL");

typedef unsigned int W32;
typedef unsigned long long W64;

// Actual update data starts at offset 64 (0x40):
unsigned char k8_ucode_0f48[]={
0x04,0x20,0x06,0x02,0x39,0x00,0x00,0x00,
0x00,0x80,0x20,0x00,0xfd,0x1c,0x33,0x3e,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x48,0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,
0x44,0x06,0x00,0x00,0x40,0x01,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xfb,0xff,0x00,0xc3,0x35,0x90,0xfd,
0xf8,0x7b,0xfe,0xa9,0x0f,0xff,0x00,0x3f,
0xfe,0xdf,0xff,0xc7,0x1e,0xbc,0x03,0x3c,
0x80,0x7a,0xd5,0x00,0x14,0xfe,0xff,0xff,
0xfe,0xe1,0x1f,0xeb,0x4c,0xf6,0x3c,0xff,
0x9f,0x87,0x7f,0x80,0x27,0x60,0x26,0xf0,
0x74,0x1d,0xfe,0x18,0x00,0x20,0xbd,0x6a,
0xcf,0xfb,0xff,0xe7,0xf0,0xf3,0xf0,0x0f,
0x3f,0xee,0xff,0x9f,0x40,0xe4,0xc3,0x1b,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0x35,0x00,0xe0,0xeb,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x30,0x1a,0x00,0x78,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0x81,0x7f,0x00,0xc3,0x3f,0x80,0x7f,
0xfc,0x7f,0x0f,0x00,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xc0,0x3f,
0xbf,0xe1,0x1f,0xc0,0x00,0xfe,0xbf,0x07,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x1f,0xc0,0x7f,0xe0,0xe0,0xdf,0xf0,0x0f,
0x03,0x00,0xff,0xdf,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xef,0x01,0x80,0xff,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0xff,0xf7,0x00,0xc0,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0xff,0x7b,0x00,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x00,0xf0,0xff,0x3d,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0x1e,0x00,0xf8,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x7f,0x0f,0x00,0xfc,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0xbf,0x07,0x00,
0xfc,0x07,0xfe,0x01,0x0d,0xff,0x00,0xfe,
0xf0,0x1f,0xf8,0x07,0x37,0xfc,0x03,0xf8,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0xdf,0x03,0x00,0xfe,0x03,0xff,
0xff,0x86,0x7f,0x00,0x03,0xf8,0x0f,0xfc,
0xfc,0x1b,0xfe,0x01,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x01,0x80,0xff,0xef,
0x7f,0x00,0xff,0x81,0x80,0x7f,0xc3,0x3f,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xf7,0x00,0xc0,0xff,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0xff,0x7b,0x00,0xe0,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0x81,0x7f,0x00,0xc3,0x3f,0x80,0x7f,
0xfc,0x07,0xfe,0x01,0x0d,0xff,0x00,0xfe,
0xf0,0xff,0x3d,0x00,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xc0,0x3f,
0xbf,0xe1,0x1f,0xc0,0x00,0xfe,0x03,0xff,
0xff,0x86,0x7f,0x00,0x00,0xf8,0xff,0x1e,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x1f,0xc0,0x7f,0xe0,0xe0,0xdf,0xf0,0x0f,
0x7f,0x00,0xff,0x81,0x80,0x7f,0xc3,0x3f,
0x0f,0x00,0xfc,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0xbf,0x07,0x00,0xfe,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0xdf,0x03,0x00,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xef,0x01,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x00,0xc0,0xff,0xf7,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0x7b,0x00,0xe0,0xff,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0xff,0x3d,0x00,0xf0,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0xff,0x1e,0x00,
0xf0,0x1f,0xf8,0x07,0x37,0xfc,0x03,0xf8,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x00,0xfc,0x7f,0x0f,0x03,0xf8,0x0f,0xfc,
0xfc,0x1b,0xfe,0x01,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0x07,0x00,0xfe,0xbf,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0xdf,0x03,0x00,0xff,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xef,0x01,0x80,
};

unsigned char k8_ucode_0f4a[]={
0x04,0x20,0x06,0x02,0x3a,0x00,0x00,0x00,
0x00,0x80,0x20,0x00,0xfd,0xc6,0x88,0x3e,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x4a,0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,
0x44,0x06,0x00,0x00,0x40,0x01,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xfb,0xff,0x00,0xc3,0x35,0x90,0xfd,
0xf8,0x7b,0xfe,0xa9,0x0f,0xff,0x00,0x3f,
0xfe,0xdf,0xff,0xc7,0x1e,0xbc,0x03,0x3c,
0x80,0x24,0xd6,0x00,0x14,0xfe,0xff,0xff,
0xfe,0xe1,0x1f,0xeb,0x4c,0xf6,0x3c,0xff,
0x9f,0x87,0x7f,0x80,0x27,0x60,0x26,0xf0,
0x74,0x1d,0xfe,0x18,0x00,0x20,0x12,0x6b,
0xcf,0xfb,0xff,0xe7,0xf0,0xf3,0xf0,0x0f,
0x3f,0xee,0xff,0x9f,0x40,0xe4,0xc3,0x1b,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0x35,0x00,0xe0,0xeb,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x30,0x1a,0x00,0x78,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0x81,0x7f,0x00,0xc3,0x3f,0x80,0x7f,
0xfc,0x7f,0x0f,0x00,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xc0,0x3f,
0xbf,0xe1,0x1f,0xc0,0x00,0xfe,0xbf,0x07,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x1f,0xc0,0x7f,0xe0,0xe0,0xdf,0xf0,0x0f,
0x03,0x00,0xff,0xdf,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xef,0x01,0x80,0xff,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0xff,0xf7,0x00,0xc0,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0xff,0x7b,0x00,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x00,0xf0,0xff,0x3d,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0x1e,0x00,0xf8,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x7f,0x0f,0x00,0xfc,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0xbf,0x07,0x00,
0xfc,0x07,0xfe,0x01,0x0d,0xff,0x00,0xfe,
0xf0,0x1f,0xf8,0x07,0x37,0xfc,0x03,0xf8,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0xdf,0x03,0x00,0xfe,0x03,0xff,
0xff,0x86,0x7f,0x00,0x03,0xf8,0x0f,0xfc,
0xfc,0x1b,0xfe,0x01,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x01,0x80,0xff,0xef,
0x7f,0x00,0xff,0x81,0x80,0x7f,0xc3,0x3f,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xf7,0x00,0xc0,0xff,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0xff,0x7b,0x00,0xe0,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0x81,0x7f,0x00,0xc3,0x3f,0x80,0x7f,
0xfc,0x07,0xfe,0x01,0x0d,0xff,0x00,0xfe,
0xf0,0xff,0x3d,0x00,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xc0,0x3f,
0xbf,0xe1,0x1f,0xc0,0x00,0xfe,0x03,0xff,
0xff,0x86,0x7f,0x00,0x00,0xf8,0xff,0x1e,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x1f,0xc0,0x7f,0xe0,0xe0,0xdf,0xf0,0x0f,
0x7f,0x00,0xff,0x81,0x80,0x7f,0xc3,0x3f,
0x0f,0x00,0xfc,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0xbf,0x07,0x00,0xfe,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0xdf,0x03,0x00,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xef,0x01,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x00,0xc0,0xff,0xf7,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0x7b,0x00,0xe0,0xff,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0xff,0x3d,0x00,0xf0,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0xff,0x1e,0x00,
0xf0,0x1f,0xf8,0x07,0x37,0xfc,0x03,0xf8,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x00,0xfc,0x7f,0x0f,0x03,0xf8,0x0f,0xfc,
0xfc,0x1b,0xfe,0x01,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0x07,0x00,0xfe,0xbf,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0xdf,0x03,0x00,0xff,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xef,0x01,0x80,
};

unsigned char k8_ucode_0f50[] = {
0x04,0x20,0x25,0x02,0x41,0x00,0x00,0x00,
0x00,0x80,0x20,0x00,0xfd,0x6a,0x5a,0x3e,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x50,0x01,0x00,0x00,0x00,0xaa,0xaa,0xaa,
0x44,0x06,0x00,0x00,0x40,0x01,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xfb,0xff,0x00,0xc3,0x35,0x90,0xfd,
0xf8,0x7b,0xfe,0xa9,0x0f,0xff,0x00,0x3f,
0xfe,0xdf,0xff,0xc7,0x1e,0xbc,0x03,0x3c,
0x80,0xc8,0xd5,0x00,0x14,0xfe,0xff,0xff,
0xfe,0xe1,0x1f,0xeb,0x4c,0xf6,0x3c,0xff,
0x9f,0x87,0x7f,0x80,0x27,0x60,0x26,0xf0,
0x74,0x1d,0xfe,0x18,0x00,0x20,0xe4,0x6a,
0xcf,0xfb,0xff,0xe7,0xf0,0xf3,0xf0,0x0f,
0x3f,0xee,0xff,0x9f,0x40,0xe4,0xc3,0x1b,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0x35,0x00,0xe0,0xeb,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x30,0x1a,0x00,0x78,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0x81,0x7f,0x00,0xc3,0x3f,0x80,0x7f,
0xfc,0x7f,0x0f,0x00,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xc0,0x3f,
0xbf,0xe1,0x1f,0xc0,0x00,0xfe,0xbf,0x07,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x1f,0xc0,0x7f,0xe0,0xe0,0xdf,0xf0,0x0f,
0x03,0x00,0xff,0xdf,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xef,0x01,0x80,0xff,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0xff,0xf7,0x00,0xc0,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0xff,0x7b,0x00,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x00,0xf0,0xff,0x3d,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0x1e,0x00,0xf8,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x7f,0x0f,0x00,0xfc,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0xbf,0x07,0x00,
0xfc,0x07,0xfe,0x01,0x0d,0xff,0x00,0xfe,
0xf0,0x1f,0xf8,0x07,0x37,0xfc,0x03,0xf8,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0xdf,0x03,0x00,0xfe,0x03,0xff,
0xff,0x86,0x7f,0x00,0x03,0xf8,0x0f,0xfc,
0xfc,0x1b,0xfe,0x01,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x01,0x80,0xff,0xef,
0x7f,0x00,0xff,0x81,0x80,0x7f,0xc3,0x3f,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xf7,0x00,0xc0,0xff,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0xff,0x7b,0x00,0xe0,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0x81,0x7f,0x00,0xc3,0x3f,0x80,0x7f,
0xfc,0x07,0xfe,0x01,0x0d,0xff,0x00,0xfe,
0xf0,0xff,0x3d,0x00,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xc0,0x3f,
0xbf,0xe1,0x1f,0xc0,0x00,0xfe,0x03,0xff,
0xff,0x86,0x7f,0x00,0x00,0xf8,0xff,0x1e,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x1f,0xc0,0x7f,0xe0,0xe0,0xdf,0xf0,0x0f,
0x7f,0x00,0xff,0x81,0x80,0x7f,0xc3,0x3f,
0x0f,0x00,0xfc,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0xf0,0x0f,0xe0,0x3f,
0x07,0xf0,0x6f,0xf8,0xc0,0x3f,0x80,0xff,
0x1f,0xc0,0xbf,0xe1,0xbf,0x07,0x00,0xfe,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0x1f,0xf8,0x07,0xf0,0xfc,0x03,0xf8,0x37,
0x7f,0xe0,0x1f,0xc0,0xf0,0x0f,0xe0,0xdf,
0xff,0xdf,0x03,0x00,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0x0f,0xfc,0x03,
0x1b,0xfe,0x01,0xfc,0xe0,0x3f,0xf0,0x0f,
0x6f,0xf8,0x07,0xf0,0x80,0xff,0xef,0x01,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x01,0xfc,0x07,0xfe,0xfe,0x0d,0xff,0x00,
0x07,0xf0,0x1f,0xf8,0xf8,0x37,0xfc,0x03,
0x00,0xc0,0xff,0xf7,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0xff,0x00,0xfe,0x03,
0x00,0xff,0x86,0x7f,0xfc,0x03,0xf8,0x0f,
0x01,0xfc,0x1b,0xfe,0x7b,0x00,0xe0,0xff,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0x81,0x7f,0x00,0xff,0x3f,0x80,0x7f,0xc3,
0x07,0xfe,0x01,0xfc,0xff,0x00,0xfe,0x0d,
0xff,0x3d,0x00,0xf0,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xc0,0x3f,0x80,
0xe1,0x1f,0xc0,0xbf,0xfe,0x03,0xff,0x00,
0x86,0x7f,0x00,0xff,0xf8,0xff,0x1e,0x00,
0xf0,0x1f,0xf8,0x07,0x37,0xfc,0x03,0xf8,
0xc0,0x7f,0xe0,0x1f,0xdf,0xf0,0x0f,0xe0,
0x00,0xff,0x81,0x7f,0x7f,0xc3,0x3f,0x80,
0x00,0xfc,0x7f,0x0f,0x03,0xf8,0x0f,0xfc,
0xfc,0x1b,0xfe,0x01,0x0f,0xe0,0x3f,0xf0,
0xf0,0x6f,0xf8,0x07,0x3f,0x80,0xff,0xc0,
0xc0,0xbf,0xe1,0x1f,0x07,0x00,0xfe,0xbf,
0xfe,0x01,0xfc,0x07,0x00,0xfe,0x0d,0xff,
0xf8,0x07,0xf0,0x1f,0x03,0xf8,0x37,0xfc,
0xe0,0x1f,0xc0,0x7f,0x0f,0xe0,0xdf,0xf0,
0xdf,0x03,0x00,0xff,0x03,0xff,0x00,0xfe,
0x7f,0x00,0xff,0x86,0x0f,0xfc,0x03,0xf8,
0xfe,0x01,0xfc,0x1b,0x3f,0xf0,0x0f,0xe0,
0xf8,0x07,0xf0,0x6f,0xff,0xef,0x01,0x80,
};

struct k8_ucode_header {
W32 creation_date;
W32 unknown1;
W32 unknown2;
W32 checksum;

W32 unknown3;
W32 unknown4;
W32 cpuid;
W32 signature; // 0xXXaaaaaa

W32 unknown5;
W32 unknown6;
W32 unused[6];

unsigned char ucode[];
};

static W32 print_cpuid(W32 cpuid) {
int stepping, model, family, extmodel, extfam;

stepping = (cpuid >> 0) & 0xf;
model    = (cpuid >> 4) & 0xf;
family   = (cpuid >> 8) & 0xf;
extmodel = (cpuid >> 16) & 0xf;
extfam   = (cpuid >> 20) & 0xff;

printk("k8-ucode: id 0x%08x -> "
"family=%d model=%d stepping=%d extfam=%d extmodel=%d\n",
cpuid, family, model, stepping, extfam, extmodel);

return cpuid;
}

struct k8_ucode_set {
W32 cpuid;
unsigned char* ucode;
};

#define k8_ucode_set_count 3
struct k8_ucode_set ucodeset[k8_ucode_set_count] = {
{0x0f48, k8_ucode_0f48},
{0x0f4a, k8_ucode_0f4a},
{0x0f50, k8_ucode_0f50}
};

static unsigned char* find_ucode_for_cpu(void) {
W32 id = cpuid_eax(0x00000001);
int i = 0;

for (i = 0; i < k8_ucode_set_count; i++) {
if (ucodeset[i].cpuid == id)
return ucodeset[i].ucode;
}

return NULL;
}

static W32 k8_ucode_checksum(unsigned char* ucode) {
W32* p = (W32*)(ucode + 64);
int i;
W32 c = 0;

for (i = 0; i < 896/4; i++) {
c += p[i];
}

return c;
}

static void print_ucode_header(const struct k8_ucode_header* header) {
printk("K8 microcode update header:\n");
printk("  Creation date:      %08x\n", header->creation_date);
printk("  Checksum:           %08x\n", header->checksum);
printk("  CPUID:              %08x\n", header->cpuid);
printk("  Signature:          %08x\n", header->signature);
}

#define MSR_K8_UCODE_UPDATE  0xc0010020
#define K8_UCODE_HEADER_SIZE 64

static int k8_ucode_update(unsigned char* address) {
W64 tsc1, tsc2;
int rc;

printk("k8-ucode: Doing update from virtual address 0x%p\n", address);
#if 1
rdtscll(tsc1);
// Actually perform the update:
rc = checking_wrmsrl(MSR_K8_UCODE_UPDATE, (unsigned long long)address);
rdtscll(tsc2);

if (rc) {
printk("k8-ucode: Error: update rejected by processor\n");
} else {
printk("k8-ucode: OK: update accepted by processor\n");
}

// ~5566 cycles with readable addr, (varies) with invalid addr
printk("k8-ucode: Update took %lld clock cycles\n", tsc2 - tsc1);
#endif

print_cpuid(cpuid_eax(1));
return rc;
}

static int __init k8_ucode_init(void) {
W32 cpuid;
W32 checksum;
unsigned char* ucode;

printk("k8-ucode loaded\n");

if (cpuid_ebx(0) != 0x68747541) {
// 0x68747541 = 'Auth'enticAMD
printk("k8-ucode: this is not an AMD processor;"
"k8-ucode will not work on it\n");
return -ENOTSUPP;
}

cpuid = print_cpuid(cpuid_eax(1));

ucode = find_ucode_for_cpu();

if (!ucode) {
printk("k8-ucode: no microcode update for cpuid 0x%08x is present\n", cpuid);
return -ENOSYS;
}

printk("k8-ucode: Selected microcode update block for cpuid 0x%08x:\n", cpuid);
print_ucode_header((struct k8_ucode_header*)ucode);

checksum = k8_ucode_checksum(ucode);

if (checksum != ((struct k8_ucode_header*)ucode)->checksum) {
printk("k8-ucode: selected update block's expected checksum was 0x%08x, "
"but real checksum was %08x\n",
((struct k8_ucode_header*)ucode)->checksum, checksum);
return -EINVAL;
}

k8_ucode_update(ucode);

printk("k8-ucode: update done; unloading module\n");

//
// Return a pseudo-error so the module is immediately unloaded;
// it has done all its work in the initialization phase.
//
return -EINPROGRESS;
}

static void __exit k8_ucode_exit(void) {
// (should not get here)
}

module_init(k8_ucode_init)
module_exit(k8_ucode_exit)