Introducing cpupdate, a microcode tool for FreeBSD

There is a new microcode updating utility. (Currently for Intel only. Although I'd add AMD and VIA support and write a man page, if there is sufficient interest to put it into base system or ports collection).
Code:
# cpupdate -h
Usage: cpupdate [-qwvviIAVh] [-<f|U> <microcodefile>] [-<u|c> <datadir>] [-x <dirname>]
  -i   show processor information
  -f   show version information of microcode file
  -u   update microcode using microcode files in <datadir>
  -U   update microcode using file <microcodefile>
  -w   write it: without this option cpupdate only simulates updating
  -c   Check integrity of microcode files in <datadir>. Vendor mode must be set!
  -IAV for -c option: set vendor mode to Intel/AMD/VIA [atm only Intel implemented]
  -x   extract microcode files from multi-blob intel-ucode files to <dirname>
  -q   quiet mode
  -v   verbose mode, -vv very verbose
  -h   show this help
#
First, why another CPU microcode updater?
The microcode updater in devcpu-data is rudimentary, has critical bugs and does not support the modern multi-blobbed Intel microcode data file format.
And, this new tool "cpupdate" has a number of useful functions which devcpu-data does not offer.

To stay below the tl;dr threshold, just a very short intro how to build and install:
Download the source and build it using make.
Then run its "show processor information" function to get information about your CPU and microcode release:
(example output shown for Xeon 56xx series with unpatched memory sinkhole bug)
Code:
# ./cpupdate -i
Found CPU(s) from Intel
Processor Core: 0
-> CPUID: 206c2  Family: 06  Model: 2C  Stepping: 02  uCodeRev: 0000001A
<other cores' output snipped>
#
(Note: Use the verbosity flags -v[v] to get more detailed infos)
(Note 2: The cpuctl kernel module is needed to use cpupdate. If it is not loaded you'll get an error message reminding to load it)
(Note 3: The examples assume the current working directory is the one where you built the program)

Go to platomav's microcode collection page, read it, and go to the Intel subdirectory.
Search for the CPUID string (in the above example '206c2') and download the newest microcode file you find for your CPU, if there is any. In the example the file "cpu206C2_plat03_ver0000001D_2015-08-04_PRD_F7DC758B.bin" matched the CPUID and contains microcode 0x1D, which should fix the memory sinkhole.
Your CPUID is probably different. So just hit ctrl-F, enter your CPUID, and let the browser search for a matching file.

(Note: If your computer vendor offers a BIOS update that updates the microcode, updating the BIOS is the preferred way - these microcodes are intended mainly for updating computers for which no updated BIOS is available. So, in case you cannot obtain that microcode revision by a BIOS update, download the microcode file.)

Then verify the integrity of the downloaded file:
Code:
% ./cpupdate -I -f /home/myname/Downloads/cpu206C2_plat03_ver0000001D_2015-08-04_PRD_F7DC758B.bin
  Blob 1 of 1 headers info:
    ucode rev  0x0000001d
-> Family: 06  Model: 2C  Stepping: 02
%
If there is no complaint (checksums etc) and the data matches the processor, then you can do the actual processor update with...
Code:
# ./cpupdate -w -U /home/myname/Downloads/cpu206C2_plat03_ver0000001D_2015-08-04_PRD_F7DC758B.bin
(Note: Remove the -w option to only simulate the update process; the output will reflect this accordingly.)
If you want to avoid having to configure every computer individually, just save the file in Intel's Family-Model-Stepping FF-MM-SS scheme, in this case "06-2c-02".
Then you can use just the directory containing the update files as parameter, like in the sample rc file:
Code:
# ./cpupdate -vv -w -u /usr/local/share/microcodes/intel-ucode
Found CPU(s) from Intel
/dev/cpuctl0 identification successful!
Update filename: 06-2c-02!
File /usr/local/share/microcodes/intel-ucode/06-2c-02 contains 1 update blobs
Updated core 0 from microcode revision 0x001a to 0x001d
<snipped other cores' output>
#
Finally, if all is to your satisfaction, you can make the update changes permanent by adding a rc script to /etc/rc.d and adding a line to /etc/rc.conf:
Code:
cpupdate_enable="YES"
A sample rc script (rc.cpupdate) is included in the github repo. Verify that it matches your preferences, edit it and place it in /etc/rc.d/cpupdate. In particular you probably will want to replace the -vv option with the -q option to avoid the program spamming the screen with messages about cpu microcode uploading.
In the sample script, the microcode files being placed in /usr/local/share/microcodes/intel-ucode, and the executable in /usr/local/sbin/cpupdate.
As cpupdate requires the cpuctl kernel module being loaded, add to /boot/loader.conf:
Code:
cpuctl_load="YES"
(Note 1: If you want to use a particular file, not matching the Intel naming convention, you would instead use the -U option together with the complete path and file name.
(Note 2: This rc config above is probably not the best way, as I am no rc expert, and as long no update removes cpu features, it should not matter when in the system startup process the microcode gets updated. Expert comments and advice very welcome!)

If you discover bugs, experience difficulties, have questions or suggestions, I would appreciate if you'd leave a comment.
 
might I suggest that the most useful thing you could do now is either extend it using libfetch to actually go get the firmware itself or supply scripts to do the work you suggest in terms of firmware fetching.
 
A little birdie told me that the microcode for my Haswell processor was available to use... now thanks to your great work I don't have to rely on my OEM from HP to release a BIOS update (if they ever do release one). I can't wait to try this out. Thank you!
 
So it look a little figuring out, but I got it to update the microcode for my Haswell processor. Now it can use the IBRS mitigation. Now I just need to figure out how to issue the cpucontrol -e command to the processors automatically after the CPU update so they enable the mitigation after it is updated.
 
I'll add an -e option tonight.

I think I should rework the cpupdate program for some reasons anyway.
  • cleaner code and output
  • adding multiple-directory functionality with clear order of execution and priority of microcode files (good for local microcode repository)
  • making it a drop-in-replacement for devcpu-data (changing option flags accordingly)
  • adding the reload processor flags function that is available in kernel versions from beginning of Jan 2018
  • adding documentation
I'll post when I made an updated version.

You can use devcpu-data instead, of course.
But you have then to use the old microcode format, and this means with old cpucontrol program that multiple microcodes might be uploaded subsequently (which Intel wanted to avoid, thus the new microcode file format). And as there are two bugs in cpucontrol's updater, this can occasionally fail due to incorrect recognition of a recently uploaded microcode version. Even if it was the BIOS which uploaded a microcode over the original hardcoded one.
The FreeBSD devs have been alerted of that bug already in 2014, and again in January 2018, but do not bother fixing it.
This was one of the reasons why I made that cpupdate utility.
 
Update:
I just looked into the current version of devcpu-data to see what was changed to make IBRS etc work.
Then I decided that I won't bother trying to change options to make cpupdate "compatible" to devcpu-data aka cpucontrol.
That cpucontrol thing works that way that it needs to get called for every core. (probably per shell script, we'll see when 11.2 is out)
It then reads every microcode file and checks whether it can be applied to that kind of processor. Thus for a 12-core-processor it reads 2000 files! (~170 microcode files, repeated for each core).
After that cpucontrol must be called with the -e option, again for each core. Thus for a 12-core processor it needs to be run 24 times.
This is just too stupid imho.
But, I know that devcpu-data is sacrosanct for the devs, because it was paid sponsored by the FreeBSD foundation..

I don't care. I'll stay with cpupdate's approach of reading only the correct microcode file just once and apply the microcodes in it to every core.
Intel has with good reason changed the microcode file format that way to include all microcode variants applicable to a processor series in a single file.
This way even with multiprocessor system with processors of different revisions (i.e. cpu flags) reading just one microcode file is sufficient, and uploading multiple versions of microcodes is easily avoided.

So I guess it would make no sense to add an -e option, but instead let cpupdate do this automatically if applicable/necessary, to keep things simple.
Remember, the aim is that cpupdate is compatible with older FreeBSD versions, just to allow them to update microcodes, if there is no BIOS update available. It does not require to update to newest (and unstable) sources.

Will have a nap now and and add IBRS etc support after that.
 
The only reason I mentioned cpucontrol -e is this. When FreeBSD's kernel is first loaded, none of the CPU's have the IBPB feature installed. Once cpupdate is run, in the instance for my Haswell processor, the microcode is updated from 17 to 24, and the IBPB feature is installed. However, the kernel won't recognize that until cpucontrol -e /dev/cpuctl0 , etc. is run for each CPU. Only then will the kernel actually activate and use the IBRS mitigation. I had to reread the FreeBSD SpeculativeExecutionVulnerabilities wiki to understand how the

hw.ibrs_active hw.ibrs_disable

OIDs work.

I believe right now cpupdate is the only solution that allows the individual Intel microcode files that apply to an individual's CPU to be loaded. I think I saw some work to attempt to get sysutils/devcpu-data to load individual microcode files with no luck yet.

So again I say thank you for your program. Now I will evaluate how much slower the IBRS mitigation is. The real test will be when I decide to update FreeBSD again and recompile everything. But right now the mitigation is barely noticable and it seems stable.

Also, for anyone reading, just to clarify: IBPB stands for Indirect Branch Predictor Barrier, the feature installed with Intel's updated microcode files. That feature allows the kernel mitigation called IBRS or Indirect Branch Restricted Speculation to work.
 
Have added the code that does automatically what cpucontrol -e does after updating the microcode.
It's uploaded to Github now.
(Didn't test it yet. It compiles, but to test I'd have to reboot which I am not in the mood of, and it would be useless anyway as my cpus aren't gifted with Meltdown updates yet).
There is some program that prints out the CPU features listed at the head of dmesg, but I didn't find it yet. Otherwise it might be sufficient to just reboot and see if IBRS, IBPB etc show up in dmesg...

I am not exactly happy to find that the new Intel microcode releases and documentations seem to be confidential and not publicly available.
It seems that they are being delivered only to OEMs for BIOS updates, but not to the public.
So if one does not have BIOS updates available for their computers, there is no way except than extracting the microcodes with tools like https://github.com/platomav/MCExtractor from the BIOS updates that HP etc are pushing out these days.
But it is not even clear to me whether the MCE tool supports the new Intel microcode format, as this is still not publicly documented.
It could well be the case that it extracts a combined new format microcode file as single files with different flags or not at all.
(I had to do a bit simple reverse-engineering to find its format details myself)

Anyway as I did not find any official downloads for the newly released updates yet, I do not know which format they are in.
I'd be very grateful if you could post the output of cpupdate -Ivv -f <microcodefilename> , as this would reveal some useful information about the internals of the microcode file you got.
 
So I tried to compile your updated code and it gave me an implicit declaration warning, then errored out while trying to link. I'm using Clang 6 from FreeBSD-current. Here's the output of that:
Rich (BB code):
administrator@riotskates:/onetr/cpupdate % make -f Makefile
echo cpupdate.full: /usr/lib/libc.a  >> .depend
Warning: Object directory not changed from original /onetr/cpupdate
cc  -O2 -pipe -fno-strict-aliasing -march=haswell  -g -MD  -MF.depend.cpupdate.o -MTcpupdate.o -std=gnu99 -fstack-protector-strong    -Qunused-arguments  -c cpupdate.c -o cpupdate.o
cpupdate.c:155:5: warning: implicit declaration of function 'WARN' is invalid in C99 [-Wimplicit-function-declaration]
    WARN(0, "error opening %s for writing", dev);
    ^
cpupdate.c:160:5: warning: implicit declaration of function 'WARN' is invalid in C99 [-Wimplicit-function-declaration]
    WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
    ^
2 warnings generated.
cc  -O2 -pipe -fno-strict-aliasing -march=haswell  -g -MD  -MF.depend.intel.o -MTintel.o -std=gnu99 -fstack-protector-strong    -Qunused-arguments  -c intel.c -o intel.o
cc -O2 -pipe -fno-strict-aliasing -march=haswell -g -std=gnu99 -fstack-protector-strong -Qunused-arguments  -o cpupdate.full cpupdate.o intel.o
cpupdate.o: In function `do_eval_cpu_features':
/onetr/cpupdate/cpupdate.c:155: undefined reference to `WARN'
/onetr/cpupdate/cpupdate.c:160: undefined reference to `WARN'
cc: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1

Stop.
make: stopped in /onetr/cpupdate

And the output from cpupdate -Ivv -f <microcodefilename>:
Code:
administrator@riotskates:/usr/local/share/microcodes/intel-ucode % sudo cpupdate -Ivv -f cpu306C3_plat32_ver00000024_2018-01-21_PRD_FCD4700E.bin
File cpu306C3_plat32_ver00000024_2018-01-21_PRD_FCD4700E.bin contains 1 update blobs
  Blob 1 of 1 headers info:
    Date 2018/01/21
    ucode rev  0x00000024
    Header ver 0x00000001
    Loader rev 0x00000001
    Data size  23504 (0x5bd0)
ProcessorType:  00
ExtFamily: 00  ExtModel: 03
IntFamily: 06  IntModel: 0C
SignatureInt:    306C3
-> Family: 06  Model: 3C  Stepping: 03
    Flags      50 (0x00000032)
Has no extended header.
I simply renamed the file cpu306C3_plat32_ver00000024_2018-01-21_PRD_FCD4700E.bin to 06-3c-03 and your program accepted it. I have found microcode updaters are quite finicky about which forms of microcode files they accept (fw, bin, or dat). Luckily, cpupdate accepts the bin file format that platomav's microcode collection page uses. I have also been watching Intel's Microcode Revision Guidance like a hawk. The latest one I read is from February 26, 2018. But now it seems Intel has decided to require a login in order to see it anymore.

Which firmware are you looking for? I think Intel has put into production most microcode updates for Skylake, Coffee Lake, Kaby Lake, Broadwell, and Haswell CPUs with the exception of some Haswell and Broadwell Xeons. Ivy Bridge and Sandy Bridge along with the Haswell and Broadwell Xeons are in Beta status, which is a good sign. It means at least they are testing their updates.
 
Oops, I notice I have downloaded the wrong (not newest) kernel source tree, which does not yet define the macro CPUCTL_EVAL_CPU_FEATURES.
This is used as switch to include the code that does the stuff that does the functionality of cpucontrol -e eval option in cpupdate.
So I did not notice the error in the code (forgot to rename the WARN macro to INFO). Will have to download the correct source tree before I can test it myself.
Have now changed that incorrect macro and uploaded the (hopefully) now correct file.

I also noticed that I cannot access the Intel microcode guidance file with my Intel forums account :(
They have obviously decided to do all that in secrecy now :(

The stats output shows that it's a single-blob file, thus should be the old format. The filename looks like it is from platomav's library, which in turn probably got extracted from some BIOS.
Theoretically it should be possible to use that with devcpu-data by putting it into the directory where all these .fw files are.

But I didn't try that out.
It could be simply the problem that cpucontrol does not follow the procedure defined by Intel for reading the currently installed microcode version.
The PR in FreeBSD bugzilla about this is already 4 years old, and what's even more funny, the PR was written by the guy who wrote the Linux microcode uploader :)
I did experiments myself and found out that this indeed leads to occasionally bogus microcode versions being detected.

The .dat files are an archive format that needs to be extracted by the ucode-tool. (It is the old format that Intel announced to discontinue some time in future.)
But on the public download page the most recent available download is the one from Nov. 2017.

I particularly am looking for the Westmere generation updates, which were classified as "Planned" in the last microcode guidance I read a few weeks ago.
So they will be released in a few months, if ever. I am still not sure whether Intel is telling the truth, as I have information dated January that Intel stated that they will never release an update for these.
Even the memory sinkhole fixes for this generation were never released to the public, only to OEMs. :( So I have little hope for my old processors :(
 
I recompiled your updated code and it works just fine and automatically does the CPUCTL_EVAL_CPU_FEATURES functionality on all of the CPUs. So there's no need to do cpucontrol -e manually anymore.

Also, I uploaded the last version of the Intel Microcode Guidance I had access to from February 26th. I don't understand why Intel is being so secretive about it.
 

Attachments

  • microcode-update-guidance.pdf
    362.6 KB · Views: 1,154
Thank you very much for uploading!
It is really nice to see that my Westmere cpus have now changed from "Planning" to "Pre-Beta" :) This makes hope that there might be a microcode update in the near future :)
 
I tried the Intel Microcode Guidance link and it seems to be working again. The new guidance is dated March 1st. Perhaps Intel prevented access by mistake?
 
First of all: thank you very much for cpupdate. I have tested both methods to update the microcode of an Intel CPU, and I covered my findings here (in German):

I have two questions regarding cpupdate as they limit me to switch from the older devcpu-data method to cpupdate:
  1. In https://github.com/kernschmelze/cpupdate/blob/master/README.md you mention that cpupdate should not be installed via pkg, instead the ports method should be used. According to Absolute FreeBSD 3rd Edition ports should not be used directly on a production system (see the boooks Chapter 16, page 371: Ports and Production: I would strongly encourage you to build build your own package repository with poudriere and manage your servers' ports from that repository. Upgrading ports directly installed on a host is annoying and difficult. [...] You have been warned.) So what is the reason why a user should not install cpupdate via "pkg install cpupdate"?
  2. Updating the microcode via devcpu-data is possible at a very early stage of the system boot (using https://reviews.freebsd.org/D16370?id=45663). In my example I see e.g. all four "Structured Extended Features3=0x9c000000<IBPB,STIBP,L1DFL,SSBD>" in dmesg at the top. When I use cpupdate I see only "Structured Extended Features3=0xc000000<IBPB,STIBP>" at the beginning and only after a while after mounting the filesystem all four features - see https://www.thomas-krenn.com/de/wiki/Intel_Microcode_unter_FreeBSD_aktualisieren#Unterschiede So my question is: will it be possible in the future, that cpupdate uses this early method of loading the new microcode?
    • It works, or in other words - cpupdate is not necessary to do the early loading. As the early loading mechanism supports the microcode format which contains only the microcode for a specific CPUID, one can configure it in /boot/loader.conf. In my example I have just set two lines in /boot/loader.conf (containing cpu_microcode_load="YES" and cpu_microcode_name="/usr/local/share/cpupdate/CPUMicrocodes/primary/Intel/06-3c-03" for my CPU - you have to use your FF-MM-SS family-model-stepping file for your CPUID):
Code:
root@freebsd12:~ # cpupdate -i
Found CPU(s) from Intel
Core 0 to 1: CPUID: 306c3  Fam 06  Mod 3c  Step 03  Flag 02 uCode 00000024
root@freebsd12:~ # ls -l /usr/local/share/cpupdate/CPUMicrocodes/primary/Intel/06-3c-03 
-rw-r--r--  2 root  wheel  23552 Aug  7 10:45 /usr/local/share/cpupdate/CPUMicrocodes/primary/Intel/06-3c-03
root@freebsd12:~ # vi /boot/loader.conf 
root@freebsd12:~ # cat /boot/loader.conf
cpu_microcode_load="YES"
cpu_microcode_name="/usr/local/share/cpupdate/CPUMicrocodes/primary/Intel/06-3c-03"
root@freebsd12:~ # reboot

After the reboot:
Code:
root@freebsd12:~ # cpupdate -i
Found CPU(s) from Intel
Core 0 to 1: CPUID: 306c3  Fam 06  Mod 3c  Step 03  Flag 02 uCode 00000025
root@freebsd12:~ # dmesg | head -22
---<<BOOT>>---
Copyright (c) 1992-2018 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
    The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 12.0-RELEASE-p2 GENERIC amd64
FreeBSD clang version 6.0.1 (tags/RELEASE_601/final 335540) (based on LLVM 6.0.1)
VT(efifb): resolution 800x600
CPU: Intel(R) Celeron(R) CPU G1820T @ 2.40GHz (2394.51-MHz K8-class CPU)
  Origin="GenuineIntel"  Id=0x306c3  Family=0x6  Model=0x3c  Stepping=3
  Features=0xbfebfbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CLFLUSH,DTS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE>
  Features2=0x4ddaebbf<SSE3,PCLMULQDQ,DTES64,MON,DS_CPL,VMX,EST,TM2,SSSE3,SDBG,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,MOVBE,POPCNT,TSCDLT,XSAVE,OSXSAVE,RDRAND>
  AMD Features=0x2c100800<SYSCALL,NX,Page1GB,RDTSCP,LM>
  AMD Features2=0x21<LAHF,ABM>
  Structured Extended Features=0x2603<FSGSBASE,TSCADJ,ERMS,INVPCID,NFPUSG>
  Structured Extended Features3=0x9c000000<IBPB,STIBP,L1DFL,SSBD>
  XSAVE Features=0x1<XSAVEOPT>
  VT-x: PAT,HLT,MTF,PAUSE,EPT,UG,VPID
  TSC: P-state invariant, performance statistics
real memory  = 2147483648 (2048 MB)
avail memory = 2002997248 (1910 MB)
CPU microcode: updated from 0x24 to 0x25
 
Back
Top