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