Eliminate user_page_limit global symbol.
[pintos-anon] / src / threads / init.c
index dbfd56be94be1af05439bbdaed05784a27581214..8a037058a7358bea829c75153cfdca313570febd 100644 (file)
@@ -9,22 +9,26 @@
 #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 "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"
 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
-/* 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
+/* -q: Power off after kernel tasks complete? */
+bool power_off_when_done;
 
 
-/* Power off after running requested actions? */
-static bool power_off;
+/* -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 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);
+
+static void print_stats (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));
-
-  /* Parse command line. */
-  argv_init ();
+  printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
 
 
-  /* 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 +114,25 @@ 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 ();
+  /* Finish up. */
+  if (reboot_when_done)
+    reboot ();
 
 
-  /* Terminate this thread. */
+  if (power_off_when_done)
+    power_off ();
   thread_exit ();
 }
 \f
   thread_exit ();
 }
 \f
@@ -140,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
@@ -157,118 +167,257 @@ 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;
   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)
         {
-          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;
+    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];
+  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;
-    else if (!strcmp (argv[i], "-D"))
-      fsutil_dump_filesys = 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            Seed random seed to SEED.\n"
-          " -d CLASS[,...]      Enable the given classes of debug messages.\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"
+          "  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"
-          " -D                  Dump complete filesystem contents\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"
           );
           );
-        do_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
 void
-do_power_off (void) 
+power_off (void) 
 {
   const char s[] = "Shutdown";
   const char *p;
 {
   const char s[] = "Shutdown";
   const char *p;
@@ -277,8 +426,30 @@ do_power_off (void)
   filesys_done ();
 #endif
 
   filesys_done ();
 #endif
 
+  print_stats ();
+
   printf ("Powering off...\n");
   printf ("Powering off...\n");
+  serial_flush ();
+
   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 (;;);
 }
+
+/* Print statistics about Pintos execution. */
+static void
+print_stats (void) 
+{
+  timer_print_stats ();
+  thread_print_stats ();
+#ifdef FILESYS
+  disk_print_stats ();
+#endif
+  console_print_stats ();
+  kbd_print_stats ();
+#ifdef USERPROG
+  exception_print_stats ();
+#endif
+}