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