sakaki- / buildkernel

Utility to facilitate the building and installation of EFI secure boot kernels under Gentoo Linux.
GNU General Public License v3.0
43 stars 61 forks source link

Support for early microcode loading #18

Open reanimus opened 6 years ago

reanimus commented 6 years ago

So, I use buildkernel as part of your EFI guide, as I'm sure many others do.

I was looking to enable early microcode updates for my system in order to patch it for recent CPU vulnerabilities. The usual way of doing this is one of three methods:

  1. Load the microcode archive before initrd from bootloader (not applicable; EFI stub)
  2. Append the initrd to the microcode archive (buildkernel doesn't support this, as it builds and appends it internally)
  3. Include the microcode binaries directly in the kernel via CONFIG_EXTRA_FIRMWARE.

I'm currently including it in the kernel via method 3 in buildkernel.conf.

user_conform_config_file() {
    FW=$(ls -xw0 /lib/firmware/intel-ucode/ | sed -r 's/([^ ]+)\s*/intel-ucode\/\1 /g')
    set_kernel_config "EXTRA_FIRMWARE" "\"${FW}\""
}

This will stick the contents of the intel-ucode directory into the list of firmware in the kernel. This works, but it's a bit clunky considering gentoo offers the ability for intel-ucode to produce a cpio archive at /lib/firmware/microcode.cpio that includes the microcode for you. If buildkernel could pick this up and create the appropriate conjoined image, that would be nice. Alternatively, including the support for AMD and Intel microcode updates in buildkernel would be good in general in light of CPU-level issues like this.

sakaki- commented 6 years ago

I use method 3 myself, it comes to the same thing anyway as the initramfs ends up bundled in the kernel (so both can be covered by a single secure-boot signature).

However, if you have a chance, perhaps you could try adding the following in your /etc/buildkernel.conf:

user_modify_initramfs() {
    # do stuff with ${INITRAMFSDIR} directory contents;
    # the cpio archive is already unpacked here upon function entry
    # and will be repacked again automatically for you afterwards

    mkdir -p "${INITRAMFSDIR}/lib/firmware"
    cp -r "/lib/firmware/intel-ucode" "${INITRAMFSDIR}/lib/firmware/"
    # prune firmware in ${INITRAMFSDIR}/lib/firmware/ if desired
    # (but full archive isn't very large, so this is optional)
}

With CONFIG_MICROCODE and CONFIG_MICROCODE_INTEL set, does this work on your system?

If so, it'd be relatively straightforward to add this (force-set of uc options + initramfs append) to the main code, controlled by an option... I'd be happy to accept a PR to this effect ^-^

reanimus commented 6 years ago

Nope. The microcode has a special process for loading that's done in very early kernel init, at a fixed location. The details are located at https://www.kernel.org/doc/Documentation/x86/early-microcode.txt

"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 -p kernel/x86/microcode
cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin (or AuthenticAMD.bin)
find . | cpio -o -H newc >../ucode.cpio
cd ..
cat ucode.cpio /boot/initrd-3.5.0.img >/boot/initrd-3.5.0.ucode.img

I thought it could be stuck into that location as part of the initrd rolled together by buildkernel, but it seems that the kernel specifically expects a separate cpio archive, either prepended to the initramfs cpio archive directly or simply loaded before the initramfs.

sakaki- commented 6 years ago

OK, that's interesting info, thanks. I'll look into adding an option to create the cpio and prepend it, in that case.

alex-serbsky commented 6 years ago

I'll look into adding an option to create the cpio and prepend it, in that case.

I think cpio prepend is not needed. I use this approach:

  1. emerge sys-firmware/intel-microcode with "+split-ucode -initramfs" USE flags.

  2. Add this to kernel config via menuconfig: CONFIG_MICROCODE=y CONFIG_MICROCODE_INTEL=y CONFIG_MICROCODE_OLD_INTERFACE=y CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3c-03" CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"

  3. In buildkernel.conf, set: ADDITIONALGENKERNELOPTS="--all-ramdisk-modules --firmware"

  4. Run buildkernel. Then reboot and ensure microcode is loaded properly: $ dmesg | grep -i "updated early" [ 0.000000] microcode: microcode updated early to revision 0x23, date = 2017-11-20

sakaki- commented 6 years ago

@alex-serbsky, this approach does indeed work and is the 'method 3' mentioned by @reanimus above (and I currently use it).

The only (minor) issue is the need to set CONFIG_EXTRA_FIRMWARE correctly via a grep, whereas with the cpio approach, you can directly build the cpio with just the necessary components, via iucode_tool -S --write-earlyfw=<install_path>/early_ucode.cpio /lib/firmware/intel-ucode/* per the wiki (or force the initramfs USE flag to sys-firmware/intel-microcode, which does basically the same thing).

VTimofeenko commented 5 years ago

For somebody stumbling upon this from search:

I'll look into adding an option to create the cpio and prepend it, in that case.

I think cpio prepend is not needed. I use this approach:

1. emerge sys-firmware/intel-microcode with "+split-ucode -initramfs" USE flags.

2. Add this to kernel config via menuconfig:
   CONFIG_MICROCODE=y
   CONFIG_MICROCODE_INTEL=y
   CONFIG_MICROCODE_OLD_INTERFACE=y
   CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3c-03"
   CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"

3. In buildkernel.conf, set:
   ADDITIONALGENKERNELOPTS="--all-ramdisk-modules --firmware"

4. Run buildkernel. Then reboot and ensure microcode is loaded properly:
   $ dmesg | grep -i "updated early"
   [    0.000000] microcode: microcode updated early to revision 0x23, date = 2017-11-20

To determine the exact value needed for CONFIG_EXTRA_FIRMWARE, run the iucode_tool -S -l /lib/firmware/intel-ucode/* from that wiki article

_microcode=$(iucode_tool -S -l /lib/firmware/intel-ucode/* 2>&1 | tail -n1 | sed 's/\/[0-9].*$//' | awk '{print $1}'); iucode_tool -S -l /lib/firmware/intel-ucode/* 2>&1 | grep " bundle ${_microcode}"

sarnold commented 4 years ago

This is basically my 3rd-priority update problem-of-the-day, and for a minute I thought genkernel would handle it but support does not seem to exist in genkernel-next-70 (only in sys-kernel/genkernel-3.5.0.7 and up according to the wiki page). I was just about to file a different buildkernel issue (you're welcome ;) but since I can no longer build my kernel easily I was hoping for some clarity here, since I have way too many machines/arch builds to fiddle with this manually every time I touch a kernel build (also I just got the exact answer I did not want in #toolchain...)

So, did you decide on a fix for this one yet?

sakaki- commented 3 years ago

31 Oct 2020: sadly, due to legal obligations arising from a recent change in my 'real world' job, I must announce I am standing down as maintainer of this project with immediate effect. For the meantime, I will leave the repo up (for historical interest, and it may be of use to others); however, I plan no further updates, nor will I be accepting / actioning further pull requests or bug reports from this point. Email requests for support will also have to be politely declined, so, please treat this as an effective EOL notice.

For further details, please see my post here.

With sincere apologies, sakaki ><