Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
/utsversion-tmp.h
+3
View File
@@ -0,0 +1,3 @@
CONFIG_KUNIT=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_TEST=y
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#
ccflags-y := -fno-function-sections -fno-data-sections
obj-y := main.o version.o mounts.o
ifneq ($(CONFIG_BLK_DEV_INITRD),y)
obj-y += noinitramfs.o
else
obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o
endif
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
obj-$(CONFIG_INITRAMFS_TEST) += initramfs_test.o
obj-y += init_task.o
mounts-y := do_mounts.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o
mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o
#
# UTS_VERSION
#
smp-flag-$(CONFIG_SMP) := SMP
preempt-flag-$(CONFIG_PREEMPT_BUILD) := PREEMPT
preempt-flag-$(CONFIG_PREEMPT_DYNAMIC) := PREEMPT_DYNAMIC
preempt-flag-$(CONFIG_PREEMPT_RT) := PREEMPT_RT
build-version = $(or $(KBUILD_BUILD_VERSION), $(build-version-auto))
build-timestamp = $(or $(KBUILD_BUILD_TIMESTAMP), $(build-timestamp-auto))
# Maximum length of UTS_VERSION is 64 chars
filechk_uts_version = \
utsver=$$(echo '$(pound)'"$(build-version)" $(smp-flag-y) $(preempt-flag-y) "$(build-timestamp)" | cut -b -64); \
echo '$(pound)'define UTS_VERSION \""$${utsver}"\"
#
# Build version.c with temporary UTS_VERSION
#
$(obj)/utsversion-tmp.h: FORCE
$(call filechk,uts_version)
clean-files += utsversion-tmp.h
$(obj)/version.o: $(obj)/utsversion-tmp.h
CFLAGS_version.o := -include $(obj)/utsversion-tmp.h
#
# Build version-timestamp.c with final UTS_VERSION
#
include/generated/utsversion.h: build-version-auto = $(shell $(srctree)/scripts/build-version)
include/generated/utsversion.h: build-timestamp-auto = $(shell LC_ALL=C date)
include/generated/utsversion.h: FORCE
$(call filechk,uts_version)
$(obj)/version-timestamp.o: include/generated/utsversion.h
CFLAGS_version-timestamp.o := -include include/generated/utsversion.h
+319
View File
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0
/* calibrate.c: default delay calibration
*
* Excised from init/main.c
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kstrtox.h>
#include <linux/percpu.h>
#include <linux/printk.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/timex.h>
unsigned long lpj_fine;
unsigned long preset_lpj;
static int __init lpj_setup(char *str)
{
return kstrtoul(str, 0, &preset_lpj) == 0;
}
__setup("lpj=", lpj_setup);
#ifdef ARCH_HAS_READ_CURRENT_TIMER
/* This routine uses the read_current_timer() routine and gets the
* loops per jiffy directly, instead of guessing it using delay().
* Also, this code tries to handle non-maskable asynchronous events
* (like SMIs)
*/
#define DELAY_CALIBRATION_TICKS ((HZ < 100) ? 1 : (HZ/100))
#define MAX_DIRECT_CALIBRATION_RETRIES 5
static unsigned long calibrate_delay_direct(void)
{
unsigned long pre_start, start, post_start;
unsigned long pre_end, end, post_end;
unsigned long start_jiffies;
unsigned long timer_rate_min, timer_rate_max;
unsigned long good_timer_sum = 0;
unsigned long good_timer_count = 0;
unsigned long measured_times[MAX_DIRECT_CALIBRATION_RETRIES];
int max = -1; /* index of measured_times with max/min values or not set */
int min = -1;
int i;
if (read_current_timer(&pre_start) < 0 )
return 0;
/*
* A simple loop like
* while ( jiffies < start_jiffies+1)
* start = read_current_timer();
* will not do. As we don't really know whether jiffy switch
* happened first or timer_value was read first. And some asynchronous
* event can happen between these two events introducing errors in lpj.
*
* So, we do
* 1. pre_start <- When we are sure that jiffy switch hasn't happened
* 2. check jiffy switch
* 3. start <- timer value before or after jiffy switch
* 4. post_start <- When we are sure that jiffy switch has happened
*
* Note, we don't know anything about order of 2 and 3.
* Now, by looking at post_start and pre_start difference, we can
* check whether any asynchronous event happened or not
*/
for (i = 0; i < MAX_DIRECT_CALIBRATION_RETRIES; i++) {
pre_start = 0;
read_current_timer(&start);
start_jiffies = jiffies;
while (time_before_eq(jiffies, start_jiffies + 1)) {
pre_start = start;
read_current_timer(&start);
}
read_current_timer(&post_start);
pre_end = 0;
end = post_start;
while (time_before_eq(jiffies, start_jiffies + 1 +
DELAY_CALIBRATION_TICKS)) {
pre_end = end;
read_current_timer(&end);
}
read_current_timer(&post_end);
timer_rate_max = (post_end - pre_start) /
DELAY_CALIBRATION_TICKS;
timer_rate_min = (pre_end - post_start) /
DELAY_CALIBRATION_TICKS;
/*
* If the upper limit and lower limit of the timer_rate is
* >= 12.5% apart, redo calibration.
*/
if (start >= post_end)
printk(KERN_NOTICE "calibrate_delay_direct() ignoring "
"timer_rate as we had a TSC wrap around"
" start=%lu >=post_end=%lu\n",
start, post_end);
if (start < post_end && pre_start != 0 && pre_end != 0 &&
(timer_rate_max - timer_rate_min) < (timer_rate_max >> 3)) {
good_timer_count++;
good_timer_sum += timer_rate_max;
measured_times[i] = timer_rate_max;
if (max < 0 || timer_rate_max > measured_times[max])
max = i;
if (min < 0 || timer_rate_max < measured_times[min])
min = i;
} else
measured_times[i] = 0;
}
/*
* Find the maximum & minimum - if they differ too much throw out the
* one with the largest difference from the mean and try again...
*/
while (good_timer_count > 1) {
unsigned long estimate;
unsigned long maxdiff;
/* compute the estimate */
estimate = (good_timer_sum/good_timer_count);
maxdiff = estimate >> 3;
/* if range is within 12% let's take it */
if ((measured_times[max] - measured_times[min]) < maxdiff)
return estimate;
/* ok - drop the worse value and try again... */
good_timer_sum = 0;
good_timer_count = 0;
if ((measured_times[max] - estimate) <
(estimate - measured_times[min])) {
printk(KERN_NOTICE "calibrate_delay_direct() dropping "
"min bogoMips estimate %d = %lu\n",
min, measured_times[min]);
measured_times[min] = 0;
min = max;
} else {
printk(KERN_NOTICE "calibrate_delay_direct() dropping "
"max bogoMips estimate %d = %lu\n",
max, measured_times[max]);
measured_times[max] = 0;
max = min;
}
for (i = 0; i < MAX_DIRECT_CALIBRATION_RETRIES; i++) {
if (measured_times[i] == 0)
continue;
good_timer_count++;
good_timer_sum += measured_times[i];
if (measured_times[i] < measured_times[min])
min = i;
if (measured_times[i] > measured_times[max])
max = i;
}
}
printk(KERN_NOTICE "calibrate_delay_direct() failed to get a good "
"estimate for loops_per_jiffy.\nProbably due to long platform "
"interrupts. Consider using \"lpj=\" boot option.\n");
return 0;
}
#else
static unsigned long calibrate_delay_direct(void)
{
return 0;
}
#endif
/*
* This is the number of bits of precision for the loops_per_jiffy. Each
* time we refine our estimate after the first takes 1.5/HZ seconds, so try
* to start with a good estimate.
* For the boot cpu we can skip the delay calibration and assign it a value
* calculated based on the timer frequency.
* For the rest of the CPUs we cannot assume that the timer frequency is same as
* the cpu frequency, hence do the calibration for those.
*/
#define LPS_PREC 8
static unsigned long calibrate_delay_converge(void)
{
/* First stage - slowly accelerate to find initial bounds */
unsigned long lpj, lpj_base, ticks, loopadd, loopadd_base, chop_limit;
int trials = 0, band = 0, trial_in_band = 0;
lpj = (1<<12);
/* wait for "start of" clock tick */
ticks = jiffies;
while (ticks == jiffies)
; /* nothing */
/* Go .. */
ticks = jiffies;
do {
if (++trial_in_band == (1<<band)) {
++band;
trial_in_band = 0;
}
__delay(lpj * band);
trials += band;
} while (ticks == jiffies);
/*
* We overshot, so retreat to a clear underestimate. Then estimate
* the largest likely undershoot. This defines our chop bounds.
*/
trials -= band;
loopadd_base = lpj * band;
lpj_base = lpj * trials;
recalibrate:
lpj = lpj_base;
loopadd = loopadd_base;
/*
* Do a binary approximation to get lpj set to
* equal one clock (up to LPS_PREC bits)
*/
chop_limit = lpj >> LPS_PREC;
while (loopadd > chop_limit) {
lpj += loopadd;
ticks = jiffies;
while (ticks == jiffies)
; /* nothing */
ticks = jiffies;
__delay(lpj);
if (jiffies != ticks) /* longer than 1 tick */
lpj -= loopadd;
loopadd >>= 1;
}
/*
* If we incremented every single time possible, presume we've
* massively underestimated initially, and retry with a higher
* start, and larger range. (Only seen on x86_64, due to SMIs)
*/
if (lpj + loopadd * 2 == lpj_base + loopadd_base * 2) {
lpj_base = lpj;
loopadd_base <<= 2;
goto recalibrate;
}
return lpj;
}
static DEFINE_PER_CPU(unsigned long, cpu_loops_per_jiffy) = { 0 };
/*
* Check if cpu calibration delay is already known. For example,
* some processors with multi-core sockets may have all cores
* with the same calibration delay.
*
* Architectures should override this function if a faster calibration
* method is available.
*/
unsigned long __attribute__((weak)) calibrate_delay_is_known(void)
{
return 0;
}
/*
* Indicate the cpu delay calibration is done. This can be used by
* architectures to stop accepting delay timer registrations after this point.
*/
void __attribute__((weak)) calibration_delay_done(void)
{
}
void calibrate_delay(void)
{
unsigned long lpj;
static bool printed;
int this_cpu = smp_processor_id();
if (per_cpu(cpu_loops_per_jiffy, this_cpu)) {
lpj = per_cpu(cpu_loops_per_jiffy, this_cpu);
if (!printed)
pr_info("Calibrating delay loop (skipped) "
"already calibrated this CPU");
} else if (preset_lpj) {
lpj = preset_lpj;
if (!printed)
pr_info("Calibrating delay loop (skipped) "
"preset value.. ");
} else if ((!printed) && lpj_fine) {
lpj = lpj_fine;
pr_info("Calibrating delay loop (skipped), "
"value calculated using timer frequency.. ");
} else if ((lpj = calibrate_delay_is_known())) {
;
} else if ((lpj = calibrate_delay_direct()) != 0) {
if (!printed)
pr_info("Calibrating delay using timer "
"specific routine.. ");
} else {
if (!printed)
pr_info("Calibrating delay loop... ");
lpj = calibrate_delay_converge();
}
per_cpu(cpu_loops_per_jiffy, this_cpu) = lpj;
if (!printed)
pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",
lpj/(500000/HZ),
(lpj/(5000/HZ)) % 100, lpj);
loops_per_jiffy = lpj;
printed = true;
calibration_delay_done();
}
+520
View File
@@ -0,0 +1,520 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/fd.h>
#include <linux/tty.h>
#include <linux/suspend.h>
#include <linux/root_dev.h>
#include <linux/security.h>
#include <linux/delay.h>
#include <linux/mount.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/initrd.h>
#include <linux/async.h>
#include <linux/fs_struct.h>
#include <linux/slab.h>
#include <linux/ramfs.h>
#include <linux/shmem_fs.h>
#include <linux/ktime.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_fs_sb.h>
#include <linux/nfs_mount.h>
#include <linux/raid/detect.h>
#include <uapi/linux/mount.h>
#include "do_mounts.h"
int root_mountflags = MS_RDONLY | MS_SILENT;
static char __initdata saved_root_name[64];
static int root_wait;
dev_t ROOT_DEV;
static int __init readonly(char *str)
{
if (*str)
return 0;
root_mountflags |= MS_RDONLY;
return 1;
}
static int __init readwrite(char *str)
{
if (*str)
return 0;
root_mountflags &= ~MS_RDONLY;
return 1;
}
__setup("ro", readonly);
__setup("rw", readwrite);
static int __init root_dev_setup(char *line)
{
strscpy(saved_root_name, line, sizeof(saved_root_name));
return 1;
}
__setup("root=", root_dev_setup);
static int __init rootwait_setup(char *str)
{
if (*str)
return 0;
root_wait = -1;
return 1;
}
__setup("rootwait", rootwait_setup);
static int __init rootwait_timeout_setup(char *str)
{
int sec;
if (kstrtoint(str, 0, &sec) || sec < 0) {
pr_warn("ignoring invalid rootwait value\n");
goto ignore;
}
if (check_mul_overflow(sec, MSEC_PER_SEC, &root_wait)) {
pr_warn("ignoring excessive rootwait value\n");
goto ignore;
}
return 1;
ignore:
/* Fallback to indefinite wait */
root_wait = -1;
return 1;
}
__setup("rootwait=", rootwait_timeout_setup);
static char * __initdata root_mount_data;
static int __init root_data_setup(char *str)
{
root_mount_data = str;
return 1;
}
static char * __initdata root_fs_names;
static int __init fs_names_setup(char *str)
{
root_fs_names = str;
return 1;
}
static unsigned int __initdata root_delay;
static int __init root_delay_setup(char *str)
{
if (kstrtouint(str, 0, &root_delay))
return 0;
return 1;
}
__setup("rootflags=", root_data_setup);
__setup("rootfstype=", fs_names_setup);
__setup("rootdelay=", root_delay_setup);
/* This can return zero length strings. Caller should check */
static int __init split_fs_names(char *page, size_t size)
{
int count = 1;
char *p = page;
strscpy(p, root_fs_names, size);
while (*p++) {
if (p[-1] == ',') {
p[-1] = '\0';
count++;
}
}
return count;
}
static int __init do_mount_root(const char *name, const char *fs,
const int flags, const void *data)
{
struct super_block *s;
char *data_page = NULL;
int ret;
if (data) {
/* init_mount() requires a full page as fifth argument */
data_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!data_page)
return -ENOMEM;
strscpy_pad(data_page, data, PAGE_SIZE);
}
ret = init_mount(name, "/root", fs, flags, data_page);
if (ret)
goto out;
init_chdir("/root");
s = current->fs->pwd.dentry->d_sb;
ROOT_DEV = s->s_dev;
printk(KERN_INFO
"VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",
s->s_type->name,
sb_rdonly(s) ? " readonly" : "",
MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
out:
kfree(data_page);
return ret;
}
void __init mount_root_generic(char *name, char *pretty_name, int flags)
{
char *fs_names = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *p;
char b[BDEVNAME_SIZE];
int num_fs, i;
if (!fs_names)
panic("VFS: Unable to mount root fs: not enough memory");
scnprintf(b, BDEVNAME_SIZE, "unknown-block(%u,%u)",
MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
if (root_fs_names)
num_fs = split_fs_names(fs_names, PAGE_SIZE);
else
num_fs = list_bdev_fs_names(fs_names, PAGE_SIZE);
retry:
for (i = 0, p = fs_names; i < num_fs; i++, p += strlen(p)+1) {
int err;
if (!*p)
continue;
err = do_mount_root(name, p, flags, root_mount_data);
switch (err) {
case 0:
goto out;
case -EACCES:
case -EINVAL:
#ifdef CONFIG_BLOCK
init_flush_fput();
#endif
continue;
}
/*
* Allow the user to distinguish between failed sys_open
* and bad superblock on root device.
* and give them a list of the available devices
*/
printk("VFS: Cannot open root device \"%s\" or %s: error %d\n",
pretty_name, b, err);
printk("Please append a correct \"root=\" boot option; here are the available partitions:\n");
printk_all_partitions();
if (root_fs_names)
num_fs = list_bdev_fs_names(fs_names, PAGE_SIZE);
if (!num_fs)
pr_err("Can't find any bdev filesystem to be used for mount!\n");
else {
pr_err("List of all bdev filesystems:\n");
for (i = 0, p = fs_names; i < num_fs; i++, p += strlen(p)+1)
pr_err(" %s", p);
pr_err("\n");
}
panic("VFS: Unable to mount root fs on %s", b);
}
if (!(flags & SB_RDONLY)) {
flags |= SB_RDONLY;
goto retry;
}
printk("List of all partitions:\n");
printk_all_partitions();
printk("No filesystem could mount root, tried: ");
for (i = 0, p = fs_names; i < num_fs; i++, p += strlen(p)+1)
printk(" %s", p);
printk("\n");
panic("VFS: Unable to mount root fs on \"%s\" or %s", pretty_name, b);
out:
kfree(fs_names);
}
#ifdef CONFIG_ROOT_NFS
#define NFSROOT_TIMEOUT_MIN 5
#define NFSROOT_TIMEOUT_MAX 30
#define NFSROOT_RETRY_MAX 5
static void __init mount_nfs_root(void)
{
char *root_dev, *root_data;
unsigned int timeout;
int try;
if (nfs_root_data(&root_dev, &root_data))
goto fail;
/*
* The server or network may not be ready, so try several
* times. Stop after a few tries in case the client wants
* to fall back to other boot methods.
*/
timeout = NFSROOT_TIMEOUT_MIN;
for (try = 1; ; try++) {
if (!do_mount_root(root_dev, "nfs", root_mountflags, root_data))
return;
if (try > NFSROOT_RETRY_MAX)
break;
/* Wait, in case the server refused us immediately */
ssleep(timeout);
timeout <<= 1;
if (timeout > NFSROOT_TIMEOUT_MAX)
timeout = NFSROOT_TIMEOUT_MAX;
}
fail:
pr_err("VFS: Unable to mount root fs via NFS.\n");
}
#else
static inline void mount_nfs_root(void)
{
}
#endif /* CONFIG_ROOT_NFS */
#ifdef CONFIG_CIFS_ROOT
#define CIFSROOT_TIMEOUT_MIN 5
#define CIFSROOT_TIMEOUT_MAX 30
#define CIFSROOT_RETRY_MAX 5
static void __init mount_cifs_root(void)
{
char *root_dev, *root_data;
unsigned int timeout;
int try;
if (cifs_root_data(&root_dev, &root_data))
goto fail;
timeout = CIFSROOT_TIMEOUT_MIN;
for (try = 1; ; try++) {
if (!do_mount_root(root_dev, "cifs", root_mountflags,
root_data))
return;
if (try > CIFSROOT_RETRY_MAX)
break;
ssleep(timeout);
timeout <<= 1;
if (timeout > CIFSROOT_TIMEOUT_MAX)
timeout = CIFSROOT_TIMEOUT_MAX;
}
fail:
pr_err("VFS: Unable to mount root fs via SMB.\n");
}
#else
static inline void mount_cifs_root(void)
{
}
#endif /* CONFIG_CIFS_ROOT */
static bool __init fs_is_nodev(char *fstype)
{
struct file_system_type *fs = get_fs_type(fstype);
bool ret = false;
if (fs) {
ret = !(fs->fs_flags & FS_REQUIRES_DEV);
put_filesystem(fs);
}
return ret;
}
static int __init mount_nodev_root(char *root_device_name)
{
char *fs_names, *fstype;
int err = -EINVAL;
int num_fs, i;
fs_names = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!fs_names)
return -EINVAL;
num_fs = split_fs_names(fs_names, PAGE_SIZE);
for (i = 0, fstype = fs_names; i < num_fs;
i++, fstype += strlen(fstype) + 1) {
if (!*fstype)
continue;
if (!fs_is_nodev(fstype))
continue;
err = do_mount_root(root_device_name, fstype, root_mountflags,
root_mount_data);
if (!err)
break;
}
kfree(fs_names);
return err;
}
#ifdef CONFIG_BLOCK
static void __init mount_block_root(char *root_device_name)
{
int err = create_dev("/dev/root", ROOT_DEV);
if (err < 0)
pr_emerg("Failed to create /dev/root: %d\n", err);
mount_root_generic("/dev/root", root_device_name, root_mountflags);
}
#else
static inline void mount_block_root(char *root_device_name)
{
}
#endif /* CONFIG_BLOCK */
void __init mount_root(char *root_device_name)
{
switch (ROOT_DEV) {
case Root_NFS:
mount_nfs_root();
break;
case Root_CIFS:
mount_cifs_root();
break;
case Root_Generic:
mount_root_generic(root_device_name, root_device_name,
root_mountflags);
break;
case 0:
if (root_device_name && root_fs_names &&
mount_nodev_root(root_device_name) == 0)
break;
fallthrough;
default:
mount_block_root(root_device_name);
break;
}
}
/* wait for any asynchronous scanning to complete */
static void __init wait_for_root(char *root_device_name)
{
ktime_t end;
if (ROOT_DEV != 0)
return;
pr_info("Waiting for root device %s...\n", root_device_name);
end = ktime_add_ms(ktime_get_raw(), root_wait);
while (!driver_probe_done() ||
early_lookup_bdev(root_device_name, &ROOT_DEV) < 0) {
msleep(5);
if (root_wait > 0 && ktime_after(ktime_get_raw(), end))
break;
}
async_synchronize_full();
}
static dev_t __init parse_root_device(char *root_device_name)
{
int error;
dev_t dev;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3))
return Root_Generic;
if (strcmp(root_device_name, "/dev/nfs") == 0)
return Root_NFS;
if (strcmp(root_device_name, "/dev/cifs") == 0)
return Root_CIFS;
if (strcmp(root_device_name, "/dev/ram") == 0)
return Root_RAM0;
error = early_lookup_bdev(root_device_name, &dev);
if (error) {
if (error == -EINVAL && root_wait) {
pr_err("Disabling rootwait; root= is invalid.\n");
root_wait = 0;
}
return 0;
}
return dev;
}
/*
* Prepare the namespace - decide what/where to mount, load ramdisks, etc.
*/
void __init prepare_namespace(void)
{
if (root_delay) {
printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
/*
* wait for the known devices to complete their probing
*
* Note: this is a potential source of long boot delays.
* For example, it is not atypical to wait 5 seconds here
* for the touchpad of a laptop to initialize.
*/
wait_for_device_probe();
md_run_setup();
if (saved_root_name[0])
ROOT_DEV = parse_root_device(saved_root_name);
initrd_load();
if (root_wait)
wait_for_root(saved_root_name);
mount_root(saved_root_name);
devtmpfs_mount();
if (init_pivot_root(".", ".")) {
pr_err("VFS: Failed to pivot into new rootfs\n");
return;
}
if (init_umount(".", MNT_DETACH)) {
pr_err("VFS: Failed to unmount old rootfs\n");
return;
}
pr_info("VFS: Pivoted into new rootfs\n");
}
static bool is_tmpfs;
static int rootfs_init_fs_context(struct fs_context *fc)
{
if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs)
return shmem_init_fs_context(fc);
return ramfs_init_fs_context(fc);
}
struct file_system_type rootfs_fs_type = {
.name = "rootfs",
.init_fs_context = rootfs_init_fs_context,
.kill_sb = kill_anon_super,
};
void __init init_rootfs(void)
{
if (IS_ENABLED(CONFIG_TMPFS)) {
if (!saved_root_name[0] && !root_fs_names)
is_tmpfs = true;
else if (root_fs_names && !!strstr(root_fs_names, "tmpfs"))
is_tmpfs = true;
}
}
+42
View File
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/mount.h>
#include <linux/major.h>
#include <linux/root_dev.h>
#include <linux/init_syscalls.h>
#include <linux/task_work.h>
#include <linux/file.h>
void mount_root_generic(char *name, char *pretty_name, int flags);
void mount_root(char *root_device_name);
extern int root_mountflags;
static inline __init int create_dev(char *name, dev_t dev)
{
init_unlink(name);
return init_mknod(name, S_IFBLK | 0600, new_encode_dev(dev));
}
#ifdef CONFIG_BLK_DEV_RAM
int __init rd_load_image(void);
#else
static inline int rd_load_image(void) { return 0; }
#endif
#ifdef CONFIG_BLK_DEV_INITRD
void __init initrd_load(void);
#else
static inline void initrd_load(void) { }
#endif
/* Ensure that async file closing finished to prevent spurious errors. */
static inline void init_flush_fput(void)
{
flush_delayed_fput();
task_work_run();
}
+63
View File
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/unistd.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/initrd.h>
#include "do_mounts.h"
unsigned long initrd_start, initrd_end;
int initrd_below_start_ok;
static int __initdata mount_initrd = 1;
phys_addr_t phys_initrd_start __initdata;
unsigned long phys_initrd_size __initdata;
static int __init no_initrd(char *str)
{
pr_warn("noinitrd option is deprecated and will be removed soon\n");
mount_initrd = 0;
return 1;
}
__setup("noinitrd", no_initrd);
static int __init early_initrdmem(char *p)
{
phys_addr_t start;
unsigned long size;
char *endp;
start = memparse(p, &endp);
if (*endp == ',') {
size = memparse(endp + 1, NULL);
phys_initrd_start = start;
phys_initrd_size = size;
}
return 0;
}
early_param("initrdmem", early_initrdmem);
static int __init early_initrd(char *p)
{
return early_initrdmem(p);
}
early_param("initrd", early_initrd);
void __init initrd_load(void)
{
if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0);
/*
* Load the initrd data into /dev/ram0.
*/
if (rd_load_image()) {
pr_warn("using deprecated initrd support, will be removed in January 2027; "
"use initramfs instead or (as a last resort) /sys/firmware/initrd; "
"see section \"Workaround\" in "
"https://lore.kernel.org/lkml/20251010094047.3111495-1-safinaskar@gmail.com\n");
}
}
init_unlink("/initrd.image");
}
+315
View File
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/minix_fs.h>
#include <linux/ext2_fs.h>
#include <linux/romfs_fs.h>
#include <uapi/linux/cramfs_fs.h>
#include <linux/initrd.h>
#include <linux/string.h>
#include <linux/string_choices.h>
#include <linux/slab.h>
#include "do_mounts.h"
#include "../fs/squashfs/squashfs_fs.h"
#include <linux/decompress/generic.h>
static struct file *in_file, *out_file;
static loff_t in_pos, out_pos;
int __initdata rd_image_start; /* starting block # of image */
static int __init ramdisk_start_setup(char *str)
{
pr_warn("ramdisk_start= option is deprecated and will be removed soon\n");
return kstrtoint(str, 0, &rd_image_start) == 0;
}
__setup("ramdisk_start=", ramdisk_start_setup);
static int __init crd_load(decompress_fn deco);
/*
* This routine tries to find a RAM disk image to load, and returns the
* number of blocks to read for a non-compressed image, 0 if the image
* is a compressed image, and -1 if an image with the right magic
* numbers could not be found.
*
* We currently check for the following magic numbers:
* minix
* ext2
* romfs
* cramfs
* squashfs
* gzip
* bzip2
* lzma
* xz
* lzo
* lz4
*/
static int __init
identify_ramdisk_image(struct file *file, loff_t pos,
decompress_fn *decompressor)
{
const int size = 512;
struct minix_super_block *minixsb;
struct romfs_super_block *romfsb;
struct cramfs_super *cramfsb;
struct squashfs_super_block *squashfsb;
int nblocks = -1;
unsigned char *buf;
const char *compress_name;
unsigned long n;
int start_block = rd_image_start;
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
minixsb = (struct minix_super_block *) buf;
romfsb = (struct romfs_super_block *) buf;
cramfsb = (struct cramfs_super *) buf;
squashfsb = (struct squashfs_super_block *) buf;
memset(buf, 0xe5, size);
/*
* Read block 0 to test for compressed kernel
*/
pos = start_block * BLOCK_SIZE;
kernel_read(file, buf, size, &pos);
*decompressor = decompress_method(buf, size, &compress_name);
if (compress_name) {
printk(KERN_NOTICE "RAMDISK: %s image found at block %d\n",
compress_name, start_block);
if (!*decompressor)
printk(KERN_EMERG
"RAMDISK: %s decompressor not configured!\n",
compress_name);
nblocks = 0;
goto done;
}
/* romfs is at block zero too */
if (romfsb->word0 == ROMSB_WORD0 &&
romfsb->word1 == ROMSB_WORD1) {
printk(KERN_NOTICE
"RAMDISK: romfs filesystem found at block %d\n",
start_block);
nblocks = (ntohl(romfsb->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
goto done;
}
if (cramfsb->magic == CRAMFS_MAGIC) {
printk(KERN_NOTICE
"RAMDISK: cramfs filesystem found at block %d\n",
start_block);
nblocks = (cramfsb->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
goto done;
}
/* squashfs is at block zero too */
if (le32_to_cpu(squashfsb->s_magic) == SQUASHFS_MAGIC) {
printk(KERN_NOTICE
"RAMDISK: squashfs filesystem found at block %d\n",
start_block);
nblocks = (le64_to_cpu(squashfsb->bytes_used) + BLOCK_SIZE - 1)
>> BLOCK_SIZE_BITS;
goto done;
}
/*
* Read 512 bytes further to check if cramfs is padded
*/
pos = start_block * BLOCK_SIZE + 0x200;
kernel_read(file, buf, size, &pos);
if (cramfsb->magic == CRAMFS_MAGIC) {
printk(KERN_NOTICE
"RAMDISK: cramfs filesystem found at block %d\n",
start_block);
nblocks = (cramfsb->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
goto done;
}
/*
* Read block 1 to test for minix and ext2 superblock
*/
pos = (start_block + 1) * BLOCK_SIZE;
kernel_read(file, buf, size, &pos);
/* Try minix */
if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
minixsb->s_magic == MINIX_SUPER_MAGIC2) {
printk(KERN_NOTICE
"RAMDISK: Minix filesystem found at block %d\n",
start_block);
nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
goto done;
}
/* Try ext2 */
n = ext2_image_size(buf);
if (n) {
printk(KERN_NOTICE
"RAMDISK: ext2 filesystem found at block %d\n",
start_block);
nblocks = n;
goto done;
}
printk(KERN_NOTICE
"RAMDISK: Couldn't find valid RAM disk image starting at %d.\n",
start_block);
done:
kfree(buf);
return nblocks;
}
static unsigned long nr_blocks(struct file *file)
{
struct inode *inode = file->f_mapping->host;
if (!S_ISBLK(inode->i_mode))
return 0;
return i_size_read(inode) >> 10;
}
int __init rd_load_image(void)
{
int res = 0;
unsigned long rd_blocks, devblocks, nr_disks;
int nblocks, i;
char *buf = NULL;
unsigned short rotate = 0;
decompress_fn decompressor = NULL;
char rotator[4] = { '|' , '/' , '-' , '\\' };
out_file = filp_open("/dev/ram", O_RDWR, 0);
if (IS_ERR(out_file))
goto out;
in_file = filp_open("/initrd.image", O_RDONLY, 0);
if (IS_ERR(in_file))
goto noclose_input;
in_pos = rd_image_start * BLOCK_SIZE;
nblocks = identify_ramdisk_image(in_file, in_pos, &decompressor);
if (nblocks < 0)
goto done;
if (nblocks == 0) {
if (crd_load(decompressor) == 0)
goto successful_load;
goto done;
}
/*
* NOTE NOTE: nblocks is not actually blocks but
* the number of kibibytes of data to load into a ramdisk.
*/
rd_blocks = nr_blocks(out_file);
if (nblocks > rd_blocks) {
printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n",
nblocks, rd_blocks);
goto done;
}
/*
* OK, time to copy in the data
*/
devblocks = nblocks;
if (devblocks == 0) {
printk(KERN_ERR "RAMDISK: could not determine device size\n");
goto done;
}
buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR "RAMDISK: could not allocate buffer\n");
goto done;
}
nr_disks = (nblocks - 1) / devblocks + 1;
pr_notice("RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",
nblocks, nr_disks, str_plural(nr_disks));
for (i = 0; i < nblocks; i++) {
if (i && (i % devblocks == 0)) {
pr_cont("done disk #1.\n");
rotate = 0;
fput(in_file);
break;
}
kernel_read(in_file, buf, BLOCK_SIZE, &in_pos);
kernel_write(out_file, buf, BLOCK_SIZE, &out_pos);
if (!IS_ENABLED(CONFIG_S390) && !(i % 16)) {
pr_cont("%c\b", rotator[rotate & 0x3]);
rotate++;
}
}
pr_cont("done.\n");
successful_load:
res = 1;
done:
fput(in_file);
noclose_input:
fput(out_file);
out:
kfree(buf);
init_unlink("/dev/ram");
return res;
}
static int exit_code;
static int decompress_error;
static long __init compr_fill(void *buf, unsigned long len)
{
long r = kernel_read(in_file, buf, len, &in_pos);
if (r < 0)
printk(KERN_ERR "RAMDISK: error while reading compressed data");
else if (r == 0)
printk(KERN_ERR "RAMDISK: EOF while reading compressed data");
return r;
}
static long __init compr_flush(void *window, unsigned long outcnt)
{
long written = kernel_write(out_file, window, outcnt, &out_pos);
if (written != outcnt) {
if (decompress_error == 0)
printk(KERN_ERR
"RAMDISK: incomplete write (%ld != %ld)\n",
written, outcnt);
decompress_error = 1;
return -1;
}
return outcnt;
}
static void __init error(char *x)
{
printk(KERN_ERR "%s\n", x);
exit_code = 1;
decompress_error = 1;
}
static int __init crd_load(decompress_fn deco)
{
int result;
if (!deco) {
pr_emerg("Invalid ramdisk decompression routine. "
"Select appropriate config option.\n");
panic("Could not decompress initial ramdisk image.");
}
result = deco(NULL, 0, compr_fill, compr_flush, NULL, NULL, error);
if (decompress_error)
result = 1;
return result;
}
+277
View File
@@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/init_task.h>
#include <linux/export.h>
#include <linux/mqueue.h>
#include <linux/sched.h>
#include <linux/sched/sysctl.h>
#include <linux/sched/rt.h>
#include <linux/sched/task.h>
#include <linux/sched/ext.h>
#include <linux/sched/exec_state.h>
#include <linux/user_namespace.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/audit.h>
#include <linux/numa.h>
#include <linux/scs.h>
#include <linux/plist.h>
#include <linux/uaccess.h>
static struct signal_struct init_signals = {
.nr_threads = 1,
.thread_head = LIST_HEAD_INIT(init_task.thread_node),
.wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(init_signals.wait_chldexit),
.shared_pending = {
.list = LIST_HEAD_INIT(init_signals.shared_pending.list),
.signal = {{0}}
},
.multiprocess = HLIST_HEAD_INIT,
.rlim = INIT_RLIMITS,
#ifdef CONFIG_CGROUPS
.cgroup_threadgroup_rwsem = __RWSEM_INITIALIZER(init_signals.cgroup_threadgroup_rwsem),
#endif
.cred_guard_mutex = __MUTEX_INITIALIZER(init_signals.cred_guard_mutex),
.exec_update_lock = __RWSEM_INITIALIZER(init_signals.exec_update_lock),
#ifdef CONFIG_POSIX_TIMERS
.posix_timers = HLIST_HEAD_INIT,
.ignored_posix_timers = HLIST_HEAD_INIT,
.cputimer = {
.cputime_atomic = INIT_CPUTIME_ATOMIC,
},
#endif
INIT_CPU_TIMERS(init_signals)
.pids = {
[PIDTYPE_PID] = &init_struct_pid,
[PIDTYPE_TGID] = &init_struct_pid,
[PIDTYPE_PGID] = &init_struct_pid,
[PIDTYPE_SID] = &init_struct_pid,
},
INIT_PREV_CPUTIME(init_signals)
};
static struct sighand_struct init_sighand = {
.count = REFCOUNT_INIT(1),
.action = { { { .sa_handler = SIG_DFL, } }, },
.siglock = __SPIN_LOCK_UNLOCKED(init_sighand.siglock),
.signalfd_wqh = __WAIT_QUEUE_HEAD_INITIALIZER(init_sighand.signalfd_wqh),
};
/* init to 2 - one for init_task, one to ensure it is never freed */
struct task_exec_state init_task_exec_state = {
.count = REFCOUNT_INIT(2),
.dumpable = TASK_DUMPABLE_OWNER,
.user_ns = &init_user_ns,
};
#ifdef CONFIG_SHADOW_CALL_STACK
unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)] = {
[(SCS_SIZE / sizeof(long)) - 1] = SCS_END_MAGIC
};
#endif
/* init to 2 - one for init_task, one to ensure it is never freed */
static struct group_info init_groups = { .usage = REFCOUNT_INIT(2) };
/*
* The initial credentials for the initial task
*/
static struct cred init_cred = {
.usage = ATOMIC_INIT(4),
.uid = GLOBAL_ROOT_UID,
.gid = GLOBAL_ROOT_GID,
.suid = GLOBAL_ROOT_UID,
.sgid = GLOBAL_ROOT_GID,
.euid = GLOBAL_ROOT_UID,
.egid = GLOBAL_ROOT_GID,
.fsuid = GLOBAL_ROOT_UID,
.fsgid = GLOBAL_ROOT_GID,
.securebits = SECUREBITS_DEFAULT,
.cap_inheritable = CAP_EMPTY_SET,
.cap_permitted = CAP_FULL_SET,
.cap_effective = CAP_FULL_SET,
.cap_bset = CAP_FULL_SET,
.user = INIT_USER,
.user_ns = &init_user_ns,
.group_info = &init_groups,
.ucounts = &init_ucounts,
};
/*
* Set up the first task table, touch at your own risk!. Base=0,
* limit=0x1fffff (=2MB)
*/
struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
#ifdef CONFIG_THREAD_INFO_IN_TASK
.thread_info = INIT_THREAD_INFO(init_task),
.stack_refcount = REFCOUNT_INIT(1),
#endif
.__state = 0,
.stack = init_stack,
.usage = REFCOUNT_INIT(2),
.flags = PF_KTHREAD,
.prio = MAX_PRIO - 20,
.static_prio = MAX_PRIO - 20,
.normal_prio = MAX_PRIO - 20,
.policy = SCHED_NORMAL,
.cpus_ptr = &init_task.cpus_mask,
.user_cpus_ptr = NULL,
.cpus_mask = CPU_MASK_ALL,
.max_allowed_capacity = SCHED_CAPACITY_SCALE,
.nr_cpus_allowed= NR_CPUS,
.mm = NULL,
.active_mm = &init_mm,
.exec_state = &init_task_exec_state,
.restart_block = {
.fn = do_no_restart_syscall,
},
.se = {
.group_node = LIST_HEAD_INIT(init_task.se.group_node),
},
.rt = {
.run_list = LIST_HEAD_INIT(init_task.rt.run_list),
.time_slice = RR_TIMESLICE,
},
.tasks = LIST_HEAD_INIT(init_task.tasks),
#ifdef CONFIG_SMP
.pushable_tasks = PLIST_NODE_INIT(init_task.pushable_tasks, MAX_PRIO),
#endif
#ifdef CONFIG_CGROUP_SCHED
.sched_task_group = &root_task_group,
#endif
#ifdef CONFIG_SCHED_CLASS_EXT
.scx = {
.dsq_list.node = LIST_HEAD_INIT(init_task.scx.dsq_list.node),
.sticky_cpu = -1,
.holding_cpu = -1,
.runnable_node = LIST_HEAD_INIT(init_task.scx.runnable_node),
.runnable_at = INITIAL_JIFFIES,
.ddsp_dsq_id = SCX_DSQ_INVALID,
.slice = SCX_SLICE_DFL,
},
#endif
.ptraced = LIST_HEAD_INIT(init_task.ptraced),
.ptrace_entry = LIST_HEAD_INIT(init_task.ptrace_entry),
.real_parent = &init_task,
.parent = &init_task,
.children = LIST_HEAD_INIT(init_task.children),
.sibling = LIST_HEAD_INIT(init_task.sibling),
.group_leader = &init_task,
RCU_POINTER_INITIALIZER(real_cred, &init_cred),
RCU_POINTER_INITIALIZER(cred, &init_cred),
.comm = INIT_TASK_COMM,
.thread = INIT_THREAD,
.fs = &init_fs,
.files = &init_files,
#ifdef CONFIG_IO_URING
.io_uring = NULL,
#endif
.signal = &init_signals,
.sighand = &init_sighand,
.nsproxy = &init_nsproxy,
.pending = {
.list = LIST_HEAD_INIT(init_task.pending.list),
.signal = {{0}}
},
.blocked = {{0}},
.alloc_lock = __SPIN_LOCK_UNLOCKED(init_task.alloc_lock),
.journal_info = NULL,
INIT_CPU_TIMERS(init_task)
.pi_lock = __RAW_SPIN_LOCK_UNLOCKED(init_task.pi_lock),
.blocked_lock = __RAW_SPIN_LOCK_UNLOCKED(init_task.blocked_lock),
.timer_slack_ns = 50000, /* 50 usec default slack */
.thread_pid = &init_struct_pid,
.thread_node = LIST_HEAD_INIT(init_signals.thread_head),
#ifdef CONFIG_AUDIT
.loginuid = INVALID_UID,
.sessionid = AUDIT_SID_UNSET,
#endif
#ifdef CONFIG_PERF_EVENTS
.perf_event_mutex = __MUTEX_INITIALIZER(init_task.perf_event_mutex),
.perf_event_list = LIST_HEAD_INIT(init_task.perf_event_list),
#endif
#ifdef CONFIG_PREEMPT_RCU
.rcu_read_lock_nesting = 0,
.rcu_read_unlock_special.s = 0,
.rcu_node_entry = LIST_HEAD_INIT(init_task.rcu_node_entry),
.rcu_blocked_node = NULL,
#endif
#ifdef CONFIG_TASKS_RCU
.rcu_tasks_holdout = false,
.rcu_tasks_holdout_list = LIST_HEAD_INIT(init_task.rcu_tasks_holdout_list),
.rcu_tasks_idle_cpu = -1,
.rcu_tasks_exit_list = LIST_HEAD_INIT(init_task.rcu_tasks_exit_list),
#endif
#ifdef CONFIG_TASKS_TRACE_RCU
.trc_reader_nesting = 0,
#endif
#ifdef CONFIG_CPUSETS
.mems_allowed_seq = SEQCNT_SPINLOCK_ZERO(init_task.mems_allowed_seq,
&init_task.alloc_lock),
#endif
.blocked_donor = NULL,
#ifdef CONFIG_RT_MUTEXES
.pi_waiters = RB_ROOT_CACHED,
.pi_top_task = NULL,
#endif
INIT_PREV_CPUTIME(init_task)
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
.vtime.seqcount = SEQCNT_ZERO(init_task.vtime_seqcount),
.vtime.starttime = 0,
.vtime.state = VTIME_SYS,
#endif
#ifdef CONFIG_NUMA_BALANCING
.numa_preferred_nid = NUMA_NO_NODE,
.numa_group = NULL,
.numa_faults = NULL,
#endif
#ifdef CONFIG_SCHED_CACHE
.preferred_llc = -1,
.pref_llc_queued = 0,
#endif
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
.kasan_depth = 1,
#endif
#ifdef CONFIG_KCSAN
.kcsan_ctx = {
.scoped_accesses = {LIST_POISON1, NULL},
},
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
.softirqs_enabled = 1,
#endif
#ifdef CONFIG_LOCKDEP
.lockdep_depth = 0, /* no locks held yet */
.curr_chain_key = INITIAL_CHAIN_KEY,
.lockdep_recursion = 0,
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
.ret_stack = NULL,
.tracing_graph_pause = ATOMIC_INIT(0),
#endif
#if defined(CONFIG_TRACING) && defined(CONFIG_PREEMPTION)
.trace_recursion = 0,
#endif
#ifdef CONFIG_LIVEPATCH
.patch_state = KLP_TRANSITION_IDLE,
#endif
#ifdef CONFIG_SECURITY
.security = NULL,
#endif
#ifdef CONFIG_SECCOMP_FILTER
.seccomp = { .filter_count = ATOMIC_INIT(0) },
#endif
#ifdef CONFIG_SCHED_MM_CID
.mm_cid = { .cid = MM_CID_UNSET, },
#endif
};
EXPORT_SYMBOL(init_task);
/*
* Initial thread structure. Alignment of this is handled by a special
* linker map entry.
*/
#ifndef CONFIG_THREAD_INFO_IN_TASK
struct thread_info init_thread_info __init_thread_info = INIT_THREAD_INFO(init_task);
#endif
+792
View File
@@ -0,0 +1,792 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/async.h>
#include <linux/delay.h>
#include <linux/dirent.h>
#include <linux/export.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/hex.h>
#include <linux/init.h>
#include <linux/init_syscalls.h>
#include <linux/kstrtox.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/namei.h>
#include <linux/overflow.h>
#include <linux/security.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/umh.h>
#include <linux/utime.h>
#include <asm/byteorder.h>
#include "do_mounts.h"
#include "initramfs_internal.h"
static __initdata bool csum_present;
static __initdata u32 io_csum;
static ssize_t __init xwrite(struct file *file, const unsigned char *p,
size_t count, loff_t *pos)
{
ssize_t out = 0;
/* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */
while (count) {
ssize_t rv = kernel_write(file, p, count, pos);
if (rv < 0) {
if (rv == -EINTR || rv == -EAGAIN)
continue;
return out ? out : rv;
} else if (rv == 0)
break;
if (csum_present) {
ssize_t i;
for (i = 0; i < rv; i++)
io_csum += p[i];
}
p += rv;
out += rv;
count -= rv;
}
return out;
}
static __initdata char *message;
static void __init error(char *x)
{
if (!message)
message = x;
}
#define panic_show_mem(fmt, ...) \
({ show_mem(); panic(fmt, ##__VA_ARGS__); })
/* link hash */
#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
static __initdata struct hash {
int ino, minor, major;
umode_t mode;
struct hash *next;
char name[N_ALIGN(PATH_MAX)];
} *head[32];
static __initdata bool hardlink_seen;
static inline int hash(int major, int minor, int ino)
{
unsigned long tmp = ino + minor + (major << 3);
tmp += tmp >> 5;
return tmp & 31;
}
static char __init *find_link(int major, int minor, int ino,
umode_t mode, char *name)
{
struct hash **p, *q;
for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
if ((*p)->ino != ino)
continue;
if ((*p)->minor != minor)
continue;
if ((*p)->major != major)
continue;
if (((*p)->mode ^ mode) & S_IFMT)
continue;
return (*p)->name;
}
q = kmalloc_obj(struct hash);
if (!q)
panic_show_mem("can't allocate link hash entry");
q->major = major;
q->minor = minor;
q->ino = ino;
q->mode = mode;
strscpy(q->name, name);
q->next = NULL;
*p = q;
hardlink_seen = true;
return NULL;
}
static void __init free_hash(void)
{
struct hash **p, *q;
for (p = head; hardlink_seen && p < head + 32; p++) {
while (*p) {
q = *p;
*p = q->next;
kfree(q);
}
}
hardlink_seen = false;
}
#ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
static void __init do_utime(char *filename, time64_t mtime)
{
struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } };
init_utimes(filename, t);
}
static void __init do_utime_path(const struct path *path, time64_t mtime)
{
struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } };
vfs_utimes(path, t);
}
static __initdata LIST_HEAD(dir_list);
struct dir_entry {
struct list_head list;
time64_t mtime;
char name[];
};
static void __init dir_add(const char *name, size_t nlen, time64_t mtime)
{
struct dir_entry *de;
de = kmalloc_flex(*de, name, nlen);
if (!de)
panic_show_mem("can't allocate dir_entry buffer");
INIT_LIST_HEAD(&de->list);
strscpy(de->name, name, nlen);
de->mtime = mtime;
list_add(&de->list, &dir_list);
}
static void __init dir_utime(void)
{
struct dir_entry *de, *tmp;
list_for_each_entry_safe(de, tmp, &dir_list, list) {
list_del(&de->list);
do_utime(de->name, de->mtime);
kfree(de);
}
}
#else
static void __init do_utime(char *filename, time64_t mtime) {}
static void __init do_utime_path(const struct path *path, time64_t mtime) {}
static void __init dir_add(const char *name, size_t nlen, time64_t mtime) {}
static void __init dir_utime(void) {}
#endif
static __initdata time64_t mtime;
/* cpio header parsing */
static __initdata unsigned long ino, major, minor, nlink;
static __initdata umode_t mode;
static __initdata unsigned long body_len, name_len;
static __initdata uid_t uid;
static __initdata gid_t gid;
static __initdata unsigned rdev;
static __initdata u32 hdr_csum;
static int __init parse_header(char *s)
{
__be32 header[13];
int ret;
ret = hex2bin((u8 *)header, s + 6, sizeof(header));
if (ret) {
error("damaged header");
return ret;
}
ino = be32_to_cpu(header[0]);
mode = be32_to_cpu(header[1]);
uid = be32_to_cpu(header[2]);
gid = be32_to_cpu(header[3]);
nlink = be32_to_cpu(header[4]);
mtime = be32_to_cpu(header[5]); /* breaks in y2106 */
body_len = be32_to_cpu(header[6]);
major = be32_to_cpu(header[7]);
minor = be32_to_cpu(header[8]);
rdev = new_encode_dev(MKDEV(be32_to_cpu(header[9]), be32_to_cpu(header[10])));
name_len = be32_to_cpu(header[11]);
hdr_csum = be32_to_cpu(header[12]);
return 0;
}
/* Finite-state machine */
static __initdata enum state {
Start,
Collect,
GotHeader,
SkipIt,
GotName,
CopyFile,
GotSymlink,
Reset
} state, next_state;
static __initdata char *victim;
static unsigned long byte_count __initdata;
static __initdata loff_t this_header, next_header;
static inline void __init eat(unsigned n)
{
victim += n;
this_header += n;
byte_count -= n;
}
static __initdata char *collected;
static long remains __initdata;
static __initdata char *collect;
static void __init read_into(char *buf, unsigned size, enum state next)
{
if (byte_count >= size) {
collected = victim;
eat(size);
state = next;
} else {
collect = collected = buf;
remains = size;
next_state = next;
state = Collect;
}
}
static __initdata char *header_buf, *symlink_buf, *name_buf;
static int __init do_start(void)
{
read_into(header_buf, CPIO_HDRLEN, GotHeader);
return 0;
}
static int __init do_collect(void)
{
unsigned long n = remains;
if (byte_count < n)
n = byte_count;
memcpy(collect, victim, n);
eat(n);
collect += n;
if ((remains -= n) != 0)
return 1;
state = next_state;
return 0;
}
static int __init do_header(void)
{
if (!memcmp(collected, "070701", 6)) {
csum_present = false;
} else if (!memcmp(collected, "070702", 6)) {
csum_present = true;
} else {
if (memcmp(collected, "070707", 6) == 0)
error("incorrect cpio method used: use -H newc option");
else
error("no cpio magic");
return 1;
}
if (parse_header(collected))
return 1;
next_header = this_header + N_ALIGN(name_len) + body_len;
next_header = (next_header + 3) & ~3;
state = SkipIt;
if (name_len <= 0 || name_len > PATH_MAX)
return 0;
if (S_ISLNK(mode)) {
if (body_len > PATH_MAX)
return 0;
collect = collected = symlink_buf;
remains = N_ALIGN(name_len) + body_len;
next_state = GotSymlink;
state = Collect;
return 0;
}
if (S_ISREG(mode) || !body_len)
read_into(name_buf, N_ALIGN(name_len), GotName);
return 0;
}
static int __init do_skip(void)
{
if (this_header + byte_count < next_header) {
eat(byte_count);
return 1;
} else {
eat(next_header - this_header);
state = next_state;
return 0;
}
}
static int __init do_reset(void)
{
while (byte_count && *victim == '\0')
eat(1);
if (byte_count && (this_header & 3))
error("broken padding");
return 1;
}
static void __init clean_path(char *path, umode_t fmode)
{
struct kstat st;
if (!init_stat(path, &st, AT_SYMLINK_NOFOLLOW) &&
(st.mode ^ fmode) & S_IFMT) {
if (S_ISDIR(st.mode))
init_rmdir(path);
else
init_unlink(path);
}
}
static int __init maybe_link(void)
{
if (nlink >= 2) {
char *old = find_link(major, minor, ino, mode, collected);
if (old) {
clean_path(collected, 0);
return (init_link(old, collected) < 0) ? -1 : 1;
}
}
return 0;
}
static __initdata struct file *wfile;
static __initdata loff_t wfile_pos;
static int __init do_name(void)
{
state = SkipIt;
next_state = Reset;
/* name_len > 0 && name_len <= PATH_MAX checked in do_header */
if (collected[name_len - 1] != '\0') {
pr_err("initramfs name without nulterm: %.*s\n",
(int)name_len, collected);
error("malformed archive");
return 1;
}
if (strcmp(collected, "TRAILER!!!") == 0) {
free_hash();
return 0;
}
clean_path(collected, mode);
if (S_ISREG(mode)) {
int ml = maybe_link();
if (ml >= 0) {
int openflags = O_WRONLY|O_CREAT|O_LARGEFILE;
if (ml != 1)
openflags |= O_TRUNC;
wfile = filp_open(collected, openflags, mode);
if (IS_ERR(wfile))
return 0;
wfile_pos = 0;
io_csum = 0;
vfs_fchown(wfile, uid, gid);
vfs_fchmod(wfile, mode);
if (body_len)
vfs_truncate(&wfile->f_path, body_len);
state = CopyFile;
}
} else if (S_ISDIR(mode)) {
init_mkdir(collected, mode);
init_chown(collected, uid, gid, 0);
init_chmod(collected, mode);
dir_add(collected, name_len, mtime);
} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
S_ISFIFO(mode) || S_ISSOCK(mode)) {
if (maybe_link() == 0) {
init_mknod(collected, mode, rdev);
init_chown(collected, uid, gid, 0);
init_chmod(collected, mode);
do_utime(collected, mtime);
}
}
return 0;
}
static int __init do_copy(void)
{
if (byte_count >= body_len) {
if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len)
error("write error");
do_utime_path(&wfile->f_path, mtime);
fput(wfile);
if (csum_present && io_csum != hdr_csum)
error("bad data checksum");
eat(body_len);
state = SkipIt;
return 0;
} else {
if (xwrite(wfile, victim, byte_count, &wfile_pos) != byte_count)
error("write error");
body_len -= byte_count;
eat(byte_count);
return 1;
}
}
static int __init do_symlink(void)
{
if (collected[name_len - 1] != '\0') {
pr_err("initramfs symlink without nulterm: %.*s\n",
(int)name_len, collected);
error("malformed archive");
return 1;
}
collected[N_ALIGN(name_len) + body_len] = '\0';
clean_path(collected, 0);
init_symlink(collected + N_ALIGN(name_len), collected);
init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW);
do_utime(collected, mtime);
state = SkipIt;
next_state = Reset;
return 0;
}
static __initdata int (*actions[])(void) = {
[Start] = do_start,
[Collect] = do_collect,
[GotHeader] = do_header,
[SkipIt] = do_skip,
[GotName] = do_name,
[CopyFile] = do_copy,
[GotSymlink] = do_symlink,
[Reset] = do_reset,
};
static long __init write_buffer(char *buf, unsigned long len)
{
byte_count = len;
victim = buf;
while (!actions[state]())
;
return len - byte_count;
}
static long __init flush_buffer(void *bufv, unsigned long len)
{
char *buf = bufv;
long written;
long origLen = len;
if (message)
return -1;
while ((written = write_buffer(buf, len)) < len && !message) {
char c = buf[written];
if (c == '0') {
buf += written;
len -= written;
state = Start;
} else if (c == 0) {
buf += written;
len -= written;
state = Reset;
} else
error("junk within compressed archive");
}
return origLen;
}
static unsigned long my_inptr __initdata; /* index of next byte to be processed in inbuf */
#include <linux/decompress/generic.h>
/**
* unpack_to_rootfs - decompress and extract an initramfs archive
* @buf: input initramfs archive to extract
* @len: length of initramfs data to process
*
* Returns: NULL for success or an error message string
*
* This symbol shouldn't be used externally. It's available for unit tests.
*/
char * __init unpack_to_rootfs(char *buf, unsigned long len)
{
long written;
decompress_fn decompress;
const char *compress_name;
struct {
char header[CPIO_HDRLEN];
char symlink[PATH_MAX + N_ALIGN(PATH_MAX) + 1];
char name[N_ALIGN(PATH_MAX)];
} *bufs = kmalloc_obj(*bufs);
if (!bufs)
panic_show_mem("can't allocate buffers");
header_buf = bufs->header;
symlink_buf = bufs->symlink;
name_buf = bufs->name;
state = Start;
this_header = 0;
message = NULL;
while (!message && len) {
loff_t saved_offset = this_header;
if (*buf == '0' && !(this_header & 3)) {
state = Start;
written = write_buffer(buf, len);
buf += written;
len -= written;
continue;
}
if (!*buf) {
buf++;
len--;
this_header++;
continue;
}
this_header = 0;
decompress = decompress_method(buf, len, &compress_name);
pr_debug("Detected %s compressed data\n", compress_name);
if (decompress) {
int res = decompress(buf, len, NULL, flush_buffer, NULL,
&my_inptr, error);
if (res)
error("decompressor failed");
} else if (compress_name) {
pr_err("compression method %s not configured\n",
compress_name);
error("decompressor failed");
} else
error("invalid magic at start of compressed archive");
if (state != Reset)
error("junk at the end of compressed archive");
this_header = saved_offset + my_inptr;
buf += my_inptr;
len -= my_inptr;
}
dir_utime();
/* free any hardlink state collected without optional TRAILER!!! */
free_hash();
kfree(bufs);
return message;
}
static int __initdata do_retain_initrd;
static int __init retain_initrd_param(char *str)
{
if (*str)
return 0;
do_retain_initrd = 1;
return 1;
}
__setup("retain_initrd", retain_initrd_param);
#ifdef CONFIG_ARCH_HAS_KEEPINITRD
static int __init keepinitrd_setup(char *__unused)
{
do_retain_initrd = 1;
return 1;
}
__setup("keepinitrd", keepinitrd_setup);
#endif
static bool __initdata initramfs_async = true;
static int __init initramfs_async_setup(char *str)
{
return kstrtobool(str, &initramfs_async) == 0;
}
__setup("initramfs_async=", initramfs_async_setup);
extern char __initramfs_start[];
extern unsigned long __initramfs_size;
#include <linux/initrd.h>
#include <linux/kexec.h>
static BIN_ATTR(initrd, 0440, sysfs_bin_attr_simple_read, NULL, 0);
void __init reserve_initrd_mem(void)
{
phys_addr_t start;
unsigned long size;
/* Ignore the virtul address computed during device tree parsing */
initrd_start = initrd_end = 0;
if (!phys_initrd_size)
return;
/*
* Round the memory region to page boundaries as per free_initrd_mem()
* This allows us to detect whether the pages overlapping the initrd
* are in use, but more importantly, reserves the entire set of pages
* as we don't want these pages allocated for other purposes.
*/
start = round_down(phys_initrd_start, PAGE_SIZE);
size = phys_initrd_size + (phys_initrd_start - start);
size = round_up(size, PAGE_SIZE);
if (!memblock_is_region_memory(start, size)) {
pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region",
(u64)start, size);
goto disable;
}
if (memblock_is_region_reserved(start, size)) {
pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region\n",
(u64)start, size);
goto disable;
}
memblock_reserve(start, size);
/* Now convert initrd to virtual addresses */
initrd_start = (unsigned long)__va(phys_initrd_start);
initrd_end = initrd_start + phys_initrd_size;
initrd_below_start_ok = 1;
return;
disable:
pr_cont(" - disabling initrd\n");
initrd_start = 0;
initrd_end = 0;
}
void __weak __init free_initrd_mem(unsigned long start, unsigned long end)
{
free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM,
"initrd");
}
#ifdef CONFIG_CRASH_RESERVE
static bool __init kexec_free_initrd(void)
{
unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
unsigned long crashk_end = (unsigned long)__va(crashk_res.end);
/*
* If the initrd region is overlapped with crashkernel reserved region,
* free only memory that is not part of crashkernel region.
*/
if (initrd_start >= crashk_end || initrd_end <= crashk_start)
return false;
/*
* Initialize initrd memory region since the kexec boot does not do.
*/
memset((void *)initrd_start, 0, initrd_end - initrd_start);
if (initrd_start < crashk_start)
free_initrd_mem(initrd_start, crashk_start);
if (initrd_end > crashk_end)
free_initrd_mem(crashk_end, initrd_end);
return true;
}
#else
static inline bool kexec_free_initrd(void)
{
return false;
}
#endif /* CONFIG_KEXEC_CORE */
#ifdef CONFIG_BLK_DEV_RAM
static void __init populate_initrd_image(char *err)
{
ssize_t written;
struct file *file;
loff_t pos = 0;
printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd\n",
err);
file = filp_open("/initrd.image", O_WRONLY|O_CREAT|O_LARGEFILE, 0700);
if (IS_ERR(file))
return;
written = xwrite(file, (char *)initrd_start, initrd_end - initrd_start,
&pos);
if (written != initrd_end - initrd_start)
pr_err("/initrd.image: incomplete write (%zd != %ld)\n",
written, initrd_end - initrd_start);
fput(file);
}
#endif /* CONFIG_BLK_DEV_RAM */
static void __init do_populate_rootfs(void *unused, async_cookie_t cookie)
{
/* Load the built in initramfs */
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
if (err)
panic_show_mem("%s", err); /* Failed to decompress INTERNAL initramfs */
if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE))
goto done;
if (IS_ENABLED(CONFIG_BLK_DEV_RAM))
printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
else
printk(KERN_INFO "Unpacking initramfs...\n");
err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);
if (err) {
#ifdef CONFIG_BLK_DEV_RAM
populate_initrd_image(err);
#else
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
#endif
}
done:
security_initramfs_populated();
/*
* If the initrd region is overlapped with crashkernel reserved region,
* free only memory that is not part of crashkernel region.
*/
if (!do_retain_initrd && initrd_start && !kexec_free_initrd()) {
free_initrd_mem(initrd_start, initrd_end);
} else if (do_retain_initrd && initrd_start) {
bin_attr_initrd.size = initrd_end - initrd_start;
bin_attr_initrd.private = (void *)initrd_start;
if (sysfs_create_bin_file(firmware_kobj, &bin_attr_initrd))
pr_err("Failed to create initrd sysfs file");
}
initrd_start = 0;
initrd_end = 0;
init_flush_fput();
}
static ASYNC_DOMAIN_EXCLUSIVE(initramfs_domain);
static async_cookie_t initramfs_cookie;
void wait_for_initramfs(void)
{
if (!initramfs_cookie) {
/*
* Something before rootfs_initcall wants to access
* the filesystem/initramfs. Probably a bug. Make a
* note, avoid deadlocking the machine, and let the
* caller's access fail as it used to.
*/
pr_warn_once("wait_for_initramfs() called before rootfs_initcalls\n");
return;
}
async_synchronize_cookie_domain(initramfs_cookie + 1, &initramfs_domain);
}
EXPORT_SYMBOL_GPL(wait_for_initramfs);
static int __init populate_rootfs(void)
{
initramfs_cookie = async_schedule_domain(do_populate_rootfs, NULL,
&initramfs_domain);
usermodehelper_enable();
if (!initramfs_async)
wait_for_initramfs();
return 0;
}
rootfs_initcall(populate_rootfs);
+8
View File
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __INITRAMFS_INTERNAL_H__
#define __INITRAMFS_INTERNAL_H__
char *unpack_to_rootfs(char *buf, unsigned long len);
#define CPIO_HDRLEN 110
#endif
+585
View File
@@ -0,0 +1,585 @@
// SPDX-License-Identifier: GPL-2.0
#include <kunit/test.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/init_syscalls.h>
#include <linux/initrd.h>
#include <linux/stringify.h>
#include <linux/timekeeping.h>
#include "initramfs_internal.h"
struct initramfs_test_cpio {
char *magic;
unsigned int ino;
unsigned int mode;
unsigned int uid;
unsigned int gid;
unsigned int nlink;
unsigned int mtime;
unsigned int filesize;
unsigned int devmajor;
unsigned int devminor;
unsigned int rdevmajor;
unsigned int rdevminor;
unsigned int namesize;
unsigned int csum;
char *fname;
char *data;
};
/* regular newc header format */
#define CPIO_HDR_FMT "%s%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%s"
/*
* Bogus newc header with "0x" prefixes on the uid, gid, and namesize values.
* parse_header()/simple_str[n]toul() accepted this, contrary to the initramfs
* specification. hex2bin() now fails.
*/
#define CPIO_HDR_OX_INJECT \
"%s%08x%08x0x%06x0X%06x%08x%08x%08x%08x%08x%08x%08x0x%06x%08x%s"
static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz,
bool inject_ox, char *out)
{
int i;
size_t off = 0;
for (i = 0; i < csz; i++) {
char *pos = &out[off];
struct initramfs_test_cpio *c = &cs[i];
size_t thislen;
/* +1 to account for nulterm */
thislen = sprintf(pos,
inject_ox ? CPIO_HDR_OX_INJECT : CPIO_HDR_FMT,
c->magic, c->ino, c->mode, c->uid, c->gid, c->nlink,
c->mtime, c->filesize, c->devmajor, c->devminor,
c->rdevmajor, c->rdevminor, c->namesize, c->csum,
c->fname) + 1;
pr_debug("packing (%zu): %.*s\n", thislen, (int)thislen, pos);
if (thislen != CPIO_HDRLEN + c->namesize)
pr_debug("padded to: %u\n", CPIO_HDRLEN + c->namesize);
off += CPIO_HDRLEN + c->namesize;
while (off & 3)
out[off++] = '\0';
memcpy(&out[off], c->data, c->filesize);
off += c->filesize;
while (off & 3)
out[off++] = '\0';
}
return off;
}
static void __init initramfs_test_extract(struct kunit *test)
{
char *err, *cpio_srcbuf;
size_t len;
struct timespec64 ts_before, ts_after;
struct kstat st = {};
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.uid = 12,
.gid = 34,
.nlink = 1,
.mtime = 56,
.filesize = 0,
.devmajor = 0,
.devminor = 1,
.rdevmajor = 0,
.rdevminor = 0,
.namesize = sizeof("initramfs_test_extract"),
.csum = 0,
.fname = "initramfs_test_extract",
}, {
.magic = "070701",
.ino = 2,
.mode = S_IFDIR | 0777,
.nlink = 1,
.mtime = 57,
.devminor = 1,
.namesize = sizeof("initramfs_test_extract_dir"),
.fname = "initramfs_test_extract_dir",
}, {
.magic = "070701",
.namesize = sizeof("TRAILER!!!"),
.fname = "TRAILER!!!",
} };
/* +3 to cater for any 4-byte end-alignment */
cpio_srcbuf = kzalloc(ARRAY_SIZE(c) * (CPIO_HDRLEN + PATH_MAX + 3),
GFP_KERNEL);
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
ktime_get_real_ts64(&ts_before);
err = unpack_to_rootfs(cpio_srcbuf, len);
ktime_get_real_ts64(&ts_after);
if (err) {
KUNIT_FAIL(test, "unpack failed %s", err);
goto out;
}
KUNIT_EXPECT_EQ(test, init_stat(c[0].fname, &st, 0), 0);
KUNIT_EXPECT_TRUE(test, S_ISREG(st.mode));
KUNIT_EXPECT_TRUE(test, uid_eq(st.uid, KUIDT_INIT(c[0].uid)));
KUNIT_EXPECT_TRUE(test, gid_eq(st.gid, KGIDT_INIT(c[0].gid)));
KUNIT_EXPECT_EQ(test, st.nlink, 1);
if (IS_ENABLED(CONFIG_INITRAMFS_PRESERVE_MTIME)) {
KUNIT_EXPECT_EQ(test, st.mtime.tv_sec, c[0].mtime);
} else {
KUNIT_EXPECT_GE(test, st.mtime.tv_sec, ts_before.tv_sec);
KUNIT_EXPECT_LE(test, st.mtime.tv_sec, ts_after.tv_sec);
}
KUNIT_EXPECT_EQ(test, st.blocks, c[0].filesize);
KUNIT_EXPECT_EQ(test, init_stat(c[1].fname, &st, 0), 0);
KUNIT_EXPECT_TRUE(test, S_ISDIR(st.mode));
if (IS_ENABLED(CONFIG_INITRAMFS_PRESERVE_MTIME)) {
KUNIT_EXPECT_EQ(test, st.mtime.tv_sec, c[1].mtime);
} else {
KUNIT_EXPECT_GE(test, st.mtime.tv_sec, ts_before.tv_sec);
KUNIT_EXPECT_LE(test, st.mtime.tv_sec, ts_after.tv_sec);
}
KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
KUNIT_EXPECT_EQ(test, init_rmdir(c[1].fname), 0);
out:
kfree(cpio_srcbuf);
}
/*
* Don't terminate filename. Previously, the cpio filename field was passed
* directly to filp_open(collected, O_CREAT|..) without nulterm checks. See
* https://lore.kernel.org/linux-fsdevel/20241030035509.20194-2-ddiss@suse.de
*/
static void __init initramfs_test_fname_overrun(struct kunit *test)
{
char *err, *cpio_srcbuf;
size_t len, suffix_off;
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.uid = 0,
.gid = 0,
.nlink = 1,
.mtime = 1,
.filesize = 0,
.devmajor = 0,
.devminor = 1,
.rdevmajor = 0,
.rdevminor = 0,
.namesize = sizeof("initramfs_test_fname_overrun"),
.csum = 0,
.fname = "initramfs_test_fname_overrun",
} };
/*
* poison cpio source buffer, so we can detect overrun. source
* buffer is used by read_into() when hdr or fname
* are already available (e.g. no compression).
*/
cpio_srcbuf = kmalloc(CPIO_HDRLEN + PATH_MAX + 3, GFP_KERNEL);
memset(cpio_srcbuf, 'B', CPIO_HDRLEN + PATH_MAX + 3);
/* limit overrun to avoid crashes / filp_open() ENAMETOOLONG */
cpio_srcbuf[CPIO_HDRLEN + strlen(c[0].fname) + 20] = '\0';
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
/* overwrite trailing fname terminator and padding */
suffix_off = len - 1;
while (cpio_srcbuf[suffix_off] == '\0') {
cpio_srcbuf[suffix_off] = 'P';
suffix_off--;
}
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NOT_NULL(test, err);
kfree(cpio_srcbuf);
}
static void __init initramfs_test_data(struct kunit *test)
{
char *err, *cpio_srcbuf;
size_t len;
struct file *file;
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.uid = 0,
.gid = 0,
.nlink = 1,
.mtime = 1,
.filesize = sizeof("ASDF") - 1,
.devmajor = 0,
.devminor = 1,
.rdevmajor = 0,
.rdevminor = 0,
.namesize = sizeof("initramfs_test_data"),
.csum = 0,
.fname = "initramfs_test_data",
.data = "ASDF",
} };
/* +6 for max name and data 4-byte padding */
cpio_srcbuf = kmalloc(CPIO_HDRLEN + c[0].namesize + c[0].filesize + 6,
GFP_KERNEL);
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
file = filp_open(c[0].fname, O_RDONLY, 0);
if (IS_ERR(file)) {
KUNIT_FAIL(test, "open failed");
goto out;
}
/* read back file contents into @cpio_srcbuf and confirm match */
len = kernel_read(file, cpio_srcbuf, c[0].filesize, NULL);
KUNIT_EXPECT_EQ(test, len, c[0].filesize);
KUNIT_EXPECT_MEMEQ(test, cpio_srcbuf, c[0].data, len);
fput(file);
KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
out:
kfree(cpio_srcbuf);
}
static void __init initramfs_test_csum(struct kunit *test)
{
char *err, *cpio_srcbuf;
size_t len;
struct initramfs_test_cpio c[] = { {
/* 070702 magic indicates a valid csum is present */
.magic = "070702",
.ino = 1,
.mode = S_IFREG | 0777,
.nlink = 1,
.filesize = sizeof("ASDF") - 1,
.devminor = 1,
.namesize = sizeof("initramfs_test_csum"),
.csum = 'A' + 'S' + 'D' + 'F',
.fname = "initramfs_test_csum",
.data = "ASDF",
}, {
/* mix csum entry above with no-csum entry below */
.magic = "070701",
.ino = 2,
.mode = S_IFREG | 0777,
.nlink = 1,
.filesize = sizeof("ASDF") - 1,
.devminor = 1,
.namesize = sizeof("initramfs_test_csum_not_here"),
/* csum ignored */
.csum = 5555,
.fname = "initramfs_test_csum_not_here",
.data = "ASDF",
} };
cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), 0);
/* mess up the csum and confirm that unpack fails */
c[0].csum--;
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NOT_NULL(test, err);
/*
* file (with content) is still retained in case of bad-csum abort.
* Perhaps we should change this.
*/
KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), -ENOENT);
kfree(cpio_srcbuf);
}
/*
* hardlink hashtable may leak when the archive omits a trailer:
* https://lore.kernel.org/r/20241107002044.16477-10-ddiss@suse.de/
*/
static void __init initramfs_test_hardlink(struct kunit *test)
{
char *err, *cpio_srcbuf;
size_t len;
struct kstat st0 = {}, st1 = {};
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.nlink = 2,
.devminor = 1,
.namesize = sizeof("initramfs_test_hardlink"),
.fname = "initramfs_test_hardlink",
}, {
/* hardlink data is present in last archive entry */
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.nlink = 2,
.filesize = sizeof("ASDF") - 1,
.devminor = 1,
.namesize = sizeof("initramfs_test_hardlink_link"),
.fname = "initramfs_test_hardlink_link",
.data = "ASDF",
} };
cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
KUNIT_EXPECT_EQ(test, init_stat(c[0].fname, &st0, 0), 0);
KUNIT_EXPECT_EQ(test, init_stat(c[1].fname, &st1, 0), 0);
KUNIT_EXPECT_EQ(test, st0.ino, st1.ino);
KUNIT_EXPECT_EQ(test, st0.nlink, 2);
KUNIT_EXPECT_EQ(test, st1.nlink, 2);
KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), 0);
kfree(cpio_srcbuf);
}
#define INITRAMFS_TEST_MANY_LIMIT 1000
#define INITRAMFS_TEST_MANY_PATH_MAX (sizeof("initramfs_test_many-") \
+ sizeof(__stringify(INITRAMFS_TEST_MANY_LIMIT)))
static void __init initramfs_test_many(struct kunit *test)
{
char *err, *cpio_srcbuf, *p;
size_t len = INITRAMFS_TEST_MANY_LIMIT *
(CPIO_HDRLEN + INITRAMFS_TEST_MANY_PATH_MAX + 3);
char thispath[INITRAMFS_TEST_MANY_PATH_MAX];
int i;
p = cpio_srcbuf = kmalloc(len, GFP_KERNEL);
for (i = 0; i < INITRAMFS_TEST_MANY_LIMIT; i++) {
struct initramfs_test_cpio c = {
.magic = "070701",
.ino = i,
.mode = S_IFREG | 0777,
.nlink = 1,
.devminor = 1,
.fname = thispath,
};
c.namesize = 1 + sprintf(thispath, "initramfs_test_many-%d", i);
p += fill_cpio(&c, 1, false, p);
}
len = p - cpio_srcbuf;
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
for (i = 0; i < INITRAMFS_TEST_MANY_LIMIT; i++) {
sprintf(thispath, "initramfs_test_many-%d", i);
KUNIT_EXPECT_EQ(test, init_unlink(thispath), 0);
}
kfree(cpio_srcbuf);
}
/*
* An initramfs filename is namesize in length, including the zero-terminator.
* A filename can be zero-terminated prior to namesize, with the remainder used
* as padding. This can be useful for e.g. alignment of file data segments with
* a 4KB filesystem block, allowing for extent sharing (reflinks) between cpio
* source and destination. This hack works with both GNU cpio and initramfs, as
* long as PATH_MAX isn't exceeded.
*/
static void __init initramfs_test_fname_pad(struct kunit *test)
{
char *err;
size_t len;
struct file *file;
char fdata[] = "this file data is aligned at 4K in the archive";
struct test_fname_pad {
char padded_fname[4096 - CPIO_HDRLEN];
char cpio_srcbuf[CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)];
} *tbufs = kzalloc_obj(struct test_fname_pad);
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.uid = 0,
.gid = 0,
.nlink = 1,
.mtime = 1,
.filesize = sizeof(fdata),
.devmajor = 0,
.devminor = 1,
.rdevmajor = 0,
.rdevminor = 0,
/* align file data at 4K archive offset via padded fname */
.namesize = 4096 - CPIO_HDRLEN,
.csum = 0,
.fname = tbufs->padded_fname,
.data = fdata,
} };
memcpy(tbufs->padded_fname, "padded_fname", sizeof("padded_fname"));
len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_srcbuf);
err = unpack_to_rootfs(tbufs->cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
file = filp_open(c[0].fname, O_RDONLY, 0);
if (IS_ERR(file)) {
KUNIT_FAIL(test, "open failed");
goto out;
}
/* read back file contents into @cpio_srcbuf and confirm match */
len = kernel_read(file, tbufs->cpio_srcbuf, c[0].filesize, NULL);
KUNIT_EXPECT_EQ(test, len, c[0].filesize);
KUNIT_EXPECT_MEMEQ(test, tbufs->cpio_srcbuf, c[0].data, len);
fput(file);
KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
out:
kfree(tbufs);
}
static void __init initramfs_test_fname_path_max(struct kunit *test)
{
char *err;
size_t len;
struct kstat st0 = {}, st1 = {};
char fdata[] = "this file data will not be unpacked";
struct test_fname_path_max {
char fname_oversize[PATH_MAX + 1];
char fname_ok[PATH_MAX];
char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2];
} *tbufs = kzalloc_obj(struct test_fname_path_max);
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFDIR | 0777,
.nlink = 1,
.namesize = sizeof(tbufs->fname_oversize),
.fname = tbufs->fname_oversize,
.filesize = sizeof(fdata),
.data = fdata,
}, {
.magic = "070701",
.ino = 2,
.mode = S_IFDIR | 0777,
.nlink = 1,
.namesize = sizeof(tbufs->fname_ok),
.fname = tbufs->fname_ok,
} };
memset(tbufs->fname_oversize, '/', sizeof(tbufs->fname_oversize) - 1);
memset(tbufs->fname_ok, '/', sizeof(tbufs->fname_ok) - 1);
memcpy(tbufs->fname_oversize, "fname_oversize",
sizeof("fname_oversize") - 1);
memcpy(tbufs->fname_ok, "fname_ok", sizeof("fname_ok") - 1);
len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_src);
/* unpack skips over fname_oversize instead of returning an error */
err = unpack_to_rootfs(tbufs->cpio_src, len);
KUNIT_EXPECT_NULL(test, err);
KUNIT_EXPECT_EQ(test, init_stat("fname_oversize", &st0, 0), -ENOENT);
KUNIT_EXPECT_EQ(test, init_stat("fname_ok", &st1, 0), 0);
KUNIT_EXPECT_EQ(test, init_rmdir("fname_ok"), 0);
kfree(tbufs);
}
static void __init initramfs_test_hdr_hex(struct kunit *test)
{
char *err;
size_t len;
char fdata[] = "this file data will not be unpacked";
struct initramfs_test_bufs {
char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2];
} *tbufs = kzalloc(sizeof(struct initramfs_test_bufs), GFP_KERNEL);
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
.mode = S_IFREG | 0777,
.uid = 0x123456,
.gid = 0x123457,
.nlink = 1,
.namesize = sizeof("initramfs_test_hdr_hex_0"),
.fname = "initramfs_test_hdr_hex_0",
.filesize = sizeof(fdata),
.data = fdata,
}, {
.magic = "070701",
.ino = 2,
.mode = S_IFDIR | 0777,
.uid = 0x000056,
.gid = 0x000057,
.nlink = 1,
.namesize = sizeof("initramfs_test_hdr_hex_1"),
.fname = "initramfs_test_hdr_hex_1",
} };
/* inject_ox=true to add "0x" cpio field prefixes */
len = fill_cpio(c, ARRAY_SIZE(c), true, tbufs->cpio_src);
err = unpack_to_rootfs(tbufs->cpio_src, len);
KUNIT_EXPECT_NOT_NULL(test, err);
kfree(tbufs);
}
/*
* The kunit_case/_suite struct cannot be marked as __initdata as this will be
* used in debugfs to retrieve results after test has run.
*/
static struct kunit_case __refdata initramfs_test_cases[] = {
KUNIT_CASE(initramfs_test_extract),
KUNIT_CASE(initramfs_test_fname_overrun),
KUNIT_CASE(initramfs_test_data),
KUNIT_CASE(initramfs_test_csum),
KUNIT_CASE(initramfs_test_hardlink),
KUNIT_CASE(initramfs_test_many),
KUNIT_CASE(initramfs_test_fname_pad),
KUNIT_CASE(initramfs_test_fname_path_max),
KUNIT_CASE(initramfs_test_hdr_hex),
{},
};
static int __init initramfs_test_init(struct kunit_suite *suite)
{
/*
* unpack_to_rootfs() uses module-static state (victim, byte_count,
* state, ...). The boot-time async do_populate_rootfs() may still be
* running, so wait for it to finish before we call unpack_to_rootfs()
* from the test thread, otherwise the two writers race and crash.
*/
wait_for_initramfs();
return 0;
}
static struct kunit_suite __refdata initramfs_test_suite = {
.name = "initramfs",
.suite_init = initramfs_test_init,
.test_cases = initramfs_test_cases,
};
kunit_test_init_section_suites(&initramfs_test_suite);
MODULE_DESCRIPTION("Initramfs KUnit test suite");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* init/noinitramfs.c
*
* Copyright (C) 2006, NXP Semiconductors, All Rights Reserved
* 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>
/*
* Create a simple rootfs that is similar to the default initramfs
*/
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;
return 0;
out:
printk(KERN_WARNING "Failed to create a rootfs\n");
return err;
}
rootfs_initcall(default_rootfs);
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <generated/compile.h>
#include <generated/utsrelease.h>
#include <linux/proc_ns.h>
#include <linux/refcount.h>
#include <linux/uts.h>
#include <linux/utsname.h>
struct uts_namespace init_uts_ns = {
.ns = NS_COMMON_INIT(init_uts_ns),
.name = {
.sysname = UTS_SYSNAME,
.nodename = UTS_NODENAME,
.release = UTS_RELEASE,
.version = UTS_VERSION,
.machine = UTS_MACHINE,
.domainname = UTS_DOMAINNAME,
},
.user_ns = &init_user_ns,
};
/* FIXED STRINGS! Don't touch! */
const char linux_banner[] =
"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
+54
View File
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/init/version.c
*
* Copyright (C) 1992 Theodore Ts'o
*
* May be freely distributed as part of Linux.
*/
#include <generated/compile.h>
#include <linux/build-salt.h>
#include <linux/elfnote-lto.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/uts.h>
#include <linux/utsname.h>
#include <linux/proc_ns.h>
static int __init early_hostname(char *arg)
{
size_t bufsize = sizeof(init_uts_ns.name.nodename);
size_t maxlen = bufsize - 1;
ssize_t arglen;
arglen = strscpy(init_uts_ns.name.nodename, arg, bufsize);
if (arglen < 0) {
pr_warn("hostname parameter exceeds %zd characters and will be truncated",
maxlen);
}
return 0;
}
early_param("hostname", early_hostname);
const char linux_proc_banner[] =
"%s version %s"
" (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ")"
" (" LINUX_COMPILER ") %s\n";
BUILD_SALT;
BUILD_LTO_INFO;
/*
* init_uts_ns and linux_banner contain the build version and timestamp,
* which are really fixed at the very last step of build process.
* They are compiled with __weak first, and without __weak later.
*/
struct uts_namespace init_uts_ns __weak;
const char linux_banner[] __weak;
#include "version-timestamp.c"
EXPORT_SYMBOL_GPL(init_uts_ns);