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-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html