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