Eliminate user_page_limit global symbol.
[pintos-anon] / src / threads / init.c
index 4b7835800487b14c27d61a9b7cc5e2a2be7ca903..8a037058a7358bea829c75153cfdca313570febd 100644 (file)
@@ -9,16 +9,17 @@
 #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/timer.h"
 #include "devices/vga.h"
 #include "devices/serial.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 "userprog/process.h"
 #include "threads/thread.h"
 #ifdef USERPROG
 #include "userprog/process.h"
@@ -26,6 +27,8 @@
 #include "userprog/gdt.h"
 #include "userprog/syscall.h"
 #include "userprog/tss.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"
 size_t ram_pages;
 
 /* Page directory with kernel mappings only. */
 size_t ram_pages;
 
 /* Page directory with kernel mappings only. */
-uint32_t *base_page_dir;
+uint32_t *init_page_dir;
 
 #ifdef FILESYS
 
 #ifdef FILESYS
-/* -f: Format the filesystem? */
+/* -f: Format the file system? */
 static bool format_filesys;
 #endif
 
 static bool format_filesys;
 #endif
 
-#ifdef USERPROG
-/* -ex: Initial program to run. */
-static char *initial_program;
-#endif
-
 /* -q: Power off after kernel tasks complete? */
 bool power_off_when_done;
 
 /* -q: Power off after kernel tasks complete? */
 bool power_off_when_done;
 
+/* -r: Reboot after kernel tasks complete? */
+static bool reboot_when_done;
+
+/* -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 char **read_command_line (void);
+static char **parse_options (char **argv);
+static void run_actions (char **argv);
+static void usage (void);
+
 static void print_stats (void);
 
 static void print_stats (void);
 
+
 int main (void) NO_RETURN;
 
 int main (void) NO_RETURN;
 
+/* Pintos main program. */
 int
 main (void)
 {
 int
 main (void)
 {
+  char **argv;
+  
   /* Clear BSS and get machine's RAM size. */  
   ram_init ();
 
   /* Clear BSS and get machine's RAM size. */  
   ram_init ();
 
-  /* Initialize ourselves as a thread so we can use locks. */
-  thread_init ();
+  /* Break command line into arguments and parse options. */
+  argv = read_command_line ();
+  argv = parse_options (argv);
 
 
-  /* Initialize the console so we can use printf(). */
-  vga_init ();
-  serial_init_poll ();
-  console_init ();
+  /* Initialize ourselves as a thread so we can use locks,
+     then enable console locking. */
+  thread_init ();
+  console_init ();  
 
   /* Greet user. */
   printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
 
 
   /* Greet user. */
   printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
 
-  /* Parse command line. */
-  argv_init ();
-
   /* Initialize memory system. */
   /* Initialize memory system. */
-  palloc_init ();
+  palloc_init (user_page_limit);
   malloc_init ();
   paging_init ();
 
   malloc_init ();
   paging_init ();
 
@@ -90,13 +101,11 @@ main (void)
   gdt_init ();
 #endif
 
   gdt_init ();
 #endif
 
-  /* 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 ();
@@ -108,31 +117,23 @@ main (void)
   timer_calibrate ();
 
 #ifdef FILESYS
   timer_calibrate ();
 
 #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);
-      process_wait (process_execute (initial_program));
-    }
-#else
-  /* Run the compiled-in test function. */
-  test ();
-#endif
+  /* Run actions specified on kernel command line. */
+  run_actions (argv);
 
   /* Finish up. */
 
   /* Finish up. */
-  if (power_off_when_done) 
+  if (reboot_when_done)
+    reboot ();
+
+  if (power_off_when_done)
     power_off ();
     power_off ();
-  else 
-    thread_exit ();
+  thread_exit ();
 }
 \f
 /* Clear BSS and obtain RAM size from loader. */
 }
 \f
 /* Clear BSS and obtain RAM size from loader. */
@@ -149,12 +150,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);
+  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
@@ -166,15 +167,17 @@ 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_page (PAL_ASSERT | PAL_ZERO);
+  pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
   pt = NULL;
   for (page = 0; page < ram_pages; page++) 
     {
       uintptr_t paddr = page * PGSIZE;
   pt = NULL;
   for (page = 0; page < ram_pages; page++) 
     {
       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)
         {
@@ -182,101 +185,237 @@ paging_init (void)
           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);
     }
 
   /* 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
     }
 
   /* 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-v3] 3.7.5. */
-  asm volatile ("mov %%cr3, %0" :: "r" (vtop (base_page_dir)));
+     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], "-q"))
-      power_off_when_done = true;
+    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"))
+        power_off_when_done = true;
+      else if (!strcmp (name, "-r"))
+        reboot_when_done = true;
+#ifdef FILESYS
+      else if (!strcmp (name, "-f"))
+        format_filesys = true;
+#endif
+      else if (!strcmp (name, "-rs"))
+        random_init (atoi (value));
+      else if (!strcmp (name, "-mlfqs"))
+        thread_mlfqs = true;
+#ifdef USERPROG
+      else if (!strcmp (name, "-ul"))
+        user_page_limit = atoi (value);
+#endif
+      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
 #ifdef USERPROG
-    else if (!strcmp (argv[i], "-ex")) 
-      initial_program = argv[++i];
-    else if (!strcmp (argv[i], "-ul"))
-      user_page_limit = atoi (argv[++i]);
+  process_wait (process_execute (task));
+#else
+  run_test (task);
 #endif
 #endif
+  printf ("Execution of '%s' complete.\n", task);
+}
+
+/* Executes all of the actions specified in ARGV[]
+   up to the null pointer sentinel. */
+static void
+run_actions (char **argv) 
+{
+  /* 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
-    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;
+      {"ls", 1, fsutil_ls},
+      {"cat", 2, fsutil_cat},
+      {"rm", 2, fsutil_rm},
+      {"extract", 1, fsutil_extract},
+      {"append", 2, fsutil_append},
 #endif
 #endif
-    else if (!strcmp (argv[i], "-u"))
-      {
-        printf (
-          "Kernel options:\n"
-          " -rs SEED            Set random seed to SEED.\n"
+      {NULL, 0, NULL},
+    };
+
+  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
 #ifdef USERPROG
-          " -ex 'PROG [ARG...]' Run PROG, passing the optional arguments.\n"
-          " -ul USER_MAX        Limit user memory to USER_MAX pages.\n"
+          "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
+#else
+          "  run TEST           Run TEST.\n"
 #endif
 #ifdef FILESYS
 #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"
+          "  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
 #endif
-          " -q                  Power off after doing requested actions.\n"
-          " -u                  Print this help message and power off.\n"
           );
           );
-        power_off ();
-      }
-    else 
-      PANIC ("unknown option `%s' (use -u for help)", argv[i]);
+  power_off ();
+}
+
+/* Keyboard control register port. */
+#define CONTROL_REG 0x64
+
+/* Reboots the machine via the keyboard controller. */
+void
+reboot (void)
+{
+  int i;
+
+  printf ("Rebooting...\n");
+
+    /* See [kbd] for details on how to program the keyboard
+     * controller. */
+  for (i = 0; i < 100; i++) 
+    {
+      int j;
+
+      /* Poll keyboard controller's status byte until 
+       * 'input buffer empty' is reported. */
+      for (j = 0; j < 0x10000; j++) 
+        {
+          if ((inb (CONTROL_REG) & 0x02) == 0)   
+            break;
+          timer_udelay (2);
+        }
+
+      timer_udelay (50);
+
+      /* Pulse bit 0 of the output port P2 of the keyboard controller. 
+       * This will reset the CPU. */
+      outb (CONTROL_REG, 0xfe);
+      timer_udelay (50);
+    }
 }
 
 /* Powers down the machine we're running on,
 }
 
 /* Powers down the machine we're running on,
-   as long as we're running on Bochs or qemu. */
+   as long as we're running on Bochs or QEMU. */
 void
 power_off (void) 
 {
 void
 power_off (void) 
 {
@@ -294,6 +433,8 @@ power_off (void)
 
   for (p = s; *p != '\0'; p++)
     outb (0x8900, *p);
 
   for (p = s; *p != '\0'; p++)
     outb (0x8900, *p);
+  asm volatile ("cli; hlt" : : : "memory");
+  printf ("still running...\n");
   for (;;);
 }
 
   for (;;);
 }