d2d3e087de286cb8f5b48fd960d1568ae2d1cab4
[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/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/disk.h"
36 #include "filesys/filesys.h"
37 #include "filesys/fsutil.h"
38 #endif
39
40 /* Amount of physical memory, in 4 kB pages. */
41 size_t init_ram_pages;
42
43 /* Page directory with kernel mappings only. */
44 uint32_t *init_page_dir;
45
46 #ifdef FILESYS
47 /* -f: Format the file system? */
48 static bool format_filesys;
49 #endif
50
51 /* -ul: Maximum number of pages to put into palloc's user pool. */
52 static size_t user_page_limit = SIZE_MAX;
53
54 static void ram_init (void);
55 static void paging_init (void);
56
57 static char **read_command_line (void);
58 static char **parse_options (char **argv);
59 static void run_actions (char **argv);
60 static void usage (void);
61
62 int main (void) NO_RETURN;
63
64 /* Pintos main program. */
65 int
66 main (void)
67 {
68   char **argv;
69   
70   /* Clear BSS and get machine's RAM size. */  
71   ram_init ();
72
73   /* Break command line into arguments and parse options. */
74   argv = read_command_line ();
75   argv = parse_options (argv);
76
77   /* Initialize ourselves as a thread so we can use locks,
78      then enable console locking. */
79   thread_init ();
80   console_init ();  
81
82   /* Greet user. */
83   printf ("Pintos booting with %'zu kB RAM...\n",
84           init_ram_pages * PGSIZE / 1024);
85
86   /* Initialize memory system. */
87   palloc_init (user_page_limit);
88   malloc_init ();
89   paging_init ();
90
91   /* Segmentation. */
92 #ifdef USERPROG
93   tss_init ();
94   gdt_init ();
95 #endif
96
97   /* Initialize interrupt handlers. */
98   intr_init ();
99   timer_init ();
100   kbd_init ();
101   input_init ();
102 #ifdef USERPROG
103   exception_init ();
104   syscall_init ();
105 #endif
106
107   /* Start thread scheduler and enable interrupts. */
108   thread_start ();
109   serial_init_queue ();
110   timer_calibrate ();
111
112 #ifdef FILESYS
113   /* Initialize file system. */
114   disk_init ();
115   filesys_init (format_filesys);
116 #endif
117
118   printf ("Boot complete.\n");
119   
120   /* Run actions specified on kernel command line. */
121   run_actions (argv);
122
123   /* Finish up. */
124   shutdown ();
125   thread_exit ();
126 }
127 \f
128 /* Clear BSS and obtain RAM size from loader. */
129 static void
130 ram_init (void) 
131 {
132   /* The "BSS" is a segment that should be initialized to zeros.
133      It isn't actually stored on disk or zeroed by the kernel
134      loader, so we have to zero it ourselves.
135
136      The start and end of the BSS segment is recorded by the
137      linker as _start_bss and _end_bss.  See kernel.lds. */
138   extern char _start_bss, _end_bss;
139   memset (&_start_bss, 0, &_end_bss - &_start_bss);
140
141   /* Get RAM size from loader.  See loader.S. */
142   init_ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS);
143 }
144
145 /* Populates the base page directory and page table with the
146    kernel virtual mapping, and then sets up the CPU to use the
147    new page directory.  Points init_page_dir to the page
148    directory it creates.
149
150    At the time this function is called, the active page table
151    (set up by loader.S) only maps the first 4 MB of RAM, so we
152    should not try to use extravagant amounts of memory.
153    Fortunately, there is no need to do so. */
154 static void
155 paging_init (void)
156 {
157   uint32_t *pd, *pt;
158   size_t page;
159   extern char _start, _end_kernel_text;
160
161   pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
162   pt = NULL;
163   for (page = 0; page < init_ram_pages; page++)
164     {
165       uintptr_t paddr = page * PGSIZE;
166       char *vaddr = ptov (paddr);
167       size_t pde_idx = pd_no (vaddr);
168       size_t pte_idx = pt_no (vaddr);
169       bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
170
171       if (pd[pde_idx] == 0)
172         {
173           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
174           pd[pde_idx] = pde_create (pt);
175         }
176
177       pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
178     }
179
180   /* Store the physical address of the page directory into CR3
181      aka PDBR (page directory base register).  This activates our
182      new page tables immediately.  See [IA32-v2a] "MOV--Move
183      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
184      of the Page Directory". */
185   asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
186 }
187
188 /* Breaks the kernel command line into words and returns them as
189    an argv-like array. */
190 static char **
191 read_command_line (void) 
192 {
193   static char *argv[LOADER_ARGS_LEN / 2 + 1];
194   char *p, *end;
195   int argc;
196   int i;
197
198   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
199   p = ptov (LOADER_ARGS);
200   end = p + LOADER_ARGS_LEN;
201   for (i = 0; i < argc; i++) 
202     {
203       if (p >= end)
204         PANIC ("command line arguments overflow");
205
206       argv[i] = p;
207       p += strnlen (p, end - p) + 1;
208     }
209   argv[argc] = NULL;
210
211   /* Print kernel command line. */
212   printf ("Kernel command line:");
213   for (i = 0; i < argc; i++)
214     if (strchr (argv[i], ' ') == NULL)
215       printf (" %s", argv[i]);
216     else
217       printf (" '%s'", argv[i]);
218   printf ("\n");
219
220   return argv;
221 }
222
223 /* Parses options in ARGV[]
224    and returns the first non-option argument. */
225 static char **
226 parse_options (char **argv) 
227 {
228   for (; *argv != NULL && **argv == '-'; argv++)
229     {
230       char *save_ptr;
231       char *name = strtok_r (*argv, "=", &save_ptr);
232       char *value = strtok_r (NULL, "", &save_ptr);
233       
234       if (!strcmp (name, "-h"))
235         usage ();
236       else if (!strcmp (name, "-q"))
237         shutdown_configure (SHUTDOWN_POWER_OFF);
238       else if (!strcmp (name, "-r"))
239         shutdown_configure (SHUTDOWN_REBOOT);
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           "  -r                 Reboot after actions.\n"
361           "  -f                 Format file system disk during startup.\n"
362           "  -rs=SEED           Set random number seed to SEED.\n"
363           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
364 #ifdef USERPROG
365           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
366 #endif
367           );
368   shutdown_power_off ();
369 }