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