Rename ram_pages to init_ram_pages.
[pintos-anon] / src / threads / init.c
index dbfd56be94be1af05439bbdaed05784a27581214..d2d3e087de286cb8f5b48fd960d1568ae2d1cab4 100644 (file)
@@ -9,22 +9,27 @@
 #include <stdlib.h>
 #include <string.h>
 #include "devices/kbd.h"
 #include <stdlib.h>
 #include <string.h>
 #include "devices/kbd.h"
+#include "devices/input.h"
 #include "devices/serial.h"
 #include "devices/serial.h"
+#include "devices/shutdown.h"
 #include "devices/timer.h"
 #include "devices/vga.h"
 #include "devices/timer.h"
 #include "devices/vga.h"
+#include "devices/rtc.h"
 #include "threads/interrupt.h"
 #include "threads/io.h"
 #include "threads/loader.h"
 #include "threads/malloc.h"
 #include "threads/interrupt.h"
 #include "threads/io.h"
 #include "threads/loader.h"
 #include "threads/malloc.h"
-#include "threads/mmu.h"
 #include "threads/palloc.h"
 #include "threads/palloc.h"
-#include "threads/test.h"
+#include "threads/pte.h"
 #include "threads/thread.h"
 #ifdef USERPROG
 #include "threads/thread.h"
 #ifdef USERPROG
+#include "userprog/process.h"
 #include "userprog/exception.h"
 #include "userprog/gdt.h"
 #include "userprog/syscall.h"
 #include "userprog/tss.h"
 #include "userprog/exception.h"
 #include "userprog/gdt.h"
 #include "userprog/syscall.h"
 #include "userprog/tss.h"
+#else
+#include "tests/threads/tests.h"
 #endif
 #ifdef FILESYS
 #include "devices/disk.h"
 #endif
 #ifdef FILESYS
 #include "devices/disk.h"
 #endif
 
 /* Amount of physical memory, in 4 kB pages. */
 #endif
 
 /* Amount of physical memory, in 4 kB pages. */
-size_t ram_pages;
+size_t init_ram_pages;
 
 /* Page directory with kernel mappings only. */
 
 /* Page directory with kernel mappings only. */
-uint32_t *base_page_dir;
+uint32_t *init_page_dir;
 
 #ifdef FILESYS
 
 #ifdef FILESYS
-/* Format the filesystem? */
+/* -f: Format the file system? */
 static bool format_filesys;
 #endif
 
 static bool format_filesys;
 #endif
 
-#ifdef USERPROG
-/* Initial program to run. */
-static char *initial_program;
-#endif
-
-/* Power off after running requested actions? */
-static bool power_off;
+/* -ul: Maximum number of pages to put into palloc's user pool. */
+static size_t user_page_limit = SIZE_MAX;
 
 static void ram_init (void);
 static void paging_init (void);
 
 static void ram_init (void);
 static void paging_init (void);
-static void argv_init (void);
-static void do_power_off (void);
+
+static char **read_command_line (void);
+static char **parse_options (char **argv);
+static void run_actions (char **argv);
+static void usage (void);
 
 int main (void) NO_RETURN;
 
 
 int main (void) NO_RETURN;
 
+/* Pintos main program. */
 int
 main (void)
 {
 int
 main (void)
 {
-  /* Initialize everything needed for printf() first. */
+  char **argv;
+  
+  /* Clear BSS and get machine's RAM size. */  
   ram_init ();
   ram_init ();
+
+  /* Break command line into arguments and parse options. */
+  argv = read_command_line ();
+  argv = parse_options (argv);
+
+  /* Initialize ourselves as a thread so we can use locks,
+     then enable console locking. */
   thread_init ();
   thread_init ();
-  vga_init ();
-  serial_init_poll ();
-  console_init ();
+  console_init ();  
 
   /* Greet user. */
 
   /* Greet user. */
-  printf ("Pintos booting with %'zd kB RAM...\n", ram_pages * (PGSIZE / 1024));
+  printf ("Pintos booting with %'zu kB RAM...\n",
+          init_ram_pages * PGSIZE / 1024);
 
 
-  /* Parse command line. */
-  argv_init ();
-
-  /* Initialize memory system, segments, paging. */
-  palloc_init ();
+  /* Initialize memory system. */
+  palloc_init (user_page_limit);
+  malloc_init ();
   paging_init ();
   paging_init ();
+
+  /* Segmentation. */
 #ifdef USERPROG
   tss_init ();
   gdt_init ();
 #endif
 #ifdef USERPROG
   tss_init ();
   gdt_init ();
 #endif
-  malloc_init ();
-
-  /* Set random seed if argv_init() didn't. */
-  random_init (0);
 
   /* Initialize interrupt handlers. */
   intr_init ();
   timer_init ();
   kbd_init ();
 
   /* Initialize interrupt handlers. */
   intr_init ();
   timer_init ();
   kbd_init ();
+  input_init ();
 #ifdef USERPROG
   exception_init ();
   syscall_init ();
 #ifdef USERPROG
   exception_init ();
   syscall_init ();
@@ -98,31 +107,21 @@ main (void)
   /* Start thread scheduler and enable interrupts. */
   thread_start ();
   serial_init_queue ();
   /* Start thread scheduler and enable interrupts. */
   thread_start ();
   serial_init_queue ();
+  timer_calibrate ();
 
 #ifdef FILESYS
 
 #ifdef FILESYS
-  /* Initialize filesystem. */
+  /* Initialize file system. */
   disk_init ();
   filesys_init (format_filesys);
   disk_init ();
   filesys_init (format_filesys);
-  fsutil_run ();
 #endif
 
   printf ("Boot complete.\n");
   
 #endif
 
   printf ("Boot complete.\n");
   
-#ifdef USERPROG
-  /* Run a user program. */
-  if (initial_program != NULL)
-    {
-      printf ("\nExecuting '%s':\n", initial_program);
-      thread_execute (initial_program); 
-    }
-#else
-  test ();
-#endif
+  /* Run actions specified on kernel command line. */
+  run_actions (argv);
 
 
-  if (power_off) 
-    do_power_off ();
-
-  /* Terminate this thread. */
+  /* Finish up. */
+  shutdown ();
   thread_exit ();
 }
 \f
   thread_exit ();
 }
 \f
@@ -140,12 +139,12 @@ ram_init (void)
   memset (&_start_bss, 0, &_end_bss - &_start_bss);
 
   /* Get RAM size from loader.  See loader.S. */
   memset (&_start_bss, 0, &_end_bss - &_start_bss);
 
   /* Get RAM size from loader.  See loader.S. */
-  ram_pages = *(uint32_t *) ptov (LOADER_RAM_PAGES);
+  init_ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS);
 }
 
 /* Populates the base page directory and page table with the
    kernel virtual mapping, and then sets up the CPU to use the
 }
 
 /* Populates the base page directory and page table with the
    kernel virtual mapping, and then sets up the CPU to use the
-   new page directory.  Points base_page_dir to the page
+   new page directory.  Points init_page_dir to the page
    directory it creates.
 
    At the time this function is called, the active page table
    directory it creates.
 
    At the time this function is called, the active page table
@@ -157,128 +156,214 @@ paging_init (void)
 {
   uint32_t *pd, *pt;
   size_t page;
 {
   uint32_t *pd, *pt;
   size_t page;
+  extern char _start, _end_kernel_text;
 
 
-  pd = base_page_dir = palloc_get (PAL_ASSERT | PAL_ZERO);
+  pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
   pt = NULL;
   pt = NULL;
-  for (page = 0; page < ram_pages; page++) 
+  for (page = 0; page < init_ram_pages; page++)
     {
       uintptr_t paddr = page * PGSIZE;
     {
       uintptr_t paddr = page * PGSIZE;
-      void *vaddr = ptov (paddr);
+      char *vaddr = ptov (paddr);
       size_t pde_idx = pd_no (vaddr);
       size_t pte_idx = pt_no (vaddr);
       size_t pde_idx = pd_no (vaddr);
       size_t pte_idx = pt_no (vaddr);
+      bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
 
       if (pd[pde_idx] == 0)
         {
 
       if (pd[pde_idx] == 0)
         {
-          pt = palloc_get (PAL_ASSERT | PAL_ZERO);
+          pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
           pd[pde_idx] = pde_create (pt);
         }
 
           pd[pde_idx] = pde_create (pt);
         }
 
-      pt[pte_idx] = pte_create_kernel (vaddr, true);
+      pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
     }
 
     }
 
-  asm volatile ("movl %0,%%cr3" :: "r" (vtop (base_page_dir)));
+  /* Store the physical address of the page directory into CR3
+     aka PDBR (page directory base register).  This activates our
+     new page tables immediately.  See [IA32-v2a] "MOV--Move
+     to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
+     of the Page Directory". */
+  asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
 }
 
 }
 
-/* Parses the command line. */
-static void
-argv_init (void) 
+/* Breaks the kernel command line into words and returns them as
+   an argv-like array. */
+static char **
+read_command_line (void) 
 {
 {
-  char *cmd_line, *pos;
-  char *argv[LOADER_CMD_LINE_LEN / 2 + 2];
-  int argc = 0;
+  static char *argv[LOADER_ARGS_LEN / 2 + 1];
+  char *p, *end;
+  int argc;
   int i;
 
   int i;
 
-  /* The command line is made up of null terminated strings
-     followed by an empty string.  Break it up into words. */
-  cmd_line = pos = ptov (LOADER_CMD_LINE);
-  printf ("Kernel command line:");
-  while (pos < cmd_line + LOADER_CMD_LINE_LEN)
+  argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
+  p = ptov (LOADER_ARGS);
+  end = p + LOADER_ARGS_LEN;
+  for (i = 0; i < argc; i++) 
     {
     {
-      ASSERT (argc < LOADER_CMD_LINE_LEN / 2);
-      if (*pos == '\0')
-        break;
-      argv[argc++] = pos;
-      printf (" %s", pos);
-      pos = strchr (pos, '\0') + 1;
+      if (p >= end)
+        PANIC ("command line arguments overflow");
+
+      argv[i] = p;
+      p += strnlen (p, end - p) + 1;
     }
     }
-  printf ("\n");
-  argv[argc] = "";
-  argv[argc + 1] = "";
+  argv[argc] = NULL;
 
 
-  /* Parse the words. */
+  /* Print kernel command line. */
+  printf ("Kernel command line:");
   for (i = 0; i < argc; i++)
   for (i = 0; i < argc; i++)
-    if (!strcmp (argv[i], "-rs")) 
-      random_init (atoi (argv[++i]));
-    else if (!strcmp (argv[i], "-d")) 
-      debug_enable (argv[++i]);
-    else if (!strcmp (argv[i], "-q"))
-      power_off = true;
-#ifdef USERPROG
-    else if (!strcmp (argv[i], "-ex")) 
-      initial_program = argv[++i];
-#endif
+    if (strchr (argv[i], ' ') == NULL)
+      printf (" %s", argv[i]);
+    else
+      printf (" '%s'", argv[i]);
+  printf ("\n");
+
+  return argv;
+}
+
+/* Parses options in ARGV[]
+   and returns the first non-option argument. */
+static char **
+parse_options (char **argv) 
+{
+  for (; *argv != NULL && **argv == '-'; argv++)
+    {
+      char *save_ptr;
+      char *name = strtok_r (*argv, "=", &save_ptr);
+      char *value = strtok_r (NULL, "", &save_ptr);
+      
+      if (!strcmp (name, "-h"))
+        usage ();
+      else if (!strcmp (name, "-q"))
+        shutdown_configure (SHUTDOWN_POWER_OFF);
+      else if (!strcmp (name, "-r"))
+        shutdown_configure (SHUTDOWN_REBOOT);
 #ifdef FILESYS
 #ifdef FILESYS
-  else if (!strcmp (argv[i], "-f"))
-      format_filesys = true;
-    else if (!strcmp (argv[i], "-ci")) 
-      {
-        fsutil_copyin_file = argv[++i]; 
-        fsutil_copyin_size = atoi (argv[++i]); 
-      }
-    else if (!strcmp (argv[i], "-co"))
-      fsutil_copyout_file = argv[++i];
-    else if (!strcmp (argv[i], "-p")) 
-      fsutil_print_file = argv[++i];
-    else if (!strcmp (argv[i], "-r"))
-      fsutil_remove_file = argv[++i];
-    else if (!strcmp (argv[i], "-ls"))
-      fsutil_list_files = true;
-    else if (!strcmp (argv[i], "-D"))
-      fsutil_dump_filesys = true;
+      else if (!strcmp (name, "-f"))
+        format_filesys = true;
 #endif
 #endif
-    else if (!strcmp (argv[i], "-u"))
-      {
-        printf (
-          "Kernel options:\n"
-          " -rs SEED            Seed random seed to SEED.\n"
-          " -d CLASS[,...]      Enable the given classes of debug messages.\n"
+      else if (!strcmp (name, "-rs"))
+        random_init (atoi (value));
+      else if (!strcmp (name, "-mlfqs"))
+        thread_mlfqs = true;
 #ifdef USERPROG
 #ifdef USERPROG
-          " -ex 'PROG [ARG...]' Run PROG, passing the optional arguments.\n"
+      else if (!strcmp (name, "-ul"))
+        user_page_limit = atoi (value);
 #endif
 #endif
-#ifdef FILESYS
-          " -f                  Format the filesystem disk (hdb or hd0:1).\n"
-          " -ci FILENAME SIZE   Copy SIZE bytes from the scratch disk (hdc\n"
-          "                     or hd1:0) into the filesystem as FILENAME\n"
-          " -co FILENAME        Copy FILENAME to the scratch disk, with\n"
-          "                     size at start of sector 0 and data afterward\n"
-          " -p FILENAME         Print the contents of FILENAME\n"
-          " -r FILENAME         Delete FILENAME\n"
-          " -ls                 List the files in the filesystem\n"
-          " -D                  Dump complete filesystem contents\n"
+      else
+        PANIC ("unknown option `%s' (use -h for help)", name);
+    }
+
+  /* Initialize the random number generator based on the system
+     time.  This has no effect if an "-rs" option was specified.
+
+     When running under Bochs, this is not enough by itself to
+     get a good seed value, because the pintos script sets the
+     initial time to a predictable value, not to the local time,
+     for reproducibility.  To fix this, give the "-r" option to
+     the pintos script to request real-time execution. */
+  random_init (rtc_get_time ());
+  
+  return argv;
+}
+
+/* Runs the task specified in ARGV[1]. */
+static void
+run_task (char **argv)
+{
+  const char *task = argv[1];
+  
+  printf ("Executing '%s':\n", task);
+#ifdef USERPROG
+  process_wait (process_execute (task));
+#else
+  run_test (task);
 #endif
 #endif
-          " -q                  Power off after doing requested actions.\n"
-          " -u                  Print this help message and power off.\n"
-          );
-        do_power_off ();
-      }
-    else 
-      PANIC ("unknown option `%s' (use -u for help)", argv[i]);
+  printf ("Execution of '%s' complete.\n", task);
 }
 
 }
 
-/* Powers down the machine we're running on,
-   as long as we're running on Bochs or qemu. */
-void
-do_power_off (void
+/* Executes all of the actions specified in ARGV[]
+   up to the null pointer sentinel. */
+static void
+run_actions (char **argv
 {
 {
-  const char s[] = "Shutdown";
-  const char *p;
+  /* An action. */
+  struct action 
+    {
+      char *name;                       /* Action name. */
+      int argc;                         /* # of args, including action name. */
+      void (*function) (char **argv);   /* Function to execute action. */
+    };
 
 
+  /* Table of supported actions. */
+  static const struct action actions[] = 
+    {
+      {"run", 2, run_task},
 #ifdef FILESYS
 #ifdef FILESYS
-  filesys_done ();
+      {"ls", 1, fsutil_ls},
+      {"cat", 2, fsutil_cat},
+      {"rm", 2, fsutil_rm},
+      {"extract", 1, fsutil_extract},
+      {"append", 2, fsutil_append},
 #endif
 #endif
+      {NULL, 0, NULL},
+    };
 
 
-  printf ("Powering off...\n");
-  for (p = s; *p != '\0'; p++)
-    outb (0x8900, *p);
-  for (;;);
+  while (*argv != NULL)
+    {
+      const struct action *a;
+      int i;
+
+      /* Find action name. */
+      for (a = actions; ; a++)
+        if (a->name == NULL)
+          PANIC ("unknown action `%s' (use -h for help)", *argv);
+        else if (!strcmp (*argv, a->name))
+          break;
+
+      /* Check for required arguments. */
+      for (i = 1; i < a->argc; i++)
+        if (argv[i] == NULL)
+          PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
+
+      /* Invoke action and advance. */
+      a->function (argv);
+      argv += a->argc;
+    }
+  
+}
+
+/* Prints a kernel command line help message and powers off the
+   machine. */
+static void
+usage (void)
+{
+  printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
+          "Options must precede actions.\n"
+          "Actions are executed in the order specified.\n"
+          "\nAvailable actions:\n"
+#ifdef USERPROG
+          "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
+#else
+          "  run TEST           Run TEST.\n"
+#endif
+#ifdef FILESYS
+          "  ls                 List files in the root directory.\n"
+          "  cat FILE           Print FILE to the console.\n"
+          "  rm FILE            Delete FILE.\n"
+          "Use these actions indirectly via `pintos' -g and -p options:\n"
+          "  extract            Untar from scratch disk into file system.\n"
+          "  append FILE        Append FILE to tar file on scratch disk.\n"
+#endif
+          "\nOptions:\n"
+          "  -h                 Print this help message and power off.\n"
+          "  -q                 Power off VM after actions or on panic.\n"
+          "  -r                 Reboot after actions.\n"
+          "  -f                 Format file system disk during startup.\n"
+          "  -rs=SEED           Set random number seed to SEED.\n"
+          "  -mlfqs             Use multi-level feedback queue scheduler.\n"
+#ifdef USERPROG
+          "  -ul=COUNT          Limit user memory to COUNT pages.\n"
+#endif
+          );
+  shutdown_power_off ();
 }
 }