main.c 14.1 KB
Newer Older
1 2 3
/*
 *  linux/init/main.c
 *
4
 *  Copyright (C) 1991, 1992  Linus Torvalds
5 6
 */

7
#include <stdarg.h>
8

9 10 11 12 13 14 15 16 17 18
#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>
19
#include <linux/string.h>
20
#include <linux/timer.h>
21
#include <linux/fs.h>
22 23
#include <linux/ctype.h>
#include <linux/delay.h>
24
#include <linux/utsname.h>
25
#include <linux/ioport.h>
26

27 28
extern unsigned long * prof_buffer;
extern unsigned long prof_len;
29
extern char edata, end;
30
extern char *linux_banner;
31
asmlinkage void lcall7(void);
32
struct desc_struct default_ldt;
33

34 35 36 37 38 39 40 41 42 43 44 45
/*
 * 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.
 */
46
#define __NR__exit __NR_exit
47
static inline _syscall0(int,idle)
48 49
static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
Linus Torvalds's avatar
Linus Torvalds committed
50
static inline _syscall0(int,setup)
51
static inline _syscall0(int,sync)
52 53 54 55 56 57
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)
58
static inline _syscall1(int,_exit,int,exitcode)
59 60 61 62 63 64
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);
}
Linus Torvalds's avatar
Linus Torvalds committed
65

66 67
static char printbuf[1024];

68 69
extern int console_loglevel;

70
extern char empty_zero_page[PAGE_SIZE];
71
extern int vsprintf(char *,const char *,va_list);
72
extern void init(void);
73
extern void init_IRQ(void);
Linus Torvalds's avatar
Linus Torvalds committed
74
extern void init_modules(void);
75
extern long kmalloc_init (long,long);
76 77
extern long blk_dev_init(long,long);
extern long chr_dev_init(long,long);
78
extern void floppy_init(void);
79
extern void sock_init(void);
Linus Torvalds's avatar
Linus Torvalds committed
80
extern long rd_init(long mem_start, int length);
81
unsigned long net_dev_init(unsigned long, unsigned long);
82 83 84 85

extern void hd_setup(char *str, int *ints);
extern void bmouse_setup(char *str, int *ints);
extern void eth_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
86
extern void xd_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
87
extern void mcd_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
88
extern void st_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
89 90 91 92 93
extern void st0x_setup(char *str, int *ints);
extern void tmc8xx_setup(char *str, int *ints);
extern void t128_setup(char *str, int *ints);
extern void generic_NCR5380_setup(char *str, int *intr);
extern void aha152x_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
94
extern void scsi_luns_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
95
extern void sound_setup(char *str, int *ints);
Linus Torvalds's avatar
Linus Torvalds committed
96 97 98
#ifdef CONFIG_SBPCD
extern void sbpcd_setup(char *str, int *ints);
#endif CONFIG_SBPCD
Linus Torvalds's avatar
Linus Torvalds committed
99

100 101 102
#ifdef CONFIG_SYSVIPC
extern void ipc_init(void);
#endif
103
#ifdef CONFIG_SCSI
104
extern unsigned long scsi_dev_init(unsigned long, unsigned long);
105 106
#endif

107 108 109
/*
 * This is set up by the setup-routine at boot-time
 */
110 111
#define PARAM	empty_zero_page
#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
112
#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
113 114 115 116 117
#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
#define RAMDISK_SIZE (*(unsigned short *) (PARAM+0x1F8))
#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
118

119 120 121 122 123
/*
 * Boot command-line arguments
 */
#define MAX_INIT_ARGS 8
#define MAX_INIT_ENVS 8
124
#define COMMAND_LINE ((char *) (PARAM+2048))
Linus Torvalds's avatar
Linus Torvalds committed
125
#define COMMAND_LINE_SIZE 256
126

Linus Torvalds's avatar
Linus Torvalds committed
127
extern void time_init(void);
128

129 130
static unsigned long memory_start = 0;	/* After mem_init, stores the */
					/* amount of free user memory */
131
static unsigned long memory_end = 0;
132
static unsigned long low_memory_start = 0;
133

Linus Torvalds's avatar
Linus Torvalds committed
134 135 136
static char term[21];
int rows, cols;

137
static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
Linus Torvalds's avatar
Linus Torvalds committed
138
static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", term, NULL, };
139

Linus Torvalds's avatar
Linus Torvalds committed
140
static char * argv_rc[] = { "/bin/sh", NULL };
Linus Torvalds's avatar
Linus Torvalds committed
141
static char * envp_rc[] = { "HOME=/", term, NULL };
Linus Torvalds's avatar
Linus Torvalds committed
142 143

static char * argv[] = { "-/bin/sh",NULL };
Linus Torvalds's avatar
Linus Torvalds committed
144
static char * envp[] = { "HOME=/usr/root", term, NULL };
145

146
struct drive_info_struct { char dummy[32]; } drive_info;
147
struct screen_info screen_info;
148

149
unsigned char aux_device_present;
150
int ramdisk_size;
151 152 153
int root_mountflags = 0;

static char fpu_error = 0;
154

Linus Torvalds's avatar
Linus Torvalds committed
155
static char command_line[COMMAND_LINE_SIZE] = { 0, };
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
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);
}

struct {
	char *str;
	void (*setup_func)(char *, int *);
} bootsetups[] = {
175
	{ "reserve=", reserve_setup },
176 177 178
#ifdef CONFIG_INET
	{ "ether=", eth_setup },
#endif
Linus Torvalds's avatar
Linus Torvalds committed
179 180 181
#ifdef CONFIG_SCSI
	{ "max_scsi_luns=", scsi_luns_setup },
#endif
182 183 184
#ifdef CONFIG_BLK_DEV_HD
	{ "hd=", hd_setup },
#endif
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187
#ifdef CONFIG_CHR_DEV_ST
	{ "st=", st_setup },
#endif
188 189
#ifdef CONFIG_BUSMOUSE
	{ "bmouse=", bmouse_setup },
Linus Torvalds's avatar
Linus Torvalds committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
#endif
#ifdef CONFIG_SCSI_SEAGATE
	{ "st0x=", st0x_setup },
	{ "tmc8xx=", tmc8xx_setup },
#endif
#ifdef CONFIG_SCSI_T128
	{ "t128=", t128_setup },
#endif
#ifdef CONFIG_SCSI_GENERIC_NCR5380
	{ "ncr5380=", generic_NCR5380_setup },
#endif
#ifdef CONFIG_SCSI_AHA152X
        { "aha152x=", aha152x_setup},
#endif
#ifdef CONFIG_BLK_DEV_XD
	{ "xd=", xd_setup },
Linus Torvalds's avatar
Linus Torvalds committed
206 207 208 209 210 211
#endif
#ifdef CONFIG_MCD
	{ "mcd=", mcd_setup },
#endif
#ifdef CONFIG_SOUND
	{ "sound=", sound_setup },
212
#endif
Linus Torvalds's avatar
Linus Torvalds committed
213 214 215
#ifdef CONFIG_SBPCD
	{ "sbpcd=", sbpcd_setup },
#endif CONFIG_SBPCD
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
	{ 0, 0 }
};

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(0);
		}
		i++;
	}
	return(1);
}

unsigned long loops_per_sec = 1;

static void calibrate_delay(void)
{
	int ticks;

	printk("Calibrating delay loop.. ");
	while (loops_per_sec <<= 1) {
		ticks = jiffies;
		__delay(loops_per_sec);
		ticks = jiffies - ticks;
		if (ticks >= HZ) {
			__asm__("mull %1 ; divl %2"
				:"=a" (loops_per_sec)
				:"d" (HZ),
				 "r" (ticks),
				 "0" (loops_per_sec)
				:"dx");
Linus Torvalds's avatar
Linus Torvalds committed
253
			printk("ok - %lu.%02lu BogoMips\n",
254 255 256 257 258 259 260 261 262
				loops_per_sec/500000,
				(loops_per_sec/5000) % 100);
			return;
		}
	}
	printk("failed\n");
}
	

263 264 265 266 267 268 269 270 271 272 273 274 275 276
/*
 * 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.
 */
static void parse_options(char *line)
{
	char *next;
Linus Torvalds's avatar
Linus Torvalds committed
277
	char *devnames[] = { "hda", "hdb", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL };
Linus Torvalds's avatar
Linus Torvalds committed
278
	int devnums[]    = { 0x300, 0x340, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0};
279 280 281 282 283 284 285
	int args, envs;

	if (!*line)
		return;
	args = 0;
	envs = 1;	/* TERM is set to 'console' by default */
	next = line;
286 287
	while ((line = next) != NULL) {
		if ((next = strchr(line,' ')) != NULL)
288 289 290 291 292
			*next++ = 0;
		/*
		 * check for kernel options first..
		 */
		if (!strncmp(line,"root=",5)) {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
			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,16);
					break;
				}
			}
		} else if (!strcmp(line,"ro"))
308
			root_mountflags |= MS_RDONLY;
309
		else if (!strcmp(line,"rw"))
310
			root_mountflags &= ~MS_RDONLY;
311 312
		else if (!strcmp(line,"debug"))
			console_loglevel = 10;
313
		else if (!strcmp(line,"no387")) {
314 315
			hard_math = 0;
			__asm__("movl %%cr0,%%eax\n\t"
316 317 318 319
				"orl $0xE,%%eax\n\t"
				"movl %%eax,%%cr0\n\t" : : : "ax");
		} else
			checksetup(line);
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
		/*
		 * 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;
}

338 339 340
static void copy_options(char * to, char * from)
{
	char c = ' ';
Linus Torvalds's avatar
Linus Torvalds committed
341
	int len = 0;
342

Linus Torvalds's avatar
Linus Torvalds committed
343 344
	for (;;) {
		if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=")
345
			memory_end = simple_strtoul(from+4, &from, 0);
Linus Torvalds's avatar
Linus Torvalds committed
346 347 348 349 350 351 352 353
		c = *(from++);
		if (!c)
			break;
		if (COMMAND_LINE_SIZE <= ++len)
			break;
		*(to++) = c;
	}
	*to = '\0';
354 355
}

356 357
static void copro_timeout(void)
{
358 359 360 361 362 363 364
	fpu_error = 1;
	timer_table[COPRO_TIMER].expires = jiffies+100;
	timer_active |= 1<<COPRO_TIMER;
	printk("387 failed: trying to reset\n");
	send_sig(SIGFPE, last_task_used_math, 1);
	outb_p(0,0xf1);
	outb_p(0,0xf0);
365 366
}

367
asmlinkage void start_kernel(void)
368
{
369 370 371 372
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
373
	set_call_gate(&default_ldt,lcall7);
374
 	ROOT_DEV = ORIG_ROOT_DEV;
375 376
 	drive_info = DRIVE_INFO;
 	screen_info = SCREEN_INFO;
377
	aux_device_present = AUX_DEVICE_INFO;
378
	memory_end = (1<<20) + (EXT_MEM_K<<10);
379
	memory_end &= PAGE_MASK;
380
	ramdisk_size = RAMDISK_SIZE;
381
	copy_options(command_line,COMMAND_LINE);
382
#ifdef CONFIG_MAX_16M
383 384 385
	if (memory_end > 16*1024*1024)
		memory_end = 16*1024*1024;
#endif
386 387
	if (MOUNT_ROOT_RDONLY)
		root_mountflags |= MS_RDONLY;
388 389
	if ((unsigned long)&end >= (1024*1024)) {
		memory_start = (unsigned long) &end;
390
		low_memory_start = PAGE_SIZE;
391 392 393 394
	} else {
		memory_start = 1024*1024;
		low_memory_start = (unsigned long) &end;
	}
395
	low_memory_start = PAGE_ALIGN(low_memory_start);
396
	memory_start = paging_init(memory_start,memory_end);
Linus Torvalds's avatar
Linus Torvalds committed
397 398
	if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0)
		EISA_bus = 1;
399
	trap_init();
400
	init_IRQ();
401
	sched_init();
402
	parse_options(command_line);
Linus Torvalds's avatar
Linus Torvalds committed
403
	init_modules();
404
#ifdef CONFIG_PROFILE
405 406
	prof_buffer = (unsigned long *) memory_start;
	prof_len = (unsigned long) &end;
407
	prof_len >>= 2;
408 409
	memory_start += prof_len * sizeof(unsigned long);
#endif
410
	memory_start = kmalloc_init(memory_start,memory_end);
411 412
	memory_start = chr_dev_init(memory_start,memory_end);
	memory_start = blk_dev_init(memory_start,memory_end);
Linus Torvalds's avatar
Linus Torvalds committed
413 414
	sti();
	calibrate_delay();
415 416 417
#ifdef CONFIG_INET
	memory_start = net_dev_init(memory_start,memory_end);
#endif
418 419 420
#ifdef CONFIG_SCSI
	memory_start = scsi_dev_init(memory_start,memory_end);
#endif
421
	memory_start = inode_init(memory_start,memory_end);
422
	memory_start = file_table_init(memory_start,memory_end);
Linus Torvalds's avatar
Linus Torvalds committed
423
	memory_start = name_cache_init(memory_start,memory_end);
424
	mem_init(low_memory_start,memory_start,memory_end);
425 426
	buffer_init();
	time_init();
427
	floppy_init();
428
	sock_init();
429 430 431
#ifdef CONFIG_SYSVIPC
	ipc_init();
#endif
432
	sti();
Linus Torvalds's avatar
Linus Torvalds committed
433
	
434 435 436 437 438 439 440 441 442 443 444
	/*
	 * check if exception 16 works correctly.. This is truly evil
	 * code: it disables the high 8 interrupts to make sure that
	 * the irq13 doesn't happen. But as this will lead to a lockup
	 * if no exception16 arrives, it depends on the fact that the
	 * high 8 interrupts will be re-enabled by the next timer tick.
	 * So the irq13 will happen eventually, but the exception 16
	 * should get there first..
	 */
	if (hard_math) {
		unsigned short control_word;
445

446
		printk("Checking 386/387 coupling... ");
447 448 449 450
		timer_table[COPRO_TIMER].expires = jiffies+50;
		timer_table[COPRO_TIMER].fn = copro_timeout;
		timer_active |= 1<<COPRO_TIMER;
		__asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word));
451
		control_word &= 0xffc0;
452
		__asm__("fldcw %0 ; fwait": :"m" (*&control_word));
453 454
		outb_p(inb_p(0x21) | (1 << 2), 0x21);
		__asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait");
455 456
		timer_active &= ~(1<<COPRO_TIMER);
		if (!fpu_error)
457
			printk("Ok, fpu using %s error reporting.\n",
458
				ignore_irq13?"exception 16":"irq13");
459
	}
460 461 462 463 464 465 466
#ifndef CONFIG_MATH_EMULATION
	else {
		printk("No coprocessor found and no math emulation present.\n");
		printk("Giving up.\n");
		for (;;) ;
	}
#endif
Linus Torvalds's avatar
Linus Torvalds committed
467 468 469 470

	system_utsname.machine[1] = '0' + x86;
	printk(linux_banner);

471
	move_to_user_mode();
472
	if (!fork())		/* we count on this going ok */
473 474
		init();
/*
475 476 477 478 479 480
 * task[0] is meant to be used as an "idle" task: it may not sleep, but
 * it might do some general things like count free pages or it could be
 * used to implement a reasonable LRU algorithm for the paging routines:
 * anything that can be useful, but shouldn't take time from the real
 * processes.
 *
481
 * Right now task[0] just does a infinite idle loop.
482
 */
Linus Torvalds's avatar
Linus Torvalds committed
483
	for(;;)
484
		idle();
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
}

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)
{
Linus Torvalds's avatar
Linus Torvalds committed
500
	int pid,i;
501

Linus Torvalds's avatar
Linus Torvalds committed
502
	setup();
Linus Torvalds's avatar
Linus Torvalds committed
503
	sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES);
Linus Torvalds's avatar
Linus Torvalds committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

	#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

Linus Torvalds's avatar
Linus Torvalds committed
520
	(void) open("/dev/tty1",O_RDWR,0);
521 522
	(void) dup(0);
	(void) dup(0);
523

524
	execve("/etc/init",argv_init,envp_init);
525
	execve("/bin/init",argv_init,envp_init);
526
	execve("/sbin/init",argv_init,envp_init);
527 528
	/* if this fails, fall through to original stuff */

Linus Torvalds's avatar
Linus Torvalds committed
529 530 531
	if (!(pid=fork())) {
		close(0);
		if (open("/etc/rc",O_RDONLY,0))
532
			_exit(1);
Linus Torvalds's avatar
Linus Torvalds committed
533
		execve("/bin/sh",argv_rc,envp_rc);
534
		_exit(2);
Linus Torvalds's avatar
Linus Torvalds committed
535 536 537 538 539
	}
	if (pid>0)
		while (pid != wait(&i))
			/* nothing */;
	while (1) {
540
		if ((pid = fork()) < 0) {
541
			printf("Fork failed in init\n\r");
Linus Torvalds's avatar
Linus Torvalds committed
542 543 544 545 546
			continue;
		}
		if (!pid) {
			close(0);close(1);close(2);
			setsid();
Linus Torvalds's avatar
Linus Torvalds committed
547
			(void) open("/dev/tty1",O_RDWR,0);
Linus Torvalds's avatar
Linus Torvalds committed
548 549
			(void) dup(0);
			(void) dup(0);
550
			_exit(execve("/bin/sh",argv,envp));
Linus Torvalds's avatar
Linus Torvalds committed
551 552 553 554 555 556
		}
		while (1)
			if (pid == wait(&i))
				break;
		printf("\n\rchild %d died with code %04x\n\r",pid,i);
		sync();
557
	}
558
	_exit(0);
559
}