]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
gpt-auto-generator: generate an initrd ESP mount if it makes sense
authorLennart Poettering <lennart@amutable.com>
Mon, 9 Mar 2026 17:52:20 +0000 (18:52 +0100)
committerLennart Poettering <lennart@amutable.com>
Thu, 26 Mar 2026 13:34:56 +0000 (14:34 +0100)
We need to store state persistently for the software TPM (i.e. the root
key). But given that TPMs are generally used to unlock the rootfs, this
storage cannot be on the rootfs. Hence let's use the ESP instead, as the
next best thing, that is guaranteed to exist during early boot, given we
just were booted from it.

This defines automatic logic for this, but does not cause the ESP mount
job to be enqueued (since typically we don't actually want that
mounted), this is left for the actual services that needs to be done.

Note that the mount here is set up quite differently from the one from
the host: since initrds are short-lived anyway, it seemed pointless to
use autofs. Moreover this uses a fixed place to mount the ESP, inspired
by the /sysroot/ + /sysusr/ mount naming. All that to simplify things a
bit for the consumers (which is mostly swtpm)

man/systemd-gpt-auto-generator.xml
src/gpt-auto-generator/gpt-auto-generator.c

index e267fc952870e6a5aac914ceef45607b558e1c72..6494a79d22dccae11e1fa5361859b61e7235bc21 100644 (file)
       discovery based on the boot loader reported ESP which is also enabled if no <literal>root=</literal>
       parameter is specified at all. (The latter relies on <command>systemd-udevd.service</command>'s
       <filename>/dev/gpt-auto-root</filename> block device symlink generation).</para></listitem>
+
+      <listitem><para>It will also generate a unit file mounting the EFI System Partition (ESP) to
+      <filename>/sysefi/</filename>, if applicable. Unlike the file systems mentioned above this mount is not
+      activated by default however, but can be pulled in by services requiring ESP access from within the
+      initrd. Note that this mount point is initrd-specific and does not make use of autofs. The ESP is
+      typically mounted at a different place and via autofs once the system transitions out of the
+      initrd.</para></listitem>
     </itemizedlist>
     </para>
 
             <entry><constant>SD_GPT_ESP</constant></entry>
             <entry><constant>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</constant></entry>
             <entry>EFI System Partition (ESP)</entry>
-            <entry><filename>/efi/</filename> or <filename>/boot/</filename></entry>
+            <entry><filename>/efi/</filename> or <filename>/boot/</filename> once the system transitioned out of the initrd, <filename>/sysefi/</filename> before</entry>
             <entry>The first partition with this type UUID located on the same disk as the root partition is mounted to <filename>/boot/</filename> or <filename>/efi/</filename>, see below.</entry>
           </row>
           <row>
 
     <para>The ESP is mounted to <filename>/boot/</filename> if that directory exists and is not used for
     XBOOTLDR, and otherwise to <filename>/efi/</filename>. Same as for <filename>/boot/</filename>, an
-    automount unit is used. The mount point will be created if necessary.</para>
+    automount unit is used. The mount point will be created if necessary. These apply once the system
+    transitioned out of the initrd phase. Before that, if components in the initrd require ESP access, it
+    will be mounted to <filename>/sysefi/</filename>.</para>
 
     <para>No configuration is created for mount points that are configured in <citerefentry
     project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry> or when
index 4fd92a6057f6a97e593a6ef36da382263d44ad4c..a87b5140805873d8872fa2e752a563ebaabed963 100644 (file)
@@ -307,7 +307,8 @@ static int add_mount(
                 MountPointFlags flags,
                 const char *options,
                 const char *description,
-                const char *post) {
+                const char *post,
+                const char *conflicts) {
 
         _cleanup_free_ char *unit = NULL, *crypto_what = NULL, *opts_filtered = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -377,6 +378,12 @@ static int add_mount(
         if (r < 0)
                 return r;
 
+        if (conflicts)
+                fprintf(f,
+                        "Conflicts=%1$s\n"
+                        "Before=%1$s\n",
+                        conflicts);
+
         fprintf(f,
                 "\n"
                 "[Mount]\n"
@@ -493,7 +500,8 @@ static int add_partition_mount(
                         (STR_IN_SET(id, "root", "var") ? MOUNT_MEASURE : 0), /* by default measure rootfs and /var, since they contain the "identity" of the system */
                         options,
                         description,
-                        SPECIAL_LOCAL_FS_TARGET);
+                        SPECIAL_LOCAL_FS_TARGET,
+                        /* conflicts= */ NULL);
 }
 
 static int add_partition_swap(DissectedPartition *p) {
@@ -582,7 +590,8 @@ static int add_automount(
                       flags,
                       options,
                       description,
-                      /* post= */ NULL);
+                      /* post= */ NULL,
+                      /* conflicts= */ NULL);
         if (r < 0)
                 return r;
 
@@ -919,7 +928,8 @@ static int add_root_mount(void) {
                         MOUNT_MEASURE,
                         options,
                         "Root Partition",
-                        in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
+                        in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET,
+                        /* conflicts= */ NULL);
 #else
         return 0;
 #endif
@@ -995,7 +1005,8 @@ static int add_usr_mount(void) {
                       /* flags= */ 0,
                       options,
                       "/usr/ Partition",
-                      in_initrd() ? SPECIAL_INITRD_USR_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
+                      in_initrd() ? SPECIAL_INITRD_USR_FS_TARGET : SPECIAL_LOCAL_FS_TARGET,
+                      /* conflicts= */ NULL);
         if (r < 0)
                 return r;
 
@@ -1009,7 +1020,8 @@ static int add_usr_mount(void) {
                               MOUNT_VALIDATEFS,
                               "bind",
                               "/usr/ Partition (Final)",
-                              SPECIAL_INITRD_FS_TARGET);
+                              SPECIAL_INITRD_FS_TARGET,
+                              /* conflicts= */ NULL);
                 if (r < 0)
                         return r;
         }
@@ -1017,6 +1029,49 @@ static int add_usr_mount(void) {
         return 0;
 }
 
+static int add_early_esp_mount(void) {
+        int r;
+
+        /* Early ESP discovery is a bit different than the other mounts here: it's purely about the initrd,
+         * and goes away during the transition to the host (where it might likely be mounted again, but then
+         * via autofs, hence lazily). Moreover, the location is fixed → /sysefi/, i.e. we do not bother with
+         * XBOOTLDR vs. ESP for this. Also, the mount is not pulled in by default, but is expected to be
+         * pulled in by the component that uses it.
+         *
+         * The initial usecase for this is software TPM that needs a place to store its state before the root
+         * file system can be mounted.
+         *
+         * Or in other words: this is much simpler, more focussed on a short-lived boot-time operation than
+         * the regular logic during later boot. */
+
+        if (!in_initrd())
+                return 0;
+
+        if (!is_efi_boot())
+                return 0;
+
+        _cleanup_free_ char *options = NULL;
+        r = partition_pick_mount_options(
+                        PARTITION_ESP,
+                        "vfat",
+                        /* rw= */ true,
+                        /* discard= */ false,
+                        &options,
+                        /* ret_ms_flags= */ NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to pick ESP mount options: %m");
+
+        return add_mount("esp",
+                         "/dev/disk/by-designator/esp",
+                         "/sysefi/",
+                         "vfat",
+                         MOUNT_RW,
+                         options,
+                         "EFI System Partition (Early)",
+                         /* post= */ NULL,
+                         /* conflicts= */ "initrd-switch-root.target");
+}
+
 static int process_loader_partitions(DissectedPartition *esp, DissectedPartition *xbootldr) {
         sd_id128_t loader_uuid;
         int r;
@@ -1190,7 +1245,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         return 0;
 
                 /* Disable root disk logic if there's a root= value specified (unless it happens to be
-                 * "gpt-auto" or "gpt-auto-force") */
+                 * "gpt-auto", "gpt-auto-force", "dissect", "dissect-force") */
 
                 arg_auto_root = parse_gpt_auto_root("root=", value);
                 assert(arg_auto_root >= 0);
@@ -1244,9 +1299,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 if (proc_cmdline_value_missing(key, value))
                         return 0;
 
-                /* Disable root disk logic if there's a root= value specified (unless it happens to be
-                 * "gpt-auto" or "gpt-auto-force") */
-
                 arg_auto_usr = parse_gpt_auto_root("mount.usr=", value);
                 assert(arg_auto_usr >= 0);
 
@@ -1325,6 +1377,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
         r = 0;
         RET_GATHER(r, add_root_mount());
         RET_GATHER(r, add_usr_mount());
+        RET_GATHER(r, add_early_esp_mount());
         RET_GATHER(r, add_mounts());
 
         return r;