Fix early microcode loading on AMD
PATCH 1/1 Fix early microcode loading on AMD
Fixup the early AMD microcode loading.
* load_microcode_amd() (and the helper its using) should not have an cpu parameter. The microcode loading is not depending on the CPU it is executed and all the loaded patches will end up in a global list for all CPUs anyway.
* Return -1 (like Intels apply_microcode) when the loading fails, also do not set the active microcode level on failure.
* Save the amd_bsp_mpb on apply, not on load. Otherwise someone could later load an older microcode file that would overwrite amd_bsp_mpb, but would not be applied to the CPUs
* Save the amd_bsp_mpb on every update. Otherwise someone could offline the BSP, update the microcode and this would be lost on resume
* apply_ucode_in_initrd() now also needs to save amd_bsp_mbp, because load_microcode_amd() its no longer doing this and its not using apply_microcode_amd().
* extract common checks and initialisations from load_ucode_ap() and load_microcode_amd() to load_microcode_amd_early(). The change from cpu to x86family in load_microcode_amd() allows to drop the code messing with cpu_data(cpu), with is wrong anyway because at that point the per-cpu cpu_info is not yet setup. And these values would later be overwritten by smp_store_boot_cpu_info() / smp_store_cpu_info().
* fold collect_cpu_info_amd_early() into load_ucode_amd_ap(), because its only used at one place.
* load_ucode_ap(): Quick exit for !cpu, because without load_microcode_amd() getting called apply_microcode_amd() can't do anything. Exit, if no microcode could be loaded.
* reduce save_microcode_in_initrd_amd() by reusing load_microcode_amd_early()
Main benefit is, that the early microcode loading no longer plays games with the not-yet-initialised per-cpu cpu_info. apply_microcode_amd() will still write into cpu_data(cpu)->microcode, but I see no good way to remove that there, because for not-early microcode updates that is exactly the right place for that update.
---
This alone also fixes the hang-on-boot I experienced with 3.11-rc1 even if the fix for cpu_has_amd_erratum() is not applied, because now the trigger (filling ->x86 but not ->x86_vendor) is no longer there. But I think both patches should be applied.
Boot tested on 64 and 32bit, but as my BIOS already provides up-to-date microcode I could not test, if that gets applied correctly.
arch/x86/include/asm/microcode_amd.h +1 -1
@@ -59,7 +59,7 @@ static inline u16 find_equiv_id(struct eextern int __apply_microcode_amd (struct microcode_amd *mc_amd);extern int apply_microcode_amd (int cpu); -extern enum ucode_state load_microcode_amd (int cpu,const u8 *data,size_t size); +extern enum ucode_state load_microcode_amd (u8 x86family,const u8 *data,size_t size);#ifdef CONFIG_MICROCODE_AMD_EARLY #ifdef CONFIG_X86_32
arch/x86/kernel/microcode_amd.c +22 -27
124 lines not on this page · raw message
arch/x86/kernel/microcode_amd_early.c +39 -37
111 lines not on this page · raw message
H. Peter Anvin
reviewerBorislav, do you agree with these patches? (This one and the errata check?) If so I will queue them up in x86/urgent.
-hpa
Borislav Petkov
maintainerQuoted 2 lines · H. Peter Anvin
Borislav, do you agree with these patches? (This one and the errata check?) If so I will queue them up in x86/urgent.
I was just about to have another look at the whole deal. Give me a couple of days - I'll let you know.
Thanks.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Borislav Petkov
maintainerQuoted 4 lines · Torsten Kaiser
Fixup the early AMD microcode loading.
*
load_microcode_amd()(and the helper its using) should not have an cpu parameter.
Hmm, I don't think so - we get the cpu handed down from microcode_core and besides the early load on 32bit needs to do find_patch(cpu).
Quoted 1 line
The microcode loading is not depending on the CPU it is
Mostly. There are mixed-stepping boxes which need to differentiate between which cpu we're applying the patch for.
Btw, your config boots on my F14h box with "nomodeset" on the command line because it is missing radeon firmware for my gpu.
Quoted 4 lines
executed and all the loaded patches will end up in a global list for all CPUs anyway.
* Return -1 (like Intelsapply_microcode) when the loading fails, also do not set the active microcode level on failure.
Yep, this part I want. Please send it as a separate patch.
Quoted 3 lines
* Save the
amd_bsp_mpbon apply, not on load. Otherwise someone could later load an older microcode file that would overwriteamd_bsp_mpb, but would not be applied to the CPUs
See the patch id check in apply_ucode_in_initrd()?
if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id)
Quoted 2 lines
* Save the
amd_bsp_mpbon every update. Otherwise someone could offline the BSP, update the microcode and this would be lost on resume
Huh, is amd_bsp_mpb going to disappear all of a sudden?
And that doesn't matter because when we online the BSP later, it goes through the CPU hotplug notifier mc_cpu_callback. Or am I missing something?
Quoted 9 lines
*
apply_ucode_in_initrd()now also needs to saveamd_bsp_mbp, becauseload_microcode_amd()its no longer doing this and its not usingapply_microcode_amd().
* extract common checks and initialisations fromload_ucode_ap()andload_microcode_amd()toload_microcode_amd_early(). The change from cpu to x86family inload_microcode_amd()allows to drop the code messing withcpu_data(cpu), with is wrong anyway because at that point the per-cpucpu_infois not yet setup. And these values would later be overwritten bysmp_store_boot_cpu_info()/smp_store_cpu_info().
Right, so I was thinking about this. And the code is pretty nasty: we do a load_ucode_amd_ap() but we do add the ucode for the BSP:
if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK)
so the actual problem is apply_microcode_amd() using as an arg cpu != 0. And we can take care of that by using, say __apply_microcode_amd() and hand it the BSP's patch.
The other problem I see is not updating c->microcode since it is going to be overwritten by smp_store_cpu_info, which is unfortunate.
And I don't see where Intel are updating that cpuinfo_x86.microcode field on early load too.
So, AFAICT, c->microcode would remain unset when we only do early microcode load. But that is something we should fix as a later patch.
So I think you should switch load_ucode_amd_ap to __apply_microcode_amd:
p = find_patch() __apply_microcode_amd(p->mc_data);
which should take care of the issue you're seeing, IMHO.
Quoted 2 lines
* fold
collect_cpu_info_amd_early()intoload_ucode_amd_ap(), because its only used at one place.
Yes, that should be done especially since we are not going to touch cpuinfo_x86 fields anymore.
Quoted 3 lines
*
load_ucode_ap(): Quick exit for !cpu, because withoutload_microcode_amd()getting calledapply_microcode_amd()can't do anything. Exit, if no microcode could be loaded.
This could probably be a WARN_ON(!cpu) to catch errors...
And so on.
So I see a couple of issues in this patch and they should be separated into single patches - one patch taking care of one issue and explaining what the problem is in the commit message (I know you can do that good :)).
Thanks.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Quoted 8 lines · Borislav Petkov
Quoted 4 lines · Torsten Kaiser
Fixup the early AMD microcode loading.
*
load_microcode_amd()(and the helper its using) should not have an cpu parameter.Hmm, I don't think so - we get the cpu handed down from
microcode_coreand besides the early load on 32bit needs to dofind_patch(cpu).
Thats why I moved that part into apply_microcode_amd(). See later on more, why I think that move is the right thing.
And without that the current cpu parameter will only be used to get the (in the early load case not even correctly set up!) per-cpu data. But the only member of cpuinfo_x86 that gets uses is ->x86, the family. Line 159: switch(c->x86) and Line 301: if (proc_fam!)c->x86)
I really wanted to make that switch from cpu to x86family a separate patch, that it would be more obvious correct, but because of that amd_bsp_mpb hunk I can't find a good cut and thats why this patch is larger that I would have preferred.
Quoted 4 lines
Quoted 1 line
The microcode loading is not depending on the CPU it is
Mostly. There are mixed-stepping boxes which need to differentiate between which cpu we're applying the patch for.
Nothing looks at ->x86_model or ->x86_mask during load. It will always load all patches from the current family. If loading would really depend on the current cpu in a mixed system that would be horrible: Depending on which CPU gets execute load_microcode_amd() it there would be different patches loaded into RAM?
Quoted 2 lines
Btw, your config boots on my F14h box with "nomodeset" on the command line because it is missing radeon firmware for my gpu.
I suspect a F14h box will never see that hang. It trips over the the C1E erratum and amd_erratum_400[] looks like it only affects 0xfh and 0x10h (like my Phenom II X6).
Quoted 6 lines
Quoted 4 lines
executed and all the loaded patches will end up in a global list for all CPUs anyway.
* Return -1 (like Intelsapply_microcode) when the loading fails, also do not set the active microcode level on failure.Yep, this part I want. Please send it as a separate patch.
OK, will send that together with the resend for cpu_has_amd_erratum().
Quoted 7 lines
Quoted 3 lines
* Save the
amd_bsp_mpbon apply, not on load. Otherwise someone could later load an older microcode file that would overwriteamd_bsp_mpb, but would not be applied to the CPUsSee the patch id check in
apply_ucode_in_initrd()?if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id)
I meant with "load" load_microcode_amd() not the loading of the microcode into the CPU.
1.: load microcode rev X into CPU (early or normal is not important) 2.: get older microcode file that only contains rev Y with Y<X 3.: trigger load_microcode_amd() with a corrupt file: This will call cleanup() and empty pcache.
4.: trigger load_microcode_amd() with the older file:
* this will now load rev Y into pcache
* rev Y will be returned by find_patch and copied into amd_bsp_mpb
* any try to apply rev Y will be skipped in apply_microcode_amd()
So now the CPU still correctly has rev X, but amd_bsp_mpb will contain the wrong rev Y.
That copying already in load_microcode also is suspicious if someone would only load the microcode but not apply it. But I did not find a codepath in arch/x86/kernel/microcode_core.c to load it without a followup apply.
Quoted 8 lines
Quoted 2 lines
* Save the
amd_bsp_mpbon every update. Otherwise someone could offline the BSP, update the microcode and this would be lost on resumeHuh, is
amd_bsp_mpbgoing to disappear all of a sudden?And that doesn't matter because when we online the BSP later, it goes through the CPU hotplug notifier
mc_cpu_callback. Or am I missing something?
Yeah, me correctly describing what I was meaning. ;-)
1.: boot system, BIOS give microcode rev. X
2.: offline the BSP
3.: update microcode to rev. Y with Y > X
Because the BSP is not online rev. Y will not be copied into amd_bsp_mpb 4.: supend
5.: resume, BIOS gives rev. X again
6.: amd_bsp_mpb is empty -> rev. Y will not be reapplied.
Quoted 10 lines
Quoted 9 lines
*
apply_ucode_in_initrd()now also needs to saveamd_bsp_mbp, becauseload_microcode_amd()its no longer doing this and its not usingapply_microcode_amd().
* extract common checks and initialisations fromload_ucode_ap()andload_microcode_amd()toload_microcode_amd_early(). The change from cpu to x86family inload_microcode_amd()allows to drop the code messing withcpu_data(cpu), with is wrong anyway because at that point the per-cpucpu_infois not yet setup. And these values would later be overwritten bysmp_store_boot_cpu_info()/smp_store_cpu_info().
Quoted 4 lines
Right, so I was thinking about this. And the code is pretty nasty: we do a
load_ucode_amd_ap()but we do add the ucode for the BSP:if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK)
No, that code will not be reached for the BSP, because it is behind:
if (cpu && !ucode_loaded) {
The BSP has cpu == 0. Thats why I adding the following in my patch:
+ /* BSP via load_ucode_amd_bsp() */
+ if (!cpu)
+ return;
I don't understand if that is really correct, but that was the original behavior, and I didn't feel competent enough to decree that calling load_microcode_amd() for the BSP would be save. (The code there is strange: There is a load_ucode_amd_bsp() but load_ucode_amd_ap() will also be called for the BSP.
And it seems the call to load_ucode_ap() for the BSP will come from a very different place (via trap_init()) that the other CPUs. And I did not even try to understand what X86_32 is doing...)
Quoted 3 lines
so the actual problem is
apply_microcode_amd()using as an arg cpu != 0. And we can take care of that by using, say__apply_microcode_amd()and hand it the BSP's patch.
That one I don't understand.
cpu!=0 is nessasary to patch all the other CPUs.
Quoted 8 lines
The other problem I see is not updating
c->microcodesince it is going to be overwritten bysmp_store_cpu_info, which is unfortunate.And I don't see where Intel are updating that cpuinfo_x86.microcode field on early load too.
So, AFAICT,
c->microcodewould remain unset when we only do early microcode load. But that is something we should fix as a later patch.
I don't see a problem with that staying unset. apply_microcode_amd() directly reads the rev from MSR_AMD64_PATCH_LEVEL so it does not depend on that being correct. And smp_store_(boot)_cpu_info will also read the current rev directly from the CPU to fill ->microcode.
Quoted 7 lines
So I think you should switch
load_ucode_amd_apto__apply_microcode_amd:p =
find_patch()
__apply_microcode_amd(p->mc_data);which should take care of the issue you're seeing, IMHO.
The issue I'm seeing is that collect_cpu_info_amd_early() fills c->x86 but not c->x86_vendor.
Which breaks cpu_has_amd_erratum() and then Erratum 400 breaks the boot.
I did not really want to switch from apply_microcode_amd() to __apply_microcode_amd() because then I would lose the check if the new microcode is really an upgrade.
I would like to get rid of the messing with uci->cpu_sig, but that is needed in find_patch() so can't be removed even when using __appy_microcode_amd().
Quoted 11 lines
Quoted 2 lines
* fold
collect_cpu_info_amd_early()intoload_ucode_amd_ap(), because its only used at one place.Yes, that should be done especially since we are not going to touch
cpuinfo_x86fields anymore.Quoted 3 lines
*
load_ucode_ap(): Quick exit for !cpu, because withoutload_microcode_amd()getting calledapply_microcode_amd()can't do anything. Exit, if no microcode could be loaded.This could probably be a
WARN_ON(!cpu)to catch errors...
No, load_ucode_ap() will be called for cpu == 0.init/main.c: start_kernel() calls trap_init()trap_init() calls cpu_init()cpu_init() calls load_ucode_ap()load_ucode_ap() then calls load_ucode_amd_ap()
I think that silent exit at that place is correct.
If load_microcode_amd() could do anything there needs to be correct microcode data at initrd_start + ucode_offset.
But if there is valid microcode then load_ucode_amd_bsp() should also have found it an applied it to the BSP already.
So skipping applay_microcode_amd() for cpu==0 is OK.
Quoted 6 lines
And so on.
So I see a couple of issues in this patch and they should be separated into single patches - one patch taking care of one issue and explaining what the problem is in the commit message (I know you can do that good :)).
I know, I know.
I really wanted "just" a small patch that only killed the accesses to cpu_data. Which seemed like it only needed the API change from cpu to x86family in microcode_amd.c.
But then the amd_bsp_mbp made this much more complicated.
OK, I will try to splitt this, at least seperating the correct error exit for when __apply_microcode_amd() fails will get an separat patch.
Maybe I will get the rest split up as well. :-)
Torsten
Quoted 8 lines
Thanks.
--
Regards/Gruss,
Boris.Sent from a fat crate under my desk. Formatting is fine. --
Borislav Petkov
maintainerLet me try to answer this as well as I can, peacemeal-wise.
Quoted 17 lines · Torsten Kaiser
Quoted 8 lines · Borislav Petkov
Quoted 4 lines · Torsten Kaiser
Fixup the early AMD microcode loading.
*
load_microcode_amd()(and the helper its using) should not have an cpu parameter.Hmm, I don't think so - we get the cpu handed down from
microcode_coreand besides the early load on 32bit needs to dofind_patch(cpu).Thats why I moved that part into
apply_microcode_amd(). See later on more, why I think that move is the right thing.
And without that the current cpu parameter will only be used to get the (in the early load case not even correctly set up!) per-cpu data. But the only member ofcpuinfo_x86that gets uses is ->x86, the family. Line 159:switch(c->x86)and Line 301: if (proc_fam!)c->x86)
Quoted 4 lines
I really wanted to make that switch from cpu to x86family a separate patch, that it would be more obvious correct, but because of that
amd_bsp_mpbhunk I can't find a good cut and thats why this patch is larger that I would have preferred.
Ok.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The microcode loading is not depending on the CPU it is
Mostly. There are mixed-stepping boxes which need to differentiate between which cpu we're applying the patch for.
Nothing looks at ->x86_model or ->x86_mask during load. It will always load all patches from the current family.
Yes, that's the idea. We want to have all patches for the current family loaded.
Quoted 4 lines
If loading would really depend on the current cpu in a mixed system that would be horrible: Depending on which CPU gets execute
load_microcode_amd()it there would be different patches loaded into RAM?
No, we load the microcode based on CPUID(1).EAX which is in the equivalence table. Look at find_equiv_id().
But for that we need all patches belonging to the current family to be in the cache.
Quoted 6 lines
Quoted 2 lines
Btw, your config boots on my F14h box with "nomodeset" on the command line because it is missing radeon firmware for my gpu.
I suspect a F14h box will never see that hang. It trips over the the C1E erratum and amd_erratum_400[] looks like it only affects 0xfh and 0x10h (like my Phenom II X6).
I could fire up my F10h if needed :)
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
executed and all the loaded patches will end up in a global list for all CPUs anyway.
* Return -1 (like Intelsapply_microcode) when the loading fails, also do not set the active microcode level on failure.Yep, this part I want. Please send it as a separate patch.
OK, will send that together with the resend for
cpu_has_amd_erratum().
Ok.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Quoted 20 lines · Borislav Petkov
Let me try to answer this as well as I can, peacemeal-wise.
Quoted 17 lines · Torsten Kaiser
Quoted 8 lines · Borislav Petkov
Quoted 4 lines · Torsten Kaiser
Fixup the early AMD microcode loading.
*
load_microcode_amd()(and the helper its using) should not have an cpu parameter.Hmm, I don't think so - we get the cpu handed down from
microcode_coreand besides the early load on 32bit needs to dofind_patch(cpu).Thats why I moved that part into
apply_microcode_amd(). See later on more, why I think that move is the right thing.
And without that the current cpu parameter will only be used to get the (in the early load case not even correctly set up!) per-cpu data. But the only member ofcpuinfo_x86that gets uses is ->x86, the family. Line 159:switch(c->x86)and Line 301: if (proc_fam!)c->x86)
Quoted 6 lines
Quoted 4 lines
I really wanted to make that switch from cpu to x86family a separate patch, that it would be more obvious correct, but because of that
amd_bsp_mpbhunk I can't find a good cut and thats why this patch is larger that I would have preferred.Ok.
First moving that hunk, then switching from cpu to x86family did work. See patch 4/5 and 5/5. :-)
Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The microcode loading is not depending on the CPU it is
Mostly. There are mixed-stepping boxes which need to differentiate between which cpu we're applying the patch for.
Nothing looks at ->x86_model or ->x86_mask during load. It will always load all patches from the current family.
Yes, that's the idea. We want to have all patches for the current family loaded.
And thats why switching from cpu to x86family is OK: during *load* we only care for the family.
Quoted 10 lines
Quoted 4 lines
If loading would really depend on the current cpu in a mixed system that would be horrible: Depending on which CPU gets execute
load_microcode_amd()it there would be different patches loaded into RAM?No, we load the microcode based on CPUID(1).EAX which is in the equivalence table. Look at
find_equiv_id().But for that we need all patches belonging to the current family to be in the cache.
I think you confused *load* and *apply*.load_microcode_amd() *loads* the microcode from a firmwarefile into the pcache list.
This wants all patches for the family and thats why my switch here is OK. apply_microcode_amd() *applies* the microcode to the CPU / "loads" it into the CPU. That function (or better its helper find_patch()) need the full stepping/masking. I did not change that function, because in that case 'cpu' makes sense as a parameter, because the microcode needs to be applied for each CPU. (You could argue that that parameter is also stupid: If you ever pass something else as raw_smp_processor_id() then it will BUG(). But removing that parameter would need to change the whole microcode_core.c and also microcode_intel.c. And there that parameter might make sense, so it's better to keep 'cpu' for apply_microcode_amd())
But wrt. you concern about mixed stepping systems: There early microcode loading is definitly broken for 32bit.
The current mainline code will save the patch for the BSP in amd_bsp_mpb and then apply that to all CPUs irregardless of its stepping. With my change in 4/5 to move the amd_bsp_mpb setup to apply time it will now wrongly patch all CPUs with the microcode that was loaded last.
But u8 amd_bsp_mpb[NR_CPUS][MPB_MAX_SIZE] doesn't look like a good idea. Maybe the best way here is to fail apply_microcode_amd() if amd_bsp_mpb already contains an incompatible patch and in load_ucode_amd_ap() only apply it when the cpu_sig matches. Or u8 amd_bsp_mpb[4][MPB_MAX_SIZE] which would support up to 4 different steppings per system.
No patch yet, because I do not understand why that is not a problem on 64bit. load_ucode_amd_bsp() is shared between 32 and 64 so if that code works then I can't really find a need for amd_bsp_mpb at all.
So my current plan is to look into who calls load_ucode_amd_bsp() and load_ucode_amd_ap() and in what sequence (..hopefully in the same sequence on 32 and 64bit...) and if I can find a rational why amd_bsp_mpb can be killed, I will send you a patch. Otherwise I will try to create something that will fail apply_microcode_amd() in a safe way, if CONFIG_MICROCODE_AMD_EARLY gets uses on a mixed system.
Quoted 26 lines
Quoted 6 lines
Quoted 2 lines
Btw, your config boots on my F14h box with "nomodeset" on the command line because it is missing radeon firmware for my gpu.
I suspect a F14h box will never see that hang. It trips over the the C1E erratum and amd_erratum_400[] looks like it only affects 0xfh and 0x10h (like my Phenom II X6).
I could fire up my F10h if needed :)
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
executed and all the loaded patches will end up in a global list for all CPUs anyway.
* Return -1 (like Intelsapply_microcode) when the loading fails, also do not set the active microcode level on failure.Yep, this part I want. Please send it as a separate patch.
OK, will send that together with the resend for
cpu_has_amd_erratum().Ok.
--
Regards/Gruss,
Boris.Sent from a fat crate under my desk. Formatting is fine. --
Borislav Petkov
maintainerQuoted 2 lines · Torsten Kaiser
First moving that hunk, then switching from cpu to x86family did work. See patch 4/5 and 5/5. :-)
I will get to those eventually.
Quoted 7 lines
Quoted 5 lines
No, we load the microcode based on CPUID(1).EAX which is in the equivalence table. Look at
find_equiv_id().But for that we need all patches belonging to the current family to be in the cache.
I think you confused *load* and *apply*.
No I didn't - I meant what I said, I simply pointed at the wrong function. find_cpu_family_by_equiv_cpu() at the beginning of verify_and_add_patch() does look at the family when *loading*.
Quoted 9 lines
That function (or better its helper
find_patch()) need the full stepping/masking. I did not change that function, because in that case 'cpu' makes sense as a parameter, because the microcode needs to be applied for each CPU. (You could argue that that parameter is also
stupid: If you ever pass something else asraw_smp_processor_id()then it willBUG(). But removing that parameter would need to change the wholemicrocode_core.cand alsomicrocode_intel.c. And there that parameter might make sense, so it's better to keep 'cpu' forapply_microcode_amd())
No reason to deal with it if it is only a hypothetical issue.
Quoted 7 lines
But wrt. you concern about mixed stepping systems: There early microcode loading is definitly broken for 32bit. The current mainline code will save the patch for the BSP in
amd_bsp_mpband then apply that to all CPUs irregardless of its stepping. With my change in 4/5 to move theamd_bsp_mpbsetup to apply time it will now wrongly patch all CPUs with the microcode that was loaded last.
Quoted 6 lines
But u8 amd_bsp_mpb[NR_CPUS][MPB_MAX_SIZE] doesn't look like a good idea. Maybe the best way here is to fail
apply_microcode_amd()ifamd_bsp_mpbalready contains an incompatible patch and inload_ucode_amd_ap()only apply it when thecpu_sigmatches. Or u8 amd_bsp_mpb[4][MPB_MAX_SIZE] which would support up to 4 different steppings per system.
Yes, I think 4 should be more than enough.
And I think this should be prepared in save_microcode_in_initrd_amd() like this: look at all APs - if they have mixed steppings, save the following mapping:
CPUID(1).EAX -> patch blob.
Then, at load_ucode_amd_ap() during resume from suspend, we get do CPUID(1) on the current AP, get the patch blob and apply it.
Something like that...
Quoted 3 lines
No patch yet, because I do not understand why that is not a problem on 64bit.
load_ucode_amd_bsp()is shared between 32 and 64 so if that code works then I can't really find a need foramd_bsp_mpbat all.
On 64-bit we create the pcache with the first call to load_microcode_amd() on the first AP. For all the subsequent APs, we do find_patch(cpu) so no need to cache a BSP patch.
Quoted 4 lines
So my current plan is to look into who calls
load_ucode_amd_bsp()andload_ucode_amd_ap()and in what sequence (..hopefully in the same sequence on 32 and 64bit...) and if I can find a rational whyamd_bsp_mpbcan be killed, I will send you a patch.
See above.
Quoted 3 lines
Otherwise I will try to create something that will fail
apply_microcode_amd()in a safe way, ifCONFIG_MICROCODE_AMD_EARLYgets uses on a mixed system.
Remember: we either *must* apply a microcode patch on *all* cores or not at all. But with the suggestion above I think that should be not that hard.
Thanks.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Borislav Petkov
maintainerQuoted 8 lines · Torsten Kaiser
Quoted 7 lines
Quoted 3 lines
* Save the
amd_bsp_mpbon apply, not on load. Otherwise someone could later load an older microcode file that would overwriteamd_bsp_mpb, but would not be applied to the CPUsSee the patch id check in
apply_ucode_in_initrd()?if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id)
Quoted 7 lines
I meant with "load"
load_microcode_amd()not the loading of the microcode into the CPU.1.: load microcode rev X into CPU (early or normal is not important) 2.: get older microcode file that only contains rev Y with Y<X 3.: trigger
load_microcode_amd()with a corrupt file: This will callcleanup()and empty pcache.
Ok, that's actually a good catch. So I wonder: why in hell would we flush the pcache if some of the blobs we're loading are corrupted. So what?! Jacob, what were you thinking - I'd be very interested to know what the idea behind this was.
So, just to refresh everybody: the idea of the pcache is to keep all patches for the current family in memory so that we can support all sorts of hotplug and cpu mixed stepping diddling.
Quoted 7 lines
4.: trigger
load_microcode_amd()with the older file:
* this will now load rev Y into pcache
* rev Y will be returned byfind_patchand copied intoamd_bsp_mpb
* any try to apply rev Y will be skipped inapply_microcode_amd()So now the CPU still correctly has rev X, but
amd_bsp_mpbwill contain the wrong rev Y.
Right, so this shouldn't happen - what should happen is, pcache would hold both X and Y and find_patch would automatically give you the right one.
And this is guaranteed since we keep the patches in a sorted linked list by ->patch_id which is guaranteed to be increasing.
So actually load_microcode_amd() shouldn't be doing cleanup() but simply return ret upwards.
Quoted 4 lines
That copying already in
load_microcodealso is suspicious if someone would only load the microcode but not apply it. But I did not find a codepath inarch/x86/kernel/microcode_core.cto load it without a followup apply.
Yeah, we always load and apply.
So now back to the original problem - load_microcode_amd() shouldn't clear the pcache and, in that case, a subsequent find_patch() would always give the right patch.
Ok?
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Quoted 17 lines · Borislav Petkov
Quoted 8 lines · Torsten Kaiser
Quoted 7 lines
Quoted 3 lines
* Save the
amd_bsp_mpbon apply, not on load. Otherwise someone could later load an older microcode file that would overwriteamd_bsp_mpb, but would not be applied to the CPUsSee the patch id check in
apply_ucode_in_initrd()?if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id)Quoted 7 lines
I meant with "load"
load_microcode_amd()not the loading of the microcode into the CPU.1.: load microcode rev X into CPU (early or normal is not important) 2.: get older microcode file that only contains rev Y with Y<X 3.: trigger
load_microcode_amd()with a corrupt file: This will callcleanup()and empty pcache.
Quoted 8 lines
Ok, that's actually a good catch. So I wonder: why in hell would we flush the pcache if some of the blobs we're loading are corrupted. So what?! Jacob, what were you thinking - I'd be very interested to know what the idea behind this was.
So, just to refresh everybody: the idea of the pcache is to keep all patches for the current family in memory so that we can support all sorts of hotplug and cpu mixed stepping diddling.
Then it would probably be the best to kill free_cache() completely. Which would mean cleanup() should also go.
Which will make unloading microcode_amd.ko impossible. But that is probably a good idea anyway: If you unload the module there is no way to keep pcache.
But I still have another way to kill you: free_equiv_cpu_table() Without that table find_patch() can't work and will not return the correct information.
And that can be triggered by:
* start of load_microcode_amd(): If you reach that function (Only UCODE_MAGIC needs to be in the file) that table is dead.
* __load_microcode_amd(): If the file only contains the table but no patches ("invalid type field in container file section header\n")
Quoted 12 lines
Quoted 7 lines
4.: trigger
load_microcode_amd()with the older file:
* this will now load rev Y into pcache
* rev Y will be returned byfind_patchand copied intoamd_bsp_mpb
* any try to apply rev Y will be skipped inapply_microcode_amd()So now the CPU still correctly has rev X, but
amd_bsp_mpbwill contain the wrong rev Y.Right, so this shouldn't happen - what should happen is, pcache would hold both X and Y and
find_patchwould automatically give you the right one.
Quoted 5 lines
And this is guaranteed since we keep the patches in a sorted linked list by ->patch_id which is guaranteed to be increasing.
So actually
load_microcode_amd()shouldn't be doingcleanup()but simply return ret upwards.
But it already called free_equiv_cpu_table() and so pcache is inaccessible.
And I don't think just preserving equiv_cpu_table for restoring in the error case will be the right solution: If the new firmware file contains a new table with fewer entries (or different entries!) some of the patches in pcache might become inaccessible.
Quoted 10 lines
Quoted 4 lines
That copying already in
load_microcodealso is suspicious if someone would only load the microcode but not apply it. But I did not find a codepath inarch/x86/kernel/microcode_core.cto load it without a followup apply.Yeah, we always load and apply.
So now back to the original problem -
load_microcode_amd()shouldn't clear the pcache and, in that case, a subsequentfind_patch()would always give the right patch.
Not if equiv_cpu_table got mangled.
So should install_equiv_cpu_table() be turned into add_to_equiv_cpu_table() or should pcache save all cpu_sig with each patch, so that find_patch() no longer needs equiv_cpu_table? I suspect saving that in struct ucode_patch might be better, to prevent changes in equiv_id <-> cpu_sig mapping to make a patch inaccessible.
Torsten
Borislav Petkov
maintainerQuoted 10 lines · Torsten Kaiser
Then it would probably be the best to kill
free_cache()completely. Which would meancleanup()should also go.
Which will make unloading microcode_amd.ko impossible. But that is probably a good idea anyway: If you unload the module there is no way to keep pcache.But I still have another way to kill you:
free_equiv_cpu_table()Without that tablefind_patch()can't work and will not return the correct information.
Quoted 5 lines
And that can be triggered by:
* start ofload_microcode_amd(): If you reach that function (OnlyUCODE_MAGICneeds to be in the file) that table is dead.
*__load_microcode_amd(): If the file only contains the table but no patches ("invalid type field in container file section header\n")
If you have root, there are a gazillion ways to kill the system. Those are just a couple more, and a bit complicated even :)
Quoted 1 line
But it already called
free_equiv_cpu_table()and so pcache is inaccessible.
That's the old table. __load_microcode_amd() installs the current one.
Ok, I remember now, the situation is a bit different: the container file has all patches. If we load a new container file, it contains newer replacements + old ones which remained unchanged:
http://marc.info/?l=linux-kernel&m=137427483928693
So actually, when we encounter an error when loading a new blob, we have to *keep* the old pcache and equiv_table. Which means load_microcode_amd() shouldn't free the table *before* doing __load_microcode_amd() but *after* verifying it succeeded.
Quoted 4 lines
And I don't think just preserving
equiv_cpu_tablefor restoring in the error case will be the right solution: If the new firmware file contains a new table with fewer entries (or different entries!) some of the patches in pcache might become inaccessible.
Yes, see above.
Quoted 11 lines
Quoted 10 lines
Quoted 4 lines
That copying already in
load_microcodealso is suspicious if someone would only load the microcode but not apply it. But I did not find a codepath inarch/x86/kernel/microcode_core.cto load it without a followup apply.Yeah, we always load and apply.
So now back to the original problem -
load_microcode_amd()shouldn't clear the pcache and, in that case, a subsequentfind_patch()would always give the right patch.
Quoted 7 lines
Not if
equiv_cpu_tablegot mangled.
So shouldinstall_equiv_cpu_table()be turned intoadd_to_equiv_cpu_table()or should pcache save allcpu_sigwith each patch, so thatfind_patch()no longer needsequiv_cpu_table? I suspect saving that in structucode_patchmight be better, to prevent changes inequiv_id<->cpu_sigmapping to make a patch inaccessible.
Yeah. I remembered :) See above.
Thanks.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Borislav Petkov
maintainerQuoted 9 lines · Torsten Kaiser
Quoted 8 lines
Quoted 2 lines
* Save the
amd_bsp_mpbon every update. Otherwise someone could offline the BSP, update the microcode and this would be lost on resumeHuh, is
amd_bsp_mpbgoing to disappear all of a sudden?And that doesn't matter because when we online the BSP later, it goes through the CPU hotplug notifier
mc_cpu_callback. Or am I missing something?
Quoted 5 lines
Yeah, me correctly describing what I was meaning. ;-)
1.: boot system, BIOS give microcode rev. X
2.: offline the BSP
3.: update microcode to rev. Y with Y > X
Right, with cleanup() removed, when you do that step, you go through load_microcode_amd() which adds the patch to the pcache with __load_microcode_amd() and a subsequent find_patch will give you Y which you memcpy to amd_bsp_mpb.
Quoted 15 lines
Because the BSP is not online rev. Y will not be copied into
amd_bsp_mpb4.: supend
5.: resume, BIOS gives rev. X again
6.:amd_bsp_mpbis empty -> rev. Y will not be reapplied.Quoted 10 lines
Quoted 9 lines
*
apply_ucode_in_initrd()now also needs to saveamd_bsp_mbp, becauseload_microcode_amd()its no longer doing this and its not usingapply_microcode_amd().
* extract common checks and initialisations fromload_ucode_ap()andload_microcode_amd()toload_microcode_amd_early(). The change from cpu to x86family inload_microcode_amd()allows to drop the code messing withcpu_data(cpu), with is wrong anyway because at that point the per-cpucpu_infois not yet setup. And these values would later be overwritten bysmp_store_boot_cpu_info()/smp_store_cpu_info().
Quoted 6 lines
Quoted 4 lines
Right, so I was thinking about this. And the code is pretty nasty: we do a
load_ucode_amd_ap()but we do add the ucode for the BSP:if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK)No, that code will not be reached for the BSP, because it is behind:
That's correct - load_ucode_amd_ap() is not supposed to load ucode on the BSP.
Quoted 11 lines
if (cpu && !ucode_loaded) {
The BSP has cpu == 0. Thats why I adding the following in my patch:arch/x86/kernel/microcode_amd_early.c +3
-0+/* BSP via load_ucode_amd_bsp() */ +if (!cpu) +return ;I don't understand if that is really correct, but that was the original behavior, and I didn't feel competent enough to decree that calling
load_microcode_amd()for the BSP would be save. (The code there is strange: There is aload_ucode_amd_bsp()butload_ucode_amd_ap()will also be called for the BSP.
Yes, this is strange and this is the confusing issue.
Here's how it should work: we want the BSP to load the microcode, put it in the pcache and also write it into amd_bsp_mpb. The 32-bit version of load_ucode_amd_ap() takes it and applies it because we run with paging off at the time.
Quoted 3 lines
And it seems the call to
load_ucode_ap()for the BSP will come from a very different place (viatrap_init()) that the other CPUs. And I did not even try to understand whatX86_32is doing...)
Yep. That question needs sorting too.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Borislav Petkov
maintainerQuoted 6 lines · Torsten Kaiser
Quoted 6 lines
The other problem I see is not updating
c->microcodesince it is going to be overwritten bysmp_store_cpu_info, which is unfortunate.And I don't see where Intel are updating that cpuinfo_x86.microcode field on early load too.
Quoted 8 lines
Quoted 2 lines
So, AFAICT,
c->microcodewould remain unset when we only do early microcode load. But that is something we should fix as a later patch.I don't see a problem with that staying unset.
apply_microcode_amd()directly reads the rev fromMSR_AMD64_PATCH_LEVELso it does not depend on that being correct. And smp_store_(boot)_cpu_info will also read the current rev directly from the CPU to fill ->microcode.
We need to store the actual microcode revision to c->microcode for /proc/cpuinfo and MCE.
Quoted 9 lines
Quoted 8 lines
So I think you should switch
load_ucode_amd_apto__apply_microcode_amd:p =
find_patch()
__apply_microcode_amd(p->mc_data);which should take care of the issue you're seeing, IMHO.
Quoted 7 lines
The issue I'm seeing is that
collect_cpu_info_amd_early()fillsc->x86but notc->x86_vendor.
Which breakscpu_has_amd_erratum()and then Erratum 400 breaks the boot.I did not really want to switch from
apply_microcode_amd()to__apply_microcode_amd()because then I would lose the check if the new microcode is really an upgrade.
Well, if the BSP has already loaded the pcache, there's no need for the AP to parse and load the same microcode blobs file for the initrd, right?
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
*
load_ucode_ap(): Quick exit for !cpu, because withoutload_microcode_amd()getting calledapply_microcode_amd()can't do anything. Exit, if no microcode could be loaded.This could probably be a
WARN_ON(!cpu)to catch errors...No,
load_ucode_ap()will be called for cpu == 0.
This needs fixing IMO...
Btw, thanks for looking at this and asking critical questions!
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Quoted 10 lines · Borislav Petkov
Quoted 9 lines · Torsten Kaiser
Quoted 8 lines
The other problem I see is not updating
c->microcodesince it is going to be overwritten bysmp_store_cpu_info, which is unfortunate.And I don't see where Intel are updating that cpuinfo_x86.microcode field on early load too.
So, AFAICT,
c->microcodewould remain unset when we only do early microcode load. But that is something we should fix as a later patch.
Quoted 8 lines
Quoted 5 lines
I don't see a problem with that staying unset.
apply_microcode_amd()directly reads the rev fromMSR_AMD64_PATCH_LEVELso it does not depend on that being correct. And smp_store_(boot)_cpu_info will also read the current rev directly from the CPU to fill ->microcode.We need to store the actual microcode revision to
c->microcodefor/proc/cpuinfoand MCE.
init_amd() will fill that field. (You could alway compile with CONFIG_MICROCODE_AMD=n and that field would still need filling) And as that will get called before smp_store_(boo)_cpu_info() everything should be fine.
Quoted 12 lines
Quoted 12 lines
Quoted 7 lines
So I think you should switch
load_ucode_amd_apto__apply_microcode_amd:p =
find_patch()
__apply_microcode_amd(p->mc_data);which should take care of the issue you're seeing, IMHO.
The issue I'm seeing is that
collect_cpu_info_amd_early()fillsc->x86but notc->x86_vendor.
Which breakscpu_has_amd_erratum()and then Erratum 400 breaks the boot.
Quoted 7 lines
Quoted 3 lines
I did not really want to switch from
apply_microcode_amd()to__apply_microcode_amd()because then I would lose the check if the new microcode is really an upgrade.Well, if the BSP has already loaded the pcache, there's no need for the AP to parse and load the same microcode blobs file for the initrd, right?
loading != applying.
load_ucode_amd_ap() should probably called apply_ucode_amd_ap() because that is primarily for applying the microcode.
That it also loads it (but really only once thanks to ucode_loaded) is only because nobody else has run yet.
That whole place is hairy: Because on 32bit that seems to run much earlier the 64 and 32 cases are very different.
64bit can and will use pcache/apply_microcode_amd() for the non BSP CPUs, but on 32 bit everything directly applys the patches from initrd memory into the CPUs be directly calling __apply_microcode_amd(). And so bypassing pcache.
See comment above the 32bit version of load_ucode_amd_ap():
/*
* On 32-bit, since AP's early load occurs before paging is turned on, we
* cannot traverse cpu_equiv_table and pcache in kernel heap memory. So during
* cold boot, AP will apply_ucode_in_initrd() just like the BSP. During
* save_microcode_in_initrd_amd() BSP's patch is copied to amd_bsp_mpb, which
* is used upon resume from suspend.
*/
As written in the other email: I'm currently trying to see if I can kill amd_bsp_mpb...
Quoted 9 lines
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
*
load_ucode_ap(): Quick exit for !cpu, because withoutload_microcode_amd()getting calledapply_microcode_amd()can't do anything. Exit, if no microcode could be loaded.This could probably be a
WARN_ON(!cpu)to catch errors...No,
load_ucode_ap()will be called for cpu == 0.This needs fixing IMO...
Can't answer that. I have only seen that it is called for cpu == 0 and that there is no special case für CPU#0 in all the places that call load_ucode_ap()...
Quoted 8 lines
Btw, thanks for looking at this and asking critical questions!
--
Regards/Gruss,
Boris.Sent from a fat crate under my desk. Formatting is fine. --
Borislav Petkov
maintainerQuoted 4 lines · Torsten Kaiser
init_amd()will fill that field. (You could alway compile withCONFIG_MICROCODE_AMD=nand that field would still need filling) And as that will get called before smp_store_(boo)_cpu_info() everything should be fine.
Ah yes, I missed that. Thanks.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Quoted 6 lines · Borislav Petkov
Quoted 4 lines · Torsten Kaiser
Fixup the early AMD microcode loading.
*
load_microcode_amd()(and the helper its using) should not have an cpu parameter.
Quoted 7 lines
Hmm, I don't think so - we get the cpu handed down from
microcode_coreand besides the early load on 32bit needs to dofind_patch(cpu).Quoted 1 line
The microcode loading is not depending on the CPU it is
Mostly. There are mixed-stepping boxes which need to differentiate between which cpu we're applying the patch for.
I redid the patch in 5 parts, hopefully now better to understand. Without the other changes the microcode_amd.c-part of patch 5/5 should make it much more obvious that my change did not result in a different behavior about which patches get loaded into the microcode cache 'pcache'.
Quoted 9 lines
Btw, your config boots on my F14h box with "nomodeset" on the command line because it is missing radeon firmware for my gpu.
Quoted 4 lines
executed and all the loaded patches will end up in a global list for all CPUs anyway.
* Return -1 (like Intelsapply_microcode) when the loading fails, also do not set the active microcode level on failure.Yep, this part I want. Please send it as a separate patch.
That is now patch 1/5.
Patch 2/5 is new, I skipped that part originally because I did not want to make it even bigger...
Quoted 4 lines
So I see a couple of issues in this patch and they should be separated into single patches - one patch taking care of one issue and explaining what the problem is in the commit message (I know you can do that good :)).
I'm still seeing some things in the microcode code that look suspicious:
Why is the X86_64 code updating uci->cpu_sig.rev, but the 32bit version does not? And I can't see anything that reads that value.
Should apply_microcode_amd() really update uci->mc even before checking if the microcode is newer?
The X86_32 hunk in save_microcode_in_initrd_amd() now seems obsolete. load_microcode_amd() is no longer using find_patch() so it doesn't use ucode_cpu_info anymore. But why is that code using boot_cpu_data.cpu_index to find the BSP but always then passing 0 as cpu parameter to load_microcode_amd()? If boot_cpu_data.cpu_index is ever !=0 that code would fail.
... and collect_cpu_info_amd() also looks very weird. If csig would not point to uci->cpu_sig then find_patch() will not be happy. Wouldn't directly passing cpuid_eax(0x00000001) to find_patch() be a better interface? Then the early microcode loading code would not need to access ucode_cpu_info at all.
Torsten