Add support for "keyboard" input over the serial port.
[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
172   pd = base_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       void *vaddr = ptov (paddr);
178       size_t pde_idx = pd_no (vaddr);
179       size_t pte_idx = pt_no (vaddr);
180
181       if (pd[pde_idx] == 0)
182         {
183           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
184           pd[pde_idx] = pde_create (pt);
185         }
186
187       pt[pte_idx] = pte_create_kernel (vaddr, true);
188     }
189
190   /* Store the physical address of the page directory into CR3
191      aka PDBR (page directory base register).  This activates our
192      new page tables immediately.  See [IA32-v2a] "MOV--Move
193      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
194      of the Page Directory". */
195   asm volatile ("movl %0, %%cr3" :: "r" (vtop (base_page_dir)));
196 }
197
198 /* Breaks the kernel command line into words and returns them as
199    an argv-like array. */
200 static char **
201 read_command_line (void) 
202 {
203   static char *argv[LOADER_ARGS_LEN / 2 + 1];
204   char *p, *end;
205   int argc;
206   int i;
207
208   argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
209   p = ptov (LOADER_ARGS);
210   end = p + LOADER_ARGS_LEN;
211   for (i = 0; i < argc; i++) 
212     {
213       if (p >= end)
214         PANIC ("command line arguments overflow");
215
216       argv[i] = p;
217       p += strnlen (p, end - p) + 1;
218     }
219   argv[argc] = NULL;
220
221   /* Print kernel command line. */
222   printf ("Kernel command line:");
223   for (i = 0; i < argc; i++)
224     if (strchr (argv[i], ' ') == NULL)
225       printf (" %s", argv[i]);
226     else
227       printf (" '%s'", argv[i]);
228   printf ("\n");
229
230   return argv;
231 }
232
233 /* Parses options in ARGV[]
234    and returns the first non-option argument. */
235 static char **
236 parse_options (char **argv) 
237 {
238   for (; *argv != NULL && **argv == '-'; argv++)
239     {
240       char *save_ptr;
241       char *name = strtok_r (*argv, "=", &save_ptr);
242       char *value = strtok_r (NULL, "", &save_ptr);
243       
244       if (!strcmp (name, "-h"))
245         usage ();
246       else if (!strcmp (name, "-q"))
247         power_off_when_done = true;
248 #ifdef FILESYS
249       else if (!strcmp (name, "-f"))
250         format_filesys = true;
251 #endif
252       else if (!strcmp (name, "-rs"))
253         random_init (atoi (value));
254       else if (!strcmp (name, "-mlfqs"))
255         enable_mlfqs = true;
256 #ifdef USERPROG
257       else if (!strcmp (name, "-ul"))
258         user_page_limit = atoi (value);
259 #endif
260       else
261         PANIC ("unknown option `%s' (use -h for help)", name);
262     }
263   
264   return argv;
265 }
266
267 /* Runs the task specified in ARGV[1]. */
268 static void
269 run_task (char **argv)
270 {
271   const char *task = argv[1];
272   
273   printf ("Executing '%s':\n", task);
274 #ifdef USERPROG
275   process_wait (process_execute (task));
276 #else
277   run_test (task);
278 #endif
279   printf ("Execution of '%s' complete.\n", task);
280 }
281
282 /* Executes all of the actions specified in ARGV[]
283    up to the null pointer sentinel. */
284 static void
285 run_actions (char **argv) 
286 {
287   /* An action. */
288   struct action 
289     {
290       char *name;                       /* Action name. */
291       int argc;                         /* # of args, including action name. */
292       void (*function) (char **argv);   /* Function to execute action. */
293     };
294
295   /* Table of supported actions. */
296   static const struct action actions[] = 
297     {
298       {"run", 2, run_task},
299 #ifdef FILESYS
300       {"ls", 1, fsutil_ls},
301       {"cat", 2, fsutil_cat},
302       {"rm", 2, fsutil_rm},
303       {"put", 2, fsutil_put},
304       {"get", 2, fsutil_get},
305 #endif
306       {NULL, 0, NULL},
307     };
308
309   while (*argv != NULL)
310     {
311       const struct action *a;
312       int i;
313
314       /* Find action name. */
315       for (a = actions; ; a++)
316         if (a->name == NULL)
317           PANIC ("unknown action `%s' (use -h for help)", *argv);
318         else if (!strcmp (*argv, a->name))
319           break;
320
321       /* Check for required arguments. */
322       for (i = 1; i < a->argc; i++)
323         if (argv[i] == NULL)
324           PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
325
326       /* Invoke action and advance. */
327       a->function (argv);
328       argv += a->argc;
329     }
330   
331 }
332
333 /* Prints a kernel command line help message and powers off the
334    machine. */
335 static void
336 usage (void)
337 {
338   printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
339           "Options must precede actions.\n"
340           "Actions are executed in the order specified.\n"
341           "\nAvailable actions:\n"
342 #ifdef USERPROG
343           "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
344 #else
345           "  run TEST           Run TEST.\n"
346 #endif
347 #ifdef FILESYS
348           "  ls                 List files in the root directory.\n"
349           "  cat FILE           Print FILE to the console.\n"
350           "  rm FILE            Delete FILE.\n"
351           "Use these actions indirectly via `pintos' -g and -p options:\n"
352           "  put FILE           Put FILE into file system from scratch disk.\n"
353           "  get FILE           Get FILE from file system into scratch disk.\n"
354 #endif
355           "\nOptions:\n"
356           "  -h                 Print this help message and power off.\n"
357           "  -q                 Power off VM after actions or on panic.\n"
358           "  -f                 Format file system disk during startup.\n"
359           "  -rs=SEED           Set random number seed to SEED.\n"
360           "  -mlfqs             Use multi-level feedback queue scheduler.\n"
361 #ifdef USERPROG
362           "  -ul=COUNT          Limit user memory to COUNT pages.\n"
363 #endif
364           );
365   power_off ();
366 }
367
368
369 /* Powers down the machine we're running on,
370    as long as we're running on Bochs or qemu. */
371 void
372 power_off (void) 
373 {
374   const char s[] = "Shutdown";
375   const char *p;
376
377 #ifdef FILESYS
378   filesys_done ();
379 #endif
380
381   print_stats ();
382
383   printf ("Powering off...\n");
384   serial_flush ();
385
386   for (p = s; *p != '\0'; p++)
387     outb (0x8900, *p);
388   for (;;);
389 }
390
391 /* Print statistics about Pintos execution. */
392 static void
393 print_stats (void) 
394 {
395   timer_print_stats ();
396   thread_print_stats ();
397 #ifdef FILESYS
398   disk_print_stats ();
399 #endif
400   console_print_stats ();
401   kbd_print_stats ();
402 #ifdef USERPROG
403   exception_print_stats ();
404 #endif
405 }