Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

2021
init/Kconfig Normal file

File diff suppressed because it is too large Load diff

38
init/Makefile Normal file
View file

@ -0,0 +1,38 @@
#
# Makefile for the linux kernel.
#
obj-y := main.o version.o mounts.o
obj-y += noinitramfs.o
obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
ifeq ($(CONFIG_TIMA_RKP), y)
obj-y += _vmm.o vmm.o
obj-y += ld.o
endif
ifneq ($(CONFIG_ARCH_INIT_TASK),y)
obj-y += init_task.o
endif
mounts-y := do_mounts.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o
mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o
mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o
# dependencies on generated files need to be listed explicitly
$(obj)/version.o: include/generated/compile.h
# compile.h changes depending on hostname, generation number, etc,
# so we regenerate it always.
# mkcompile_h will make sure to only update the
# actual file if its content has changed.
chk_compile.h = :
quiet_chk_compile.h = echo ' CHK $@'
silent_chk_compile.h = :
include/generated/compile.h: FORCE
@$($(quiet)chk_compile.h)
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \
"$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" "$(CC) $(KBUILD_CFLAGS)"

44
init/_vmm.S Executable file
View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/vmm.h>
#define vmm_ELF_PATH "init/vmm.elf"
#define SMC_64BIT_RET_MAGIC 0xC2000401
.global _vmm_goto_EL2
_vmm_goto_EL2:
smc #0
isb
ret
.global _vmm_disable
_vmm_disable:
ldr x0, =SMC_64BIT_RET_MAGIC
smc #0
isb
.section .vmm, "ax"
.global _svmm
_svmm:
.incbin vmm_ELF_PATH
.global _evmm
_evmm:
.section .text

315
init/calibrate.c Normal file
View file

@ -0,0 +1,315 @@
/* calibrate.c: default delay calibration
*
* Excised from init/main.c
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <linux/smp.h>
#include <linux/percpu.h>
unsigned long lpj_fine;
unsigned long preset_lpj;
static int __init lpj_setup(char *str)
{
preset_lpj = simple_strtoul(str,NULL,0);
return 1;
}
__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();
}

638
init/do_mounts.c Normal file
View file

@ -0,0 +1,638 @@
/*
* Many of the syscalls used in this file expect some of the arguments
* to be __user pointers not __kernel pointers. To limit the sparse
* noise, turn off sparse checking for this file.
*/
#ifdef __CHECKER__
#undef __CHECKER__
#warning "Sparse checking disabled for this file"
#endif
#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/genhd.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/nfs_fs.h>
#include <linux/nfs_fs_sb.h>
#include <linux/nfs_mount.h>
#include "do_mounts.h"
int __initdata rd_doload; /* 1 = load RAM disk, 0 = don't load */
int root_mountflags = MS_RDONLY | MS_SILENT;
static char * __initdata root_device_name;
static char __initdata saved_root_name[64];
static int root_wait;
dev_t ROOT_DEV;
static int __init load_ramdisk(char *str)
{
rd_doload = simple_strtol(str,NULL,0) & 3;
return 1;
}
__setup("load_ramdisk=", load_ramdisk);
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);
#ifdef CONFIG_BLOCK
struct uuidcmp {
const char *uuid;
int len;
};
/**
* match_dev_by_uuid - callback for finding a partition using its uuid
* @dev: device passed in by the caller
* @data: opaque pointer to the desired struct uuidcmp to match
*
* Returns 1 if the device matches, and 0 otherwise.
*/
static int match_dev_by_uuid(struct device *dev, const void *data)
{
const struct uuidcmp *cmp = data;
struct hd_struct *part = dev_to_part(dev);
if (!part->info)
goto no_match;
if (strncasecmp(cmp->uuid, part->info->uuid, cmp->len))
goto no_match;
return 1;
no_match:
return 0;
}
/**
* devt_from_partuuid - looks up the dev_t of a partition by its UUID
* @uuid_str: char array containing ascii UUID
*
* The function will return the first partition which contains a matching
* UUID value in its partition_meta_info struct. This does not search
* by filesystem UUIDs.
*
* If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be
* extracted and used as an offset from the partition identified by the UUID.
*
* Returns the matching dev_t on success or 0 on failure.
*/
static dev_t devt_from_partuuid(const char *uuid_str)
{
dev_t res = 0;
struct uuidcmp cmp;
struct device *dev = NULL;
struct gendisk *disk;
struct hd_struct *part;
int offset = 0;
bool clear_root_wait = false;
char *slash;
cmp.uuid = uuid_str;
slash = strchr(uuid_str, '/');
/* Check for optional partition number offset attributes. */
if (slash) {
char c = 0;
/* Explicitly fail on poor PARTUUID syntax. */
if (sscanf(slash + 1,
"PARTNROFF=%d%c", &offset, &c) != 1) {
clear_root_wait = true;
goto done;
}
cmp.len = slash - uuid_str;
} else {
cmp.len = strlen(uuid_str);
}
if (!cmp.len) {
clear_root_wait = true;
goto done;
}
dev = class_find_device(&block_class, NULL, &cmp,
&match_dev_by_uuid);
if (!dev)
goto done;
res = dev->devt;
/* Attempt to find the partition by offset. */
if (!offset)
goto no_offset;
res = 0;
disk = part_to_disk(dev_to_part(dev));
part = disk_get_part(disk, dev_to_part(dev)->partno + offset);
if (part) {
res = part_devt(part);
put_device(part_to_dev(part));
}
no_offset:
put_device(dev);
done:
if (clear_root_wait) {
pr_err("VFS: PARTUUID= is invalid.\n"
"Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n");
if (root_wait)
pr_err("Disabling rootwait; root= is invalid.\n");
root_wait = 0;
}
return res;
}
#endif
/*
* Convert a name into device number. We accept the following variants:
*
* 1) <hex_major><hex_minor> device number in hexadecimal represents itself
* no leading 0x, for example b302.
* 2) /dev/nfs represents Root_NFS (0xff)
* 3) /dev/<disk_name> represents the device number of disk
* 4) /dev/<disk_name><decimal> represents the device number
* of partition - device number of disk plus the partition number
* 5) /dev/<disk_name>p<decimal> - same as the above, that form is
* used when disk name of partitioned disk ends on a digit.
* 6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
* unique id of a partition if the partition table provides it.
* The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
* partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
* filled hex representation of the 32-bit "NT disk signature", and PP
* is a zero-filled hex representation of the 1-based partition number.
* 7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
* a partition with a known unique id.
* 8) <major>:<minor> major and minor number of the device separated by
* a colon.
*
* If name doesn't have fall into the categories above, we return (0,0).
* block_class is used to check if something is a disk name. If the disk
* name contains slashes, the device name has them replaced with
* bangs.
*/
dev_t name_to_dev_t(char *name)
{
char s[32];
char *p;
dev_t res = 0;
int part;
#ifdef CONFIG_BLOCK
if (strncmp(name, "PARTUUID=", 9) == 0) {
name += 9;
res = devt_from_partuuid(name);
if (!res)
goto fail;
goto done;
}
#endif
if (strncmp(name, "/dev/", 5) != 0) {
unsigned maj, min;
if (sscanf(name, "%u:%u", &maj, &min) == 2) {
res = MKDEV(maj, min);
if (maj != MAJOR(res) || min != MINOR(res))
goto fail;
} else {
res = new_decode_dev(simple_strtoul(name, &p, 16));
if (*p)
goto fail;
}
goto done;
}
name += 5;
res = Root_NFS;
if (strcmp(name, "nfs") == 0)
goto done;
res = Root_RAM0;
if (strcmp(name, "ram") == 0)
goto done;
if (strlen(name) > 31)
goto fail;
strcpy(s, name);
for (p = s; *p; p++)
if (*p == '/')
*p = '!';
res = blk_lookup_devt(s, 0);
if (res)
goto done;
/*
* try non-existent, but valid partition, which may only exist
* after revalidating the disk, like partitioned md devices
*/
while (p > s && isdigit(p[-1]))
p--;
if (p == s || !*p || *p == '0')
goto fail;
/* try disk name without <part number> */
part = simple_strtoul(p, NULL, 10);
*p = '\0';
res = blk_lookup_devt(s, part);
if (res)
goto done;
/* try disk name without p<part number> */
if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p')
goto fail;
p[-1] = '\0';
res = blk_lookup_devt(s, part);
if (res)
goto done;
fail:
return 0;
done:
return res;
}
static int __init root_dev_setup(char *line)
{
strlcpy(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 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)
{
root_delay = simple_strtoul(str, NULL, 0);
return 1;
}
__setup("rootflags=", root_data_setup);
__setup("rootfstype=", fs_names_setup);
__setup("rootdelay=", root_delay_setup);
static void __init get_fs_names(char *page)
{
char *s = page;
if (root_fs_names) {
strcpy(page, root_fs_names);
while (*s++) {
if (s[-1] == ',')
s[-1] = '\0';
}
} else {
int len = get_filesystem_list(page);
char *p, *next;
page[len] = '\0';
for (p = page-1; p; p = next) {
next = strchr(++p, '\n');
if (*p++ != '\t')
continue;
while ((*s++ = *p++) != '\n')
;
s[-1] = '\0';
}
}
*s = '\0';
}
static int __init do_mount_root(char *name, char *fs, int flags, void *data)
{
struct super_block *s;
int err = sys_mount(name, "/root", fs, flags, data);
if (err)
return err;
sys_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,
s->s_flags & MS_RDONLY ? " readonly" : "",
MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
return 0;
}
void __init mount_block_root(char *name, int flags)
{
struct page *page = alloc_page(GFP_KERNEL |
__GFP_NOTRACK_FALSE_POSITIVE);
char *fs_names = page_address(page);
char *p;
#ifdef CONFIG_BLOCK
char b[BDEVNAME_SIZE];
#else
const char *b = name;
#endif
get_fs_names(fs_names);
retry:
for (p = fs_names; *p; p += strlen(p)+1) {
int err = do_mount_root(name, p, flags, root_mount_data);
switch (err) {
case 0:
goto out;
case -EACCES:
flags |= MS_RDONLY;
goto retry;
case -EINVAL:
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
*/
#ifdef CONFIG_BLOCK
__bdevname(ROOT_DEV, b);
#endif
printk("VFS: Cannot open root device \"%s\" or %s: error %d\n",
root_device_name, b, err);
printk("Please append a correct \"root=\" boot option; here are the available partitions:\n");
printk_all_partitions();
#ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
printk("DEBUG_BLOCK_EXT_DEVT is enabled, you need to specify "
"explicit textual name for \"root=\" boot option.\n");
#endif
panic("VFS: Unable to mount root fs on %s", b);
}
printk("List of all partitions:\n");
printk_all_partitions();
printk("No filesystem could mount root, tried: ");
for (p = fs_names; *p; p += strlen(p)+1)
printk(" %s", p);
printk("\n");
#ifdef CONFIG_BLOCK
__bdevname(ROOT_DEV, b);
#endif
panic("VFS: Unable to mount root fs on %s", b);
out:
put_page(page);
}
#ifdef CONFIG_ROOT_NFS
#define NFSROOT_TIMEOUT_MIN 5
#define NFSROOT_TIMEOUT_MAX 30
#define NFSROOT_RETRY_MAX 5
static int __init mount_nfs_root(void)
{
char *root_dev, *root_data;
unsigned int timeout;
int try, err;
err = nfs_root_data(&root_dev, &root_data);
if (err != 0)
return 0;
/*
* 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++) {
err = do_mount_root(root_dev, "nfs",
root_mountflags, root_data);
if (err == 0)
return 1;
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;
}
return 0;
}
#endif
#if defined(CONFIG_BLK_DEV_RAM) || defined(CONFIG_BLK_DEV_FD)
void __init change_floppy(char *fmt, ...)
{
struct termios termios;
char buf[80];
char c;
int fd;
va_list args;
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
fd = sys_open("/dev/root", O_RDWR | O_NDELAY, 0);
if (fd >= 0) {
sys_ioctl(fd, FDEJECT, 0);
sys_close(fd);
}
printk(KERN_NOTICE "VFS: Insert %s and press ENTER\n", buf);
fd = sys_open("/dev/console", O_RDWR, 0);
if (fd >= 0) {
sys_ioctl(fd, TCGETS, (long)&termios);
termios.c_lflag &= ~ICANON;
sys_ioctl(fd, TCSETSF, (long)&termios);
sys_read(fd, &c, 1);
termios.c_lflag |= ICANON;
sys_ioctl(fd, TCSETSF, (long)&termios);
sys_close(fd);
}
}
#endif
void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS
if (ROOT_DEV == Root_NFS) {
if (mount_nfs_root())
return;
printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
ROOT_DEV = Root_FD0;
}
#endif
#ifdef CONFIG_BLK_DEV_FD
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
/* rd_doload is 2 for a dual initrd/ramload setup */
if (rd_doload==2) {
if (rd_load_disk(1)) {
ROOT_DEV = Root_RAM1;
root_device_name = NULL;
}
} else
change_floppy("root floppy");
}
#endif
#ifdef CONFIG_BLOCK
create_dev("/dev/root", ROOT_DEV);
mount_block_root("/dev/root", root_mountflags);
#endif
}
/*
* Prepare the namespace - decide what/where to mount, load ramdisks, etc.
*/
void __init prepare_namespace(void)
{
int is_floppy;
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_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
if (initrd_load())
goto out;
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root();
out:
devtmpfs_mount("dev");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
}
static bool is_tmpfs;
static struct dentry *rootfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
static unsigned long once;
void *fill = ramfs_fill_super;
if (test_and_set_bit(0, &once))
return ERR_PTR(-ENODEV);
if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs)
fill = shmem_fill_super;
return mount_nodev(fs_type, flags, data, fill);
}
static struct file_system_type rootfs_fs_type = {
.name = "rootfs",
.mount = rootfs_mount,
.kill_sb = kill_litter_super,
};
int __init init_rootfs(void)
{
int err = register_filesystem(&rootfs_fs_type);
if (err)
return err;
if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] &&
(!root_fs_names || strstr(root_fs_names, "tmpfs"))) {
err = shmem_init();
is_tmpfs = true;
} else {
err = init_ramfs_fs();
}
if (err)
unregister_filesystem(&rootfs_fs_type);
return err;
}

76
init/do_mounts.h Normal file
View file

@ -0,0 +1,76 @@
#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>
void change_floppy(char *fmt, ...);
void mount_block_root(char *name, int flags);
void mount_root(void);
extern int root_mountflags;
static inline int create_dev(char *name, dev_t dev)
{
sys_unlink(name);
return sys_mknod(name, S_IFBLK|0600, new_encode_dev(dev));
}
#if BITS_PER_LONG == 32
static inline u32 bstat(char *name)
{
struct stat64 stat;
if (sys_stat64(name, &stat) != 0)
return 0;
if (!S_ISBLK(stat.st_mode))
return 0;
if (stat.st_rdev != (u32)stat.st_rdev)
return 0;
return stat.st_rdev;
}
#else
static inline u32 bstat(char *name)
{
struct stat stat;
if (sys_newstat(name, &stat) != 0)
return 0;
if (!S_ISBLK(stat.st_mode))
return 0;
return stat.st_rdev;
}
#endif
#ifdef CONFIG_BLK_DEV_RAM
int __init rd_load_disk(int n);
int __init rd_load_image(char *from);
#else
static inline int rd_load_disk(int n) { return 0; }
static inline int rd_load_image(char *from) { return 0; }
#endif
#ifdef CONFIG_BLK_DEV_INITRD
int __init initrd_load(void);
#else
static inline int initrd_load(void) { return 0; }
#endif
#ifdef CONFIG_BLK_DEV_MD
void md_run_setup(void);
#else
static inline void md_run_setup(void) {}
#endif

137
init/do_mounts_initrd.c Normal file
View file

@ -0,0 +1,137 @@
/*
* Many of the syscalls used in this file expect some of the arguments
* to be __user pointers not __kernel pointers. To limit the sparse
* noise, turn off sparse checking for this file.
*/
#ifdef __CHECKER__
#undef __CHECKER__
#warning "Sparse checking disabled for this file"
#endif
#include <linux/unistd.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/minix_fs.h>
#include <linux/romfs_fs.h>
#include <linux/initrd.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include <linux/kmod.h>
#include "do_mounts.h"
unsigned long initrd_start, initrd_end;
int initrd_below_start_ok;
unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
static int __initdata mount_initrd = 1;
static int __init no_initrd(char *str)
{
mount_initrd = 0;
return 1;
}
__setup("noinitrd", no_initrd);
static int init_linuxrc(struct subprocess_info *info, struct cred *new)
{
sys_unshare(CLONE_FS | CLONE_FILES);
/* stdin/stdout/stderr for /linuxrc */
sys_open("/dev/console", O_RDWR, 0);
sys_dup(0);
sys_dup(0);
/* move initrd over / and chdir/chroot in initrd root */
sys_chdir("/root");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
sys_setsid();
return 0;
}
static void __init handle_initrd(void)
{
struct subprocess_info *info;
static char *argv[] = { "linuxrc", NULL, };
extern char *envp_init[];
int error;
real_root_dev = new_encode_dev(ROOT_DEV);
create_dev("/dev/root.old", Root_RAM0);
/* mount initrd on rootfs' /root */
mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
sys_mkdir("/old", 0700);
sys_chdir("/old");
/* try loading default modules from initrd */
load_default_modules();
/*
* In case that a resume from disk is carried out by linuxrc or one of
* its children, we need to tell the freezer not to wait for us.
*/
current->flags |= PF_FREEZER_SKIP;
info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
GFP_KERNEL, init_linuxrc, NULL, NULL);
if (!info)
return;
call_usermodehelper_exec(info, UMH_WAIT_PROC);
current->flags &= ~PF_FREEZER_SKIP;
/* move initrd to rootfs' /old */
sys_mount("..", ".", NULL, MS_MOVE, NULL);
/* switch root and cwd back to / of rootfs */
sys_chroot("..");
if (new_decode_dev(real_root_dev) == Root_RAM0) {
sys_chdir("/old");
return;
}
sys_chdir("/");
ROOT_DEV = new_decode_dev(real_root_dev);
mount_root();
printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
if (!error)
printk("okay\n");
else {
int fd = sys_open("/dev/root.old", O_RDWR, 0);
if (error == -ENOENT)
printk("/initrd does not exist. Ignored.\n");
else
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
sys_umount("/old", MNT_DETACH);
printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
if (fd < 0) {
error = fd;
} else {
error = sys_ioctl(fd, BLKFLSBUF, 0);
sys_close(fd);
}
printk(!error ? "okay\n" : "failed\n");
}
}
int __init initrd_load(void)
{
if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0);
/*
* Load the initrd data into /dev/ram0. Execute it as initrd
* unless /dev/ram0 is supposed to be our actual root device,
* in that case the ram disk is just set up here, and gets
* mounted in the normal path.
*/
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
sys_unlink("/initrd.image");
handle_initrd();
return 1;
}
}
sys_unlink("/initrd.image");
return 0;
}

312
init/do_mounts_md.c Normal file
View file

@ -0,0 +1,312 @@
/*
* Many of the syscalls used in this file expect some of the arguments
* to be __user pointers not __kernel pointers. To limit the sparse
* noise, turn off sparse checking for this file.
*/
#ifdef __CHECKER__
#undef __CHECKER__
#warning "Sparse checking disabled for this file"
#endif
#include <linux/delay.h>
#include <linux/raid/md_u.h>
#include <linux/raid/md_p.h>
#include "do_mounts.h"
/*
* When md (and any require personalities) are compiled into the kernel
* (not a module), arrays can be assembles are boot time using with AUTODETECT
* where specially marked partitions are registered with md_autodetect_dev(),
* and with MD_BOOT where devices to be collected are given on the boot line
* with md=.....
* The code for that is here.
*/
#ifdef CONFIG_MD_AUTODETECT
static int __initdata raid_noautodetect;
#else
static int __initdata raid_noautodetect=1;
#endif
static int __initdata raid_autopart;
static struct {
int minor;
int partitioned;
int level;
int chunk;
char *device_names;
} md_setup_args[256] __initdata;
static int md_setup_ents __initdata;
/*
* Parse the command-line parameters given our kernel, but do not
* actually try to invoke the MD device now; that is handled by
* md_setup_drive after the low-level disk drivers have initialised.
*
* 27/11/1999: Fixed to work correctly with the 2.3 kernel (which
* assigns the task of parsing integer arguments to the
* invoked program now). Added ability to initialise all
* the MD devices (by specifying multiple "md=" lines)
* instead of just one. -- KTK
* 18May2000: Added support for persistent-superblock arrays:
* md=n,0,factor,fault,device-list uses RAID0 for device n
* md=n,-1,factor,fault,device-list uses LINEAR for device n
* md=n,device-list reads a RAID superblock from the devices
* elements in device-list are read by name_to_kdev_t so can be
* a hex number or something like /dev/hda1 /dev/sdb
* 2001-06-03: Dave Cinege <dcinege@psychosis.com>
* Shifted name_to_kdev_t() and related operations to md_set_drive()
* for later execution. Rewrote section to make devfs compatible.
*/
static int __init md_setup(char *str)
{
int minor, level, factor, fault, partitioned = 0;
char *pername = "";
char *str1;
int ent;
if (*str == 'd') {
partitioned = 1;
str++;
}
if (get_option(&str, &minor) != 2) { /* MD Number */
printk(KERN_WARNING "md: Too few arguments supplied to md=.\n");
return 0;
}
str1 = str;
for (ent=0 ; ent< md_setup_ents ; ent++)
if (md_setup_args[ent].minor == minor &&
md_setup_args[ent].partitioned == partitioned) {
printk(KERN_WARNING "md: md=%s%d, Specified more than once. "
"Replacing previous definition.\n", partitioned?"d":"", minor);
break;
}
if (ent >= ARRAY_SIZE(md_setup_args)) {
printk(KERN_WARNING "md: md=%s%d - too many md initialisations\n", partitioned?"d":"", minor);
return 0;
}
if (ent >= md_setup_ents)
md_setup_ents++;
switch (get_option(&str, &level)) { /* RAID level */
case 2: /* could be 0 or -1.. */
if (level == 0 || level == LEVEL_LINEAR) {
if (get_option(&str, &factor) != 2 || /* Chunk Size */
get_option(&str, &fault) != 2) {
printk(KERN_WARNING "md: Too few arguments supplied to md=.\n");
return 0;
}
md_setup_args[ent].level = level;
md_setup_args[ent].chunk = 1 << (factor+12);
if (level == LEVEL_LINEAR)
pername = "linear";
else
pername = "raid0";
break;
}
/* FALL THROUGH */
case 1: /* the first device is numeric */
str = str1;
/* FALL THROUGH */
case 0:
md_setup_args[ent].level = LEVEL_NONE;
pername="super-block";
}
printk(KERN_INFO "md: Will configure md%d (%s) from %s, below.\n",
minor, pername, str);
md_setup_args[ent].device_names = str;
md_setup_args[ent].partitioned = partitioned;
md_setup_args[ent].minor = minor;
return 1;
}
static void __init md_setup_drive(void)
{
int minor, i, ent, partitioned;
dev_t dev;
dev_t devices[MD_SB_DISKS+1];
for (ent = 0; ent < md_setup_ents ; ent++) {
int fd;
int err = 0;
char *devname;
mdu_disk_info_t dinfo;
char name[16];
minor = md_setup_args[ent].minor;
partitioned = md_setup_args[ent].partitioned;
devname = md_setup_args[ent].device_names;
sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor);
if (partitioned)
dev = MKDEV(mdp_major, minor << MdpMinorShift);
else
dev = MKDEV(MD_MAJOR, minor);
create_dev(name, dev);
for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) {
char *p;
char comp_name[64];
u32 rdev;
p = strchr(devname, ',');
if (p)
*p++ = 0;
dev = name_to_dev_t(devname);
if (strncmp(devname, "/dev/", 5) == 0)
devname += 5;
snprintf(comp_name, 63, "/dev/%s", devname);
rdev = bstat(comp_name);
if (rdev)
dev = new_decode_dev(rdev);
if (!dev) {
printk(KERN_WARNING "md: Unknown device name: %s\n", devname);
break;
}
devices[i] = dev;
devname = p;
}
devices[i] = 0;
if (!i)
continue;
printk(KERN_INFO "md: Loading md%s%d: %s\n",
partitioned ? "_d" : "", minor,
md_setup_args[ent].device_names);
fd = sys_open(name, 0, 0);
if (fd < 0) {
printk(KERN_ERR "md: open failed - cannot start "
"array %s\n", name);
continue;
}
if (sys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) {
printk(KERN_WARNING
"md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n",
minor);
sys_close(fd);
continue;
}
if (md_setup_args[ent].level != LEVEL_NONE) {
/* non-persistent */
mdu_array_info_t ainfo;
ainfo.level = md_setup_args[ent].level;
ainfo.size = 0;
ainfo.nr_disks =0;
ainfo.raid_disks =0;
while (devices[ainfo.raid_disks])
ainfo.raid_disks++;
ainfo.md_minor =minor;
ainfo.not_persistent = 1;
ainfo.state = (1 << MD_SB_CLEAN);
ainfo.layout = 0;
ainfo.chunk_size = md_setup_args[ent].chunk;
err = sys_ioctl(fd, SET_ARRAY_INFO, (long)&ainfo);
for (i = 0; !err && i <= MD_SB_DISKS; i++) {
dev = devices[i];
if (!dev)
break;
dinfo.number = i;
dinfo.raid_disk = i;
dinfo.state = (1<<MD_DISK_ACTIVE)|(1<<MD_DISK_SYNC);
dinfo.major = MAJOR(dev);
dinfo.minor = MINOR(dev);
err = sys_ioctl(fd, ADD_NEW_DISK, (long)&dinfo);
}
} else {
/* persistent */
for (i = 0; i <= MD_SB_DISKS; i++) {
dev = devices[i];
if (!dev)
break;
dinfo.major = MAJOR(dev);
dinfo.minor = MINOR(dev);
sys_ioctl(fd, ADD_NEW_DISK, (long)&dinfo);
}
}
if (!err)
err = sys_ioctl(fd, RUN_ARRAY, 0);
if (err)
printk(KERN_WARNING "md: starting md%d failed\n", minor);
else {
/* reread the partition table.
* I (neilb) and not sure why this is needed, but I cannot
* boot a kernel with devfs compiled in from partitioned md
* array without it
*/
sys_close(fd);
fd = sys_open(name, 0, 0);
sys_ioctl(fd, BLKRRPART, 0);
}
sys_close(fd);
}
}
static int __init raid_setup(char *str)
{
int len, pos;
len = strlen(str) + 1;
pos = 0;
while (pos < len) {
char *comma = strchr(str+pos, ',');
int wlen;
if (comma)
wlen = (comma-str)-pos;
else wlen = (len-1)-pos;
if (!strncmp(str, "noautodetect", wlen))
raid_noautodetect = 1;
if (!strncmp(str, "autodetect", wlen))
raid_noautodetect = 0;
if (strncmp(str, "partitionable", wlen)==0)
raid_autopart = 1;
if (strncmp(str, "part", wlen)==0)
raid_autopart = 1;
pos += wlen+1;
}
return 1;
}
__setup("raid=", raid_setup);
__setup("md=", md_setup);
static void __init autodetect_raid(void)
{
int fd;
/*
* Since we don't want to detect and use half a raid array, we need to
* wait for the known devices to complete their probing
*/
printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n");
printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n");
wait_for_device_probe();
fd = sys_open("/dev/md0", 0, 0);
if (fd >= 0) {
sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
sys_close(fd);
}
}
void __init md_run_setup(void)
{
create_dev("/dev/md0", MKDEV(MD_MAJOR, 0));
if (raid_noautodetect)
printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=autodetect will force)\n");
else
autodetect_raid();
md_setup_drive();
}

361
init/do_mounts_rd.c Normal file
View file

@ -0,0 +1,361 @@
/*
* Many of the syscalls used in this file expect some of the arguments
* to be __user pointers not __kernel pointers. To limit the sparse
* noise, turn off sparse checking for this file.
*/
#ifdef __CHECKER__
#undef __CHECKER__
#warning "Sparse checking disabled for this file"
#endif
#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/slab.h>
#include "do_mounts.h"
#include "../fs/squashfs/squashfs_fs.h"
#include <linux/decompress/generic.h>
int __initdata rd_prompt = 1;/* 1 = prompt for RAM disk, 0 = don't prompt */
static int __init prompt_ramdisk(char *str)
{
rd_prompt = simple_strtol(str,NULL,0) & 1;
return 1;
}
__setup("prompt_ramdisk=", prompt_ramdisk);
int __initdata rd_image_start; /* starting block # of image */
static int __init ramdisk_start_setup(char *str)
{
rd_image_start = simple_strtol(str,NULL,0);
return 1;
}
__setup("ramdisk_start=", ramdisk_start_setup);
static int __init crd_load(int in_fd, int out_fd, 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(int fd, int start_block, 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;
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
*/
sys_lseek(fd, start_block * BLOCK_SIZE, 0);
sys_read(fd, buf, size);
*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
*/
sys_lseek(fd, start_block * BLOCK_SIZE + 0x200, 0);
sys_read(fd, buf, size);
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
*/
sys_lseek(fd, (start_block+1) * BLOCK_SIZE, 0);
sys_read(fd, buf, size);
/* 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:
sys_lseek(fd, start_block * BLOCK_SIZE, 0);
kfree(buf);
return nblocks;
}
int __init rd_load_image(char *from)
{
int res = 0;
int in_fd, out_fd;
unsigned long rd_blocks, devblocks;
int nblocks, i, disk;
char *buf = NULL;
unsigned short rotate = 0;
decompress_fn decompressor = NULL;
#if !defined(CONFIG_S390)
char rotator[4] = { '|' , '/' , '-' , '\\' };
#endif
out_fd = sys_open("/dev/ram", O_RDWR, 0);
if (out_fd < 0)
goto out;
in_fd = sys_open(from, O_RDONLY, 0);
if (in_fd < 0)
goto noclose_input;
nblocks = identify_ramdisk_image(in_fd, rd_image_start, &decompressor);
if (nblocks < 0)
goto done;
if (nblocks == 0) {
if (crd_load(in_fd, out_fd, 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.
* So any ramdisk block size that is a multiple of 1KiB should
* work when the appropriate ramdisk_blocksize is specified
* on the command line.
*
* The default ramdisk_blocksize is 1KiB and it is generally
* silly to use anything else, so make sure to use 1KiB
* blocksize while generating ext2fs ramdisk-images.
*/
if (sys_ioctl(out_fd, BLKGETSIZE, (unsigned long)&rd_blocks) < 0)
rd_blocks = 0;
else
rd_blocks >>= 1;
if (nblocks > rd_blocks) {
printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n",
nblocks, rd_blocks);
goto done;
}
/*
* OK, time to copy in the data
*/
if (sys_ioctl(in_fd, BLKGETSIZE, (unsigned long)&devblocks) < 0)
devblocks = 0;
else
devblocks >>= 1;
if (strcmp(from, "/initrd.image") == 0)
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;
}
printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",
nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");
for (i = 0, disk = 1; i < nblocks; i++) {
if (i && (i % devblocks == 0)) {
printk("done disk #%d.\n", disk++);
rotate = 0;
if (sys_close(in_fd)) {
printk("Error closing the disk.\n");
goto noclose_input;
}
change_floppy("disk #%d", disk);
in_fd = sys_open(from, O_RDONLY, 0);
if (in_fd < 0) {
printk("Error opening disk.\n");
goto noclose_input;
}
printk("Loading disk #%d... ", disk);
}
sys_read(in_fd, buf, BLOCK_SIZE);
sys_write(out_fd, buf, BLOCK_SIZE);
#if !defined(CONFIG_S390)
if (!(i % 16)) {
printk("%c\b", rotator[rotate & 0x3]);
rotate++;
}
#endif
}
printk("done.\n");
successful_load:
res = 1;
done:
sys_close(in_fd);
noclose_input:
sys_close(out_fd);
out:
kfree(buf);
sys_unlink("/dev/ram");
return res;
}
int __init rd_load_disk(int n)
{
if (rd_prompt)
change_floppy("root floppy disk to be loaded into RAM disk");
create_dev("/dev/root", ROOT_DEV);
create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n));
return rd_load_image("/dev/root");
}
static int exit_code;
static int decompress_error;
static int crd_infd, crd_outfd;
static long __init compr_fill(void *buf, unsigned long len)
{
long r = sys_read(crd_infd, buf, len);
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 = sys_write(crd_outfd, window, outcnt);
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(int in_fd, int out_fd, decompress_fn deco)
{
int result;
crd_infd = in_fd;
crd_outfd = out_fd;
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;
}

253
init/elf.h Executable file
View file

@ -0,0 +1,253 @@
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __ELF_H__
#define __ELF_H__
#define __TARGET_64__
#define _SHN_UNDEF 0
#ifdef __TARGET_64__
#define _DT_NULL 0
#define _DT_NEEDED 1
#define _DT_PLTRELSZ 2
#define _DT_PLTGOT 3
#define _DT_STRTAB 5
#define _DT_SYMTAB 6
#define _DT_RELA 7
#define _DT_RELASZ 8
#define _DT_RELAENT 9
#define _DT_STRSZ 10
#define _DT_SYMENT 11
#define _DT_INIT 12
#define _DT_FINI 13
#define _DT_SONAME 14
#define _DT_REL 17
#define _DT_PLTREL 20
#define _DT_JMPREL 23
#define _R_AARCH64_NONE 0
#define _R_AARCH64_ABS64 257
#define _R_AARCH64_RELATIVE 1027
#define _R_AARCH64_IRELATIVE 1032
#define _R_AARCH64_GLOB_DAT 1025
#define _R_AARCH64_JUMP_SLOT 1026
#define _R_AARCH64_RELATIVE 1027
#else //__TARGET_32__
#define _DT_INIT 12
#define _DT_FINI 13
#define _DT_SONAME 1
#define _DT_STRTAB 5
#define _DT_SYMTAB 6
#define _DT_RELA 7
#define _DT_RELASZ 8
#define _DT_RELAENT 9
#define _DT_STRSZ 10
#define _DT_SYMENT 11
#define _DT_REL 17
#define _DT_RELSZ 18
#define _DT_RELENT 19
#define _DT_JMPREL 23
#define _DT_PLTRELSZ 2
#define _DT_PLTREL 20
#define _R_ARM_NONE 0
#define _R_ARM_ABS32 2
#define _R_ARM_GLOB_DAT 21
#define _R_ARM_JUMP_SLOT 22
#define _R_ARM_RELATIVE 23
#endif
#define _PT_LOAD 1
typedef unsigned short _Elf_Half;
typedef unsigned int _Elf_Word;
typedef int _Elf_Sword;
typedef unsigned long long _Elf_Xword;
typedef long long _Elf_Sxword;
typedef unsigned long long _Elf_Addr;
typedef unsigned long long _Elf_Off;
typedef unsigned short _Elf_Section;
#define _EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[_EI_NIDENT];
_Elf_Half e_type;
_Elf_Half e_machine;
_Elf_Word e_version;
_Elf_Addr e_entry;
_Elf_Off e_phoff;
_Elf_Off e_shoff;
_Elf_Word e_flags;
_Elf_Half e_ehsize;
_Elf_Half e_phentsize;
_Elf_Half e_phnum;
_Elf_Half e_shentsize;
_Elf_Half e_shnum;
_Elf_Half e_shstrndx;
} _Elf_Ehdr;
#ifdef __TARGET_64__
typedef struct
{
_Elf_Word sh_name;
_Elf_Word sh_type;
_Elf_Xword sh_flags;
_Elf_Addr sh_addr;
_Elf_Off sh_offset;
_Elf_Xword sh_size;
_Elf_Word sh_link;
_Elf_Word sh_info;
_Elf_Xword sh_addralign;
_Elf_Xword sh_entsize;
} _Elf_Shdr;
#else //__TARGET_32__
typedef struct
{
_Elf_Word sh_name;
_Elf_Word sh_type;
_Elf_Word sh_flags;
_Elf_Addr sh_addr;
_Elf_Off sh_offset;
_Elf_Word sh_size;
_Elf_Word sh_link;
_Elf_Word sh_info;
_Elf_Word sh_addralign;
_Elf_Word sh_entsize;
} _Elf_Shdr;
#endif //__TARGET_64__ | __TARGET_32__
#ifdef __TARGET_64__
typedef struct
{
_Elf_Word st_name;
unsigned char st_info;
unsigned char st_other;
_Elf_Section st_shndx;
_Elf_Addr st_value;
_Elf_Xword st_size;
} _Elf_Sym;
#else //__TARGET_32__
typedef struct
{
_Elf_Word st_name;
_Elf_Addr st_value;
_Elf_Word st_size;
unsigned char st_info;
unsigned char st_other;
_Elf_Section st_shndx;
} _Elf_Sym;
#endif //__TARGET_64__ | __TARGET_32__
#define _ELF_ST_BIND(val) (((unsigned char) (val)) >> 4)
#define _ELF_ST_TYPE(val) ((val) & 0xf)
#define _ELF_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
#ifdef __TARGET_64__
typedef struct
{
_Elf_Addr r_offset; /* Address */
_Elf_Xword r_info; /* Relocation type and symbol index */
_Elf_Sxword r_addend; /* Addend */
} _Elf_Rela;
#define _ELF_R_SYM(i) ((i) >> 32)
#define _ELF_R_TYPE(i) ((i) & 0xffffffff)
#else //__TARGET_32__
typedef struct
{
_Elf_Addr r_offset; /* Address */
_Elf_Word r_info; /* Relocation type and symbol index */
} _Elf_Rel;
#define _ELF_R_SYM(val) ((val) >> 8)
#define _ELF_R_TYPE(val) ((val) & 0xff)
#endif //__TARGET_64__ | __TARGET_32__
#ifdef __TARGET_64__
typedef struct
{
_Elf_Word p_type; /* Segment type */
_Elf_Word p_flags; /* Segment flags */
_Elf_Off p_offset; /* Segment file offset */
_Elf_Addr p_vaddr; /* Segment virtual address */
_Elf_Addr p_paddr; /* Segment physical address */
_Elf_Xword p_filesz; /* Segment size in file */
_Elf_Xword p_memsz; /* Segment size in memory */
_Elf_Xword p_align; /* Segment alignment */
} _Elf_Phdr;
#else //__TARGET_32__
typedef struct
{
_Elf_Word p_type; /* Segment type */
_Elf_Off p_offset; /* Segment file offset */
_Elf_Addr p_vaddr; /* Segment virtual address */
_Elf_Addr p_paddr; /* Segment physical address */
_Elf_Word p_filesz; /* Segment size in file */
_Elf_Word p_memsz; /* Segment size in memory */
_Elf_Word p_flags; /* Segment flags */
_Elf_Word p_align; /* Segment alignment */
} _Elf_Phdr;
#endif //__TARGET_64__ | __TARGET_32__
#ifdef __TARGET_64__
typedef struct
{
_Elf_Sxword d_tag; /* Dynamic entry type */
union
{
_Elf_Xword d_val; /* Integer value */
_Elf_Addr d_ptr; /* Address value */
} d_un;
} _Elf_Dyn;
#else //__TARGET_32__
typedef struct
{
_Elf_Sword d_tag; /* Dynamic entry type */
union
{
_Elf_Word d_val; /* Integer value */
_Elf_Addr d_ptr; /* Address value */
} d_un;
} _Elf_Dyn;
#endif //__TARGET_64__ | __TARGET_32__
#endif //__ELF_H__

26
init/init_task.c Normal file
View file

@ -0,0 +1,26 @@
#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/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
/* Initial task structure */
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);
/*
* Initial thread structure. Alignment of this is handled by a special
* linker map entry.
*/
union thread_union init_thread_union __init_task_data =
{ INIT_THREAD_INFO(init_task) };

675
init/initramfs.c Normal file
View file

@ -0,0 +1,675 @@
/*
* Many of the syscalls used in this file expect some of the arguments
* to be __user pointers not __kernel pointers. To limit the sparse
* noise, turn off sparse checking for this file.
*/
#ifdef __CHECKER__
#undef __CHECKER__
#warning "Sparse checking disabled for this file"
#endif
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/dirent.h>
#include <linux/syscalls.h>
#include <linux/utime.h>
#include <linux/initramfs.h>
static ssize_t __init xwrite(int fd, const char *p, size_t count)
{
ssize_t out = 0;
/* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */
while (count) {
ssize_t rv = sys_write(fd, p, count);
if (rv < 0) {
if (rv == -EINTR || rv == -EAGAIN)
continue;
return out ? out : rv;
} else if (rv == 0)
break;
p += rv;
out += rv;
count -= rv;
}
return out;
}
static __initdata char *message;
static void __init error(char *x)
{
if (!message)
message = x;
}
/* 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 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(sizeof(struct hash), GFP_KERNEL);
if (!q)
panic("can't allocate link hash entry");
q->major = major;
q->minor = minor;
q->ino = ino;
q->mode = mode;
strcpy(q->name, name);
q->next = NULL;
*p = q;
return NULL;
}
static void __init free_hash(void)
{
struct hash **p, *q;
for (p = head; p < head + 32; p++) {
while (*p) {
q = *p;
*p = q->next;
kfree(q);
}
}
}
static long __init do_utime(char *filename, time_t mtime)
{
struct timespec t[2];
t[0].tv_sec = mtime;
t[0].tv_nsec = 0;
t[1].tv_sec = mtime;
t[1].tv_nsec = 0;
return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW);
}
static __initdata LIST_HEAD(dir_list);
struct dir_entry {
struct list_head list;
char *name;
time_t mtime;
};
static void __init dir_add(const char *name, time_t mtime)
{
struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
if (!de)
panic("can't allocate dir_entry buffer");
INIT_LIST_HEAD(&de->list);
de->name = kstrdup(name, GFP_KERNEL);
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->name);
kfree(de);
}
}
static __initdata time_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 void __init parse_header(char *s)
{
unsigned long parsed[12];
char buf[9];
int i;
buf[8] = '\0';
for (i = 0, s += 6; i < 12; i++, s += 8) {
memcpy(buf, s, 8);
parsed[i] = simple_strtoul(buf, NULL, 16);
}
ino = parsed[0];
mode = parsed[1];
uid = parsed[2];
gid = parsed[3];
nlink = parsed[4];
mtime = parsed[5];
body_len = parsed[6];
major = parsed[7];
minor = parsed[8];
rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
name_len = parsed[11];
}
/* FSM */
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 *vcollected;
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, 110, 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, "070707", 6)==0) {
error("incorrect cpio method used: use -H newc option");
return 1;
}
if (memcmp(collected, "070701", 6)) {
error("no cpio magic");
return 1;
}
parse_header(collected);
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 int __init maybe_link(void)
{
if (nlink >= 2) {
char *old = find_link(major, minor, ino, mode, collected);
if (old)
return (sys_link(old, collected) < 0) ? -1 : 1;
}
return 0;
}
static void __init clean_path(char *path, umode_t fmode)
{
struct stat st;
if (!sys_newlstat(path, &st) && (st.st_mode ^ fmode) & S_IFMT) {
if (S_ISDIR(st.st_mode))
sys_rmdir(path);
else
sys_unlink(path);
}
}
static __initdata int wfd;
static int __init do_name(void)
{
state = SkipIt;
next_state = Reset;
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;
if (ml != 1)
openflags |= O_TRUNC;
wfd = sys_open(collected, openflags, mode);
if (wfd >= 0) {
sys_fchown(wfd, uid, gid);
sys_fchmod(wfd, mode);
if (body_len)
sys_ftruncate(wfd, body_len);
vcollected = kstrdup(collected, GFP_KERNEL);
state = CopyFile;
}
}
} else if (S_ISDIR(mode)) {
sys_mkdir(collected, mode);
sys_chown(collected, uid, gid);
sys_chmod(collected, mode);
dir_add(collected, mtime);
} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
S_ISFIFO(mode) || S_ISSOCK(mode)) {
if (maybe_link() == 0) {
sys_mknod(collected, mode, rdev);
sys_chown(collected, uid, gid);
sys_chmod(collected, mode);
do_utime(collected, mtime);
}
}
return 0;
}
static int __init do_copy(void)
{
if (byte_count >= body_len) {
if (xwrite(wfd, victim, body_len) != body_len)
error("write error");
sys_close(wfd);
do_utime(vcollected, mtime);
kfree(vcollected);
eat(body_len);
state = SkipIt;
return 0;
} else {
if (xwrite(wfd, victim, byte_count) != byte_count)
error("write error");
body_len -= byte_count;
eat(byte_count);
return 1;
}
}
static int __init do_symlink(void)
{
collected[N_ALIGN(name_len) + body_len] = '\0';
clean_path(collected, 0);
sys_symlink(collected + N_ALIGN(name_len), collected);
sys_lchown(collected, uid, gid);
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 = (char *) 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 in compressed archive");
}
return origLen;
}
static unsigned long my_inptr; /* index of next byte to be processed in inbuf */
#include <linux/decompress/generic.h>
static char * __init unpack_to_rootfs(char *buf, unsigned long len)
{
long written;
decompress_fn decompress;
const char *compress_name;
static __initdata char msg_buf[64];
header_buf = kmalloc(110, GFP_KERNEL);
symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
if (!header_buf || !symlink_buf || !name_buf)
panic("can't allocate buffers");
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) {
if (!message) {
snprintf(msg_buf, sizeof msg_buf,
"compression method %s not configured",
compress_name);
message = msg_buf;
}
} else
error("junk in compressed archive");
if (state != Reset)
error("junk in compressed archive");
this_header = saved_offset + my_inptr;
buf += my_inptr;
len -= my_inptr;
}
dir_utime();
kfree(name_buf);
kfree(symlink_buf);
kfree(header_buf);
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);
extern char __initramfs_start[];
extern unsigned long __initramfs_size;
#include <linux/initrd.h>
#include <linux/kexec.h>
static void __init free_initrd(void)
{
#ifdef CONFIG_KEXEC
unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
unsigned long crashk_end = (unsigned long)__va(crashk_res.end);
#endif
if (do_retain_initrd)
goto skip;
#ifdef CONFIG_KEXEC
/*
* 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) {
/*
* 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);
} else
#endif
free_initrd_mem(initrd_start, initrd_end);
skip:
initrd_start = 0;
initrd_end = 0;
}
#ifdef CONFIG_BLK_DEV_RAM
#define BUF_SIZE 1024
static void __init clean_rootfs(void)
{
int fd;
void *buf;
struct linux_dirent64 *dirp;
int num;
fd = sys_open("/", O_RDONLY, 0);
WARN_ON(fd < 0);
if (fd < 0)
return;
buf = kzalloc(BUF_SIZE, GFP_KERNEL);
WARN_ON(!buf);
if (!buf) {
sys_close(fd);
return;
}
dirp = buf;
num = sys_getdents64(fd, dirp, BUF_SIZE);
while (num > 0) {
while (num > 0) {
struct stat st;
int ret;
ret = sys_newlstat(dirp->d_name, &st);
WARN_ON_ONCE(ret);
if (!ret) {
if (S_ISDIR(st.st_mode))
sys_rmdir(dirp->d_name);
else
sys_unlink(dirp->d_name);
}
num -= dirp->d_reclen;
dirp = (void *)dirp + dirp->d_reclen;
}
dirp = buf;
memset(buf, 0, BUF_SIZE);
num = sys_getdents64(fd, dirp, BUF_SIZE);
}
sys_close(fd);
kfree(buf);
}
#endif
static int __initdata do_skip_initramfs;
static int __init skip_initramfs_param(char *str)
{
if (*str)
return 0;
do_skip_initramfs = 1;
return 1;
}
__setup("skip_initramfs", skip_initramfs_param);
static int __init populate_rootfs(void)
{
char *err;
if (do_skip_initramfs)
return default_rootfs();
err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
if (err)
panic("%s", err); /* Failed to decompress INTERNAL initramfs */
if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
int fd;
printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
if (!err) {
free_initrd();
goto done;
} else {
clean_rootfs();
unpack_to_rootfs(__initramfs_start, __initramfs_size);
}
printk(KERN_INFO "rootfs image is not initramfs (%s)"
"; looks like an initrd\n", err);
fd = sys_open("/initrd.image",
O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
ssize_t written = xwrite(fd, (char *)initrd_start,
initrd_end - initrd_start);
if (written != initrd_end - initrd_start)
pr_err("/initrd.image: incomplete write (%zd != %ld)\n",
written, initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
done:
#else
printk(KERN_INFO "Unpacking initramfs...\n");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
if (err)
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
free_initrd();
#endif
/*
* Try loading default modules from initramfs. This gives
* us a chance to load before device_initcalls.
*/
load_default_modules();
}
return 0;
}
rootfs_initcall(populate_rootfs);

715
init/ld.c Executable file
View file

@ -0,0 +1,715 @@
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/cacheflush.h>
#include <asm/irqflags.h>
#include <linux/fs.h>
#include <asm/tlbflush.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include "ld.h"
#include "elf.h"
//#define LD_DEBUG
#ifdef LD_DEBUG
#define ld_log(a, ...) printk(KERN_ALERT a, __VA_ARGS__)
#else
#define ld_log(a, ...)
#endif //LD_DEBUG
int ld_Elf_Ehdr_to_Elf_Shdr(_Elf_Ehdr *ehdr, _Elf_Shdr **shdr, size_t *size) {
ld_log("%s\n", __FUNCTION__);
if(ehdr == NULL) { return -1; }
if(shdr == NULL) { return -1; }
if(size == NULL) { return -1; }
*shdr = (_Elf_Shdr *)((unsigned long)ehdr + (unsigned long)ehdr->e_shoff);
*size = ehdr->e_shnum;
return 0;
}
int ld_Elf_Ehdr_to_Elf_Phdr(_Elf_Ehdr *ehdr, _Elf_Phdr **phdr, size_t *size) {
ld_log("%s\n", __FUNCTION__);
if(ehdr == NULL) { return -1; }
if(phdr == NULL) { return -1; }
if(size == NULL) { return -1; }
*phdr = (_Elf_Phdr *)((unsigned long)ehdr + (unsigned long)(ehdr->e_phoff));
*size = ehdr->e_phnum;
return 0;
}
int ld_binary_to_Elf_Ehdr(void *binary, _Elf_Ehdr **ehdr) {
ld_log("%s\n", __FUNCTION__);
if(ehdr == NULL) { return -1; }
*ehdr = (_Elf_Ehdr *)binary;
return 0;
}
int ld_get_entry(void *binary, void **entry) {
_Elf_Ehdr *ehdr;
void *base = NULL;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(entry == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_get_base(binary, &base)) { return -1; }
*entry = (void *)((unsigned long)base + (unsigned long)ehdr->e_entry);
return 0;
}
int ld_get_name(void *binary, char **name) {
_Elf_Dyn *dyn;
char *strtab;
size_t sz;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(name == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
if(ld_get_dynamic_strtab(binary, &strtab, &sz)) { return -1; }
*name = NULL;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_SONAME:
*name = &strtab[dyn[i].d_un.d_val];
break;
}
if(*name != NULL) { break; }
}
return 0;
}
int ld_get_version(void *binary, char **version) {
_Elf_Sym *symtab;
char *strtab;
size_t strtabsz;
size_t symtabsz;
char *name;
char **string;
_Elf_Sym *sym;
void *base = NULL;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(ld_get_dynamic_strtab(binary, &strtab, &strtabsz)) { return -1; }
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
for(i = 0; i < (symtabsz / sizeof(_Elf_Sym)); i++) {
if(ld_get_symbol(symtab, i, &sym)) { return -1; }
if(ld_get_string(strtab, sym->st_name, &name)) { return -1; }
if(strcmp(name, "version") == 0) {
if(ld_get_base(binary, &base)) { return -1; }
string = (char **)((unsigned long)sym->st_value + (unsigned long)base);
*version = *string;
return 0;
}
}
return -1;
}
int ld_get_string(char *strtab, int index, char **string) {
ld_log("%s\n", __FUNCTION__);
if(strtab == NULL) { return -1; }
if(string == NULL) { return -1; }
*string = &(((char *)strtab)[index]);
return 0;
}
int ld_get_symbol(_Elf_Sym *symtab, int index, _Elf_Sym **symbol) {
ld_log("%s\n", __FUNCTION__);
if(symtab == NULL) { return -1; }
if(symbol == NULL) { return -1; }
*symbol = &(symtab[index]);
return 0;
}
int ld_get_base(void *binary, void **address) {
_Elf_Ehdr *ehdr;
_Elf_Phdr *phdr;
int set;
size_t size;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(address == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_Elf_Ehdr_to_Elf_Phdr(ehdr, &phdr, &size)) { return -1; }
set = 0;
for(i = 0; i < ehdr->e_phnum; i++) {
if(phdr[i].p_type == _PT_LOAD) {
if((phdr[i].p_offset < (_Elf_Off)((unsigned long)*address)) || set == 0) {
*address = (void *)(unsigned long)phdr[i].p_offset;
set = 1;
}
}
}
*address = (void *)((long)*address + (long)binary);
return 0;
}
int ld_get_size(void *binary, size_t *size) {
_Elf_Ehdr *ehdr;
_Elf_Phdr *phdr;
int set;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(size == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_Elf_Ehdr_to_Elf_Phdr(ehdr, &phdr, size)) { return -1; }
set = 0;
for(i = 0; i < ehdr->e_phnum; i++) {
if(phdr[i].p_type == _PT_LOAD) {
if((phdr[i].p_vaddr + phdr[i].p_memsz) > (_Elf_Off)*size) {
*size = (size_t)phdr[i].p_vaddr + (size_t)phdr[i].p_memsz;
}
}
}
return 0;
}
int ld_get_sect(void *binary, char *name, void **section, size_t *size) {
_Elf_Ehdr *ehdr;
_Elf_Shdr *shdr;
size_t sz;
void *strtab;
char *tmp;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(name == NULL) { return -1; }
if(section == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_Elf_Ehdr_to_Elf_Shdr(ehdr, &shdr, &sz)) { return -1; }
strtab = (void *)((unsigned long)binary + (unsigned long)shdr[ehdr->e_shstrndx].sh_offset);
for(i = 0; i < sz; i++) {
if(ld_get_string(strtab, shdr[i].sh_name, &tmp)) { return -1; }
if(strcmp(name, tmp) == 0) {
*section = (void *)((unsigned long)binary + (unsigned long)shdr[i].sh_offset);
*size = shdr[i].sh_size;
return 0;
}
}
*section = NULL;
return -1;
}
int ld_get_dynamic_symtab(void *binary, _Elf_Sym **symtab, size_t *size) {
_Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(symtab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*symtab = NULL;
*size = 0;
ent = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_SYMTAB:
*symtab = (void *)((unsigned long)dyn[i].d_un.d_ptr);
break;
case _DT_SYMENT:
ent = dyn[i].d_un.d_val;
break;
}
if((*symtab != NULL) && (*size != 0) && (ent != 0)) { break; }
}
if(ent != sizeof(_Elf_Sym)) { return -1; }
*symtab = (void *)((long)*symtab + (long)binary);
if(ld_get_sect(binary, ".dynsym", (void **)&dyn, &sz)) { return -1; }
if(sz % sizeof(_Elf_Sym) != 0) { return -1; }
*size = sz;
return 0;
}
int ld_get_dynamic_strtab(void *binary, char **strtab, size_t *size) {
_Elf_Dyn *dyn;
size_t sz;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(strtab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*strtab = NULL;
*size = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_STRTAB:
*strtab = (void *)((unsigned long)dyn[i].d_un.d_ptr);
break;
case _DT_STRSZ:
*size = dyn[i].d_un.d_val;
break;
}
if((*strtab != NULL) && (*size != 0)) { break; }
}
*strtab = (void *)((long)*strtab + (long)binary);
if(ld_get_sect(binary, ".dynstr", (void **)&dyn, &sz)) { return -1; }
if(*size != sz) { return -1; }
return 0;
}
#ifdef __TARGET_64__
int ld_get_dynamic_relatab(void *binary, _Elf_Rela **relatab, size_t *size) {
_Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
//printk(KERN_ALERT "%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(relatab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*relatab = NULL;
*size = 0;
ent = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_RELA:
*relatab = (void *)(unsigned long)dyn[i].d_un.d_ptr;
break;
case _DT_RELASZ:
*size = dyn[i].d_un.d_val;
break;
case _DT_RELAENT:
ent = dyn[i].d_un.d_val;
break;
}
if((*relatab != NULL) && (*size != 0) && (ent != 0)) { break; }
}
ld_log("*relatab: %p, *size: %d, ent: %d\n", *relatab, (int)*size, (int)ent);
if(ent != sizeof(_Elf_Rela)) { return -1; }
*relatab = (void *)((long)*relatab + (long)binary);
return 0;
}
#else //__TARGET_32__
int ld_get_dynamic_reltab(void *binary, _Elf_Rel **reltab, size_t *size) {
_Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(reltab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*reltab = NULL;
*size = 0;
ent = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_REL:
*reltab = (void *)dyn[i].d_un.d_ptr;
break;
case _DT_RELSZ:
*size = dyn[i].d_un.d_val;
break;
case _DT_RELENT:
ent = dyn[i].d_un.d_val;
break;
}
if((*reltab != NULL) && (*size != 0) && (ent != 0)) { break; }
}
if(ent != sizeof(_Elf_Rel)) { return -1; }
*reltab = (void *)((long)*reltab + (long)binary);
return 0;
}
#endif //__TARGET_64__ | __TARGET_32__
#ifdef __TARGET_64__
int ld_get_dynamic_plttab(void *binary, _Elf_Rela **plttab, size_t *size) {
#else //__TARGET_32__
int ld_get_dynamic_plttab(void *binary, _Elf_Rel **plttab, size_t *size) {
#endif //__TARGET_64__ | __TARGET_32__
_Elf_Dyn *dyn;
size_t sz;
int type;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(plttab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*plttab = NULL;
*size = 0;
type = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_JMPREL:
*plttab = (void *)((unsigned long)dyn[i].d_un.d_ptr);
break;
case _DT_PLTREL:
type = dyn[i].d_un.d_val;
break;
case _DT_PLTRELSZ:
*size = dyn[i].d_un.d_val;
break;
}
if((*plttab != NULL) && (*size != 0) && (type != 0)) { break; }
}
#ifdef __TARGET_64__
if((type != _DT_RELA) || ((*size % sizeof(_Elf_Rela)) != 0)) { return -1; }
#else //__TARGET_32_
if((type != _DT_REL) || ((*size % sizeof(_Elf_Rel)) != 0)) { return -1; }
#endif //__TARGET_64__ | __TARGET_32__
*plttab = (void *)((long)*plttab + (long)binary);
return 0;
}
#ifdef __TARGET_64__
int ld_fixup_dynamic_relatab(void *binary, ld_resolve_t resolve, ld_translate_t translate) {
_Elf_Rela *relatab;
_Elf_Sym *symtab;
size_t relatabsz;
size_t symtabsz;
_Elf_Sym *sym;
unsigned int i;
void *base = NULL;
void *runtime;
_Elf_Sxword *pointer;
_Elf_Sxword value;
//printk(KERN_ALERT "%s\n", __FUNCTION__);
if(ld_get_base(binary, &base)) { return -1; }
if(ld_get_dynamic_relatab(binary, &relatab, &relatabsz)) { return 0; }
ld_log("relatab %p, relatabsz: %d\n", relatab, (int)relatabsz);
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
ld_log("symtab %p, symtabsz: %d\n", symtab, (int)symtabsz);
if(translate(binary, base, &runtime)) { return -1; }
ld_log("base: %p, runtime: %p\n", base, runtime);
for(i = 0; i < (relatabsz / sizeof(_Elf_Rela)); i++) {
switch(_ELF_R_TYPE(relatab[i].r_info)) {
case _R_AARCH64_RELATIVE:
pointer = (_Elf_Sxword *)((_Elf_Sxword)base + ((unsigned long)relatab[i].r_offset));
value = (_Elf_Sxword)runtime + ((unsigned long)relatab[i].r_addend);
*((_Elf_Sxword *)pointer) = value;
ld_log("fixed up _R_AARCH64_RELATIVE: %p, %p\n", (void *)pointer, (void *)value);
break;
case _R_AARCH64_GLOB_DAT:
if(ld_get_symbol(symtab, _ELF_R_SYM(relatab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sxword *)((_Elf_Sxword)base + ((unsigned long)relatab[i].r_offset));
ld_log("fixed up R_ARM_GLOB_DAT: %p, %p\n", (void *)pointer, (void *)value);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sxword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sxword *)pointer) = value;
ld_log("fixed up R_ARM_GLOB_DAT: %p, %p\n", (void *)pointer, (void *)value);
break;
default:
ld_log("unsupported relocate: %d\n", (int)_ELF_R_TYPE(relatab[i].r_info));
return -1;
}
}
return 0;
}
#else //__TARGET_32__
int ld_fixup_dynamic_reltab(void *binary, ld_resolve_t resolve, ld_translate_t translate) {
_Elf_Rel *reltab;
_Elf_Sym *symtab;
size_t reltabsz;
size_t symtabsz;
_Elf_Sym *sym;
unsigned int i;
void *base = NULL;
void *runtime;
_Elf_Sword *pointer;
_Elf_Sword value;
ld_log("%s\n", __FUNCTION__);
if(ld_get_base(binary, &base)) { return -1; }
if(ld_get_dynamic_reltab(binary, &reltab, &reltabsz)) { return 0; }
ld_log("reltab 0x%x, reltabsz: %d\n", reltab, (int)reltabsz);
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
ld_log("symtab 0x%x, symtabsz: %d\n", symtab, (int)symtabsz);
if(translate(binary, base, &runtime)) { return -1; }
ld_log("base: %p, runtime: %p\n", base, runtime);
for(i = 0; i < (reltabsz / sizeof(_Elf_Rel)); i++) {
switch(_ELF_R_TYPE(reltab[i].r_info)) {
case _R_ARM_RELATIVE:
pointer = (_Elf_Sword *)((_Elf_Sword)base + (_Elf_Sword)reltab[i].r_offset);
value = (_Elf_Sword)runtime + *pointer;
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_RELATIVE: 0x%x, 0x%x\n", pointer, (void *)value);
break;
case _R_ARM_ABS32:
if(ld_get_symbol(symtab, _ELF_R_SYM(reltab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sword *)((_Elf_Sword)runtime + (_Elf_Sword)reltab[i].r_offset);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_ABS32: 0x%x, 0x%x\n", pointer, (void *)value);
break;
case _R_ARM_GLOB_DAT:
if(ld_get_symbol(symtab, _ELF_R_SYM(reltab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sword *)((_Elf_Sword)base + (_Elf_Sword)reltab[i].r_offset);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_GLOB_DAT: 0x%x, 0x%x\n", pointer, (void *)value);
break;
default:
ld_log("unsupported relocate: %d\n", (int)_ELF_R_TYPE(reltab[i].r_info));
return -1;
}
}
return 0;
}
#endif //__TARGET_64__ | __TARGET_32__
int ld_fixup_dynamic_plttab(void *binary, ld_resolve_t resolve, ld_translate_t translate) {
#ifdef __TARGET_64__
_Elf_Rela *plttab;
#else //__TARGET_64__
_Elf_Rel *plttab;
#endif //__TARGET_64__ | __TARGET_32__
_Elf_Sym *symtab;
_Elf_Sym *sym;
size_t plttabsz;
size_t symtabsz;
unsigned int i;
void *base = NULL;
void *runtime;
#ifdef __TARGET_64__
_Elf_Sxword *pointer;
_Elf_Sxword value;
#else //__TARGET_32__
_Elf_Sword *pointer;
_Elf_Sword value;
#endif //__TARGET_64__ | __TARGET_32__
ld_log("%s\n", __FUNCTION__);
if(ld_get_base(binary, &base)) { return -1; }
if(ld_get_dynamic_plttab(binary, &plttab, &plttabsz)) { return 0; }
ld_log("plttab: %p, plttabsz: %d\n", plttab, (int)plttabsz);
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
ld_log("symtab: %p, symtabsz: %d\n", symtab, (int)symtabsz);
if(translate(binary, base, &runtime)) { return -1; }
ld_log("base: %p, runtime: %p\n", base, runtime);
#ifdef __TARGET_64__
for(i = 0; i < (plttabsz / sizeof(_Elf_Rela)); i++) {
#else //__TARGET_32__
for(i = 0; i < (plttabsz / sizeof(_Elf_Rel)); i++) {
#endif //__TARGET_64__ | __TARGET_32__
switch(_ELF_R_TYPE(plttab[i].r_info)) {
#ifdef __TARGET_64__
case _R_AARCH64_JUMP_SLOT:
if(ld_get_symbol(symtab, _ELF_R_SYM(plttab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sxword *)((_Elf_Sxword)base + ((unsigned long)plttab[i].r_offset));
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sxword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sxword *)pointer) = value;
ld_log("fixed up _R_AARCH64_JUMP_SLOT: %p, %p\n", (void *)pointer, (void *)value);
break;
#else //__TARGET_32__
case _R_ARM_JUMP_SLOT:
if(ld_get_symbol(symtab, _ELF_R_SYM(plttab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sword *)((_Elf_Sword)base + (_Elf_Sword)plttab[i].r_offset);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_JUMP_SLOT: 0x%x, 0x%x\n", pointer, (void *)value);
break;
#endif //__TARGET_64__ | __TARGET_32__
default:
ld_log("unsupported relocation: %d\n", (int)_ELF_R_TYPE(plttab[i].r_info));
return -1;
}
}
return 0;
}

80
init/ld.h Executable file
View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __LD_H__
#define __LD_H__
#define __TARGET_64__
#include "elf.h"
typedef int (*ld_init_t)(void);
typedef int (*ld_fini_t)(void);
#ifdef __TARGET_64__
typedef int (*ld_resolve_t)(void *binary, _Elf_Sym *sym, _Elf_Sxword *value);
#else //__TARGET_32__
typedef int (*ld_resolve_t)(void *binary, _Elf_Sym *sym, _Elf_Sword *value);
#endif //__TARGET_64__ | __TARGET_32__
typedef int (*ld_translate_t)(void *binary, void *in, void **out);
int ld_Elf_Ehdr_to_Elf_Shdr(_Elf_Ehdr *ehdr, _Elf_Shdr **shdr, size_t *size);
int ld_Elf_Ehdr_to_Elf_Phdr(_Elf_Ehdr *ehdr, _Elf_Phdr **phdr, size_t *size);
int ld_binary_to_Elf_Ehdr(void *binary, _Elf_Ehdr **ehdr);
int ld_get_entry(void *binary, void **entry);
int ld_get_name(void *binary, char **name);
int ld_get_version(void *binary, char **version);
int ld_get_string(char *strtab, int index, char **string);
int ld_get_symbol(_Elf_Sym *symtab, int index, _Elf_Sym **symbol);
int ld_get_base(void *binary, void **address);
int ld_get_size(void *binary, size_t *size);
int ld_get_sect(void *binary, char *name, void **section, size_t *size);
#ifdef __TARGET_64__
int ld_get_dynamic_relatab(void *binary, _Elf_Rela **rela, size_t *size);
#else //__TARGET_32__
int ld_get_dynamic_reltab(void *binary, _Elf_Rel **rel, size_t *size);
#endif //__TARGET_64__ | __TARGET_32__
int ld_get_dynamic_symtab(void *binary, _Elf_Sym **symtab, size_t *size);
int ld_get_dynamic_strtab(void *binary, char **strtab, size_t *size);
#ifdef __TARGET_64__
int ld_fixup_dynamic_relatab(void *binary, ld_resolve_t resolve, ld_translate_t translate);
#else //__TARGET_32__
int ld_fixup_dynamic_reltab(void *binary, ld_resolve_t resolve, ld_translate_t translate);
#endif //__TARGET_64__ | __TARGET_32__
int ld_fixup_dynamic_plttab(void *binary, ld_resolve_t resolve, ld_translate_t translate);
#endif //__LD_H__

1132
init/main.c Normal file

File diff suppressed because it is too large Load diff

59
init/noinitramfs.c Normal file
View file

@ -0,0 +1,59 @@
/*
* init/noinitramfs.c
*
* Copyright (C) 2006, NXP Semiconductors, All Rights Reserved
* Author: Jean-Paul Saman <jean-paul.saman@nxp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/syscalls.h>
#include <linux/kconfig.h>
#include <linux/initramfs.h>
/*
* Create a simple rootfs that is similar to the default initramfs
*/
#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD)
static
#endif
int __init default_rootfs(void)
{
int err;
err = sys_mkdir((const char __user __force *) "/dev", 0755);
if (err < 0)
goto out;
err = sys_mknod((const char __user __force *) "/dev/console",
S_IFCHR | S_IRUSR | S_IWUSR,
new_encode_dev(MKDEV(5, 1)));
if (err < 0)
goto out;
err = sys_mkdir((const char __user __force *) "/root", 0700);
if (err < 0)
goto out;
return 0;
out:
printk(KERN_WARNING "Failed to create a rootfs\n");
return err;
}
#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD)
rootfs_initcall(default_rootfs);
#endif

50
init/version.c Normal file
View file

@ -0,0 +1,50 @@
/*
* linux/init/version.c
*
* Copyright (C) 1992 Theodore Ts'o
*
* May be freely distributed as part of Linux.
*/
#include <generated/compile.h>
#include <linux/module.h>
#include <linux/uts.h>
#include <linux/utsname.h>
#include <generated/utsrelease.h>
#include <linux/version.h>
#include <linux/proc_ns.h>
#ifndef CONFIG_KALLSYMS
#define version(a) Version_ ## a
#define version_string(a) version(a)
extern int version_string(LINUX_VERSION_CODE);
int version_string(LINUX_VERSION_CODE);
#endif
struct uts_namespace init_uts_ns = {
.kref = {
.refcount = ATOMIC_INIT(2),
},
.name = {
.sysname = UTS_SYSNAME,
.nodename = UTS_NODENAME,
.release = UTS_RELEASE,
.version = UTS_VERSION,
.machine = UTS_MACHINE,
.domainname = UTS_DOMAINNAME,
},
.user_ns = &init_user_ns,
.proc_inum = PROC_UTS_INIT_INO,
};
EXPORT_SYMBOL_GPL(init_uts_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";
const char linux_proc_banner[] =
"%s version %s"
" (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ")"
" (" LINUX_COMPILER ") %s\n";

151
init/vmm.c Executable file
View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/cacheflush.h>
#include <asm/irqflags.h>
#include <linux/fs.h>
#include <asm/tlbflush.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/vmm.h>
#include "ld.h"
#define VMM_32BIT_SMC_CALL_MAGIC 0x82000400
#define VMM_64BIT_SMC_CALL_MAGIC 0xC2000400
#define VMM_STACK_OFFSET 4096
#define VMM_MODE_AARCH32 0
#define VMM_MODE_AARCH64 1
extern char _svmm;
extern char _evmm;
extern char _vmm_disable;
char *vmm;
size_t vmm_size;
int vmm_entry(void);
int _vmm_goto_EL2(int magic, void *label, int offset, int mode, void *base, int size);
int vmm_resolve(void *binary, _Elf_Sym *sym, _Elf_Sxword *value) {
char *name;
char *strtab;
size_t strtabsz;
printk(KERN_ALERT "%s\n", __FUNCTION__);
if(ld_get_dynamic_strtab(binary, &strtab, &strtabsz)) { return -1; }
if(ld_get_string(strtab, sym->st_name, &name)) { return -1; }
printk(KERN_ALERT "name: %s\n", name);
return -1;
}
int vmm_translate(void *binary, void *in, void **out) {
printk(KERN_ALERT "%s\n", __FUNCTION__);
*out = (void *)virt_to_phys(in);
return 0;
}
int vmm_disable(void) {
_vmm_goto_EL2(VMM_64BIT_SMC_CALL_MAGIC, (void *)virt_to_phys(&_vmm_disable), VMM_STACK_OFFSET, VMM_MODE_AARCH64, NULL, 0);
printk(KERN_ALERT "%s\n", __FUNCTION__);
return 0;
}
int vmm_entry(void) {
typedef int (*_main_)(void);
int status;
_main_ entry_point;
printk(KERN_ALERT "%s\n", __FUNCTION__);
if(ld_get_entry(vmm, (void **)&entry_point)) { return -1; }
entry_point = (_main_)virt_to_phys(entry_point);
printk(KERN_ALERT "vmm entry point: %p\n", entry_point);
vmm = (char *)virt_to_phys(vmm);
flush_cache_all();
status = _vmm_goto_EL2(VMM_64BIT_SMC_CALL_MAGIC, entry_point, VMM_STACK_OFFSET, VMM_MODE_AARCH64, vmm, vmm_size);
printk(KERN_ALERT "vmm(%p, 0x%x): %x\n", vmm, (int)vmm_size, status);
return 0;
}
int vmm_init(void) {
size_t size;
char *name;
void *base;
printk(KERN_ALERT "%s\n", __FUNCTION__);
if(smp_processor_id() != 0) { return 0; }
printk(KERN_ALERT "bin 0x%p, 0x%x\n", &_svmm, (int)(&_evmm - &_svmm));
memcpy((void *)phys_to_virt(VMM_RUNTIME_BASE), &_svmm, (size_t)(&_evmm - &_svmm));
vmm = (void *)phys_to_virt(VMM_RUNTIME_BASE);
vmm_size = VMM_RUNTIME_SIZE;
printk(KERN_ALERT "ram 0x%p, 0x%x\n", vmm, (int)vmm_size);
if(ld_get_size(vmm, &size)) { return -1; }
if(ld_get_name(vmm, &name)) { return -1; }
printk(KERN_ALERT "%s, %d\n", name, (int)size);
if(ld_fixup_dynamic_relatab(vmm, &vmm_resolve, &vmm_translate)) { return -1; }
if(ld_fixup_dynamic_plttab(vmm, &vmm_resolve, &vmm_translate)) { return -1; }
if(ld_get_sect(vmm, ".bss", &base, &size)) { return -1; }
memset(base, 0, size);
vmm_entry();
return 0;
}

BIN
init/vmm.elf Executable file

Binary file not shown.