usb.patch, with conflicts and some warnings fixed
[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/serial.h"
14 #include "devices/shutdown.h"
15 #include "devices/pci.h"
16 #include "devices/timer.h"
17 #include "devices/vga.h"
18 #include "devices/rtc.h"
19 #include "devices/usb.h"
20 #include "threads/interrupt.h"
21 #include "threads/io.h"
22 #include "threads/loader.h"
23 #include "threads/malloc.h"
24 #include "threads/palloc.h"
25 #include "threads/pte.h"
26 #include "threads/thread.h"
27 #ifdef USERPROG
28 #include "userprog/process.h"
29 #include "userprog/exception.h"
30 #include "userprog/gdt.h"
31 #include "userprog/syscall.h"
32 #include "userprog/tss.h"
33 #else
34 #include "tests/threads/tests.h"
35 #endif
36 #ifdef FILESYS
37 #include "devices/block.h"
38 #include "devices/ide.h"
39 #include "filesys/filesys.h"
40 #include "filesys/fsutil.h"
41 #endif
42
43 /* Page directory with kernel mappings only. */
44 uint32_t *init_page_dir;
45 bool init_page_dir_initialized;
46
47 #ifdef FILESYS
48 /* -f: Format the file system? */
49 static bool format_filesys;
50
51 /* -filesys, -scratch, -swap: Names of block devices to use,
52    overriding the defaults. */
53 static const char *filesys_bdev_name;
54 static const char *scratch_bdev_name;
55 #ifdef VM
56 static const char *swap_bdev_name;
57 #endif
58 #endif /* FILESYS */
59
60 /* -ul: Maximum number of pages to put into palloc's user pool. */
61 static size_t user_page_limit = SIZE_MAX;
62
63 static void bss_init (void);
64 static void paging_init (void);
65 static void pci_zone_init (void);
66
67 static char **read_command_line (void);
68 static char **parse_options (char **argv);
69 static void run_actions (char **argv);
70 static void usage (void);
71
72 #ifdef FILESYS
73 static void locate_block_devices (void);
74 static void locate_block_device (enum block_type, const char *name);
75 #endif
76
77 int main (void) NO_RETURN;
78
79 /* Pintos main program. */
80 int
81 main (void)
82 {
83   char **argv;
84
85   /* Clear BSS. */  
86   bss_init ();
87
88   /* Break command line into arguments and parse options. */
89   argv = read_command_line ();
90   argv = parse_options (argv);
91
92   /* Initialize ourselves as a thread so we can use locks,
93      then enable console locking. */
94   thread_init ();
95   console_init ();  
96
97   /* Greet user. */
98   printf ("Pintos booting with %'"PRIu32" kB RAM...\n",
99           init_ram_pages * PGSIZE / 1024);
100
101   /* Initialize memory system. */
102   palloc_init (user_page_limit);
103   malloc_init ();
104   paging_init ();
105
106   /* Segmentation. */
107 #ifdef USERPROG
108   tss_init ();
109   gdt_init ();
110 #endif
111
112   /* Initialize interrupt handlers. */
113   intr_init ();
114   timer_init ();
115   kbd_init ();
116   pci_init ();
117   input_init ();
118 #ifdef USERPROG
119   exception_init ();
120   syscall_init ();
121 #endif
122
123   /* Start thread scheduler and enable interrupts. */
124   thread_start ();
125   serial_init_queue ();
126   timer_calibrate ();
127   usb_init ();
128
129 #ifdef FILESYS
130   /* Initialize file system. */
131   usb_storage_init ();
132   ide_init ();
133   locate_block_devices ();
134   filesys_init (format_filesys);
135 #endif
136
137   printf ("Boot complete.\n");
138   
139   /* Run actions specified on kernel command line. */
140   run_actions (argv);
141
142   /* Finish up. */
143   shutdown ();
144   thread_exit ();
145 }
146 \f
147 /* Clear the "BSS", a segment that should be initialized to
148    zeros.  It isn't actually stored on disk or zeroed by the
149    kernel loader, so we have to zero it ourselves.
150
151    The start and end of the BSS segment is recorded by the
152    linker as _start_bss and _end_bss.  See kernel.lds. */
153 static void
154 bss_init (void) 
155 {
156   extern char _start_bss, _end_bss;
157   memset (&_start_bss, 0, &_end_bss - &_start_bss);
158 }
159
160 /* Populates the base page directory and page table with the
161    kernel virtual mapping, and then sets up the CPU to use the
162    new page directory.  Points init_page_dir to the page
163    directory it creates. */
164 static void
165 paging_init (void)
166 {
167   uint32_t *pd, *pt;
168   size_t page;
169   extern char _start, _end_kernel_text;
170
171   pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
172   pt = NULL;
173   for (page = 0; page < init_ram_pages; page++)
174     {
175       uintptr_t paddr = page * PGSIZE;
176       char *vaddr = ptov (paddr);
177       size_t pde_idx = pd_no (vaddr);
178       size_t pte_idx = pt_no (vaddr);
179       bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
180
181       if (pd[pde_idx] == 0)
182         {
183           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
184           pd[pde_idx] = pde_create_kernel (pt);
185         }
186
187       pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
188     }
189
190   pci_zone_init ();
191
192   /* Store the physical address of the page directory into CR3
193      aka PDBR (page directory base register).  This activates our
194      new page tables immediately.  See [IA32-v2a] "MOV--Move
195      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
196      of the Page Directory". */
197   asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
198
199   init_page_dir_initialized = true;
200 }
201
202 /* initialize PCI zone at PCI_ADDR_ZONE_BEGIN - PCI_ADDR_ZONE_END*/
203 static void
204 pci_zone_init (void)
205 {
206   int i;
207   for (i = 0; i < PCI_ADDR_ZONE_PDES; i++)
208     {
209       size_t pde_idx = pd_no ((void *) PCI_ADDR_ZONE_BEGIN) + i;
210       uint32_t pde;
211       void *pt;
212
213       pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
214       pde = pde_create_kernel (pt);
215       init_page_dir[pde_idx] = pde;
216     }
217 }
218
219 /* Breaks the kernel command line into words and returns them as
220    an argv-like array. */
221 static char **
222 read_command_line (void) 
223 {
224   static char *argv[LOADER_ARGS_LEN / 2 + 1];
225   char *p, *end;
226   int argc;
227   int i;
228
229   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
230   p = ptov (LOADER_ARGS);
231   end = p + LOADER_ARGS_LEN;
232   for (i = 0; i < argc; i++) 
233     {
234       if (p >= end)
235         PANIC ("command line arguments overflow");
236
237       argv[i] = p;
238       p += strnlen (p, end - p) + 1;
239     }
240   argv[argc] = NULL;
241
242   /* Print kernel command line. */
243   printf ("Kernel command line:");
244   for (i = 0; i < argc; i++)
245     if (strchr (argv[i], ' ') == NULL)
246       printf (" %s", argv[i]);
247     else
248       printf (" '%s'", argv[i]);
249   printf ("\n");
250
251   return argv;
252 }
253
254 /* Parses options in ARGV[]
255    and returns the first non-option argument. */
256 static char **
257 parse_options (char **argv) 
258 {
259   for (; *argv != NULL && **argv == '-'; argv++)
260     {
261       char *save_ptr;
262       char *name = strtok_r (*argv, "=", &save_ptr);
263       char *value = strtok_r (NULL, "", &save_ptr);
264       
265       if (!strcmp (name, "-h"))
266         usage ();
267       else if (!strcmp (name, "-q"))
268         shutdown_configure (SHUTDOWN_POWER_OFF);
269       else if (!strcmp (name, "-r"))
270         shutdown_configure (SHUTDOWN_REBOOT);
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 device into file system.\n"
394           "  append FILE        Append FILE to tar file on scratch device.\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           "  -r                 Reboot after actions.\n"
400 #ifdef FILESYS
401           "  -f                 Format file system device during startup.\n"
402           "  -filesys=BDEV      Use BDEV for file system instead of default.\n"
403           "  -scratch=BDEV      Use BDEV for scratch instead of default.\n"
404 #ifdef VM
405           "  -swap=BDEV         Use BDEV for swap instead of default.\n"
406 #endif
407 #endif
408           "  -rs=SEED           Set random number seed to SEED.\n"
409           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
410 #ifdef USERPROG
411           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
412 #endif
413           );
414   shutdown_power_off ();
415 }
416
417 #ifdef FILESYS
418 /* Figure out what block devices to cast in the various Pintos roles. */
419 static void
420 locate_block_devices (void)
421 {
422   locate_block_device (BLOCK_FILESYS, filesys_bdev_name);
423   locate_block_device (BLOCK_SCRATCH, scratch_bdev_name);
424 #ifdef VM
425   locate_block_device (BLOCK_SWAP, swap_bdev_name);
426 #endif
427 }
428
429 /* Figures out what block device to use for the given ROLE: the
430    block device with the given NAME, if NAME is non-null,
431    otherwise the first block device in probe order of type
432    ROLE. */
433 static void
434 locate_block_device (enum block_type role, const char *name)
435 {
436   struct block *block = NULL;
437
438   if (name != NULL)
439     {
440       block = block_get_by_name (name);
441       if (block == NULL)
442         PANIC ("No such block device \"%s\"", name);
443     }
444   else
445     {
446       for (block = block_first (); block != NULL; block = block_next (block))
447         if (block_type (block) == role)
448           break;
449     }
450
451   if (block != NULL)
452     {
453       printf ("%s: using %s\n", block_type_name (role), block_name (block));
454       block_set_role (role, block);
455     }
456 }
457 #endif