New option -r to reboot the VM after actions are complete.
[pintos-anon] / src / threads / init.c
1 #include "threads/init.h"
2 #include <console.h>
3 #include <debug.h>
4 #include <limits.h>
5 #include <random.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include "devices/kbd.h"
12 #include "devices/input.h"
13 #include "devices/serial.h"
14 #include "devices/timer.h"
15 #include "devices/vga.h"
16 #include "devices/rtc.h"
17 #include "threads/interrupt.h"
18 #include "threads/io.h"
19 #include "threads/loader.h"
20 #include "threads/malloc.h"
21 #include "threads/palloc.h"
22 #include "threads/pte.h"
23 #include "threads/thread.h"
24 #ifdef USERPROG
25 #include "userprog/process.h"
26 #include "userprog/exception.h"
27 #include "userprog/gdt.h"
28 #include "userprog/syscall.h"
29 #include "userprog/tss.h"
30 #else
31 #include "tests/threads/tests.h"
32 #endif
33 #ifdef FILESYS
34 #include "devices/disk.h"
35 #include "filesys/filesys.h"
36 #include "filesys/fsutil.h"
37 #endif
38
39 /* Amount of physical memory, in 4 kB pages. */
40 size_t ram_pages;
41
42 /* Page directory with kernel mappings only. */
43 uint32_t *base_page_dir;
44
45 #ifdef FILESYS
46 /* -f: Format the file system? */
47 static bool format_filesys;
48 #endif
49
50 /* -q: Power off after kernel tasks complete? */
51 bool power_off_when_done;
52
53 /* -r: Reboot after kernel tasks complete? */
54 static bool reboot_when_done;
55
56 static void ram_init (void);
57 static void paging_init (void);
58
59 static char **read_command_line (void);
60 static char **parse_options (char **argv);
61 static void run_actions (char **argv);
62 static void usage (void);
63
64 static void print_stats (void);
65
66
67 int main (void) NO_RETURN;
68
69 /* Pintos main program. */
70 int
71 main (void)
72 {
73   char **argv;
74   
75   /* Clear BSS and get machine's RAM size. */  
76   ram_init ();
77
78   /* Break command line into arguments and parse options. */
79   argv = read_command_line ();
80   argv = parse_options (argv);
81
82   /* Initialize ourselves as a thread so we can use locks,
83      then enable console locking. */
84   thread_init ();
85   console_init ();  
86
87   /* Greet user. */
88   printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
89
90   /* Initialize memory system. */
91   palloc_init ();
92   malloc_init ();
93   paging_init ();
94
95   /* Segmentation. */
96 #ifdef USERPROG
97   tss_init ();
98   gdt_init ();
99 #endif
100
101   /* Initialize interrupt handlers. */
102   intr_init ();
103   timer_init ();
104   kbd_init ();
105   input_init ();
106 #ifdef USERPROG
107   exception_init ();
108   syscall_init ();
109 #endif
110
111   /* Start thread scheduler and enable interrupts. */
112   thread_start ();
113   serial_init_queue ();
114   timer_calibrate ();
115
116 #ifdef FILESYS
117   /* Initialize file system. */
118   disk_init ();
119   filesys_init (format_filesys);
120 #endif
121
122   printf ("Boot complete.\n");
123   
124   /* Run actions specified on kernel command line. */
125   run_actions (argv);
126
127   /* Finish up. */
128   if (reboot_when_done)
129     reboot ();
130
131   if (power_off_when_done)
132     power_off ();
133   thread_exit ();
134 }
135 \f
136 /* Clear BSS and obtain RAM size from loader. */
137 static void
138 ram_init (void) 
139 {
140   /* The "BSS" is a segment that should be initialized to zeros.
141      It isn't actually stored on disk or zeroed by the kernel
142      loader, so we have to zero it ourselves.
143
144      The start and end of the BSS segment is recorded by the
145      linker as _start_bss and _end_bss.  See kernel.lds. */
146   extern char _start_bss, _end_bss;
147   memset (&_start_bss, 0, &_end_bss - &_start_bss);
148
149   /* Get RAM size from loader.  See loader.S. */
150   ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS);
151 }
152
153 /* Populates the base page directory and page table with the
154    kernel virtual mapping, and then sets up the CPU to use the
155    new page directory.  Points base_page_dir to the page
156    directory it creates.
157
158    At the time this function is called, the active page table
159    (set up by loader.S) only maps the first 4 MB of RAM, so we
160    should not try to use extravagant amounts of memory.
161    Fortunately, there is no need to do so. */
162 static void
163 paging_init (void)
164 {
165   uint32_t *pd, *pt;
166   size_t page;
167   extern char _start, _end_kernel_text;
168
169   pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
170   pt = NULL;
171   for (page = 0; page < ram_pages; page++) 
172     {
173       uintptr_t paddr = page * PGSIZE;
174       char *vaddr = ptov (paddr);
175       size_t pde_idx = pd_no (vaddr);
176       size_t pte_idx = pt_no (vaddr);
177       bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
178
179       if (pd[pde_idx] == 0)
180         {
181           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
182           pd[pde_idx] = pde_create (pt);
183         }
184
185       pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
186     }
187
188   /* Store the physical address of the page directory into CR3
189      aka PDBR (page directory base register).  This activates our
190      new page tables immediately.  See [IA32-v2a] "MOV--Move
191      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
192      of the Page Directory". */
193   asm volatile ("movl %0, %%cr3" : : "r" (vtop (base_page_dir)));
194 }
195
196 /* Breaks the kernel command line into words and returns them as
197    an argv-like array. */
198 static char **
199 read_command_line (void) 
200 {
201   static char *argv[LOADER_ARGS_LEN / 2 + 1];
202   char *p, *end;
203   int argc;
204   int i;
205
206   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
207   p = ptov (LOADER_ARGS);
208   end = p + LOADER_ARGS_LEN;
209   for (i = 0; i < argc; i++) 
210     {
211       if (p >= end)
212         PANIC ("command line arguments overflow");
213
214       argv[i] = p;
215       p += strnlen (p, end - p) + 1;
216     }
217   argv[argc] = NULL;
218
219   /* Print kernel command line. */
220   printf ("Kernel command line:");
221   for (i = 0; i < argc; i++)
222     if (strchr (argv[i], ' ') == NULL)
223       printf (" %s", argv[i]);
224     else
225       printf (" '%s'", argv[i]);
226   printf ("\n");
227
228   return argv;
229 }
230
231 /* Parses options in ARGV[]
232    and returns the first non-option argument. */
233 static char **
234 parse_options (char **argv) 
235 {
236   for (; *argv != NULL && **argv == '-'; argv++)
237     {
238       char *save_ptr;
239       char *name = strtok_r (*argv, "=", &save_ptr);
240       char *value = strtok_r (NULL, "", &save_ptr);
241       
242       if (!strcmp (name, "-h"))
243         usage ();
244       else if (!strcmp (name, "-q"))
245         power_off_when_done = true;
246       else if (!strcmp (name, "-r"))
247         reboot_when_done = true;
248 #ifdef FILESYS
249       else if (!strcmp (name, "-f"))
250         format_filesys = true;
251 #endif
252       else if (!strcmp (name, "-rs"))
253         random_init (atoi (value));
254       else if (!strcmp (name, "-mlfqs"))
255         thread_mlfqs = true;
256 #ifdef USERPROG
257       else if (!strcmp (name, "-ul"))
258         user_page_limit = atoi (value);
259 #endif
260       else
261         PANIC ("unknown option `%s' (use -h for help)", name);
262     }
263
264   /* Initialize the random number generator based on the system
265      time.  This has no effect if an "-rs" option was specified.
266
267      When running under Bochs, this is not enough by itself to
268      get a good seed value, because the pintos script sets the
269      initial time to a predictable value, not to the local time,
270      for reproducibility.  To fix this, give the "-r" option to
271      the pintos script to request real-time execution. */
272   random_init (rtc_get_time ());
273   
274   return argv;
275 }
276
277 /* Runs the task specified in ARGV[1]. */
278 static void
279 run_task (char **argv)
280 {
281   const char *task = argv[1];
282   
283   printf ("Executing '%s':\n", task);
284 #ifdef USERPROG
285   process_wait (process_execute (task));
286 #else
287   run_test (task);
288 #endif
289   printf ("Execution of '%s' complete.\n", task);
290 }
291
292 /* Executes all of the actions specified in ARGV[]
293    up to the null pointer sentinel. */
294 static void
295 run_actions (char **argv) 
296 {
297   /* An action. */
298   struct action 
299     {
300       char *name;                       /* Action name. */
301       int argc;                         /* # of args, including action name. */
302       void (*function) (char **argv);   /* Function to execute action. */
303     };
304
305   /* Table of supported actions. */
306   static const struct action actions[] = 
307     {
308       {"run", 2, run_task},
309 #ifdef FILESYS
310       {"ls", 1, fsutil_ls},
311       {"cat", 2, fsutil_cat},
312       {"rm", 2, fsutil_rm},
313       {"extract", 1, fsutil_extract},
314       {"append", 2, fsutil_append},
315 #endif
316       {NULL, 0, NULL},
317     };
318
319   while (*argv != NULL)
320     {
321       const struct action *a;
322       int i;
323
324       /* Find action name. */
325       for (a = actions; ; a++)
326         if (a->name == NULL)
327           PANIC ("unknown action `%s' (use -h for help)", *argv);
328         else if (!strcmp (*argv, a->name))
329           break;
330
331       /* Check for required arguments. */
332       for (i = 1; i < a->argc; i++)
333         if (argv[i] == NULL)
334           PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
335
336       /* Invoke action and advance. */
337       a->function (argv);
338       argv += a->argc;
339     }
340   
341 }
342
343 /* Prints a kernel command line help message and powers off the
344    machine. */
345 static void
346 usage (void)
347 {
348   printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
349           "Options must precede actions.\n"
350           "Actions are executed in the order specified.\n"
351           "\nAvailable actions:\n"
352 #ifdef USERPROG
353           "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
354 #else
355           "  run TEST           Run TEST.\n"
356 #endif
357 #ifdef FILESYS
358           "  ls                 List files in the root directory.\n"
359           "  cat FILE           Print FILE to the console.\n"
360           "  rm FILE            Delete FILE.\n"
361           "Use these actions indirectly via `pintos' -g and -p options:\n"
362           "  extract            Untar from scratch disk into file system.\n"
363           "  append FILE        Append FILE to tar file on scratch disk.\n"
364 #endif
365           "\nOptions:\n"
366           "  -h                 Print this help message and power off.\n"
367           "  -q                 Power off VM after actions or on panic.\n"
368           "  -r                 Reboot after actions.\n"
369           "  -f                 Format file system disk during startup.\n"
370           "  -rs=SEED           Set random number seed to SEED.\n"
371           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
372 #ifdef USERPROG
373           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
374 #endif
375           );
376   power_off ();
377 }
378
379
380 /* Reboots the machine we're running on. */
381 void
382 reboot (void)
383 {
384     int i;
385
386     printf ("Rebooting...\n");
387
388     /* based on reboot.c code by Osamu Tomita <tomita@cinet.co.jp>
389      * See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html */
390     for (i = 0; i < 100; i++) {
391         int j;
392
393         /* Poll keyboard controller's status byte until 
394          * 'input buffer empty' is reported, so it's ok to write */
395         for (j = 0; j < 0x10000; j++) 
396           {
397             if ((inb (0x64) & 0x02) == 0)   
398               break;
399             timer_usleep (2);
400           }
401
402         timer_usleep (50);
403
404         /* Pulse bit 0 of the output port P2 of the keyboard controller. 
405          * This will reset the CPU. */
406         outb (0x64, 0xfe);
407         timer_usleep (50);
408     }
409 }
410
411 /* Powers down the machine we're running on,
412    as long as we're running on Bochs or QEMU. */
413 void
414 power_off (void) 
415 {
416   const char s[] = "Shutdown";
417   const char *p;
418
419 #ifdef FILESYS
420   filesys_done ();
421 #endif
422
423   print_stats ();
424
425   printf ("Powering off...\n");
426   serial_flush ();
427
428   for (p = s; *p != '\0'; p++)
429     outb (0x8900, *p);
430   asm volatile ("cli; hlt" : : : "memory");
431   printf ("still running...\n");
432   for (;;);
433 }
434
435 /* Print statistics about Pintos execution. */
436 static void
437 print_stats (void) 
438 {
439   timer_print_stats ();
440   thread_print_stats ();
441 #ifdef FILESYS
442   disk_print_stats ();
443 #endif
444   console_print_stats ();
445   kbd_print_stats ();
446 #ifdef USERPROG
447   exception_print_stats ();
448 #endif
449 }