[PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs

Askar Safin posted 2 patches 1 month, 1 week ago
[PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Askar Safin 1 month, 1 week ago
If we generate external initramfs as normal user using "cpio"
command, then we cannot put /dev/console there.

Fortunately, in this case default builtin initramfs will
contain /dev/console (before this commit).

But if we generate builtin initramfs instead, then we will
not have /dev/console at all. Thus the kernel will be unable to
open /dev/console, and PID 1 will have stdin, stdout and stderr
closed.

This problem can be solved by using gen_init_cpio.

But I think that proper solution is to ensure that /dev/console
is always available, no matter what. This is quality-of-implementation
feature. This will reduce number of possible failure modes. And
this will make easier for developers to get early boot right.
(Early boot issues are very hard to debug.)

So I put to the beginning of function "do_populate_rootfs" a code, which
creates nodes /dev, /dev/console and /root. This ensures that
they are always available even if both builtin and external
initramfses don't contain them.

The kernel itself relies on presence of these nodes. It uses
/dev/console in "console_on_rootfs" and /root in "do_mount_root".
So, ensuring that they are always available is right thing to do.

But then code in the beginning of "do_populate_rootfs" becomes
very similar to the code in "default_rootfs". So I extract
a common helper named "create_basic_rootfs".

Also I replace S_IRUSR | S_IWUSR with 0600 (suggested by checkpatch.pl).

Then nodes in usr/default_cpio_list become not needed,
so I make this file empty.

(In fact, this patch doesn't ensure that /dev/console is truly
always available. You can still break it if you create
bad /dev/console in builtin or external initramfs. So I
update comment on "console_on_rootfs".)

This patch makes default builtin initramfs truly empty.
This happens to match description in ramfs-rootfs-initramfs.rst.
But that description says that size of this initramfs is 134 bytes.
This is wrong. I checked: its size is 512 bytes (and default
builtin initramfs is always uncompressed). So I just remove that
number. While I'm there, I remove word "gzipped",
because builtin initramfs may be also uncompressed or compressed
using different algorithm.

Signed-off-by: Askar Safin <safinaskar@gmail.com>
---
 .../filesystems/ramfs-rootfs-initramfs.rst    |  4 +--
 init/do_mounts.c                              |  8 ++++++
 init/do_mounts.h                              |  2 ++
 init/initramfs.c                              |  2 ++
 init/main.c                                   |  6 +++-
 init/noinitramfs.c                            | 28 +++----------------
 usr/Makefile                                  |  2 +-
 usr/default_cpio_list                         |  6 +---
 8 files changed, 25 insertions(+), 33 deletions(-)

diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
index 165117a721ce..c6451692294f 100644
--- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst
+++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
@@ -133,9 +133,9 @@ All this differs from the old initrd in several ways:
 Populating initramfs:
 ---------------------
 
-The 2.6 kernel build process always creates a gzipped cpio format initramfs
+The kernel build process always creates a cpio format initramfs
 archive and links it into the resulting kernel binary.  By default, this
-archive is empty (consuming 134 bytes on x86).
+archive is empty.
 
 The config option CONFIG_INITRAMFS_SOURCE (in General Setup in menuconfig,
 and living in usr/Kconfig) can be used to specify a source for the
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 55ed3ac0b70f..f911280a348e 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -519,3 +519,11 @@ void __init init_rootfs(void)
 			is_tmpfs = true;
 	}
 }
+
+void __init create_basic_rootfs(void)
+{
+	WARN_ON_ONCE(init_mkdir("/dev", 0755) != 0);
+	WARN_ON_ONCE(init_mknod("/dev/console", S_IFCHR | 0600,
+			new_encode_dev(MKDEV(5, 1))) != 0);
+	WARN_ON_ONCE(init_mkdir("/root", 0700) != 0);
+}
diff --git a/init/do_mounts.h b/init/do_mounts.h
index a386ee5314c9..907e9af77464 100644
--- a/init/do_mounts.h
+++ b/init/do_mounts.h
@@ -16,6 +16,8 @@ void  mount_root_generic(char *name, char *pretty_name, int flags);
 void  mount_root(char *root_device_name);
 extern int root_mountflags;
 
+void __init create_basic_rootfs(void);
+
 static inline __init int create_dev(char *name, dev_t dev)
 {
 	init_unlink(name);
diff --git a/init/initramfs.c b/init/initramfs.c
index 6ddbfb17fb8f..678b5050a7ac 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -717,6 +717,8 @@ static void __init populate_initrd_image(char *err)
 
 static void __init do_populate_rootfs(void *unused, async_cookie_t cookie)
 {
+	create_basic_rootfs();
+
 	/* Load the built in initramfs */
 	char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
 	if (err)
diff --git a/init/main.c b/init/main.c
index 1cb395dd94e4..76f5320d3d4c 100644
--- a/init/main.c
+++ b/init/main.c
@@ -1645,7 +1645,11 @@ static int __ref kernel_init(void *unused)
 	      "See Linux Documentation/admin-guide/init.rst for guidance.");
 }
 
-/* Open /dev/console, for stdin/stdout/stderr, this should never fail */
+/*
+ * Open /dev/console, for stdin/stdout/stderr, this should never fail,
+ * unless you intentionally make it fail, for example, by supplying
+ * wrong /dev/console in initramfs
+ */
 void __init console_on_rootfs(void)
 {
 	struct file *file = filp_open("/dev/console", O_RDWR, 0);
diff --git a/init/noinitramfs.c b/init/noinitramfs.c
index d1d26b93d25c..00bcf59cf05d 100644
--- a/init/noinitramfs.c
+++ b/init/noinitramfs.c
@@ -6,37 +6,17 @@
  * Author: Jean-Paul Saman <jean-paul.saman@nxp.com>
  */
 #include <linux/init.h>
-#include <linux/stat.h>
-#include <linux/kdev_t.h>
-#include <linux/syscalls.h>
-#include <linux/init_syscalls.h>
 #include <linux/umh.h>
 
+#include "do_mounts.h"
+
 /*
- * Create a simple rootfs that is similar to the default initramfs
+ * Create a simple rootfs
  */
 static int __init default_rootfs(void)
 {
-	int err;
-
 	usermodehelper_enable();
-	err = init_mkdir("/dev", 0755);
-	if (err < 0)
-		goto out;
-
-	err = init_mknod("/dev/console", S_IFCHR | S_IRUSR | S_IWUSR,
-			new_encode_dev(MKDEV(5, 1)));
-	if (err < 0)
-		goto out;
-
-	err = init_mkdir("/root", 0700);
-	if (err < 0)
-		goto out;
-
+	create_basic_rootfs();
 	return 0;
-
-out:
-	printk(KERN_WARNING "Failed to create a rootfs\n");
-	return err;
 }
 rootfs_initcall(default_rootfs);
diff --git a/usr/Makefile b/usr/Makefile
index e8f42478a0b7..de1ee4e78ef4 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -22,7 +22,7 @@ ramfs-input := $(CONFIG_INITRAMFS_SOURCE)
 cpio-data :=
 
 # If CONFIG_INITRAMFS_SOURCE is empty, generate a small initramfs with the
-# default contents.
+# contents, specified in default_cpio_list, which is currently empty.
 ifeq ($(ramfs-input),)
 ramfs-input := $(src)/default_cpio_list
 endif
diff --git a/usr/default_cpio_list b/usr/default_cpio_list
index 37b3864066e8..1df1ef08e504 100644
--- a/usr/default_cpio_list
+++ b/usr/default_cpio_list
@@ -1,6 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0-only
-# This is a very simple, default initramfs
-
-dir /dev 0755 0 0
-nod /dev/console 0600 0 0 c 5 1
-dir /root 0700 0 0
+# Default initramfs is empty
-- 
2.47.3
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by David Disseldorp 1 month, 1 week ago
On Thu, 19 Feb 2026 21:03:11 +0000, Askar Safin wrote:

> If we generate external initramfs as normal user using "cpio"
> command, then we cannot put /dev/console there.
> 
> Fortunately, in this case default builtin initramfs will
> contain /dev/console (before this commit).
> 
> But if we generate builtin initramfs instead, then we will
> not have /dev/console at all. Thus the kernel will be unable to
> open /dev/console, and PID 1 will have stdin, stdout and stderr
> closed.
> 
> This problem can be solved by using gen_init_cpio.
> 
> But I think that proper solution is to ensure that /dev/console
> is always available, no matter what. This is quality-of-implementation
> feature. This will reduce number of possible failure modes. And
> this will make easier for developers to get early boot right.
> (Early boot issues are very hard to debug.)

I'd prefer not to go down this path:
- I think it's reasonable to expect that users who override the default
  internal initramfs know what they're doing WRT /dev/console creation.
- initramfs can be made up of concatenated cpio archives, so tools which
  insist on using GNU cpio and run into mknod EPERM issues could append
  the nodes via gen_init_cpio, while continuing to use GNU cpio for
  everything else.

Thanks, David
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Rob Landley 1 month, 1 week ago
On 2/19/26 17:59, David Disseldorp wrote:
>> This problem can be solved by using gen_init_cpio.

It used to work, then they broke it. (See below.)

>> But I think that proper solution is to ensure that /dev/console
>> is always available, no matter what. This is quality-of-implementation
>> feature. This will reduce number of possible failure modes. And
>> this will make easier for developers to get early boot right.
>> (Early boot issues are very hard to debug.)
> 
> I'd prefer not to go down this path:
> - I think it's reasonable to expect that users who override the default
>    internal initramfs know what they're doing WRT /dev/console creation.
> - initramfs can be made up of concatenated cpio archives, so tools which
>    insist on using GNU cpio and run into mknod EPERM issues could append
>    the nodes via gen_init_cpio, while continuing to use GNU cpio for
>    everything else.

Who said anything about gnu? Busybox has a cpio, toybox has a cpio... 
once upon a time it was a posix command, removed from the standard for 
the same reason tar was removed, and that was just as widely ignored.

It's an archive format. There are tools that create that archive format 
from a directory.

The kernel itself had a fairly generic one one built-in, which you 
_could_ use to create cpio archives with /dev/console as a regular 
user... until the kernel guys decided to break it. I carried a patch to 
fix that for a little while myself:

https://landley.net/bin/mkroot/0.8.10/linux-patches/0011-gen_init_cpio-regression.patch

But I just got tired of fighting lkml.

> Thanks, David

Rob
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Askar Safin 1 month, 1 week ago
Rob, I THINK I KNOW HOW TO SOLVE YOUR PROBLEM! (See below.)

On Mon, Feb 23, 2026 at 4:27 AM Rob Landley <rob@landley.net> wrote:
>
> On 2/19/26 17:59, David Disseldorp wrote:
> >> This problem can be solved by using gen_init_cpio.

I said this, not David.

> It used to work, then they broke it. (See below.)

So you have a directory with rootfs, and you want to add /dev/console
to it? Do I understand your problem correctly?

This is how you can solve it.

Option 1 (recommended).

Let's assume you have your rootfs in a directory /tmp/rootfs . Then
create /tmp/cplist with:

dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1

And add this to your config:

CONFIG_INITRAMFS_SOURCE="/tmp/rootfs /tmp/cplist"

This will create builtin initramfs with contents of /tmp/rootfs AND
nodes from /tmp/cplist
(i. e. /dev/console).

This will work, I just checked it.
No need to build the kernel twice.
Does this solve your problem?

Option 2.
Alternatively (assuming you already built gen_init_cpio) you can
create cpio with /dev/console using gen_init_cpio and then
concatenate it with cpio archive with your rootfs.

Unfortunately, this may require building the kernel twice, as I
explained in my previous letter in this thread. But this option
is still doable.

Option 3.
Yet another way: run

usr/gen_initramfs.sh /tmp/rootfs /tmp/cplist > some-output.cpio

(again, here /tmp/rootfs is a directory with your rootfs and
/tmp/cplist is a list of nodes.)

Unfortunately, this requires gen_init_cpio to be present, so, again,
similarly to option 2, this may require building the kernel twice.
But, again, this is doable.

In fact, gen_initramfs.sh accepts same options as CONFIG_INITRAMFS_SOURCE,
this is explained in
https://elixir.bootlin.com/linux/v6.19/source/Documentation/driver-api/early-userspace/early_userspace_support.rst#L70
.

Conclusion.
As I said, option 1 is the best in my opinion. It does not require
building the kernel twice,
and, as well as I understand, fully solves your problem.

If I miss something, please, tell me this.

I really want to help you, Rob.
I sent this patch, because I want to help you.

-- 
Askar Safin
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by David Disseldorp 1 month, 1 week ago
On Sun, 22 Feb 2026 19:27:50 -0600, Rob Landley wrote:

> On 2/19/26 17:59, David Disseldorp wrote:
> >> This problem can be solved by using gen_init_cpio.  
> 
> It used to work, then they broke it. (See below.)
> 
> >> But I think that proper solution is to ensure that /dev/console
> >> is always available, no matter what. This is quality-of-implementation
> >> feature. This will reduce number of possible failure modes. And
> >> this will make easier for developers to get early boot right.
> >> (Early boot issues are very hard to debug.)  
> > 
> > I'd prefer not to go down this path:
> > - I think it's reasonable to expect that users who override the default
> >    internal initramfs know what they're doing WRT /dev/console creation.
> > - initramfs can be made up of concatenated cpio archives, so tools which
> >    insist on using GNU cpio and run into mknod EPERM issues could append
> >    the nodes via gen_init_cpio, while continuing to use GNU cpio for
> >    everything else.  
> 
> Who said anything about gnu? Busybox has a cpio, toybox has a cpio... 
> once upon a time it was a posix command, removed from the standard for 
> the same reason tar was removed, and that was just as widely ignored.

I'm not familiar with Busybox or toybox cpio. I've referred to GNU cpio
as it carries the archive-contents-must-exist-locally interface. Any
tool which uses the same interface will likely need to workaround the
same mknod EPERM issues for initramfs.

> It's an archive format. There are tools that create that archive format 
> from a directory.
> 
> The kernel itself had a fairly generic one one built-in, which you 
> _could_ use to create cpio archives with /dev/console as a regular 
> user... until the kernel guys decided to break it. I carried a patch to 
> fix that for a little while myself:
> 
> https://landley.net/bin/mkroot/0.8.10/linux-patches/0011-gen_init_cpio-regression.patch

This seems like a helpful feature to me.

Thanks, David
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Rob Landley 1 month, 1 week ago
On 2/22/26 20:33, David Disseldorp wrote:
>> It's an archive format. There are tools that create that archive format
>> from a directory.
>>
>> The kernel itself had a fairly generic one one built-in, which you
>> _could_ use to create cpio archives with /dev/console as a regular
>> user... until the kernel guys decided to break it. I carried a patch to
>> fix that for a little while myself:
>>
>> https://landley.net/bin/mkroot/0.8.10/linux-patches/0011-gen_init_cpio-regression.patch
> 
> This seems like a helpful feature to me.

It was, but I want mkroot to at least be _able_ to build with vanilla 
kernels, so couldn't depend on a feature upstream had abandoned.

The real problem isn't cpio, it's that the kernel interface for 
statically linking a cpio into the kernel wants you to point it at a 
directory of files for _it_ to cpio up, and when you do that you need 
root access (or fakeroot) to mknod.

You used to be able to point it at one of those generated files (and 
thus edit the file yourself to add the extra nodes), but that went away 
the same time they broke the rest of the interface.

> Thanks, David

Rob
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Askar Safin 1 month, 1 week ago
Rob Landley <rob@landley.net>:
> The real problem isn't cpio, it's that the kernel interface

So there is some bug here?

Then, please, describe properly this bug.

I. e. using usual formula "steps to reproduce - what I got - what I expected to see".

Also, does the kernel broke *documented* feature? If indeed some
*documented* feature doesn't work, then this is indeed very real bug.

I kindly ask you, please, please, describe this bug. I really want
to help you.

-- 
Askar Safin
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Rob Landley 1 month, 1 week ago
On 2/23/26 11:34, Askar Safin wrote:
> Rob Landley <rob@landley.net>:
>> The real problem isn't cpio, it's that the kernel interface
> 
> So there is some bug here?
> 
> Then, please, describe properly this bug.
> 
> I. e. using usual formula "steps to reproduce - what I got - what I expected to see".

I think that the kernel special case creating files in initramfs is the 
wrong design approach, but it would address the problem and it's what 
the kernel already did in a different codepath.

> Also, does the kernel broke *documented* feature? If indeed some
> *documented* feature doesn't work, then this is indeed very real bug.

Documentation/filesystems/ramfs-rootfs-initramfs.txt section "populating 
initramfs" described using the text file format back in 2005 
(https://github.com/mpe/linux-fullhistory/commit/7f46a240b0a1 line 135) 
and then commit 
https://github.com/mpe/linux-fullhistory/commit/99aef427e206f a year 
later described running usr/gen_init_cpio to create those files from a 
directory to give you an easy starting point to edit.

But I wrote that documentation, so it probably doesn't count.

> I kindly ask you, please, please, describe this bug. I really want
> to help you.
Back when the gen_init_cpio stuff let you create the file and consume 
the file separately so you could edit it in between, there was an easy 
way to address this problem by creating a cpio containing /dev/console 
as a regular user using the kernel plumbing. Then the kernel broke that 
and created a regression, which they decided to fix with special case 
code doing mknod within the kernel before launching PID 1, but only for 
one of two codepaths.

I think adding the special case hack to the other codepath is the wrong 
_WAY_ to fix it... but I guess that ship has sailed. I agree doing that 
can avoid the "init has no stdin/stdout/stderr" problem and thus address 
the issue.

(I still think if you have a CONFIG_DEVTMPFS_MOUNT config symbol, it 
should actually work, but that's a somewhat separate issue and a 
half-dozen patches ignored by linux-kernel for a decade now apparently 
argue otherwise...)

Rob
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Askar Safin 1 month, 1 week ago
David Disseldorp <ddiss@suse.de>:
> I'd prefer not to go down this path:
> - I think it's reasonable to expect that users who override the default
>   internal initramfs know what they're doing WRT /dev/console creation.
> - initramfs can be made up of concatenated cpio archives, so tools which
>   insist on using GNU cpio and run into mknod EPERM issues could append
>   the nodes via gen_init_cpio, while continuing to use GNU cpio for
>   everything else.

This cannot be done in proper way.

Let's assume we want to build *builtin* initramfs using GNU cpio and
then concatenate to it an archive made by gen_init_cpio.

Then we want CONFIG_INITRAMFS_SOURCE to accept already existing cpio
archive AND file in gen_init_cpio format. But, according to
CONFIG_INITRAMFS_SOURCE docs in usr/Kconfig, this is not possible
(I didn't check whether this is true, I just have read the docs.)

This means that we should do this:

1. Generate an archive by invoking gen_init_cpio and concatenate it to
our preexisting archive
2. Create kernel config, while specifying resulting archive in
CONFIG_INITRAMFS_SOURCE
3. Build the kernel

Unfortunately, this will not work, because to invoke gen_init_cpio you
should build it first. And you cannot build it if there is no config
(I checked this).

So, we should do this:

1. Create kernel config, while specifying an archive in
CONFIG_INITRAMFS_SOURCE, which *DOES NOT EXISTS YET*
2. Build gen_init_cpio by invoking "make usr/gen_init_cpio"
3. Create an archive by invoking gen_init_cpio and concatenate it to
our preexisting archive. Put resulting archive to the path specified in
CONFIG_INITRAMFS_SOURCE
4. Build the kernel

Unfortunately, this will not work, either, because command
"make usr/gen_init_cpio" doesn't work in clean kernel tree even if
config exists (I checked this).

This means that the only remaining way is this:

1. Create *fake* kernel config
2. Build whole kernel (!!!)
3. Create an archive by invoking gen_init_cpio and concatenate it to
our preexisting archive
4. Create config, this time for real. Specify archive created in previous
step as CONFIG_INITRAMFS_SOURCE
5. Build the kernel, this time for real

I hope you agree that this is totally insane.

So, there is no proper way to create builtin initramfs by concatenating
archives created by GNU cpio and gen_init_cpio.

I think this is a bug in kbuild, and it probably needs to be fixed.

But I think that my patchset is a better approach. My patchset simply
ensures that /dev/console and /dev/null are always available, no matter
what. And thus my patchset side steps the whole issue.

Here are additional arguments in favor of my patchset.

* The kernel itself relies on presence of /dev/console in console_on_rootfs.
There is even comment there that currently says that opening of /dev/console
should never fail. (The comment is wrong, and this patchset fixes it.) So, if you
happen to supply builtin initramfs without /dev/console, then you will
build kernel, which violates its own assumptions. This will not be
detected until we actually try to boot the kernel. And even then it will
be hard to understand what exactly went wrong.

Why should we allow this failure mode? Let's instead ensure that this will
never happen. I. e. let's make sure that the kernel's asssumptions are always
true!

* My patchset makes the kernel not more complex, but more simple!
(28 insertions(+), 33 deletions(-).) Moreover, my patchset makes it
simpler not only in LoC sense, but in conceptual sense, too!
Currently codes in usr/default_cpio_list and init/noinitramfs.c are
very similar. It is possible that they will be out of sync in
some point of future.

By the way, noticing that they are out of sync is *very* hard. Consider
this scenario: usr/default_cpio_list contains this line:

nod /dev/null 0666 0 0 c 1 3

and init/noinitramfs.c contains this line:

init_mknod("/dev/null", S_IFCHR | 0666,
                        new_encode_dev(MKDEV(1, 3)));

Are these lines equivalent? You may think they are, but they are not.

init_mknod function above in fact creates node with different rights
due to umask of kernel thread. (And thus unprivileged users will be unable
to write to /dev/null...)

My patchset merges both codes into a single helper, thus making sure they
will never be out of sync. And thus my patchset reduces complexity of
the kernel.

* Currently it is okay not to put /dev/console to external initramfs.
But it is not okay not to put it to builtin initramfs. Why creating
builtin initramfs is harder than creating external initramfs? Why we make
lifes of developers in one of these cases harder without any reason?

Consider this scenario: somebody built kernel and external initramfs.
Everything works. Now they make this initramfs to be builtin.
Of course, they expect that everything will continue to work as before.
But it doesn't work. PID 1 doesn't print anything to console,
and you cannot even debug this, because it doesn't print anything to
console and you cannot see your debug printfs!

This clearly violates principle of least surprise.

-- 
Askar Safin
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by David Disseldorp 1 month, 1 week ago
On Fri, 20 Feb 2026 22:11:50 +0300, Askar Safin wrote:

> David Disseldorp <ddiss@suse.de>:
> > I'd prefer not to go down this path:
> > - I think it's reasonable to expect that users who override the default
> >   internal initramfs know what they're doing WRT /dev/console creation.
> > - initramfs can be made up of concatenated cpio archives, so tools which
> >   insist on using GNU cpio and run into mknod EPERM issues could append
> >   the nodes via gen_init_cpio, while continuing to use GNU cpio for
> >   everything else.  
> 
> This cannot be done in proper way.
> 
> Let's assume we want to build *builtin* initramfs using GNU cpio and
> then concatenate to it an archive made by gen_init_cpio.
> 
> Then we want CONFIG_INITRAMFS_SOURCE to accept already existing cpio
> archive AND file in gen_init_cpio format. But, according to
> CONFIG_INITRAMFS_SOURCE docs in usr/Kconfig, this is not possible
> (I didn't check whether this is true, I just have read the docs.)

...

> This means that the only remaining way is this:
> 
> 1. Create *fake* kernel config
> 2. Build whole kernel (!!!)
> 3. Create an archive by invoking gen_init_cpio and concatenate it to
> our preexisting archive
> 4. Create config, this time for real. Specify archive created in previous
> step as CONFIG_INITRAMFS_SOURCE
> 5. Build the kernel, this time for real
> 
> I hope you agree that this is totally insane.

A two-phase build isn't insane alongside a custom internal initrams -
consider the case where the user wants to bundle kernel modules.
Buildroot has logic for this kind of two-phase build:
https://gitlab.com/buildroot.org/buildroot/-/blob/master/linux/linux.mk?ref_type=heads#L408
IIUC buildroot doesn't change the kernel config but uses an initial
dummy cpio which is updated for the final (incremental) build stage.

> So, there is no proper way to create builtin initramfs by concatenating
> archives created by GNU cpio and gen_init_cpio.

There are still other options:
- use a different initramfs archiving tool
- point GNU cpio at an existing /dev/console, or call mknod as root

> I think this is a bug in kbuild, and it probably needs to be fixed.
> 
> But I think that my patchset is a better approach. My patchset simply
> ensures that /dev/console and /dev/null are always available, no matter
> what. And thus my patchset side steps the whole issue.

I remain unconvinced. To me it still feels like a workaround for GNU
cpio's poor archive-contents-must-exist-locally interface.

Thanks, David
Re: [PATCH 1/2] init: ensure that /dev/console is (nearly) always available in initramfs
Posted by Askar Safin 1 month, 1 week ago
On Mon, Feb 23, 2026 at 4:20 AM David Disseldorp <ddiss@suse.de> wrote:
> There are still other options:
> - use a different initramfs archiving tool

I'm trying to solve Rob's use case. He tries to write a tool for building
kernel and rootfs called "mkroot". The tool is written in pure shell
without any C. It should not have any external dependencies.
So it cannot depend on a custom cpio tool. The only cpio tools
allowed are gen_init_cpio and whatever cpio is present on the system.

> - point GNU cpio at an existing /dev/console

This will not work if we are inside some container, such as docker.
Container engines usually replace /dev/console with something else.

> or call mknod as root

mkroot should work as normal user

> I remain unconvinced

I still believe in my approach.

According to current kernel logic, /dev/console
magically works if you use external initramfs and doesn't work
if you use internal initramfs. I simply propose to make internal
initramfs work, too.

> To me it still feels like a workaround for GNU
> cpio's poor archive-contents-must-exist-locally interface.

I will repeat: mkroot should work not only with GNU cpio,
but with whatever cpio is present on the system.

So we should either fix *all* cpio implementations, or
fix the kernel. Obviously the second option is easier.

-- 
Askar Safin