diff options
Diffstat (limited to 'init/init.c')
-rw-r--r-- | init/init.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/init/init.c b/init/init.c new file mode 100644 index 000000000..f52f7f57d --- /dev/null +++ b/init/init.c @@ -0,0 +1,301 @@ +/* + * linux/init/init.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <stdarg.h> + +#include <asm/system.h> +#include <asm/io.h> + +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/head.h> +#include <linux/unistd.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/fs.h> +#include <linux/ctype.h> +#include <linux/ioport.h> +#include <linux/kernel.h> + +extern unsigned long * prof_buffer; +extern unsigned long prof_len; +extern char edata, end; +extern char *linux_banner; +extern int parse_machine_options(char *line); +extern int root_mountflags; +extern int console_loglevel; + +extern struct { + char *str; + void (*setup_func)(char *, int *); +} *bootsetups; + +/* + * Boot command-line arguments + */ +void copy_options(char * to, char * from); +void parse_options(char *line); +#define MAX_INIT_ARGS 8 +#define MAX_INIT_ENVS 8 +#define COMMAND_LINE ((char *) (PARAM+2048)) +#define COMMAND_LINE_SIZE 256 + +extern unsigned long memory_start; /* After mem_init, stores the */ + /* amount of free user memory */ +extern unsigned long memory_end; +extern unsigned long low_memory_start; + +static char term[21]; +int rows, cols; + +static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; +static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", term, NULL, }; + +static char * argv_rc[] = { "/bin/sh", NULL }; +static char * envp_rc[] = { "HOME=/", term, NULL }; + +static char * argv[] = { "-/bin/sh",NULL }; +static char * envp[] = { "HOME=/usr/root", term, NULL }; + +/* + * we need this inline - forking from kernel space will result + * in NO COPY ON WRITE (!!!), until an execve is executed. This + * is no problem, but for the stack. This is handled by not letting + * main() use the stack at all after fork(). Thus, no function + * calls - which means inline code for fork too, as otherwise we + * would use the stack upon exit from 'fork()'. + * + * Actually only pause and fork are needed inline, so that there + * won't be any messing with the stack from main(), but we define + * some others too. + */ +#define __NR__exit __NR_exit +static inline _syscall0(int,idle) +static inline _syscall0(int,fork) +static inline _syscall0(int,pause) +static inline _syscall0(int,setup) +static inline _syscall0(int,sync) +static inline _syscall0(pid_t,setsid) +static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) +static inline _syscall1(int,dup,int,fd) +static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) +static inline _syscall3(int,open,const char *,file,int,flag,int,mode) +static inline _syscall1(int,close,int,fd) +static inline _syscall1(int,_exit,int,exitcode) +static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) + +static inline pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} + +static char printbuf[1024]; + +char *get_options(char *str, int *ints) +{ + char *cur = str; + int i=1; + + while (cur && isdigit(*cur) && i <= 10) { + ints[i++] = simple_strtoul(cur,NULL,0); + if ((cur = strchr(cur,',')) != NULL) + cur++; + } + ints[0] = i-1; + return(cur); +} + +static int checksetup(char *line) +{ + int i = 0; + int ints[11]; + + while (bootsetups[i].str) { + int n = strlen(bootsetups[i].str); + if (!strncmp(line,bootsetups[i].str,n)) { + bootsetups[i].setup_func(get_options(line+n,ints), ints); + return 1; + } + i++; + } + return 0; +} + +/* + * This is a simple kernel command line parsing function: it parses + * the command line, and fills in the arguments/environment to init + * as appropriate. Any cmd-line option is taken to be an environment + * variable if it contains the character '='. + * + * + * This routine also checks for options meant for the kernel - currently + * only the "root=XXXX" option is recognized. These options are not given + * to init - they are for internal kernel use only. + */ +void parse_options(char *line) +{ + char *next; + char *devnames[] = { "hda", "hdb", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL }; + int devnums[] = { 0x300, 0x340, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0}; + int args, envs; + + if (!*line) + return; + args = 0; + envs = 1; /* TERM is set to 'console' by default */ + next = line; + while ((line = next) != NULL) { + if ((next = strchr(line,' ')) != NULL) + *next++ = 0; + /* + * check for kernel options first.. + */ + if (!strncmp(line,"root=",5)) { + int n; + line += 5; + if (strncmp(line,"/dev/",5)) { + ROOT_DEV = simple_strtoul(line,NULL,16); + continue; + } + line += 5; + for (n = 0 ; devnames[n] ; n++) { + int len = strlen(devnames[n]); + if (!strncmp(line,devnames[n],len)) { + ROOT_DEV = devnums[n]+simple_strtoul(line+len,NULL,0); + break; + } + } + continue; + } + if (!strcmp(line,"ro")) { + root_mountflags |= MS_RDONLY; + continue; + } + if (!strcmp(line,"rw")) { + root_mountflags &= ~MS_RDONLY; + continue; + } + if (!strcmp(line,"debug")) { + console_loglevel = 10; + continue; + } + if (parse_machine_options(line)) + continue; + + if (checksetup(line)) + continue; + /* + * Then check if it's an environment variable or + * an option. + */ + if (strchr(line,'=')) { + if (envs >= MAX_INIT_ENVS) + break; + envp_init[++envs] = line; + } else { + if (args >= MAX_INIT_ARGS) + break; + argv_init[++args] = line; + } + } + argv_init[args+1] = NULL; + envp_init[envs+1] = NULL; +} + +void copy_options(char * to, char * from) +{ + char c = ' '; + int len = 0; + + for (;;) { + if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=") + memory_end = simple_strtoul(from+4, &from, 0); + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; +} + +static int printf(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + write(1,printbuf,i=vsprintf(printbuf, fmt, args)); + va_end(args); + return i; +} + +void init(void) +{ + int pid,i; + + setup(); + sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES); + + #ifdef CONFIG_UMSDOS_FS + { + /* + When mounting a umsdos fs as root, we detect + the pseudo_root (/linux) and initialise it here. + pseudo_root is defined in fs/umsdos/inode.c + */ + extern struct inode *pseudo_root; + if (pseudo_root != NULL){ + current->fs->root = pseudo_root; + current->fs->pwd = pseudo_root; + } + } + #endif + + (void) open("/dev/tty1",O_RDWR,0); + (void) dup(0); + (void) dup(0); + + execve("/etc/init",argv_init,envp_init); + execve("/bin/init",argv_init,envp_init); + execve("/sbin/init",argv_init,envp_init); + /* if this fails, fall through to original stuff */ + + if (!(pid=fork())) { + close(0); + if (open("/etc/rc",O_RDONLY,0)) + _exit(1); + execve("/bin/sh",argv_rc,envp_rc); + _exit(2); + } + if (pid>0) + while (pid != wait(&i)) + /* nothing */; + while (1) { + if ((pid = fork()) < 0) { + printf("Fork failed in init\n\r"); + continue; + } + if (!pid) { + close(0);close(1);close(2); + setsid(); + (void) open("/dev/tty1",O_RDWR,0); + (void) dup(0); + (void) dup(0); + _exit(execve("/bin/sh",argv,envp)); + } + while (1) + if (pid == wait(&i)) + break; + printf("\n\rchild %d died with code %04x\n\r",pid,i); + sync(); + } + _exit(0); +} |