mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
2021
init/Kconfig
Normal file
2021
init/Kconfig
Normal file
File diff suppressed because it is too large
Load diff
38
init/Makefile
Normal file
38
init/Makefile
Normal 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
44
init/_vmm.S
Executable 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
315
init/calibrate.c
Normal 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
638
init/do_mounts.c
Normal 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
76
init/do_mounts.h
Normal 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
137
init/do_mounts_initrd.c
Normal 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
312
init/do_mounts_md.c
Normal 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
361
init/do_mounts_rd.c
Normal 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
253
init/elf.h
Executable 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
26
init/init_task.c
Normal 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
675
init/initramfs.c
Normal 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
715
init/ld.c
Executable 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
80
init/ld.h
Executable 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
1132
init/main.c
Normal file
File diff suppressed because it is too large
Load diff
59
init/noinitramfs.c
Normal file
59
init/noinitramfs.c
Normal 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
50
init/version.c
Normal 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
151
init/vmm.c
Executable 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
BIN
init/vmm.elf
Executable file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue