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