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