x86 Platforms – Part 3: Early Kernel Boot Process with Microcode, ACPI Overrides, and Initrd

Share this post on:

In this third installment of our x86 Platforms series, following Part 2: UEFI Bootloader Management and Integration with Yocto, we’re going to explore the early kernel boot process in more depth. Specifically, we’ll cover how x86 systems handle microcode updates, ACPI table overrides, and the initrd (initial RAM disk). These elements are critical for configuring the CPU, hardware, and the system during the boot process.

For developers working with Yocto, this understanding is essential to build fully working images.

System Power-On and CPU Initialization (BIOS/UEFI Phase)

When you power on an x86 system, the BIOS/UEFI firmware initializes core hardware like the CPU, RAM, and basic peripherals. This phase is vital as it sets up the low-level configuration that the Linux kernel builds upon.

The UEFI firmware might apply a CPU microcode update during this phase, ensuring your processor is running stable before the system continues. After this initialization, the firmware loads the bootloader (e.g., GRUB), which takes over and loads both the Linux kernel and the initrd into memory, preparing the system for boot.

Linux Kernel Boot: Early Boot Stage Explained

After the bootloader hands control over to the Linux kernel, the kernel takes care of several essential tasks during the early boot stage. This includes applying CPU microcode updates, loading ACPI table overrides, and setting up the initrd (if any).

Applying CPU Microcode Updates

Microcode updates fix CPU bugs and add new functionality without requiring hardware changes.

In the early boot process, even if the BIOS/UEFI applied a microcode update, the kernel can load a newer microcode from the initrd. If you package microcode into the initrd (as we’ll show later), the kernel reads and applies it first, ensuring that the CPU is operating with the most recent updates.

Kernel Configuration for Early Microcode Loading:

To ensure that early microcode loading works correctly in custom kernels, certain kernel options must be enabled during kernel compilation. microcode  related options should be compiled directly into the kernel (not as a module). This enables the “Early load microcode” prompt, which should be set to Y.

Here are the relevant kernel configuration options:

CONFIG_BLK_DEV_INITRD=Y
CONFIG_MICROCODE=y
CONFIG_MICROCODE_INTEL=Y
CONFIG_MICROCODE_AMD=y

These options ensure that the kernel has built-in support for loading microcode at the earliest stage, which is critical for CPU stability and performance improvements.

ACPI Table Overrides

The ACPI (Advanced Configuration and Power Interface) tables provide information about system hardware and power management. These tables, typically provided by the UEFI or BIOS, can be overridden by the kernel if needed to optimize or fix specific hardware configurations.

By including ACPI overrides in the initrd, the kernel loads and applies these changes after the microcode. This ensures that hardware devices and power management are configured correctly before the main operating system loads.

Loading the (optional) Initrd (Initial RAM Disk)

The initrd contains a temporary root filesystem that the kernel uses during boot. It holds critical drivers and modules needed to initialize hardware and mount the actual root filesystem.

Once the kernel has applied the microcode and ACPI table overrides, it uses the initrd to set up the required drivers and scripts for hardware initialization.

Element Packaging

The microcode and ACPI overrides must be packaged in the form of  cpio archives and each element must be appended to the initial initrd (if any). The kernel processes the cpio archives in the order they are concatenated, ensuring that microcode is applied first, followed by ACPI table overrides, and then the rest of the initrd.

Below is a simple diagram showing how the final initrd file is structured after concatenating the microcode, the (optional) ACPI override, and the (optional) initrd itself:

+----------------+ +--------------------+ +------------------------------+
| Microcode.cpio | | ACPI Override.cpio | | core-image-initramfs.cpio.gz |
+----------------+ +--------------------+ +------------------------------+

This layout ensures the microcode is applied first, followed by the ACPI override, and then the kernel loads the initrd to initialize hardware.

Creating a CPIO Archive for Microcode

For Yocto users, creating the cpio archive for microcode is even more streamlined. There is an existing recipe provided within the meta-intel layer that uses the iucode_tool utility to process Intel microcode and package it as a cpio archive.

The relevant recipe file is meta-intel/recipes-core/microcode/intel-microcode_<version>.bb .

[...]
SRC_URI = "git://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git;protocol=https;branch=main"
SRCREV = "fbfe741896c55b36fcbf0560a68be96286103556"

DEPENDS = "iucode-tool-native"
S = "${WORKDIR}/git"

[...]

do_compile() {
${STAGING_DIR_NATIVE}${sbindir_native}/iucode_tool \
${UCODE_FILTER_PARAMETERS} \
--overwrite \
--write-earlyfw=${WORKDIR}/microcode_${PV}.cpio \
${S}/intel-ucode/* ${S}/intel-ucode-with-caveats/*
}

[...]

do_deploy() {
install -d ${DEPLOYDIR}
install ${WORKDIR}/microcode_${PV}.cpio ${DEPLOYDIR}/
cd ${DEPLOYDIR}
rm -f microcode.cpio
ln -sf microcode_${PV}.cpio microcode.cpio
}

addtask deploy before do_build after do_compile

This recipe automatically generates and deploys the microcode cpio archive as part of the Yocto build process, saving developers time and ensuring consistency.

Creating a CPIO Archive for ACPI Overrides

Similarly, to apply ACPI table overrides, package the tables into a cpio archive:

SUMMARY = "ACPI tables to override DSDT"

[...]
SRC_URI = "file://mt25qu512.asl"
DEPENDS = "acpica-native cpio-native"

inherit deploy

ACPI_OVERRIDE ?= ""

do_compile() {
rm -rf kernel
install -d kernel/firmware/acpi

for table in ${ACPI_OVERRIDE}; do
iasl -p kernel/firmware/acpi/$table".aml" ${WORKDIR}/$table".asl"
done

find kernel | cpio -H newc --create > ${WORKDIR}/acpi_override_${PV}.cpio

for table in ${WORKDIR}/*".asl"; do
if [ ! -f "$table" ]; then
continue
fi

table=$(basename "$table" ".asl")

if [ -f kernel/firmware/acpi/$table".aml" ]; then
continue
fi

iasl -p kernel/firmware/acpi/$table".aml" ${WORKDIR}/$table".asl"
done
}

[...]
do_deploy() {
install -d ${DEPLOYDIR}
cd ${DEPLOYDIR}
install ${WORKDIR}/acpi_override_${PV}.cpio ${DEPLOYDIR}/
rm -f acpi_override.cpio
ln -sf acpi_override_${PV}.cpio acpi_override.cpio
}

addtask deploy before do_build after do_compile

This recipe converts ACPI source language (.asl) files into AML tables, packages them into a cpio archive, and deploys them for use during boot.

Example Script to Build the Final Initrd

To concatenate the microcode, ACPI overrides, and the initrd, use the following script. This will build the final binary that the kernel will process during boot.

#!/bin/bash

# Define paths to microcode, ACPI overrides, and initrd
MICROCODE="microcode.cpio"
ACPI_OVERRIDE="acpi_override.cpio"
INITRD="core-image-initramfs.cpio.gz"

# Define the output file for the final initrd
FINAL_INITRD="/path/to/final_initrd.img"

# Start with an empty file
: > "${FINAL_INITRD}"

# Concatenate the microcode first
cat "${MICROCODE}" >> "${FINAL_INITRD}"
# Concatenate the ACPI override next
cat "${ACPI_OVERRIDE}" >> "${FINAL_INITRD}"
# (Optional) Concatenate the initrd last
if [ -f "${INITRD}" ]; then
cat "${INITRD}" >> "${FINAL_INITRD}"
fi

# Print success message
echo "Final initrd created at ${FINAL_INITRD}"

This script ensures the microcode is processed first, followed by the ACPI override, and then the initrd. The resulting file is a complete initrd image that can be used during the early boot process.

Conclusion

The early boot process on x86 platforms is essential for configuring hardware and the CPU before the system fully loads. By ensuring that microcode updates, ACPI table overrides, and the initrd are correctly packaged and applied, you can create a highly reliable and efficient boot process.

This guide covered how to create the necessary cpio archives for microcode and ACPI tables, and how to concatenate them with the initrd to form the final image. Now, with this understanding, you can streamline the early boot process in Yocto or any other x86 system you’re developing.

If you have any questions or want to dive deeper into customizing the x86 boot process, feel free to leave a comment below!

Share this post on:

Leave a Reply

Your email address will not be published. Required fields are marked *