Enclose kernel command line arguments that contain spaces in quotes
[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/palloc.h"
20 #include "threads/pte.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 FILESYS
49 /* -f: Format the file system? */
50 static bool format_filesys;
51 #endif
52
53 /* -q: Power off after kernel tasks complete? */
54 bool power_off_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   /* Initialize ourselves as a thread so we can use locks. */
79   thread_init ();
80
81   /* Initialize the console so we can use printf(). */
82   vga_init ();
83   serial_init_poll ();
84   console_init ();
85
86   /* Greet user. */
87   printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
88
89   /* Break command line into arguments and parse options. */
90   argv = read_command_line ();
91   argv = parse_options (argv);
92
93   /* Set random seed if parse_options() didn't. */
94   random_init (0);
95
96   /* Initialize memory system. */
97   palloc_init ();
98   malloc_init ();
99   paging_init ();
100
101   /* Segmentation. */
102 #ifdef USERPROG
103   tss_init ();
104   gdt_init ();
105 #endif
106
107   /* Initialize interrupt handlers. */
108   intr_init ();
109   timer_init ();
110   kbd_init ();
111 #ifdef USERPROG
112   exception_init ();
113   syscall_init ();
114 #endif
115
116   /* Start thread scheduler and enable interrupts. */
117   thread_start ();
118   serial_init_queue ();
119   timer_calibrate ();
120
121 #ifdef FILESYS
122   /* Initialize file system. */
123   disk_init ();
124   filesys_init (format_filesys);
125 #endif
126
127   printf ("Boot complete.\n");
128   
129   /* Run actions specified on kernel command line. */
130   run_actions (argv);
131
132   /* Finish up. */
133   if (power_off_when_done)
134     power_off ();
135   thread_exit ();
136 }
137 \f
138 /* Clear BSS and obtain RAM size from loader. */
139 static void
140 ram_init (void) 
141 {
142   /* The "BSS" is a segment that should be initialized to zeros.
143      It isn't actually stored on disk or zeroed by the kernel
144      loader, so we have to zero it ourselves.
145
146      The start and end of the BSS segment is recorded by the
147      linker as _start_bss and _end_bss.  See kernel.lds. */
148   extern char _start_bss, _end_bss;
149   memset (&_start_bss, 0, &_end_bss - &_start_bss);
150
151   /* Get RAM size from loader.  See loader.S. */
152   ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS);
153 }
154
155 /* Populates the base page directory and page table with the
156    kernel virtual mapping, and then sets up the CPU to use the
157    new page directory.  Points base_page_dir to the page
158    directory it creates.
159
160    At the time this function is called, the active page table
161    (set up by loader.S) only maps the first 4 MB of RAM, so we
162    should not try to use extravagant amounts of memory.
163    Fortunately, there is no need to do so. */
164 static void
165 paging_init (void)
166 {
167   uint32_t *pd, *pt;
168   size_t page;
169
170   pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
171   pt = NULL;
172   for (page = 0; page < ram_pages; page++) 
173     {
174       uintptr_t paddr = page * PGSIZE;
175       void *vaddr = ptov (paddr);
176       size_t pde_idx = pd_no (vaddr);
177       size_t pte_idx = pt_no (vaddr);
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, true);
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 #ifdef FILESYS
247       else if (!strcmp (name, "-f"))
248         format_filesys = true;
249 #endif
250       else if (!strcmp (name, "-rs"))
251         random_init (atoi (value));
252       else if (!strcmp (name, "-mlfqs"))
253         enable_mlfqs = true;
254 #ifdef USERPROG
255       else if (!strcmp (name, "-ul"))
256         user_page_limit = atoi (value);
257 #endif
258       else
259         PANIC ("unknown option `%s' (use -h for help)", name);
260     }
261   
262   return argv;
263 }
264
265 /* Runs the task specified in ARGV[1]. */
266 static void
267 run_task (char **argv)
268 {
269   const char *task = argv[1];
270   
271   printf ("Executing '%s':\n", task);
272 #ifdef USERPROG
273   process_wait (process_execute (task));
274 #else
275   run_test (task);
276 #endif
277   printf ("Execution of '%s' complete.\n", task);
278 }
279
280 /* Executes all of the actions specified in ARGV[]
281    up to the null pointer sentinel. */
282 static void
283 run_actions (char **argv) 
284 {
285   /* An action. */
286   struct action 
287     {
288       char *name;                       /* Action name. */
289       int argc;                         /* # of args, including action name. */
290       void (*function) (char **argv);   /* Function to execute action. */
291     };
292
293   /* Table of supported actions. */
294   static const struct action actions[] = 
295     {
296       {"run", 2, run_task},
297 #ifdef FILESYS
298       {"ls", 1, fsutil_ls},
299       {"cat", 2, fsutil_cat},
300       {"rm", 2, fsutil_rm},
301       {"put", 2, fsutil_put},
302       {"get", 2, fsutil_get},
303 #endif
304       {NULL, 0, NULL},
305     };
306
307   while (*argv != NULL)
308     {
309       const struct action *a;
310       int i;
311
312       /* Find action name. */
313       for (a = actions; ; a++)
314         if (a->name == NULL)
315           PANIC ("unknown action `%s' (use -h for help)", *argv);
316         else if (!strcmp (*argv, a->name))
317           break;
318
319       /* Check for required arguments. */
320       for (i = 1; i < a->argc; i++)
321         if (argv[i] == NULL)
322           PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
323
324       /* Invoke action and advance. */
325       a->function (argv);
326       argv += a->argc;
327     }
328   
329 }
330
331 /* Prints a kernel command line help message and powers off the
332    machine. */
333 static void
334 usage (void)
335 {
336   printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
337           "Options must precede actions.\n"
338           "Actions are executed in the order specified.\n"
339           "\nAvailable actions:\n"
340 #ifdef USERPROG
341           "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
342 #else
343           "  run TEST           Run TEST.\n"
344 #endif
345 #ifdef FILESYS
346           "  ls                 List files in the root directory.\n"
347           "  cat FILE           Print FILE to the console.\n"
348           "  rm FILE            Delete FILE.\n"
349           "Use these actions indirectly via `pintos' -g and -p options:\n"
350           "  put FILE           Put FILE into file system from scratch disk.\n"
351           "  get FILE           Get FILE from file system into scratch disk.\n"
352 #endif
353           "\nOptions:\n"
354           "  -h                 Print this help message and power off.\n"
355           "  -q                 Power off VM after actions or on panic.\n"
356           "  -f                 Format file system disk during startup.\n"
357           "  -rs=SEED           Set random number seed to SEED.\n"
358           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
359 #ifdef USERPROG
360           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
361 #endif
362           );
363   power_off ();
364 }
365
366
367 /* Powers down the machine we're running on,
368    as long as we're running on Bochs or qemu. */
369 void
370 power_off (void) 
371 {
372   const char s[] = "Shutdown";
373   const char *p;
374
375 #ifdef FILESYS
376   filesys_done ();
377 #endif
378
379   print_stats ();
380
381   printf ("Powering off...\n");
382   serial_flush ();
383
384   for (p = s; *p != '\0'; p++)
385     outb (0x8900, *p);
386   for (;;);
387 }
388
389 /* Print statistics about Pintos execution. */
390 static void
391 print_stats (void) 
392 {
393   timer_print_stats ();
394   thread_print_stats ();
395 #ifdef FILESYS
396   disk_print_stats ();
397 #endif
398   console_print_stats ();
399   kbd_print_stats ();
400 #ifdef USERPROG
401   exception_print_stats ();
402 #endif
403 }