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