]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stub: Add support for .ucode UKI section
authorTobias Fleig <tfleig@meta.com>
Tue, 2 Apr 2024 10:54:30 +0000 (03:54 -0700)
committerTobias Fleig <tfleig@meta.com>
Fri, 19 Apr 2024 12:58:46 +0000 (05:58 -0700)
This commit adds support for loading, measuring and handling a ".ucode"
UKI section. This section is functionally an initrd, intended for
microcode updates. As such it will always be passed to the kernel first.

man/systemd-stub.xml
src/boot/efi/stub.c
src/fundamental/uki.c
src/fundamental/uki.h

index 756654854ee92f7737ca0d7c9ab1e8faf8a23133..5cb9ca55345d9960bf3c6341e0d5da4a07be0104 100644 (file)
@@ -70,6 +70,9 @@
 
       <listitem><para>An <literal>.initrd</literal> section with the initrd.</para></listitem>
 
+      <listitem><para>A <literal>.ucode</literal> section with an initrd containing microcode, to be handed
+      to the kernel before any other initrd. This initrd must not be compressed.</para></listitem>
+
       <listitem><para>A <literal>.splash</literal> section with an image (in the Windows
       <filename>.BMP</filename> format) to show on screen before invoking the kernel.</para></listitem>
 
     core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
 
     <para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
-    every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
+    every type of initrd will be measured two or three times: the initrds embedded in the kernel image will be
     measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized
     from configuration extensions) will be measured to both PCR 9 and PCR 12; the initrd synthesized from
     system extensions will be measured to both PCR 4 and PCR 9. Let's summarize the OS resources and the PCRs
             <entry>4 + 9 + 11</entry>
           </row>
 
+          <row>
+            <entry>Microcode initrd (embedded in unified PE binary)</entry>
+            <entry>4 + 9 + 11</entry>
+          </row>
+
           <row>
             <entry>Default kernel command line (embedded in unified PE binary)</entry>
             <entry>4 + 11</entry>
index 58586f942faa4ccb6bee87450be72e260944af76..7dab31aa44b46699156358e4aee0c6c4d960a28b 100644 (file)
@@ -26,28 +26,27 @@ DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION
 
 DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
 
-static EFI_STATUS combine_initrd(
-                EFI_PHYSICAL_ADDRESS initrd_base, size_t initrd_size,
-                const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
+/* Combine initrds by concatenation in memory */
+static EFI_STATUS combine_initrds(
+                const void * const initrds[], const size_t initrd_sizes[], size_t n_initrds,
                 Pages *ret_initr_pages, size_t *ret_initrd_size) {
 
-        size_t n;
+        size_t n = 0;
 
         assert(ret_initr_pages);
         assert(ret_initrd_size);
 
-        /* Combines four initrds into one, by simple concatenation in memory */
-
-        n = ALIGN4(initrd_size); /* main initrd might not be padded yet */
-
-        for (size_t i = 0; i < n_extra_initrds; i++) {
-                if (!extra_initrds[i])
+        for (size_t i = 0; i < n_initrds; i++) {
+                if (!initrds[i])
                         continue;
 
-                if (n > SIZE_MAX - extra_initrd_sizes[i])
+                /* some initrds (the ones from UKI sections) need padding,
+                 * pad all to be safe */
+                size_t initrd_size = ALIGN4(initrd_sizes[i]);
+                if (n > SIZE_MAX - initrd_size)
                         return EFI_OUT_OF_RESOURCES;
 
-                n += extra_initrd_sizes[i];
+                n += initrd_size;
         }
 
         _cleanup_pages_ Pages pages = xmalloc_pages(
@@ -56,27 +55,21 @@ static EFI_STATUS combine_initrd(
                         EFI_SIZE_TO_PAGES(n),
                         UINT32_MAX /* Below 4G boundary. */);
         uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr);
-        if (initrd_base != 0) {
+        for (size_t i = 0; i < n_initrds; i++) {
+                if (!initrds[i])
+                        continue;
+
                 size_t pad;
 
-                /* Order matters, the real initrd must come first, since it might include microcode updates
-                 * which the kernel only looks for in the first cpio archive */
-                p = mempcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
+                p = mempcpy(p, initrds[i], initrd_sizes[i]);
 
-                pad = ALIGN4(initrd_size) - initrd_size;
+                pad = ALIGN4(initrd_sizes[i]) - initrd_sizes[i];
                 if (pad > 0)  {
                         memzero(p, pad);
                         p += pad;
                 }
         }
 
-        for (size_t i = 0; i < n_extra_initrds; i++) {
-                if (!extra_initrds[i])
-                        continue;
-
-                p = mempcpy(p, extra_initrds[i], extra_initrd_sizes[i]);
-        }
-
         assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p);
 
         *ret_initr_pages = pages;
@@ -503,8 +496,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
         void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
         char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
         _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
-        size_t linux_size, initrd_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
-        EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
+        size_t linux_size, initrd_size, ucode_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
+        EFI_PHYSICAL_ADDRESS linux_base, initrd_base, ucode_base, dt_base;
         _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
@@ -792,12 +785,18 @@ static EFI_STATUS run(EFI_HANDLE image) {
         initrd_size = szs[UNIFIED_SECTION_INITRD];
         initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
 
+        ucode_size = szs[UNIFIED_SECTION_UCODE];
+        ucode_base = ucode_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE] : 0;
+
         _cleanup_pages_ Pages initrd_pages = {};
-        if (credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
-                /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
-                err = combine_initrd(
-                                initrd_base, initrd_size,
+        if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
+                /* If we have generated initrds dynamically or there is a microcode initrd, combine them with the built-in initrd. */
+                err = combine_initrds(
                                 (const void*const[]) {
+                                        /* Microcode must always be first as kernel only scans uncompressed cpios
+                                         * and later initrds might be compressed. */
+                                        PHYSICAL_ADDRESS_TO_POINTER(ucode_base),
+                                        PHYSICAL_ADDRESS_TO_POINTER(initrd_base),
                                         credential_initrd,
                                         global_credential_initrd,
                                         sysext_initrd,
@@ -806,6 +805,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
                                         pcrpkey_initrd,
                                 },
                                 (const size_t[]) {
+                                        ucode_size,
+                                        initrd_size,
                                         credential_initrd_size,
                                         global_credential_initrd_size,
                                         sysext_initrd_size,
@@ -813,7 +814,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
                                         pcrsig_initrd_size,
                                         pcrpkey_initrd_size,
                                 },
-                                6,
+                                8,
                                 &initrd_pages, &initrd_size);
                 if (err != EFI_SUCCESS)
                         return err;
index b1fa04481341029bebf2c7c4a3bac255b82e0d29..3887bf57023ae6a596e6411f393b4e7b593bb22d 100644 (file)
@@ -13,6 +13,7 @@ const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
         [UNIFIED_SECTION_OSREL]   = ".osrel",
         [UNIFIED_SECTION_CMDLINE] = ".cmdline",
         [UNIFIED_SECTION_INITRD]  = ".initrd",
+        [UNIFIED_SECTION_UCODE]   = ".ucode",
         [UNIFIED_SECTION_SPLASH]  = ".splash",
         [UNIFIED_SECTION_DTB]     = ".dtb",
         [UNIFIED_SECTION_UNAME]   = ".uname",
index ffa960f01b4c2abc098efddf755fd07d0e647add..8ab742dd58f3b549e2cb801ec964614d183bd948 100644 (file)
@@ -10,6 +10,7 @@ typedef enum UnifiedSection {
         UNIFIED_SECTION_OSREL,
         UNIFIED_SECTION_CMDLINE,
         UNIFIED_SECTION_INITRD,
+        UNIFIED_SECTION_UCODE,
         UNIFIED_SECTION_SPLASH,
         UNIFIED_SECTION_DTB,
         UNIFIED_SECTION_UNAME,