Über den Prozess, den der Linux-Kernel mit x86-Mikrocode verarbeitet

Einführung

Lange nicht gesehen. @akachichon.

Nun, zuerst habe ich versucht, einen Artikel über das Root-Dateisystem zu schreiben, aber diesmal habe ich über das Mikrocode-Update geschrieben. Der angegebene Quellcode oder die zur Bestätigung verwendete Umgebung lautet "Ubuntu Server 19.10". Ausführliche Informationen zum Abrufen des Quellcodes für Ubuntu Server 19.10 finden Sie in der Ergänzung "So erhalten Sie den Kernel-Quellcode für Ubuntu Linux".

Informationen zum Root-Dateisystem-Image

Das Root-Dateisystem-Image für Ubuntu Server 19.10 lautet /boot/initrd.img-5.3.0-24-generic. Ich denke, das Root-Dateisystem-Image ist vielen Linux-Distributionen gemeinsam, nicht nur Ubuntu. Lassen Sie uns nun den Inhalt überprüfen. Wenn Sie nur die Liste der Dateien im Root-Dateisystem-Image anzeigen möchten, ist es einfacher, lsinitramfs zu verwenden.

Ausführungsergebnis von 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
(Abkürzung)
var
var/lib
var/lib/dhcp

Ich möchte den Inhalt etwas genauer überprüfen. Verwenden Sie daher den Befehl cpio, um das initramfs-Image zu extrahieren. Ich kann die Dateien jedoch nicht extrahieren, wie ich in lsinitramfs gesehen habe. Es konnte nur das Kernelverzeichnis extrahiert werden.

cpio Ausführungsergebnis


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

Nachdem ich den Grund dafür untersucht hatte, stieß ich auf die kernel.org-Dokumentation.

kernel.Auszug aus 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.

Das war's. Da das initrd-Bild ein "kombiniertes initrd-Bild" ist, kann gefolgert werden, dass der Befehl cpio nur den Teil des cpio-Formats extrahieren konnte, der den ersten Mikrocode enthält. Übrigens wurde mir klar, dass ich viel vages Wissen über Mikrocode hatte. In diesem Sinne werde ich in diesem Adventskalender einen Artikel schreiben, der auf "Wie Linux mit Mikrocode im Root-Dateisystem umgeht" basiert.

Außerdem ** Es tut mir allen AMD-Fans im ganzen Land leid. Diesmal ist nur Intel Gegenstand des Artikels **.

Erklärung basierend auf Intel SDM

Die ersten Sätze von Intel SDM Vol.3 "9.11 MICROCODE UPDATE FACILITIES" sind wichtig, um über Mikrocode-Updates Bescheid zu wissen. Die Zusammenfassung ist wie folgt.

In diesem Dokument werde ich über die Linux-Implementierung für den ersten Schritt "Übergeben von Datenblöcken an das BIOS" schreiben.

Lesen Sie den Linux-Kernel-Code

Die Quellen befinden sich in arch / x86 / kernel / cpu / microcode. Der Dateiname ist zu einfach und sehr leicht zu verstehen.

arch/x86/kernel/cpu/Mikrocode-Dateistruktur


fyoshida@fyoshida:~/source/ubuntu-kernel/linux-source-5.3.0$ ag microcode arch/x86/kernel/cpu/microcode/
amd.c     core.c    intel.c   Makefile

Der Anfang ist load_ucode_bsp ().

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


void __init load_ucode_bsp(void)
{
//Abkürzung
    cpuid_1_eax = native_cpuid_eax(1);

In den Worten von Intel SDM ist cpuid eine Anweisung, die "Informationen zur Prozessoridentifikation erhalten" lautet. Führen Sie die Anweisung cpuid aus, nachdem Sie die Nummer festgelegt haben, die die Informationen angibt, die Sie im eax-Register erfassen möchten. Das Argument von native_cpuid_eax ist eine Zahl, die die Informationen angibt, die Sie abrufen möchten. Und der Rückgabewert ist "die Information, die im eax-Register des Ergebnisses der Ausführung von cpuid gespeichert ist". Die Implementierung ist wie folgt.

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;                     \
}

Aus den Auszugsinformationen von Intel SDM können Sie ersehen, dass die hier erhaltenen Informationen "Typ, Familie, Modell, Schritt-ID" sind.

cpuid_eax_1.png

Sie können auch sehen, dass die in eax gespeicherten Informationen dem folgenden Format folgen: 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;

Hier wird festgestellt, ob die selbst laufende CPU die Mikrocode-Aktualisierung unterstützt. Denken Sie daran, dass die CPUs, die Mikrocode-Updates unterstützen, "P6 und höher" sind. Dann wird load_ucode_intel_bsp () aufgerufen.

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 () hat die folgende Funktionsaufrufkonfiguration.

calltree.png

Der Umriss jeder Funktion ist wie folgt.

Funktionsname Überblick
__load_ucode_intel Suchen Sie den anzuwendenden Datenblock
load_builtin_intel_microcode Verwenden Sie den Firmware Loader, um festzustellen, ob Datenblöcke angewendet werden müssen(※2)
find_microcode_in_initrd Suchen Sie nach Dateien, die Datenblöcke von initrd enthalten, die im Speicher erweitert wurden
collect_cpu_info_early Verwenden Sie die Anweisung cpuid usw., um die Informationen der CPU abzurufen, auf der sie arbeitet.
scan_microcode Suchen Sie den am besten geeigneten Datenblock aus den in der Datei enthaltenen Datenblöcken
apply_microcode_early Übergeben Sie den bisher im Prozess gefundenen Datenblock an das BIOS

Suchen Sie nach Datenblöcken von initrd

Ruft den Speicherort und die Größe des vom Bootloader erweiterten initrd-Images ab. Dieses RAM-Disk-Image ist zu Beginn /boot/initrd.img-5.3.0-24-generic geschrieben. Außerdem beginnt der Anfang dieses Bildes im cpio-Format. Daher wird gemäß dem cpio-Format die Datei, die den Datenblock enthält, nach dem Pfadnamen durchsucht. Informationen zum cpio-Format finden Sie unter "Eine kurze Erklärung und Abbildung von cpio" in Artikel, den ich geschrieben habe.

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


struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa)
{
/*Abkürzung*/
    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;
    }
/*Abkürzung*/
    return find_cpio_data(path, (void *)start, size, NULL);

Der Pfadname lautet wie folgt.

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


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

Suchen Sie nach CPIO-Bildern

find_cpio_data () findet die Datei, die dem vorherigen Pfad im cpio-Image im RAM entspricht, und gibt die Startadresse und die Größe des Bereichs zurück, in dem sie sich befindet. Diese Informationen werden durch die folgende Struktur dargestellt. Daten ist der Anfang der Datei, die den Datenblock enthält, und Größe ist die Dateigröße.

include/linux/earlycpio.h


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

Untersuchen Sie die Dateien in initrd, um die anzuwendenden Datenblöcke zu finden

Die Struktur in der Datei ist wie folgt. genuineintel.png

Gemäß Intel SDM Vol.3 ist das Format jedes Datenblocks wie folgt.

structure_of_microcode.png

Der wichtigste Parameter im Datenblock-Header ist die Prozessorsignatur. Dies ist ein ** Wert, der den Prozessortyp darstellt, auf den die entsprechende Mikrocode-Aktualisierung angewendet wird **, und ein Wert, der aus "Erweiterte Familie, erweitertes Modell, Typ, Familie, Modell, Schritt" besteht. Jeder Datenblock ist für einen bestimmten Prozessortyp ausgelegt.

Da es "für einen bestimmten Prozessor ausgelegt" ist, ist ein Datenblock auf einen Prozessortyp anwendbar. Einige Datenblöcke sind jedoch auf mehrere Prozessortypen anwendbar. Was würden Sie in einem solchen Fall tun? Die Lösung ist die "Optionale erweiterte Signaturtabelle". In dieser Tabelle sind die zweiten und nachfolgenden "anwendbaren Prozessortypen" aufgeführt. Weitere Informationen finden Sie unter "9.11.2 Optionale erweiterte Signaturtabelle" von Intel SDM Vol.3.

Darüber hinaus sind folgende Punkte in Bezug auf den Header zu beachten.

Kurz gesagt, die Verarbeitung von scan_microcode () "analysiert GenuineIntel.bin von Anfang an im Speicher, findet den neuesten Datenblock, der für den laufenden Prozessortyp gilt, und ermittelt dessen Adresse und Größe. Rückkehr. " Sie sollten den Code unter "9.11.3 Prozessoridentifikation" von Intel SDM Vol.3 lesen.

Übergeben Sie den Datenblock an das BIOS

Übergeben Sie schließlich den Datenblock an das BIOS.

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


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

Was macht native_wrmsrl ()? Tatsächlich entspricht der Prozess Intel SDM Vol.3 "9.11.6 Microcode Update Loader". Sie können den Datenblock jetzt an das BIOS übergeben. Ausführliche Informationen finden Sie in Intel SDM Vol.3 "9.11.6 Microcode Update Loader".

wrmsr0x79.png

abschließend

Es war ein Ansturm, aber ich suchte nach einem Datenblock aus dem Root-Dateisystem-Image und sah mir an, wie man ihn anwendet. Es gibt viele Male, in denen ich von dem abweiche, was ich ursprünglich schreiben wollte. Es ist nicht gut, bei der Arbeit abseits der ausgetretenen Pfade zu sein, aber es macht Spaß, private technische Forschung zu betreiben. Schließlich macht es Spaß, den Code und die Literatur zu lesen, um verschiedene Dinge zu wissen. Ich denke, der Adventskalender ist eine gute Gelegenheit, uns eine solche Gelegenheit zu geben.

Nun, dieses Jahr gab es verschiedene Dinge, aber dieses Jahr, wenn nur noch wenige übrig sind, und sogar nach dem nächsten Jahr Happy Hacking!

Ergänzung

So erhalten Sie den Kernel-Quellcode von Ubuntu Linux

Zumindest für Ubuntu Server 19.10 ist es am einfachsten, die Linux-Quelle mit dem Befehl apt abzurufen. Der Quellcode wird mit tar verfestigt und muss daher erweitert werden.

Ausführungsergebnis


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

Referenzen, Seiten

Insgesamt bin ich Intel SDM dankbar. Immerhin ist es ein Muss. Ich denke auch, dass Mamedanukis Artikel ein sehr wertvoller Artikel ist, der auf Japanisch gelesen werden kann. Ich habe es auch gerne gelesen. Wir möchten uns auch bei den Autoren für die anderen im Text zitierten Artikel bedanken.

Recommended Posts

Über den Prozess, den der Linux-Kernel mit x86-Mikrocode verarbeitet
Kompilieren des Linux-Kernels (Linux 5.x unter Ubuntu 20.04)
Ein Hinweis zu den Funktionen der Standard-Linux-Bibliothek, die sich mit Zeit befasst
Informationen zu Linux-Kernelparametern
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)
Probieren Sie den Linux-Kernel-Sperrmechanismus aus
Befehl [linux] kill, um den Prozess abzubrechen
Linux-Prozess
Über Linux
Über Linux
Über Linux
Über die Angelegenheit, die über Stichprobenfehler besorgt war
Linux: Benennen Sie den vom Befehl ps angezeigten Prozess um
Holen Sie sich die neueste Linux-Kernel-Version mit ArchLinux