About the process that the Linux kernel handles x86 microcode

Introduction

Long time no see. @akachichon.

Well, at first I tried to write an article about the root file system, but this time I wrote about microcode update. The source code quoted or the environment used for confirmation is "Ubuntu Server 19.10". For details on how to obtain the source code of Ubuntu Server 19.10, refer to the supplement "How to obtain the kernel source code of Ubuntu Linux".

About the root file system image

The root file system image for Ubuntu Server 19.10 is /boot/initrd.img-5.3.0-24-generic. I think the root file system image is common to many Linux distributions, not just Ubuntu. Now, let's check the contents. If you just want to see the list of files in the root filesystem image, it's easier to use lsinitramfs.

Execution result of lsinitramfs


[email protected]:~/lab/initramfs$ cp /boot/initrd.img-5.3.0-24-generic .
[email protected]:~/lab/initramfs$ lsinitramfs initrd.img-5.3.0-24-generic
.
kernel
kernel/x86
kernel/x86/microcode
kernel/x86/microcode/AuthenticAMD.bin
kernel
(Abbreviation)
var
var/lib
var/lib/dhcp

I want to check the contents a little more, so use the cpio command to extract the initramfs image. However, I can't extract the files as I saw in lsinitramfs. Only the kernel directory could be extracted.

cpio execution result


[email protected]:~/lab/initramfs$ cat initrd.img-5.3.0-24-generic | cpio -id
62 blocks
[email protected]:~/lab/initramfs$ ls
initrd.img-5.3.0-24-generic  kernel

After investigating the reason for this, I came across the kernel.org documentation.

kernel.Excerpt from org


The microcode is stored in an initrd file. During boot, it is read from it and loaded into the CPU cores.

The format of the combined initrd image is microcode in (uncompressed) cpio format followed by the (possibly compressed) initrd image. The loader parses the combined initrd image during boot.

I see. Since the initrd image is a "combined initrd image", it can be inferred that the cpio command could only extract the cpio format part that contained the first microcode. By the way, I realized that I had a lot of vague knowledge about microcode. With that in mind, in this Advent Calendar, I will write an article based on "How Linux handles microcode in the root file system".

In addition, ** I'm sorry to all AMD fans nationwide. This time, only Intel is the scope of the article **.

Description based on Intel SDM

The first few sentences of Intel SDM Vol.3 "9.11 MICROCODE UPDATE FACILITIES" are important to know about microcode updates. The summary is as follows.

In this document, I will write about the Linux implementation for the first step "passing data blocks to the BIOS".

Read the Linux kernel code

The sources are located in arch / x86 / kernel / cpu / microcode. The file name is too straightforward and very easy to understand.

arch/x86/kernel/cpu/microcode file structure


[email protected]:~/source/ubuntu-kernel/linux-source-5.3.0$ ag microcode arch/x86/kernel/cpu/microcode/
amd.c     core.c    intel.c   Makefile

The beginning is load_ucode_bsp ().

arch/x86/kernel/cpu/microcode/core.c


void __init load_ucode_bsp(void)
{
//Abbreviation
    cpuid_1_eax = native_cpuid_eax(1);

cpuid is, in Intel SDM's words, "To obtain processor identification information" instruction. Execute the cpuid instruction after setting the number indicating the information you want to acquire in the eax register. The argument of native_cpuid_eax is a number indicating the information you want to get. And the return value is "the information stored in the eax register of the result of executing cpuid". The implementation is as follows.

arch/x86/include/asm/processor.h


static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
                unsigned int *ecx, unsigned int *edx)
{
    /* ecx is often an input as well as an output. */
    asm volatile("cpuid"
        : "=a" (*eax),
          "=b" (*ebx),
          "=c" (*ecx),
          "=d" (*edx)
        : "0" (*eax), "2" (*ecx)
        : "memory");
}

#define native_cpuid_reg(reg)                   \
static inline unsigned int native_cpuid_##reg(unsigned int op)  \
{                               \
    unsigned int eax = op, ebx, ecx = 0, edx;       \
                                \
    native_cpuid(&eax, &ebx, &ecx, &edx);           \
                                \
    return reg;                     \
}

Below, from the excerpt information from Intel SDM, you can see that the information obtained here is "Type, Family, Model, Stepping ID".

cpuid_eax_1.png

You can also see that the information stored in eax follows the following format: cpuid_eax_2.png

arch/x86/kernel/cpu/microcode/core.c


    switch (x86_cpuid_vendor()) {
    case X86_VENDOR_INTEL:
        if (x86_family(cpuid_1_eax) < 6)
            return;
        break;

Here, it is determined whether the CPU that runs it supports microcode update. Recall that the CPUs that support microcode updates are "P6 and above". Then load_ucode_intel_bsp () is called.

void __init load_ucode_intel_bsp(void)
{
    struct microcode_intel *patch;
    struct ucode_cpu_info uci;

    patch = __load_ucode_intel(&uci);
    if (!patch)
        return;

    uci.mc = patch;

    apply_microcode_early(&uci, true);
}

load_ucode_intel_bsp () has the following function call configuration.

calltree.png

The outline of each function is as follows.

Function name Overview
__load_ucode_intel Find the data block to apply
load_builtin_intel_microcode Use Firmware Loader to determine if there are any data blocks to apply(※2)
find_microcode_in_initrd Search for files containing data blocks from the initrd expanded in memory
collect_cpu_info_early Use the cpuid instruction etc. to get the information of the CPU on which it operates.
scan_microcode Find the most appropriate data block from the data blocks contained in the file
apply_microcode_early Pass the data block found in the process so far to the BIOS

Search for data blocks from initrd

Gets the location and size of the initrd image expanded by the bootloader. This RAM Disk image is /boot/initrd.img-5.3.0-24-generic written at the beginning. Also, the beginning of this image starts in cpio format. Therefore, according to the cpio format, the file containing the data block is searched by the path name. For the cpio format, please refer to "Simple explanation and figure of cpio" in Article I wrote.

arch/x86/kernel/cpu/microcode/intel.c


struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa)
{
/*Abbreviation*/
    size  = (unsigned long)boot_params.ext_ramdisk_size << 32;
    size |= boot_params.hdr.ramdisk_size;

    if (size) {
        start  = (unsigned long)boot_params.ext_ramdisk_image << 32;
        start |= boot_params.hdr.ramdisk_image;

        start += PAGE_OFFSET;
    }
/*Abbreviation*/
    return find_cpio_data(path, (void *)start, size, NULL);

The path name is as follows.

arch/x86/kernel/cpu/microcode/intel.c


static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";

Search for cpio images

find_cpio_data () finds the file corresponding to the previous path in the cpio image in RAM and returns the start address and size of the area where it is located. This information is represented by the following structure. data is the beginning of the file containing the data block and size is the file size.

include/linux/earlycpio.h


struct cpio_data {
    void *data;
    size_t size;
    char name[MAX_CPIO_FILE_NAME];
};

Examine the files in the initrd to find the data blocks to apply

The structure in the file is as follows. genuineintel.png

According to Intel SDM Vol.3, the format of each data block is as follows.

structure_of_microcode.png

The most important parameter in the data block header is the Processor Signature. This is a ** value that represents the processor type to which the corresponding microcode update is applied **, and is a value that consists of "Extended family, extended model, type, family, model, stepping". Each data block is designed for a particular processor type.

One data block is applicable to one processor type because it is "designed for a particular processor". However, some data blocks are applicable to multiple processor types. What would you do in such a case? The solution is the "Optional Extended Signature Table". To put it plainly, this Table lists the second and subsequent "applicable processor types". For more information, read "9.11.2 Optional Extended Signature Table" in Intel SDM Vol.3.

In addition, the points to be held regarding the header are as follows.

In a nutshell, scan_microcode () processing is "parse GenuineIntel.bin in memory from the beginning, find the latest data block applicable to the type of processor you are running, and find its address and size. I will return it. " Please read the code by referring to "9.11.3 Processor Identification" of Intel SDM Vol.3.

Pass the data block to the BIOS

Finally pass the data block to the BIOS.

arch/x86/kernel/cpu/microcode/intel.c


static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
{
/*Abbreviation*/
    /* write microcode via MSR 0x79 */
    native_wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)mc->bits);

What is native_wrmsrl () doing? Actually, the process corresponds to Intel SDM Vol.3 "9.11.6 Microcode Update Loader". You can now pass the data block to the BIOS. For detailed conditions, refer to Intel SDM Vol.3 "9.11.6 Microcode Update Loader".

wrmsr0x79.png

in conclusion

It was a rush, but I looked for a data block in the root filesystem image and looked at how it works. There are many times when you deviate from what you originally wanted to write. It's not good to take a side road at work, but it's fun when doing private technical research. After all it is fun to read the code and literature to know various things. I think the Advent Calendar is a good opportunity to give us such an opportunity.

Well then, there were various things this year, but this year when there are only a few left, and even after next year, Happy Hacking!

Supplement

How to get kernel source code for Ubuntu Linux

At least for Ubuntu Server 19.10, it's easiest to get the linux-source with the apt command. The source code is tarred, so it needs to be unpacked.

Execution result


[email protected]:~$ sudo apt install linux-source
[email protected]:~$ cd /usr/src/linux-source-5.3.0/
[email protected]:/usr/src/linux-source-5.3.0$ ls
debian  debian.master  linux-source-5.3.0.tar.bz2
[email protected]:/usr/src/linux-source-5.3.0$ sudo tar xf linux-source-5.3.0.tar.bz2
[email protected]:/usr/src/linux-source-5.3.0$ ls
debian  debian.master  linux-source-5.3.0  linux-source-5.3.0.tar.bz2
[email protected]:/usr/src/linux-source-5.3.0/linux-source-5.3.0$ ls
arch   COPYING  Documentation  fs       ipc      kernel    MAINTAINERS  net      scripts         sound   update-version-dkms
block  CREDITS  drivers        include  Kbuild   lib       Makefile     README   security        tools   usr
certs  crypto   dropped.txt    init     Kconfig  LICENSES  mm           samples  snapcraft.yaml  ubuntu  virt

References, pages

Overall, I'm grateful to Intel SDM. After all it is a must-have. Also, I think Mamedanuki's article is a very valuable article that can be read in Japanese. I also enjoyed reading it. We would also like to thank the authors for the other articles cited in the text.

Recommended Posts

About the process that the Linux kernel handles x86 microcode
Compiling the Linux kernel (Linux 5.x on Ubuntu 20.04)
A note about the functions of the Linux standard library that handles time
About Linux kernel parameters
Linux kernel release 5.x (2/4)
Linux kernel release 5.x (3/4)
Linux kernel release 5.x (4/4)
Linux kernel release 5.x (1/4)
What is the Linux kernel?
Try the Linux kernel lockdown mechanism
[linux] kill command to kill the process
A quick overview of the Linux kernel
Linux process
About Linux
About Linux
About Linux
About Linux ①
About the matter that was worried about sampling error
Linux: Rename the process displayed by the ps command
Get the latest Linux kernel version with Arch Linux