Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 Jul 2018 14:31:16 -0400
From:      Mark Johnston <markj@freebsd.org>
To:        freebsd-arch@freebsd.org
Subject:   early x86 microcode loading
Message-ID:  <20180712183116.GB15892@raichu>

next in thread | raw e-mail | index | archive | help
I've been working on support for early loading of microcode updates and
wanted to solicit feedback on the approach before starting to get any
code changes reviewed.

Currently we support microcode updates via cpuctl(4), where
cpucontrol(8) passes microcode blobs to the kernel via an ioctl
interface.  Updates are distributed by the sysutils/devcpu-data port.
The scheme has a few shortcomings:
- Microcode updates may introduce new CPU features, but since we load
  microcode from userland, updates are performed well after the kernel
  has done CPU feature detection.
- Updates need to be reapplied after an ACPI suspend/resume, and there's
  currently no mechanism to automatically reapply the update after a
  resume.
- Updates aren't applied until userspace starts running, so there exists
  a window in which the kernel is running without vulnerability
  mitigations provided by microcode updates.

The aim of this work is to instead use the boot loader to load microcode
updates into kernel memory, and modify the kernel to apply the updates
as the first step in BSP and AP initialization, as well as after an ACPI
resume.  To configure an update, one would then just need to add the
following lines to loader.conf:

cpu_ucode_load="YES"
cpu_ucode_name="/boot/firmware/microcode.bin"
cpu_ucode_type="cpu_ucode"

The kernel would then automatically load the update during processor
initialization in the subsequent boot-up.

A given Intel microcode update applies only to CPUs of a specific
<family, model, stepping> tuple, while AMD releases a single update per
processor family.  My plan is to extend cpucontrol(8) to determine the
correct microcode update for the running system, and have the devcpu-data
port install the corresponding file to /boot/firmware.  The port could
then add the following to loader.conf.local:

devcpu_data_load="YES"
devcpu_data_name="/boot/firmware/<update file>"
devcpu_data_type="cpu_ucode"

Currently, the port doesn't automatically enable microcode updates; one
needs to enable the microcode_update rc script after installing the
port.  I'm not yet sure how this should be integrated with early
loading.  For example, if "service microcode_update onestart" enables
early loading, how should early loading be disabled?  Would it be
reasonable for the port to automatically enable updates when it is
installed?

For at least Intel, my intention is to support loading of multiple
update files concatenated together, and have the kernel determine the
correct update to apply.  This is to make it easier to support a cluster
of mixed systems which boot from some shared environment; rather than
needing to select and configure the correct update for each machine, one
can supply all updates as a single file and have each machine select the
right one automatically.  This would come at the expense of some wasted
memory: the combined set of Intel microcode files is currently about
1.8MB in size.

A small patch to the loader is needed to guarantee the 16 byte-alignment
of the loaded microcode update required by Intel.  This will require a
bootcode update on GPT-based systems in order for early loading to work
reliably.

I'm interested in any feedback on the above, and especially any
suggestions on how this feature should be integrated with the
devcpu-data port.  Thanks in advance.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20180712183116.GB15892>