[PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing

From: Thomas Renninger
Date: Wed Jul 18 2012 - 06:36:13 EST


cpio parsing code comes from H. Peter Anvin.
The CONFIG_EARLY_INITRD feature is architecture independent, but
for now only enabled/called for X86.
The problem is that initrd_start must be valid, but there is no
architecture independent reserve_initrd() call in init/main.c or
similiar.

Signed-off-by: Thomas Renninger <trenn@xxxxxxx>
CC: hpa@xxxxxxxxx
---
Documentation/initrd.txt | 22 ++++++++
arch/x86/kernel/setup.c | 2 +
include/linux/initrd.h | 12 ++++
init/Makefile | 1 +
init/initrd_early.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++
usr/Kconfig | 12 ++++
6 files changed, 180 insertions(+), 0 deletions(-)
create mode 100644 init/initrd_early.c

diff --git a/Documentation/initrd.txt b/Documentation/initrd.txt
index 4e1839c..e515e83 100644
--- a/Documentation/initrd.txt
+++ b/Documentation/initrd.txt
@@ -93,6 +93,28 @@ mkdir /tmp/imagefile
cd /tmp/imagefile
gzip -cd /boot/imagefile.img | cpio -imd --quiet

+
+Multiple cpio images glued together
+-----------------------------------
+
+Several cpio images, compressed or uncompressed can be concatenated.
+There is especially one use-case for this: see kernel accessing
+initrd data early section below.
+
+
+Accessing initrd data early
+---------------------------
+
+There is a mechanism to access data passed from the initrd much earlier.
+This only works if the data needed early is encapsulated in an uncompressed
+cpio image is passed. It must be the first cpio archive if multiple
+cpio archives are concatenated and passed as initrd.
+Typically if you want to pass data which is supposed to be consumed by
+the kernel really early, one would pass two cpio images glued together:
+ - One compressed, holding the big data which is needed by userspace
+ - One uncompressed cpio image holding files for early kernel initialization
+For further details look out for the CONFIG_EARLY_INITRD option in the sources.
+
Installation
------------

diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 16be6dc..9e039f6 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -941,6 +941,8 @@ void __init setup_arch(char **cmdline_p)

reserve_initrd();

+ early_initrd_find_cpio_data((void *)initrd_start, initrd_end - initrd_start);
+
reserve_crashkernel();

vsmp_init();
diff --git a/include/linux/initrd.h b/include/linux/initrd.h
index 55289d2..3fe262e 100644
--- a/include/linux/initrd.h
+++ b/include/linux/initrd.h
@@ -18,3 +18,15 @@ extern unsigned long initrd_start, initrd_end;
extern void free_initrd_mem(unsigned long, unsigned long);

extern unsigned int real_root_dev;
+
+
+#define MAX_EARLY_INITRD_CB 16
+
+#ifdef CONFIG_EARLY_INITRD
+extern int early_initrd_find_cpio_data(const char *data, size_t len);
+#else
+static int early_initrd_find_cpio_data(const char *data, size_t len)
+{
+ return 0;
+}
+#endif
diff --git a/init/Makefile b/init/Makefile
index 7bc47ee..c8408ec 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -9,6 +9,7 @@ else
obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o
endif
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
+obj-$(CONFIG_EARLY_INITRD) += initrd_early.o

ifneq ($(CONFIG_ARCH_INIT_TASK),y)
obj-y += init_task.o
diff --git a/init/initrd_early.c b/init/initrd_early.c
new file mode 100644
index 0000000..c657a4b
--- /dev/null
+++ b/init/initrd_early.c
@@ -0,0 +1,131 @@
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/initrd.h>
+
+struct initrd_early_data {
+ /* Path where relevant files can be found in uncompressed cpio */
+ char *namesp;
+ /* Callback called for each found file in above path */
+ int (*cb)(void *data, int size, const char *name);
+ /* Finalize callback: Called if file scanning finished and above
+ callback has been called one or more times successfully */
+ void (*final)(void);
+};
+
+/*
+ * Add here new callback functions and the path relevant files show up in an
+ * uncompressed cpio
+ */
+static __initdata struct initrd_early_data initrd_early_callbacks[] =
+{
+ {
+ .namesp = NULL,
+ }
+};
+
+enum cpio_fields {
+ C_MAGIC,
+ C_INO,
+ C_MODE,
+ C_UID,
+ C_GID,
+ C_NLINK,
+ C_MTIME,
+ C_FILESIZE,
+ C_MAJ,
+ C_MIN,
+ C_RMAJ,
+ C_RMIN,
+ C_NAMESIZE,
+ C_CHKSUM,
+ C_NFIELDS
+};
+
+#define ALIGN4(p) ((void *)(((size_t)p + 3) & ~3))
+
+void __init early_initrd_find_cpio_data(const char *data, size_t len)
+{
+ const size_t cpio_header_len = 8*C_NFIELDS - 2;
+ const char *p, *dptr, *nptr;
+ unsigned int ch[C_NFIELDS], *chp, v;
+ unsigned char c, x;
+ int i, j;
+ struct initrd_early_data *ied;
+ int str_len[MAX_EARLY_INITRD_CB];
+ int match[MAX_EARLY_INITRD_CB];
+
+ for(i = 0, ied = initrd_early_callbacks; ied->namesp; ied++, i++) {
+ str_len[i] = strlen(ied->namesp);
+ match[i] = 0;
+ }
+
+ p = data;
+
+ while (len > cpio_header_len) {
+ if (!*p) {
+ /* All cpio headers need to be 4-byte aligned */
+ p += 4;
+ len -= 4;
+ continue;
+ }
+
+ j = 6; /* The magic field is only 6 characters */
+ chp = ch;
+ for (i = C_NFIELDS; i; i--) {
+ v = 0;
+ while (j--) {
+ v <<= 4;
+ c = *p++;
+
+ x = c - '0';
+ if (x < 10) {
+ v += x;
+ continue;
+ }
+
+ x = (c | 0x20) - 'a';
+ if (x < 6) {
+ v += x + 10;
+ continue;
+ }
+
+ goto quit; /* Invalid hexadecimal */
+ }
+ *chp++ = v;
+ j = 8; /* All other fields are 8 characters */
+ }
+
+ if ((ch[C_MAGIC] - 0x070701) > 1)
+ goto quit; /* Invalid magic */
+
+ len -= cpio_header_len;
+
+ dptr = ALIGN4(p + ch[C_NAMESIZE]);
+ nptr = ALIGN4(dptr + ch[C_FILESIZE]);
+
+ if (nptr > p + len || dptr < p || nptr < dptr)
+ goto quit; /* Buffer overrun */
+
+ if ((ch[C_MODE] & 0170000) == 0100000) {
+ for(i = 0, ied = initrd_early_callbacks; ied->namesp;
+ ied++, i++) {
+ int min_len = (str_len[i] < ch[C_NAMESIZE])
+ ? str_len[i] : ch[C_NAMESIZE];
+ if (!memcmp(p, ied->namesp, min_len)) {
+ if (!ied->cb((void *)dptr,
+ ch[C_FILESIZE], p + min_len))
+ match[i]++;
+ }
+ }
+ }
+
+ len -= (nptr - p);
+ p = nptr;
+ }
+
+quit:
+ for(i = 0, ied = initrd_early_callbacks; ied->namesp; ied++, i++) {
+ if (match[i])
+ ied->final();
+ }
+}
diff --git a/usr/Kconfig b/usr/Kconfig
index 085872b..3b832ed 100644
--- a/usr/Kconfig
+++ b/usr/Kconfig
@@ -166,3 +166,15 @@ config INITRAMFS_COMPRESSION_LZO
(both compression and decompression) is the fastest.

endchoice
+
+config EARLY_INITRD
+ bool "Ability to pass data to the kernel which is needed really early"
+ default y
+ depends on BLK_DEV_INITRD && X86
+ help
+ CPU microcode updates could be loaded before CPU initialization.
+ BIOS data can be overridden via initrd for debugging purposes.
+ If you are unsure whether your Hardware or kernel makes use of this,
+ it is safe to say yes here. As long as no data is passed through an
+ uncompressed cpio via initrd the kernel could make use of, nothing
+ will happen.
--
1.7.6.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/