Formatting.
[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 FILESYS
49 /* -f: Format the filesystem? */
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   /* Initialize memory system. */
94   palloc_init ();
95   malloc_init ();
96   paging_init ();
97
98   /* Segmentation. */
99 #ifdef USERPROG
100   tss_init ();
101   gdt_init ();
102 #endif
103
104   /* Set random seed if parse_options() didn't. */
105   random_init (0);
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 filesystem. */
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-v3] 3.7.5. */
192   asm volatile ("movl %0, %%cr3" :: "r" (vtop (base_page_dir)));
193 }
194
195 /* Breaks the kernel command line into words and returns them as
196    an argv-like array. */
197 static char **
198 read_command_line (void) 
199 {
200   static char *argv[LOADER_ARGS_LEN / 2 + 1];
201   char *p, *end;
202   int argc;
203   int i;
204
205   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
206   p = ptov (LOADER_ARGS);
207   end = p + LOADER_ARGS_LEN;
208   for (i = 0; i < argc; i++) 
209     {
210       if (p >= end)
211         PANIC ("command line arguments overflow");
212
213       argv[i] = p;
214       p += strnlen (p, end - p) + 1;
215     }
216   argv[argc] = NULL;
217
218   /* Print kernel command line. */
219   printf ("Kernel command line:");
220   for (i = 0; i < argc; i++)
221     printf (" %s", argv[i]);
222   printf ("\n");
223
224   return argv;
225 }
226
227 /* Parses options in ARGV[]
228    and returns the first non-option argument. */
229 static char **
230 parse_options (char **argv) 
231 {
232   for (; *argv != NULL && **argv == '-'; argv++)
233     {
234       char *save_ptr;
235       char *name = strtok_r (*argv, "=", &save_ptr);
236       char *value = strtok_r (NULL, "", &save_ptr);
237       
238       if (!strcmp (name, "-h"))
239         usage ();
240       else if (!strcmp (name, "-q"))
241         power_off_when_done = true;
242 #ifdef FILESYS
243       else if (!strcmp (name, "-f"))
244         format_filesys = true;
245 #endif
246       else if (!strcmp (name, "-rs"))
247         random_init (atoi (value));
248       else if (!strcmp (name, "-mlfqs"))
249         enable_mlfqs = true;
250 #ifdef USERPROG
251       else if (!strcmp (name, "-ul"))
252         user_page_limit = atoi (value);
253 #endif
254       else
255         PANIC ("unknown option `%s' (use -h for help)", name);
256     }
257   
258   return argv;
259 }
260
261 /* Runs the task specified in ARGV[1]. */
262 static void
263 run_task (char **argv)
264 {
265   const char *task = argv[1];
266   
267   printf ("Executing '%s':\n", task);
268 #ifdef USERPROG
269   process_wait (process_execute (task));
270 #else
271   run_test (task);
272 #endif
273   printf ("Execution of '%s' complete.\n", task);
274 }
275
276 /* Executes all of the actions specified in ARGV[]
277    up to the null pointer sentinel. */
278 static void
279 run_actions (char **argv) 
280 {
281   /* An action. */
282   struct action 
283     {
284       char *name;                       /* Action name. */
285       int argc;                         /* # of args, including action name. */
286       void (*function) (char **argv);   /* Function to execute action. */
287     };
288
289   /* Table of supported actions. */
290   static const struct action actions[] = 
291     {
292       {"run", 2, run_task},
293 #ifdef FILESYS
294       {"ls", 1, fsutil_ls},
295       {"cat", 2, fsutil_cat},
296       {"rm", 2, fsutil_rm},
297       {"put", 2, fsutil_put},
298       {"get", 2, fsutil_get},
299 #endif
300       {NULL, 0, NULL},
301     };
302
303   while (*argv != NULL)
304     {
305       const struct action *a;
306       int i;
307
308       /* Find action name. */
309       for (a = actions; ; a++)
310         if (a->name == NULL)
311           PANIC ("unknown action `%s' (use -h for help)", *argv);
312         else if (!strcmp (*argv, a->name))
313           break;
314
315       /* Check for required arguments. */
316       for (i = 1; i < a->argc; i++)
317         if (argv[i] == NULL)
318           PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
319
320       /* Invoke action and advance. */
321       a->function (argv);
322       argv += a->argc;
323     }
324   
325 }
326
327 /* Prints a kernel command line help message and powers off the
328    machine. */
329 static void
330 usage (void)
331 {
332   printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
333           "Options must precede actions.\n"
334           "Actions are executed in the order specified.\n"
335           "\nAvailable actions:\n"
336 #ifdef USERPROG
337           "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
338 #else
339           "  run TEST           Run TEST.\n"
340 #endif
341 #ifdef FILESYS
342           "  ls                 List files in the root directory.\n"
343           "  cat FILE           Print FILE to the console.\n"
344           "  rm FILE            Delete FILE.\n"
345           "Use these actions indirectly via `pintos' -g and -p options:\n"
346           "  put FILE           Put FILE into file system from scratch disk.\n"
347           "  get FILE           Get FILE from file system into scratch disk.\n"
348 #endif
349           "\nOptions:\n"
350           "  -h                 Print this help message and power off.\n"
351           "  -q                 Power off VM after actions or on panic.\n"
352           "  -f                 Format file system disk during startup.\n"
353           "  -rs=SEED           Set random number seed to SEED.\n"
354           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
355 #ifdef USERPROG
356           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
357 #endif
358           );
359   power_off ();
360 }
361
362
363 /* Powers down the machine we're running on,
364    as long as we're running on Bochs or qemu. */
365 void
366 power_off (void) 
367 {
368   const char s[] = "Shutdown";
369   const char *p;
370
371 #ifdef FILESYS
372   filesys_done ();
373 #endif
374
375   print_stats ();
376
377   printf ("Powering off...\n");
378   serial_flush ();
379
380   for (p = s; *p != '\0'; p++)
381     outb (0x8900, *p);
382   for (;;);
383 }
384
385 /* Print statistics about Pintos execution. */
386 static void
387 print_stats (void) 
388 {
389   timer_print_stats ();
390   thread_print_stats ();
391 #ifdef FILESYS
392   disk_print_stats ();
393 #endif
394   console_print_stats ();
395   kbd_print_stats ();
396 #ifdef USERPROG
397   exception_print_stats ();
398 #endif
399 }