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