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