Eliminate user_page_limit global symbol.
[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 *init_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 /* -r: Reboot after kernel tasks complete? */
54 static bool reboot_when_done;
55
56 /* -ul: Maximum number of pages to put into palloc's user pool. */
57 static size_t user_page_limit = SIZE_MAX;
58
59 static void ram_init (void);
60 static void paging_init (void);
61
62 static char **read_command_line (void);
63 static char **parse_options (char **argv);
64 static void run_actions (char **argv);
65 static void usage (void);
66
67 static void print_stats (void);
68
69
70 int main (void) NO_RETURN;
71
72 /* Pintos main program. */
73 int
74 main (void)
75 {
76   char **argv;
77   
78   /* Clear BSS and get machine's RAM size. */  
79   ram_init ();
80
81   /* Break command line into arguments and parse options. */
82   argv = read_command_line ();
83   argv = parse_options (argv);
84
85   /* Initialize ourselves as a thread so we can use locks,
86      then enable console locking. */
87   thread_init ();
88   console_init ();  
89
90   /* Greet user. */
91   printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
92
93   /* Initialize memory system. */
94   palloc_init (user_page_limit);
95   malloc_init ();
96   paging_init ();
97
98   /* Segmentation. */
99 #ifdef USERPROG
100   tss_init ();
101   gdt_init ();
102 #endif
103
104   /* Initialize interrupt handlers. */
105   intr_init ();
106   timer_init ();
107   kbd_init ();
108   input_init ();
109 #ifdef USERPROG
110   exception_init ();
111   syscall_init ();
112 #endif
113
114   /* Start thread scheduler and enable interrupts. */
115   thread_start ();
116   serial_init_queue ();
117   timer_calibrate ();
118
119 #ifdef FILESYS
120   /* Initialize file system. */
121   disk_init ();
122   filesys_init (format_filesys);
123 #endif
124
125   printf ("Boot complete.\n");
126   
127   /* Run actions specified on kernel command line. */
128   run_actions (argv);
129
130   /* Finish up. */
131   if (reboot_when_done)
132     reboot ();
133
134   if (power_off_when_done)
135     power_off ();
136   thread_exit ();
137 }
138 \f
139 /* Clear BSS and obtain RAM size from loader. */
140 static void
141 ram_init (void) 
142 {
143   /* The "BSS" is a segment that should be initialized to zeros.
144      It isn't actually stored on disk or zeroed by the kernel
145      loader, so we have to zero it ourselves.
146
147      The start and end of the BSS segment is recorded by the
148      linker as _start_bss and _end_bss.  See kernel.lds. */
149   extern char _start_bss, _end_bss;
150   memset (&_start_bss, 0, &_end_bss - &_start_bss);
151
152   /* Get RAM size from loader.  See loader.S. */
153   ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS);
154 }
155
156 /* Populates the base page directory and page table with the
157    kernel virtual mapping, and then sets up the CPU to use the
158    new page directory.  Points init_page_dir to the page
159    directory it creates.
160
161    At the time this function is called, the active page table
162    (set up by loader.S) only maps the first 4 MB of RAM, so we
163    should not try to use extravagant amounts of memory.
164    Fortunately, there is no need to do so. */
165 static void
166 paging_init (void)
167 {
168   uint32_t *pd, *pt;
169   size_t page;
170   extern char _start, _end_kernel_text;
171
172   pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
173   pt = NULL;
174   for (page = 0; page < ram_pages; page++) 
175     {
176       uintptr_t paddr = page * PGSIZE;
177       char *vaddr = ptov (paddr);
178       size_t pde_idx = pd_no (vaddr);
179       size_t pte_idx = pt_no (vaddr);
180       bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
181
182       if (pd[pde_idx] == 0)
183         {
184           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
185           pd[pde_idx] = pde_create (pt);
186         }
187
188       pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
189     }
190
191   /* Store the physical address of the page directory into CR3
192      aka PDBR (page directory base register).  This activates our
193      new page tables immediately.  See [IA32-v2a] "MOV--Move
194      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
195      of the Page Directory". */
196   asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
197 }
198
199 /* Breaks the kernel command line into words and returns them as
200    an argv-like array. */
201 static char **
202 read_command_line (void) 
203 {
204   static char *argv[LOADER_ARGS_LEN / 2 + 1];
205   char *p, *end;
206   int argc;
207   int i;
208
209   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
210   p = ptov (LOADER_ARGS);
211   end = p + LOADER_ARGS_LEN;
212   for (i = 0; i < argc; i++) 
213     {
214       if (p >= end)
215         PANIC ("command line arguments overflow");
216
217       argv[i] = p;
218       p += strnlen (p, end - p) + 1;
219     }
220   argv[argc] = NULL;
221
222   /* Print kernel command line. */
223   printf ("Kernel command line:");
224   for (i = 0; i < argc; i++)
225     if (strchr (argv[i], ' ') == NULL)
226       printf (" %s", argv[i]);
227     else
228       printf (" '%s'", argv[i]);
229   printf ("\n");
230
231   return argv;
232 }
233
234 /* Parses options in ARGV[]
235    and returns the first non-option argument. */
236 static char **
237 parse_options (char **argv) 
238 {
239   for (; *argv != NULL && **argv == '-'; argv++)
240     {
241       char *save_ptr;
242       char *name = strtok_r (*argv, "=", &save_ptr);
243       char *value = strtok_r (NULL, "", &save_ptr);
244       
245       if (!strcmp (name, "-h"))
246         usage ();
247       else if (!strcmp (name, "-q"))
248         power_off_when_done = true;
249       else if (!strcmp (name, "-r"))
250         reboot_when_done = true;
251 #ifdef FILESYS
252       else if (!strcmp (name, "-f"))
253         format_filesys = true;
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 disk into file system.\n"
366           "  append FILE        Append FILE to tar file on scratch disk.\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           "  -f                 Format file system disk during startup.\n"
373           "  -rs=SEED           Set random number seed to SEED.\n"
374           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
375 #ifdef USERPROG
376           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
377 #endif
378           );
379   power_off ();
380 }
381
382 /* Keyboard control register port. */
383 #define CONTROL_REG 0x64
384
385 /* Reboots the machine via the keyboard controller. */
386 void
387 reboot (void)
388 {
389   int i;
390
391   printf ("Rebooting...\n");
392
393     /* See [kbd] for details on how to program the keyboard
394      * controller. */
395   for (i = 0; i < 100; i++) 
396     {
397       int j;
398
399       /* Poll keyboard controller's status byte until 
400        * 'input buffer empty' is reported. */
401       for (j = 0; j < 0x10000; j++) 
402         {
403           if ((inb (CONTROL_REG) & 0x02) == 0)   
404             break;
405           timer_udelay (2);
406         }
407
408       timer_udelay (50);
409
410       /* Pulse bit 0 of the output port P2 of the keyboard controller. 
411        * This will reset the CPU. */
412       outb (CONTROL_REG, 0xfe);
413       timer_udelay (50);
414     }
415 }
416
417 /* Powers down the machine we're running on,
418    as long as we're running on Bochs or QEMU. */
419 void
420 power_off (void) 
421 {
422   const char s[] = "Shutdown";
423   const char *p;
424
425 #ifdef FILESYS
426   filesys_done ();
427 #endif
428
429   print_stats ();
430
431   printf ("Powering off...\n");
432   serial_flush ();
433
434   for (p = s; *p != '\0'; p++)
435     outb (0x8900, *p);
436   asm volatile ("cli; hlt" : : : "memory");
437   printf ("still running...\n");
438   for (;;);
439 }
440
441 /* Print statistics about Pintos execution. */
442 static void
443 print_stats (void) 
444 {
445   timer_print_stats ();
446   thread_print_stats ();
447 #ifdef FILESYS
448   disk_print_stats ();
449 #endif
450   console_print_stats ();
451   kbd_print_stats ();
452 #ifdef USERPROG
453   exception_print_stats ();
454 #endif
455 }