x86/microcode/32: Move early loading after paging enable

32-bit loads microcode before paging is enabled. The commit which
introduced that has zero justification in the changelog. The cover
letter has slightly more content, but it does not give any technical
justification either:

  "The problem in current microcode loading method is that we load a
   microcode way, way too late; ideally we should load it before turning
   paging on.  This may only be practical on 32 bits since we can't get
   to 64-bit mode without paging on, but we should still do it as early
   as at all possible."

Handwaving word salad with zero technical content.

Someone claimed in an offlist conversation that this is required for
curing the ATOM erratum AAE44/AAF40/AAG38/AAH41. That erratum requires
an microcode update in order to make the usage of PSE safe. But during
early boot, PSE is completely irrelevant and it is evaluated way later.

Neither is it relevant for the AP on single core HT enabled CPUs as the
microcode loading on the AP is not doing anything.

On dual core CPUs there is a theoretical problem if a split of an
executable large page between enabling paging including PSE and loading
the microcode happens. But that's only theoretical, it's practically
irrelevant because the affected dual core CPUs are 64bit enabled and
therefore have paging and PSE enabled before loading the microcode on
the second core. So why would it work on 64-bit but not on 32-bit?

The erratum:

  "AAG38 Code Fetch May Occur to Incorrect Address After a Large Page is
   Split Into 4-Kbyte Pages

   Problem: If software clears the PS (page size) bit in a present PDE
   (page directory entry), that will cause linear addresses mapped through
   this PDE to use 4-KByte pages instead of using a large page after old
   TLB entries are invalidated. Due to this erratum, if a code fetch uses
   this PDE before the TLB entry for the large page is invalidated then it
   may fetch from a different physical address than specified by either the
   old large page translation or the new 4-KByte page translation. This
   erratum may also cause speculative code fetches from incorrect addresses."

The practical relevance for this is exactly zero because there is no
splitting of large text pages during early boot-time, i.e. between paging
enable and microcode loading, and neither during CPU hotplug.

IOW, this load microcode before paging enable is yet another voodoo
programming solution in search of a problem. What's worse is that it causes
at least two serious problems:

 1) When stackprotector is enabled, the microcode loader code has the
    stackprotector mechanics enabled. The read from the per CPU variable
    __stack_chk_guard is always accessing the virtual address either
    directly on UP or via %fs on SMP. In physical address mode this
    results in an access to memory above 3GB. So this works by chance as
    the hardware returns the same value when there is no RAM at this
    physical address. When there is RAM populated above 3G then the read
    is by chance the same as nothing changes that memory during the very
    early boot stage. That's not necessarily true during runtime CPU
    hotplug.

 2) When function tracing is enabled, the relevant microcode loader
    functions and the functions invoked from there will call into the
    tracing code and evaluate global and per CPU variables in physical
    address mode. What could potentially go wrong?

Cure this and move the microcode loading after the early paging enable, use
the new temporary initrd mapping and remove the gunk in the microcode
loader which is required to handle physical address mode.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20231017211722.348298216@linutronix.de
This commit is contained in:
Thomas Gleixner
2023-10-17 23:23:32 +02:00
committed by Borislav Petkov (AMD)
parent 4c585af718
commit 0b62f6cb07
9 changed files with 71 additions and 270 deletions

View File

@@ -319,15 +319,8 @@ static void save_microcode_patch(struct ucode_cpu_info *uci, void *data, unsigne
if (!intel_find_matching_signature(p->data, uci->cpu_sig.sig, uci->cpu_sig.pf))
return;
/*
* Save for early loading. On 32-bit, that needs to be a physical
* address as the APs are running from physical addresses, before
* paging has been enabled.
*/
if (IS_ENABLED(CONFIG_X86_32))
intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p->data);
else
intel_ucode_patch = p->data;
/* Save for early loading */
intel_ucode_patch = p->data;
}
/*
@@ -420,66 +413,10 @@ static bool load_builtin_intel_microcode(struct cpio_data *cp)
return false;
}
static void print_ucode_info(int old_rev, int new_rev, unsigned int date)
{
pr_info_once("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n",
old_rev,
new_rev,
date & 0xffff,
date >> 24,
(date >> 16) & 0xff);
}
#ifdef CONFIG_X86_32
static int delay_ucode_info;
static int current_mc_date;
static int early_old_rev;
/*
* Print early updated ucode info after printk works. This is delayed info dump.
*/
void show_ucode_info_early(void)
{
struct ucode_cpu_info uci;
if (delay_ucode_info) {
intel_cpu_collect_info(&uci);
print_ucode_info(early_old_rev, uci.cpu_sig.rev, current_mc_date);
delay_ucode_info = 0;
}
}
/*
* At this point, we can not call printk() yet. Delay printing microcode info in
* show_ucode_info_early() until printk() works.
*/
static void print_ucode(int old_rev, int new_rev, int date)
{
int *delay_ucode_info_p;
int *current_mc_date_p;
int *early_old_rev_p;
delay_ucode_info_p = (int *)__pa_nodebug(&delay_ucode_info);
current_mc_date_p = (int *)__pa_nodebug(&current_mc_date);
early_old_rev_p = (int *)__pa_nodebug(&early_old_rev);
*delay_ucode_info_p = 1;
*current_mc_date_p = date;
*early_old_rev_p = old_rev;
}
#else
static inline void print_ucode(int old_rev, int new_rev, int date)
{
print_ucode_info(old_rev, new_rev, date);
}
#endif
static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
static int apply_microcode_early(struct ucode_cpu_info *uci)
{
struct microcode_intel *mc;
u32 rev, old_rev;
u32 rev, old_rev, date;
mc = uci->mc;
if (!mc)
@@ -513,11 +450,9 @@ static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
uci->cpu_sig.rev = rev;
if (early)
print_ucode(old_rev, uci->cpu_sig.rev, mc->hdr.date);
else
print_ucode_info(old_rev, uci->cpu_sig.rev, mc->hdr.date);
date = mc->hdr.date;
pr_info_once("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n",
old_rev, rev, date & 0xffff, date >> 24, (date >> 16) & 0xff);
return 0;
}
@@ -535,7 +470,7 @@ int __init save_microcode_in_initrd_intel(void)
intel_ucode_patch = NULL;
if (!load_builtin_intel_microcode(&cp))
cp = find_microcode_in_initrd(ucode_path, false);
cp = find_microcode_in_initrd(ucode_path);
if (!(cp.data && cp.size))
return 0;
@@ -551,21 +486,11 @@ int __init save_microcode_in_initrd_intel(void)
*/
static struct microcode_intel *__load_ucode_intel(struct ucode_cpu_info *uci)
{
static const char *path;
struct cpio_data cp;
bool use_pa;
if (IS_ENABLED(CONFIG_X86_32)) {
path = (const char *)__pa_nodebug(ucode_path);
use_pa = true;
} else {
path = ucode_path;
use_pa = false;
}
/* try built-in microcode first */
if (!load_builtin_intel_microcode(&cp))
cp = find_microcode_in_initrd(path, use_pa);
cp = find_microcode_in_initrd(ucode_path);
if (!(cp.data && cp.size))
return NULL;
@@ -586,30 +511,21 @@ void __init load_ucode_intel_bsp(void)
uci.mc = patch;
apply_microcode_early(&uci, true);
apply_microcode_early(&uci);
}
void load_ucode_intel_ap(void)
{
struct microcode_intel *patch, **iup;
struct ucode_cpu_info uci;
if (IS_ENABLED(CONFIG_X86_32))
iup = (struct microcode_intel **) __pa_nodebug(&intel_ucode_patch);
else
iup = &intel_ucode_patch;
if (!*iup) {
patch = __load_ucode_intel(&uci);
if (!patch)
if (!intel_ucode_patch) {
intel_ucode_patch = __load_ucode_intel(&uci);
if (!intel_ucode_patch)
return;
*iup = patch;
}
uci.mc = *iup;
apply_microcode_early(&uci, true);
uci.mc = intel_ucode_patch;
apply_microcode_early(&uci);
}
static struct microcode_intel *find_patch(struct ucode_cpu_info *uci)
@@ -647,7 +563,7 @@ void reload_ucode_intel(void)
uci.mc = p;
apply_microcode_early(&uci, false);
apply_microcode_early(&uci);
}
static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)