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