/*
 * linux/init/main.c
 *
 * (C) 1991 Linus Torvalds
 */

#define __LIBRARY__		// ñΪ˰unistd.h еǶϢ
#include <unistd.h>		// *.h ͷļڵĬĿ¼include/ڴоͲȷָλá
// UNIX ı׼ͷļҪָڵĿ¼˫ס
// ׼ųļ˸ַųͣ˸ֺ
// __LIBRARY__򻹰ϵͳúźǶ_syscall0()ȡ
#include <time.h>		// ʱͷļҪtm ṹһЩйʱĺԭΡ

/*
 * 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.
 */
/*
 * ҪЩǶ - ں˿ռ䴴(forking)ûдʱƣCOPY ON WRITE!!!
 * ֱһִexecve áԶջܴ⡣ķfork()֮main()ʹ
 * κζջ˾Ͳк - ζfork ҲҪʹǶĴ룬ڴfork()˳
 * ʱҪʹöջˡ
 * ʵֻpause fork ҪʹǶʽԱ֤main()вŪҶջͬʱ
 * һЩ
 */
static inline
_syscall0 (int, fork)		// unistd.h еǶ롣Ƕʽ
	// Linux ϵͳж0x80жϵͳõ
	// ڡʵint fork()ϵͳá
	// syscall0 0 ʾ޲1 ʾ1 
     static inline _syscall0 (int, pause)	// int pause()ϵͳã̵ִͣУֱ
	// յһźš
     static inline _syscall1 (int, setup, void *, BIOS)	// int setup(void * BIOS)ϵͳã
	// linux ʼбã
     static inline _syscall0 (int, sync)	// int sync()ϵͳãļϵͳ
#include <linux/tty.h>		// tty ͷļйtty_ioͨŷĲ
#include <linux/sched.h>	// ȳͷļṹtask_struct1 ʼ
	// ݡһЩԺʽйúͻȡ
	// Ƕʽຯ
#include <linux/head.h>		// head ͷļ˶ļ򵥽ṹͼѡ
#include <asm/system.h>		// ϵͳͷļԺʽйû޸
	// /жŵȵǶʽӳ
#include <asm/io.h>		// io ͷļԺǶʽio ˿ڲĺ
#include <stddef.h>		// ׼ͷļNULL, offsetof(TYPE, MEMBER)
#include <stdarg.h>		// ׼ͷļԺʽбҪ˵-
	// (va_list)(va_start, va_arg va_end)vsprintf
	// vprintfvfprintf
#include <unistd.h>
#include <fcntl.h>		// ļͷļļĲƳŵĶ塣
#include <sys/types.h>		// ͷļ˻ϵͳ͡
#include <linux/fs.h>		// ļϵͳͷļļṹfile,buffer_head,m_inode ȣ
     static char printbuf[1024];	// ַ̬顣

     extern int vsprintf ();	// ͸ʽһַУkernel/vsprintf.c92 У
     extern void init (void);	// ԭΣʼ168 У
     extern void blk_dev_init (void);	// 豸ʼӳkernel/blk_drv/ll_rw_blk.c,157 У
     extern void chr_dev_init (void);	// ַ豸ʼkernel/chr_drv/tty_io.c, 347 У
     extern void hd_init (void);	// Ӳ̳ʼkernel/blk_drv/hd.c, 343 У
     extern void floppy_init (void);	// ʼkernel/blk_drv/floppy.c, 457 У
     extern void mem_init (long start, long end);	// ڴʼmm/memory.c, 399 У
     extern long rd_init (long mem_start, int length);	//̳ʼ(kernel/blk_drv/ramdisk.c,52)
     extern long kernel_mktime (struct tm *tm);	// ںʱ䣨룩
     extern long startup_time;	// ںʱ䣨ʱ䣩룩

	/*
	 * This is set up by the setup-routine at boot-time
	 */
	/*
	 * Щsetup.s ʱõģμ2 2.3.1 еı2.1
	 */
#define EXT_MEM_K (*(unsigned short *)0x90002)	// 1M ԺչڴСKB
#define DRIVE_INFO (*(struct drive_info *)0x90080)	// Ӳַ̲
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)	// ļϵͳ豸š

	/*
	 * Yeah, yeah, it's ugly, but I cannot find how to do this correctly
	 * and this seems to work. I anybody has more info on the real-time
	 * clock I'd be interested. Most of this was trial and error, and some
	 * bios-listing reading. Urghh.
	 */
	/*
	 * ǰǰγܲҲ֪ȷʵ֣ҺС
	 * ʵʱʱӸϣҺܸȤЩ̽ģԼһЩbios 򣬺ǣ
	 */

#define CMOS_READ(addr) ({ \	// κȡCMOS ʵʱʱϢ
outb_p (0x80 | addr, 0x70);
\				// 0x70 д˿ںţ0x80|addr ҪȡCMOS ڴַ
  inb_p (0x71);
\				// 0x71 Ƕ˿ںš
}

)
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)	// BCD ת֡
     static void time_init (void)	// ӳȡCMOS ʱӣÿʱ??startup_time()
     {
       struct tm time;

       do
	 {
	   time.tm_sec = CMOS_READ (0);	// μCMOS ڴб
	   time.tm_min = CMOS_READ (2);
	   time.tm_hour = CMOS_READ (4);
	   time.tm_mday = CMOS_READ (7);
	   time.tm_mon = CMOS_READ (8);
	   time.tm_year = CMOS_READ (9);
	 }
       while (time.tm_sec != CMOS_READ (0));
       BCD_TO_BIN (time.tm_sec);
       BCD_TO_BIN (time.tm_min);
       BCD_TO_BIN (time.tm_hour);
       BCD_TO_BIN (time.tm_mday);
       BCD_TO_BIN (time.tm_mon);
       BCD_TO_BIN (time.tm_year);
       time.tm_mon--;
       startup_time = kernel_mktime (&time);
     }

static long memory_end = 0;	// еڴ棨ֽ
static long buffer_memory_end = 0;	// ٻĩ˵ַ
static long main_memory_start = 0;	// ڴ棨ڷҳʼλá

struct drive_info
{
  char dummy[32];
}

drive_info;			// ڴӲ̲Ϣ

void main (void)		/* This really IS void, no error here. */
{				/* The startup routine assumes (well, ...) this */
  /* ȷʵvoidûstartup (head.s)оġ */
  // μhead.s 136 пʼļд롣
  /*
   * Interrupts are still disabled. Do necessary setups, then
   * enable them
   */
  /*
   * ʱжԱֹţҪúͽ俪
   */
  // δڱ棺
  // 豸 ??ROOT_DEV ٻĩ˵ַ??buffer_memory_end
  // ڴ??memory_endڴ濪ʼַ ??main_memory_start
  ROOT_DEV = ORIG_ROOT_DEV;
  drive_info = DRIVE_INFO;
  memory_end = (1 << 20) + (EXT_MEM_K << 10);	// ڴС=1Mb ֽ+չڴ(k)*1024 ֽڡ
  memory_end &= 0xfffff000;	// Բ4Kb1 ҳڴ
  if (memory_end > 16 * 1024 * 1024)	// ڴ泬16Mb16Mb ơ
    memory_end = 16 * 1024 * 1024;
  if (memory_end > 12 * 1024 * 1024)	// ڴ>12Mbûĩ=4Mb
    buffer_memory_end = 4 * 1024 * 1024;
  else if (memory_end > 6 * 1024 * 1024)	// ڴ>6Mbûĩ=2Mb
    buffer_memory_end = 2 * 1024 * 1024;
  else
    buffer_memory_end = 1 * 1024 * 1024;	// ûĩ=1Mb
  main_memory_start = buffer_memory_end;	// ڴʼλ=ĩˣ
#ifdef RAMDISK			// ̣ڴ潫١
  main_memory_start += rd_init (main_memory_start, RAMDISK * 1024);
#endif
  // ں˽зĳʼĶʱøŵõĳȥʵڿ
  // ȥˣȷһţһʼ -- Ǿ̸֮?
  mem_init (main_memory_start, memory_end);
  trap_init ();			// ţӲжʼkernel/traps.c181 У
  blk_dev_init ();		// 豸ʼ kernel/blk_dev/ll_rw_blk.c157 У
  chr_dev_init ();		// ַ豸ʼ kernel/chr_dev/tty_io.c347 У
  tty_init ();			// tty ʼ kernel/chr_dev/tty_io.c105 У
  time_init ();			// ÿʱ??startup_time76 У
  sched_init ();		// ȳʼ(0 tr, ldtr) kernel/sched.c385
  buffer_init (buffer_memory_end);	// ʼڴȡfs/buffer.c348
  hd_init ();			// Ӳ̳ʼ kernel/blk_dev/hd.c343 У
  floppy_init ();		// ʼ kernel/blk_dev/floppy.c457 У
  sti ();			// гʼˣжϡ
  // ͨڶջõĲжϷָл0
  move_to_user_mode ();		// Ƶûģʽ include/asm/system.h1 У
  if (!fork ())
    {				/* we count on this going ok */
      init ();
    }
  /*
   * NOTE!! For any other task 'pause()' would mean we have to get a
   * signal to awaken, but task0 is the sole exception (see 'schedule()')
   * as task 0 gets activated at every idle moment (when no other tasks
   * can run). For task0 'pause()' just means we go check if some other
   * task can run, and if not we return here.
   */
  /* ע!! κ'pause()'ζǱȴյһźŲŻ᷵
   * ؾ̬0task0Ψһμ'schedule()'Ϊ0 
   * κοʱﶼᱻûʱ˶0'pause()'ζ
   * Ƿ鿴ǷУûеĻǾͻصһֱѭִ'pause()'
   */
  for (;;)
    pause ();
}

static int printf (const char *fmt, ...)
	// ʽϢ׼豸stdout(1)ָĻʾ'*fmt'ָ
	// õĸʽμֱ׼C 鼮ӳvsprintf ʹõһӡ
	// óʹvsprintf()ʽַprintbuf Ȼwrite()
	// ׼豸1--stdout
{
  va_list args;
  int i;

    va_start (args, fmt);
    write (1, printbuf, i = vsprintf (printbuf, fmt, args));
    va_end (args);
    return i;
}

static char *argv_rc[] =
{
"/bin/sh", NULL};		// ִгʱַ顣
static char *envp_rc[] =
{
"HOME=/", NULL};		// ִгʱĻַ顣

static char *argv[] =
{
"-/bin/sh", NULL};		// ͬϡ
static char *envp[] =
{
"HOME=/usr/root", NULL};

void init (void)
{
  int pid, i;

  // ȡӲ̲Ϣ̺Ͱװļϵͳ豸
  // ú25 ϵĺ궨ģӦsys_setup()kernel/blk_drv/hd.c,71 С
    setup ((void *) &drive_info);
    (void) open ("/dev/tty0", O_RDWR, 0);	// öдʷʽ豸/dev/tty0
  // Ӧն˿̨
  // صľ0 -- stdin ׼豸
    (void) dup (0);		// ƾ1  -- stdout ׼豸
    (void) dup (0);		// ƾ2  -- stderr ׼豸
    printf ("%d buffers = %d bytes buffer space\n\r", NR_BUFFERS, NR_BUFFERS * BLOCK_SIZE);	// ӡֽÿ1024 ֽڡ
    printf ("Free mem: %d bytes\n\r", memory_end - main_memory_start);	//ڴֽ
  // fork()ڴһӽ()ڱӽ̣fork()0 ֵ
  // ԭ()ӽ̵Ľ̺š180-184 ӽִеݡӽ
  // ر˾0(stdin)ֻʽ/etc/rc ļִ/bin/sh 
  // ֱargv_rc envp_rc μ
  if (!(pid = fork ()))
    {
      close (0);
      if (open ("/etc/rc", O_RDONLY, 0))
	_exit (1);		// ļʧܣ˳(/lib/_exit.c,10)
      execve ("/bin/sh", argv_rc, envp_rc);	// װ/bin/sh ִС
      _exit (2);		// execve()ִʧ˳(2,ļĿ¼ڡ)
    }
  // Ǹִе䡣wait()ǵȴӽֹֹͣ䷵ֵӦӽ̵Ľ̺(pid)
  // Ǹ̵ȴӽ̵Ľ&i Ǵŷ״̬Ϣλáwait()ֵ
  // ӽ̺ţȴ
  if (pid > 0)
    while (pid != wait (&i))
      /* nothing */ ;
  // ִе˵մӽ̵ִֹֹͣˡѭٴһӽ̣
  // ʾʼ򴴽ӽʧܡϢִСӽ̹ر
  // ǰľ(stdin, stdout, stderr)´һỰýţȻ´
  // /dev/tty0 ΪstdinƳstdout stderrٴִϵͳͳ/bin/shִ
  // ѡõĲͻѡһף165-167 УȻ󸸽ٴwait()ȴ
  // ӽִֹͣУڱ׼ʾϢӽpid ֹͣУiȻ
  // ȥγɡѭ
  while (1)
    {
      if ((pid = fork ()) < 0)
	{
	  printf ("Fork failed in init\r\n");
	  continue;
	}
      if (!pid)
	{
	  close (0);
	  close (1);
	  close (2);
	  setsid ();
	  (void) open ("/dev/tty0", 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);			/* NOTE! _exit, not exit() */
}
