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".
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
fyoshida@fyoshida:~/lab/initramfs$ cp /boot/initrd.img-5.3.0-24-generic .
fyoshida@fyoshida:~/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
fyoshida@fyoshida:~/lab/initramfs$ cat initrd.img-5.3.0-24-generic | cpio -id
62 blocks
fyoshida@fyoshida:~/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 **.
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.
Has the ability to fix errata by loading Intel-provided data blocks into the processor. This feature is called microcode update.
The BIOS must provide a mechanism for performing microcode updates during system initialization.
A BIOS with this mechanism (update loader) is responsible for loading updates into the processor during system initialization. There are two steps to loading into the processor. ** The first step is to pass the required update data block (* 1) to the BIOS **, and ** the second step is for the BIOS to load the update data block into the processor **.
In this document, I will write about the Linux implementation for the first step "passing data blocks to the BIOS".
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
fyoshida@fyoshida:~/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".
You can also see that the information stored in eax follows the following format:
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.
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 |
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";
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];
};
The structure in the file is as follows.
According to Intel SDM Vol.3, the format of each data block is as follows.
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.
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".
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!
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
fyoshida@fyoshida:~$ sudo apt install linux-source
fyoshida@fyoshida:~$ cd /usr/src/linux-source-5.3.0/
fyoshida@fyoshida:/usr/src/linux-source-5.3.0$ ls
debian debian.master linux-source-5.3.0.tar.bz2
fyoshida@fyoshida:/usr/src/linux-source-5.3.0$ sudo tar xf linux-source-5.3.0.tar.bz2
fyoshida@fyoshida:/usr/src/linux-source-5.3.0$ ls
debian debian.master linux-source-5.3.0 linux-source-5.3.0.tar.bz2
fyoshida@fyoshida:/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
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