66bc8671ee0b95810199247057a4cd407ee6f1e7
[pintos-anon] / src / threads / init.c
1 #include "threads/init.h"
2 #include <console.h>
3 #include <debug.h>
4 #include <inttypes.h>
5 #include <limits.h>
6 #include <random.h>
7 #include <stddef.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/pci.h"
14 #include "devices/usb.h"
15 #include "devices/serial.h"
16 #include "devices/timer.h"
17 #include "devices/vga.h"
18 #include "devices/rtc.h"
19 #include "threads/interrupt.h"
20 #include "threads/io.h"
21 #include "threads/loader.h"
22 #include "threads/malloc.h"
23 #include "threads/palloc.h"
24 #include "threads/pte.h"
25 #include "threads/thread.h"
26 #ifdef USERPROG
27 #include "userprog/process.h"
28 #include "userprog/exception.h"
29 #include "userprog/gdt.h"
30 #include "userprog/syscall.h"
31 #include "userprog/tss.h"
32 #else
33 #include "tests/threads/tests.h"
34 #endif
35 #ifdef FILESYS
36 #include "devices/block.h"
37 #include "devices/ide.h"
38 #include "filesys/filesys.h"
39 #include "filesys/fsutil.h"
40 #endif
41
42 /* Page directory with kernel mappings only. */
43 uint32_t *base_page_dir;
44 bool base_page_dir_initialized = 0;
45
46 #ifdef FILESYS
47 /* -f: Format the file system? */
48 static bool format_filesys;
49
50 /* -filesys, -scratch, -swap: Names of block devices to use,
51    overriding the defaults. */
52 static const char *filesys_bdev_name;
53 static const char *scratch_bdev_name;
54 #ifdef VM
55 static const char *swap_bdev_name;
56 #endif
57 #endif
58
59 /* -q: Power off after kernel tasks complete? */
60 bool power_off_when_done;
61
62 static void bss_init (void);
63 static void paging_init (void);
64 static void pci_zone_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 #ifdef FILESYS
72 static void locate_block_devices (void);
73 static void locate_block_device (enum block_type, const char *name);
74 #endif
75
76 static void print_stats (void);
77
78 int main (void) NO_RETURN;
79
80 /* Pintos main program. */
81 int
82 main (void)
83 {
84   char **argv;
85
86   /* Clear BSS. */  
87   bss_init ();
88
89   /* Break command line into arguments and parse options. */
90   argv = read_command_line ();
91   argv = parse_options (argv);
92
93   /* Initialize ourselves as a thread so we can use locks,
94      then enable console locking. */
95   thread_init ();
96   console_init ();  
97
98   /* Greet user. */
99   printf ("Pintos booting with %'"PRIu32" kB RAM...\n",
100           ram_pages * PGSIZE / 1024);
101
102   /* Initialize memory system. */
103   palloc_init ();
104   malloc_init ();
105   paging_init ();
106
107   /* Segmentation. */
108 #ifdef USERPROG
109   tss_init ();
110   gdt_init ();
111 #endif
112
113   /* Initialize interrupt handlers. */
114   intr_init ();
115   timer_init ();
116   kbd_init ();
117   pci_init ();
118   input_init ();
119 #ifdef USERPROG
120   exception_init ();
121   syscall_init ();
122 #endif
123
124   /* Start thread scheduler and enable interrupts. */
125   thread_start ();
126   serial_init_queue ();
127   timer_calibrate ();
128   usb_init ();
129
130 #ifdef FILESYS
131   /* Initialize file system. */
132   usb_storage_init ();
133   ide_init ();
134   locate_block_devices ();
135   filesys_init (format_filesys);
136 #endif
137
138   printf ("Boot complete.\n");
139   
140   /* Run actions specified on kernel command line. */
141   run_actions (argv);
142
143   /* Finish up. */
144   if (power_off_when_done)
145     power_off ();
146   thread_exit ();
147 }
148 \f
149 /* Clear the "BSS", a segment that should be initialized to
150    zeros.  It isn't actually stored on disk or zeroed by the
151    kernel 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 static void
156 bss_init (void) 
157 {
158   extern char _start_bss, _end_bss;
159   memset (&_start_bss, 0, &_end_bss - &_start_bss);
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 static void
167 paging_init (void)
168 {
169   uint32_t *pd, *pt;
170   size_t page;
171   extern char _start, _end_kernel_text;
172
173   pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
174   pt = NULL;
175   for (page = 0; page < ram_pages; page++) 
176     {
177       uintptr_t paddr = page * PGSIZE;
178       char *vaddr = ptov (paddr);
179       size_t pde_idx = pd_no (vaddr);
180       size_t pte_idx = pt_no (vaddr);
181       bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
182
183       if (pd[pde_idx] == 0)
184         {
185           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
186           pd[pde_idx] = pde_create_kernel (pt);
187         }
188
189       pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
190     }
191
192   pci_zone_init ();
193
194   /* Store the physical address of the page directory into CR3
195      aka PDBR (page directory base register).  This activates our
196      new page tables immediately.  See [IA32-v2a] "MOV--Move
197      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
198      of the Page Directory". */
199   asm volatile ("movl %0, %%cr3" : : "r" (vtop (base_page_dir)));
200
201   base_page_dir_initialized = 1;
202 }
203
204 /* initialize PCI zone at PCI_ADDR_ZONE_BEGIN - PCI_ADDR_ZONE_END*/
205 static void
206 pci_zone_init (void)
207 {
208   int i;
209   for (i = 0; i < PCI_ADDR_ZONE_PDES; i++)
210     {
211       size_t pde_idx = pd_no ((void *) PCI_ADDR_ZONE_BEGIN) + i;
212       uint32_t pde;
213       void *pt;
214
215       pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
216       pde = pde_create_kernel (pt);
217       base_page_dir[pde_idx] = pde;
218     }
219 }
220
221 /* Breaks the kernel command line into words and returns them as
222    an argv-like array. */
223 static char **
224 read_command_line (void) 
225 {
226   static char *argv[LOADER_ARGS_LEN / 2 + 1];
227   char *p, *end;
228   int argc;
229   int i;
230
231   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
232   p = ptov (LOADER_ARGS);
233   end = p + LOADER_ARGS_LEN;
234   for (i = 0; i < argc; i++) 
235     {
236       if (p >= end)
237         PANIC ("command line arguments overflow");
238
239       argv[i] = p;
240       p += strnlen (p, end - p) + 1;
241     }
242   argv[argc] = NULL;
243
244   /* Print kernel command line. */
245   printf ("Kernel command line:");
246   for (i = 0; i < argc; i++)
247     if (strchr (argv[i], ' ') == NULL)
248       printf (" %s", argv[i]);
249     else
250       printf (" '%s'", argv[i]);
251   printf ("\n");
252
253   return argv;
254 }
255
256 /* Parses options in ARGV[]
257    and returns the first non-option argument. */
258 static char **
259 parse_options (char **argv) 
260 {
261   for (; *argv != NULL && **argv == '-'; argv++)
262     {
263       char *save_ptr;
264       char *name = strtok_r (*argv, "=", &save_ptr);
265       char *value = strtok_r (NULL, "", &save_ptr);
266       
267       if (!strcmp (name, "-h"))
268         usage ();
269       else if (!strcmp (name, "-q"))
270         power_off_when_done = true;
271 #ifdef FILESYS
272       else if (!strcmp (name, "-f"))
273         format_filesys = true;
274       else if (!strcmp (name, "-filesys"))
275         filesys_bdev_name = value;
276       else if (!strcmp (name, "-scratch"))
277         scratch_bdev_name = value;
278 #ifdef VM
279       else if (!strcmp (name, "-swap"))
280         swap_bdev_name = value;
281 #endif
282 #endif
283       else if (!strcmp (name, "-rs"))
284         random_init (atoi (value));
285       else if (!strcmp (name, "-mlfqs"))
286         thread_mlfqs = true;
287 #ifdef USERPROG
288       else if (!strcmp (name, "-ul"))
289         user_page_limit = atoi (value);
290 #endif
291       else
292         PANIC ("unknown option `%s' (use -h for help)", name);
293     }
294
295   /* Initialize the random number generator based on the system
296      time.  This has no effect if an "-rs" option was specified.
297
298      When running under Bochs, this is not enough by itself to
299      get a good seed value, because the pintos script sets the
300      initial time to a predictable value, not to the local time,
301      for reproducibility.  To fix this, give the "-r" option to
302      the pintos script to request real-time execution. */
303   random_init (rtc_get_time ());
304   
305   return argv;
306 }
307
308 /* Runs the task specified in ARGV[1]. */
309 static void
310 run_task (char **argv)
311 {
312   const char *task = argv[1];
313   
314   printf ("Executing '%s':\n", task);
315 #ifdef USERPROG
316   process_wait (process_execute (task));
317 #else
318   run_test (task);
319 #endif
320   printf ("Execution of '%s' complete.\n", task);
321 }
322
323 /* Executes all of the actions specified in ARGV[]
324    up to the null pointer sentinel. */
325 static void
326 run_actions (char **argv) 
327 {
328   /* An action. */
329   struct action 
330     {
331       char *name;                       /* Action name. */
332       int argc;                         /* # of args, including action name. */
333       void (*function) (char **argv);   /* Function to execute action. */
334     };
335
336   /* Table of supported actions. */
337   static const struct action actions[] = 
338     {
339       {"run", 2, run_task},
340 #ifdef FILESYS
341       {"ls", 1, fsutil_ls},
342       {"cat", 2, fsutil_cat},
343       {"rm", 2, fsutil_rm},
344       {"extract", 1, fsutil_extract},
345       {"append", 2, fsutil_append},
346 #endif
347       {NULL, 0, NULL},
348     };
349
350   while (*argv != NULL)
351     {
352       const struct action *a;
353       int i;
354
355       /* Find action name. */
356       for (a = actions; ; a++)
357         if (a->name == NULL)
358           PANIC ("unknown action `%s' (use -h for help)", *argv);
359         else if (!strcmp (*argv, a->name))
360           break;
361
362       /* Check for required arguments. */
363       for (i = 1; i < a->argc; i++)
364         if (argv[i] == NULL)
365           PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
366
367       /* Invoke action and advance. */
368       a->function (argv);
369       argv += a->argc;
370     }
371   
372 }
373
374 /* Prints a kernel command line help message and powers off the
375    machine. */
376 static void
377 usage (void)
378 {
379   printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
380           "Options must precede actions.\n"
381           "Actions are executed in the order specified.\n"
382           "\nAvailable actions:\n"
383 #ifdef USERPROG
384           "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
385 #else
386           "  run TEST           Run TEST.\n"
387 #endif
388 #ifdef FILESYS
389           "  ls                 List files in the root directory.\n"
390           "  cat FILE           Print FILE to the console.\n"
391           "  rm FILE            Delete FILE.\n"
392           "Use these actions indirectly via `pintos' -g and -p options:\n"
393           "  extract            Untar from scratch disk into file system.\n"
394           "  get FILE           Get FILE from file system into scratch disk.\n"
395 #endif
396           "\nOptions:\n"
397           "  -h                 Print this help message and power off.\n"
398           "  -q                 Power off VM after actions or on panic.\n"
399 #ifdef FILESYS
400           "  -f                 Format file system disk during startup.\n"
401           "  -filesys=BDEV      Use BDEV for file system instead of default.\n"
402           "  -scratch=BDEV      Use BDEV for scratch instead of default.\n"
403 #ifdef VM
404           "  -swap=BDEV         Use BDEV for swap instead of default.\n"
405 #endif
406 #endif
407           "  -rs=SEED           Set random number seed to SEED.\n"
408           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
409 #ifdef USERPROG
410           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
411 #endif
412           );
413   power_off ();
414 }
415
416 #ifdef FILESYS
417 /* Figure out what disks to cast in the various Pintos roles. */
418 static void
419 locate_block_devices (void)
420 {
421   locate_block_device (BLOCK_FILESYS, filesys_bdev_name);
422   locate_block_device (BLOCK_SCRATCH, scratch_bdev_name);
423 #ifdef VM
424   locate_block_device (BLOCK_SWAP, swap_bdev_name);
425 #endif
426 }
427
428 /* Figures out what block device to use for the given ROLE: the
429    block device with the given NAME, if NAME is non-null,
430    otherwise the first block device in probe order of type
431    ROLE. */
432 static void
433 locate_block_device (enum block_type role, const char *name)
434 {
435   struct block *block = NULL;
436
437   if (name != NULL)
438     {
439       block = block_get_by_name (name);
440       if (block == NULL)
441         PANIC ("No such block device \"%s\"", name);
442     }
443   else
444     {
445       for (block = block_first (); block != NULL; block = block_next (block))
446         if (block_type (block) == role)
447           break;
448     }
449
450   if (block != NULL)
451     {
452       printf ("%s: using %s\n", block_type_name (role), block_name (block));
453       block_set_role (role, block);
454     }
455 }
456 #endif
457
458 /* Powers down the machine we're running on,
459    as long as we're running on Bochs or QEMU. */
460 void
461 power_off (void) 
462 {
463   const char s[] = "Shutdown";
464   const char *p;
465
466 #ifdef FILESYS
467   filesys_done ();
468 #endif
469
470   print_stats ();
471
472   printf ("Powering off...\n");
473   serial_flush ();
474
475   for (p = s; *p != '\0'; p++)
476     outb (0x8900, *p);
477   asm volatile ("cli; hlt" : : : "memory");
478   printf ("still running...\n");
479   for (;;);
480 }
481
482 /* Print statistics about Pintos execution. */
483 static void
484 print_stats (void) 
485 {
486   timer_print_stats ();
487   thread_print_stats ();
488 #ifdef FILESYS
489   block_print_stats ();
490 #endif
491   console_print_stats ();
492   kbd_print_stats ();
493 #ifdef USERPROG
494   exception_print_stats ();
495 #endif
496 }