Skip to content

Commit

Permalink
Merge branch 'x86/microcode' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/git/tip/tip

Pull x86 microcode loading update from Peter Anvin:
 "This patchset lets us update the CPU microcode very, very early in
  initialization if the BIOS fails to do so (never happens, right?)

  This is handy for dealing with things like the Atom erratum where we
  have to run without PSE because microcode loading happens too late.

  As I mentioned in the x86/mm push request it depends on that
  infrastructure but it is otherwise a standalone feature."

* 'x86/microcode' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/Kconfig: Make early microcode loading a configuration feature
  x86/mm/init.c: Copy ucode from initrd image to kernel memory
  x86/head64.c: Early update ucode in 64-bit
  x86/head_32.S: Early update ucode in 32-bit
  x86/microcode_intel_early.c: Early update ucode on Intel's CPU
  x86/tlbflush.h: Define __native_flush_tlb_global_irq_disabled()
  x86/microcode_intel_lib.c: Early update ucode on Intel's CPU
  x86/microcode_core_early.c: Define interfaces for early loading ucode
  x86/common.c: load ucode in 64 bit or show loading ucode info in 32 bit on AP
  x86/common.c: Make have_cpuid_p() a global function
  x86/microcode_intel.h: Define functions and macros for early loading ucode
  x86, doc: Documentation for early microcode loading
  • Loading branch information
torvalds committed Feb 23, 2013
2 parents 0cc9129 + da76f64 commit c47f39e
Show file tree
Hide file tree
Showing 16 changed files with 1,301 additions and 183 deletions.
43 changes: 43 additions & 0 deletions Documentation/x86/early-microcode.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Early load microcode
====================
By Fenghua Yu <[email protected]>

Kernel can update microcode in early phase of boot time. Loading microcode early
can fix CPU issues before they are observed during kernel boot time.

Microcode is stored in an initrd file. The microcode is read from the initrd
file and loaded to CPUs during boot time.

The format of the combined initrd image is microcode in cpio format followed by
the initrd image (maybe compressed). Kernel parses the combined initrd image
during boot time. The microcode file in cpio name space is:
kernel/x86/microcode/GenuineIntel.bin

During BSP boot (before SMP starts), if the kernel finds the microcode file in
the initrd file, it parses the microcode and saves matching microcode in memory.
If matching microcode is found, it will be uploaded in BSP and later on in all
APs.

The cached microcode patch is applied when CPUs resume from a sleep state.

There are two legacy user space interfaces to load microcode, either through
/dev/cpu/microcode or through /sys/devices/system/cpu/microcode/reload file
in sysfs.

In addition to these two legacy methods, the early loading method described
here is the third method with which microcode can be uploaded to a system's
CPUs.

The following example script shows how to generate a new combined initrd file in
/boot/initrd-3.5.0.ucode.img with original microcode microcode.bin and
original initrd image /boot/initrd-3.5.0.img.

mkdir initrd
cd initrd
mkdir kernel
mkdir kernel/x86
mkdir kernel/x86/microcode
cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin
find .|cpio -oc >../ucode.cpio
cd ..
cat ucode.cpio /boot/initrd-3.5.0.img >/boot/initrd-3.5.0.ucode.img
18 changes: 18 additions & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,24 @@ config MICROCODE_OLD_INTERFACE
def_bool y
depends on MICROCODE

config MICROCODE_INTEL_LIB
def_bool y
depends on MICROCODE_INTEL

config MICROCODE_INTEL_EARLY
bool "Early load microcode"
depends on MICROCODE_INTEL && BLK_DEV_INITRD
default y
help
This option provides functionality to read additional microcode data
at the beginning of initrd image. The data tells kernel to load
microcode to CPU's as early as possible. No functional change if no
microcode data is glued to the initrd, therefore it's safe to say Y.

config MICROCODE_EARLY
def_bool y
depends on MICROCODE_INTEL_EARLY

config X86_MSR
tristate "/dev/cpu/*/msr - Model-specific register support"
---help---
Expand Down
14 changes: 14 additions & 0 deletions arch/x86/include/asm/microcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,18 @@ static inline struct microcode_ops * __init init_amd_microcode(void)
static inline void __exit exit_amd_microcode(void) {}
#endif

#ifdef CONFIG_MICROCODE_EARLY
#define MAX_UCODE_COUNT 128
extern void __init load_ucode_bsp(void);
extern __init void load_ucode_ap(void);
extern int __init save_microcode_in_initrd(void);
#else
static inline void __init load_ucode_bsp(void) {}
static inline __init void load_ucode_ap(void) {}
static inline int __init save_microcode_in_initrd(void)
{
return 0;
}
#endif

#endif /* _ASM_X86_MICROCODE_H */
85 changes: 85 additions & 0 deletions arch/x86/include/asm/microcode_intel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifndef _ASM_X86_MICROCODE_INTEL_H
#define _ASM_X86_MICROCODE_INTEL_H

#include <asm/microcode.h>

struct microcode_header_intel {
unsigned int hdrver;
unsigned int rev;
unsigned int date;
unsigned int sig;
unsigned int cksum;
unsigned int ldrver;
unsigned int pf;
unsigned int datasize;
unsigned int totalsize;
unsigned int reserved[3];
};

struct microcode_intel {
struct microcode_header_intel hdr;
unsigned int bits[0];
};

/* microcode format is extended from prescott processors */
struct extended_signature {
unsigned int sig;
unsigned int pf;
unsigned int cksum;
};

struct extended_sigtable {
unsigned int count;
unsigned int cksum;
unsigned int reserved[3];
struct extended_signature sigs[0];
};

#define DEFAULT_UCODE_DATASIZE (2000)
#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel))
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable))
#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature))
#define DWSIZE (sizeof(u32))

#define get_totalsize(mc) \
(((struct microcode_intel *)mc)->hdr.totalsize ? \
((struct microcode_intel *)mc)->hdr.totalsize : \
DEFAULT_UCODE_TOTALSIZE)

#define get_datasize(mc) \
(((struct microcode_intel *)mc)->hdr.datasize ? \
((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)

#define sigmatch(s1, s2, p1, p2) \
(((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0))))

#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)

extern int
get_matching_microcode(unsigned int csig, int cpf, void *mc, int rev);
extern int microcode_sanity_check(void *mc, int print_err);
extern int get_matching_sig(unsigned int csig, int cpf, void *mc, int rev);
extern int
update_match_revision(struct microcode_header_intel *mc_header, int rev);

#ifdef CONFIG_MICROCODE_INTEL_EARLY
extern void __init load_ucode_intel_bsp(void);
extern void __cpuinit load_ucode_intel_ap(void);
extern void show_ucode_info_early(void);
#else
static inline __init void load_ucode_intel_bsp(void) {}
static inline __cpuinit void load_ucode_intel_ap(void) {}
static inline void show_ucode_info_early(void) {}
#endif

#if defined(CONFIG_MICROCODE_INTEL_EARLY) && defined(CONFIG_HOTPLUG_CPU)
extern int save_mc_for_early(u8 *mc);
#else
static inline int save_mc_for_early(u8 *mc)
{
return 0;
}
#endif

#endif /* _ASM_X86_MICROCODE_INTEL_H */
8 changes: 8 additions & 0 deletions arch/x86/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ extern void init_amd_cacheinfo(struct cpuinfo_x86 *c);
extern void detect_extended_topology(struct cpuinfo_x86 *c);
extern void detect_ht(struct cpuinfo_x86 *c);

#ifdef CONFIG_X86_32
extern int have_cpuid_p(void);
#else
static inline int have_cpuid_p(void)
{
return 1;
}
#endif
static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
Expand Down
18 changes: 12 additions & 6 deletions arch/x86/include/asm/tlbflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ static inline void __native_flush_tlb(void)
native_write_cr3(native_read_cr3());
}

static inline void __native_flush_tlb_global_irq_disabled(void)
{
unsigned long cr4;

cr4 = native_read_cr4();
/* clear PGE */
native_write_cr4(cr4 & ~X86_CR4_PGE);
/* write old PGE again and flush TLBs */
native_write_cr4(cr4);
}

static inline void __native_flush_tlb_global(void)
{
unsigned long flags;
unsigned long cr4;

/*
* Read-modify-write to CR4 - protect it from preemption and
Expand All @@ -32,11 +42,7 @@ static inline void __native_flush_tlb_global(void)
*/
raw_local_irq_save(flags);

cr4 = native_read_cr4();
/* clear PGE */
native_write_cr4(cr4 & ~X86_CR4_PGE);
/* write old PGE again and flush TLBs */
native_write_cr4(cr4);
__native_flush_tlb_global_irq_disabled();

raw_local_irq_restore(flags);
}
Expand Down
3 changes: 3 additions & 0 deletions arch/x86/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o

obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o

obj-$(CONFIG_MICROCODE_EARLY) += microcode_core_early.o
obj-$(CONFIG_MICROCODE_INTEL_EARLY) += microcode_intel_early.o
obj-$(CONFIG_MICROCODE_INTEL_LIB) += microcode_intel_lib.o
microcode-y := microcode_core.o
microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o
microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o
Expand Down
17 changes: 11 additions & 6 deletions arch/x86/kernel/cpu/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <asm/mce.h>
#include <asm/msr.h>
#include <asm/pat.h>
#include <asm/microcode.h>
#include <asm/microcode_intel.h>

#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/uv/uv.h>
Expand Down Expand Up @@ -213,7 +215,7 @@ static inline int flag_is_changeable_p(u32 flag)
}

/* Probe for the CPUID instruction */
static int __cpuinit have_cpuid_p(void)
int __cpuinit have_cpuid_p(void)
{
return flag_is_changeable_p(X86_EFLAGS_ID);
}
Expand Down Expand Up @@ -249,11 +251,6 @@ static inline int flag_is_changeable_p(u32 flag)
{
return 1;
}
/* Probe for the CPUID instruction */
static inline int have_cpuid_p(void)
{
return 1;
}
static inline void squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
{
}
Expand Down Expand Up @@ -1223,6 +1220,12 @@ void __cpuinit cpu_init(void)
int cpu;
int i;

/*
* Load microcode on this cpu if a valid microcode is available.
* This is early microcode loading procedure.
*/
load_ucode_ap();

cpu = stack_smp_processor_id();
t = &per_cpu(init_tss, cpu);
oist = &per_cpu(orig_ist, cpu);
Expand Down Expand Up @@ -1314,6 +1317,8 @@ void __cpuinit cpu_init(void)
struct tss_struct *t = &per_cpu(init_tss, cpu);
struct thread_struct *thread = &curr->thread;

show_ucode_info_early();

if (cpumask_test_and_set_cpu(cpu, cpu_initialized_mask)) {
printk(KERN_WARNING "CPU#%d already initialized!\n", cpu);
for (;;)
Expand Down
6 changes: 6 additions & 0 deletions arch/x86/kernel/head64.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <asm/e820.h>
#include <asm/bios_ebda.h>
#include <asm/bootparam_utils.h>
#include <asm/microcode.h>

/*
* Manage page tables very early on.
Expand Down Expand Up @@ -165,6 +166,11 @@ void __init x86_64_start_kernel(char * real_mode_data)

copy_bootdata(__va(real_mode_data));

/*
* Load microcode early on BSP.
*/
load_ucode_bsp();

if (console_loglevel == 10)
early_printk("Kernel alive\n");

Expand Down
11 changes: 11 additions & 0 deletions arch/x86/kernel/head_32.S
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ ENTRY(startup_32)
movl %eax, pa(olpc_ofw_pgd)
#endif

#ifdef CONFIG_MICROCODE_EARLY
/* Early load ucode on BSP. */
call load_ucode_bsp
#endif

/*
* Initialize page tables. This creates a PDE and a set of page
* tables, which are located immediately beyond __brk_base. The variable
Expand Down Expand Up @@ -299,6 +304,12 @@ ENTRY(startup_32_smp)
movl %eax,%ss
leal -__PAGE_OFFSET(%ecx),%esp

#ifdef CONFIG_MICROCODE_EARLY
/* Early load ucode on AP. */
call load_ucode_ap
#endif


default_entry:
#define CR0_STATE (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | \
X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | \
Expand Down
7 changes: 4 additions & 3 deletions arch/x86/kernel/microcode_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,7 @@ static struct attribute_group mc_attr_group = {

static void microcode_fini_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

microcode_ops->microcode_fini_cpu(cpu);
uci->valid = 0;
}

static enum ucode_state microcode_resume_cpu(int cpu)
Expand All @@ -383,6 +380,10 @@ static enum ucode_state microcode_resume_cpu(int cpu)
static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw)
{
enum ucode_state ustate;
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

if (uci && uci->valid)
return UCODE_OK;

if (collect_cpu_info(cpu))
return UCODE_ERROR;
Expand Down
Loading

0 comments on commit c47f39e

Please sign in to comment.