Support accurate short delays in the timer code, to speed up disk
[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/mmu.h"
20 #include "threads/palloc.h"
21 #include "threads/test.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 #endif
30 #ifdef FILESYS
31 #include "devices/disk.h"
32 #include "filesys/filesys.h"
33 #include "filesys/fsutil.h"
34 #endif
35
36 /* Amount of physical memory, in 4 kB pages. */
37 size_t ram_pages;
38
39 /* Page directory with kernel mappings only. */
40 uint32_t *base_page_dir;
41
42 #ifdef FILESYS
43 /* -f: Format the filesystem? */
44 static bool format_filesys;
45 #endif
46
47 #ifdef USERPROG
48 /* -ex: Initial program to run. */
49 static char *initial_program;
50 #endif
51
52 /* -q: Power off after kernel tasks complete? */
53 bool power_off_when_done;
54
55 static void ram_init (void);
56 static void paging_init (void);
57 static void argv_init (void);
58 static void print_stats (void);
59
60 int main (void) NO_RETURN;
61
62 int
63 main (void)
64 {
65   /* Clear BSS and get machine's RAM size. */  
66   ram_init ();
67
68   /* Initialize ourselves as a thread so we can use locks. */
69   thread_init ();
70
71   /* Initialize the console so we can use printf(). */
72   vga_init ();
73   serial_init_poll ();
74   console_init ();
75
76   /* Greet user. */
77   printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
78
79   /* Parse command line. */
80   argv_init ();
81
82   /* Initialize memory system. */
83   palloc_init ();
84   malloc_init ();
85   paging_init ();
86
87   /* Segmentation. */
88 #ifdef USERPROG
89   tss_init ();
90   gdt_init ();
91 #endif
92
93   /* Set random seed if argv_init() didn't. */
94   random_init (0);
95
96   /* Initialize interrupt handlers. */
97   intr_init ();
98   timer_init ();
99   kbd_init ();
100 #ifdef USERPROG
101   exception_init ();
102   syscall_init ();
103 #endif
104
105   /* Start thread scheduler and enable interrupts. */
106   thread_start ();
107   serial_init_queue ();
108   timer_calibrate ();
109
110 #ifdef FILESYS
111   /* Initialize filesystem. */
112   disk_init ();
113   filesys_init (format_filesys);
114   fsutil_run ();
115 #endif
116
117   printf ("Boot complete.\n");
118   
119 #ifdef USERPROG
120   /* Run a user program. */
121   if (initial_program != NULL)
122     {
123       tid_t tid;
124       printf ("\nExecuting '%s':\n", initial_program);
125       tid = process_execute (initial_program);
126 #ifdef THREAD_JOIN_IMPLEMENTED
127       if (tid != TID_ERROR)
128         thread_join (tid);
129 #endif
130     }
131 #else
132   /* Run the compiled-in test function. */
133   test ();
134 #endif
135
136   /* Finish up. */
137   if (power_off_when_done) 
138     power_off ();
139   else 
140     thread_exit ();
141 }
142 \f
143 /* Clear BSS and obtain RAM size from loader. */
144 static void
145 ram_init (void) 
146 {
147   /* The "BSS" is a segment that should be initialized to zeros.
148      It isn't actually stored on disk or zeroed by the kernel
149      loader, so we have to zero it ourselves.
150
151      The start and end of the BSS segment is recorded by the
152      linker as _start_bss and _end_bss.  See kernel.lds. */
153   extern char _start_bss, _end_bss;
154   memset (&_start_bss, 0, &_end_bss - &_start_bss);
155
156   /* Get RAM size from loader.  See loader.S. */
157   ram_pages = *(uint32_t *) ptov (LOADER_RAM_PAGES);
158 }
159
160 /* Populates the base page directory and page table with the
161    kernel virtual mapping, and then sets up the CPU to use the
162    new page directory.  Points base_page_dir to the page
163    directory it creates.
164
165    At the time this function is called, the active page table
166    (set up by loader.S) only maps the first 4 MB of RAM, so we
167    should not try to use extravagant amounts of memory.
168    Fortunately, there is no need to do so. */
169 static void
170 paging_init (void)
171 {
172   uint32_t *pd, *pt;
173   size_t page;
174
175   pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
176   pt = NULL;
177   for (page = 0; page < ram_pages; page++) 
178     {
179       uintptr_t paddr = page * PGSIZE;
180       void *vaddr = ptov (paddr);
181       size_t pde_idx = pd_no (vaddr);
182       size_t pte_idx = pt_no (vaddr);
183
184       if (pd[pde_idx] == 0)
185         {
186           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
187           pd[pde_idx] = pde_create (pt);
188         }
189
190       pt[pte_idx] = pte_create_kernel (vaddr, true);
191     }
192
193   asm volatile ("movl %0,%%cr3" :: "r" (vtop (base_page_dir)));
194 }
195
196 /* Parses the command line. */
197 static void
198 argv_init (void) 
199 {
200   char *cmd_line, *pos;
201   char *argv[LOADER_CMD_LINE_LEN / 2 + 2];
202   int argc = 0;
203   int i;
204
205   /* The command line is made up of null terminated strings
206      followed by an empty string.  Break it up into words. */
207   cmd_line = pos = ptov (LOADER_CMD_LINE);
208   printf ("Kernel command line:");
209   while (pos < cmd_line + LOADER_CMD_LINE_LEN)
210     {
211       ASSERT (argc < LOADER_CMD_LINE_LEN / 2);
212       if (*pos == '\0')
213         break;
214       argv[argc++] = pos;
215       printf (" %s", pos);
216       pos = strchr (pos, '\0') + 1;
217     }
218   printf ("\n");
219   argv[argc] = "";
220   argv[argc + 1] = "";
221
222   /* Parse the words. */
223   for (i = 0; i < argc; i++)
224     if (!strcmp (argv[i], "-rs")) 
225       random_init (atoi (argv[++i]));
226     else if (!strcmp (argv[i], "-d")) 
227       debug_enable (argv[++i]);
228     else if (!strcmp (argv[i], "-q"))
229       power_off_when_done = true;
230 #ifdef USERPROG
231     else if (!strcmp (argv[i], "-ex")) 
232       initial_program = argv[++i];
233     else if (!strcmp (argv[i], "-ul"))
234       user_page_limit = atoi (argv[++i]);
235 #endif
236 #ifdef FILESYS
237     else if (!strcmp (argv[i], "-f"))
238       format_filesys = true;
239     else if (!strcmp (argv[i], "-ci")) 
240       {
241         fsutil_copyin_file = argv[++i]; 
242         fsutil_copyin_size = atoi (argv[++i]); 
243       }
244     else if (!strcmp (argv[i], "-co"))
245       fsutil_copyout_file = argv[++i];
246     else if (!strcmp (argv[i], "-p")) 
247       fsutil_print_file = argv[++i];
248     else if (!strcmp (argv[i], "-r"))
249       fsutil_remove_file = argv[++i];
250     else if (!strcmp (argv[i], "-ls"))
251       fsutil_list_files = true;
252     else if (!strcmp (argv[i], "-D"))
253       fsutil_dump_filesys = true;
254 #endif
255     else if (!strcmp (argv[i], "-u"))
256       {
257         printf (
258           "Kernel options:\n"
259           " -rs SEED            Seed random seed to SEED.\n"
260           " -d CLASS[,...]      Enable the given classes of debug messages.\n"
261 #ifdef USERPROG
262           " -ex 'PROG [ARG...]' Run PROG, passing the optional arguments.\n"
263           " -ul USER_MAX        Limit user memory to USER_MAX pages.\n"
264 #endif
265 #ifdef FILESYS
266           " -f                  Format the filesystem disk (hdb or hd0:1).\n"
267           " -ci FILENAME SIZE   Copy SIZE bytes from the scratch disk (hdc\n"
268           "                     or hd1:0) into the filesystem as FILENAME\n"
269           " -co FILENAME        Copy FILENAME to the scratch disk, with\n"
270           "                     size at start of sector 0 and data afterward\n"
271           " -p FILENAME         Print the contents of FILENAME\n"
272           " -r FILENAME         Delete FILENAME\n"
273           " -ls                 List the files in the filesystem\n"
274           " -D                  Dump complete filesystem contents\n"
275 #endif
276           " -q                  Power off after doing requested actions.\n"
277           " -u                  Print this help message and power off.\n"
278           );
279         power_off ();
280       }
281     else 
282       PANIC ("unknown option `%s' (use -u for help)", argv[i]);
283 }
284
285 /* Powers down the machine we're running on,
286    as long as we're running on Bochs or qemu. */
287 void
288 power_off (void) 
289 {
290   const char s[] = "Shutdown";
291   const char *p;
292
293 #ifdef FILESYS
294   filesys_done ();
295 #endif
296
297   print_stats ();
298
299   printf ("Powering off...\n");
300   serial_flush ();
301
302   for (p = s; *p != '\0'; p++)
303     outb (0x8900, *p);
304   for (;;);
305 }
306
307 /* Print statistics about Pintos execution. */
308 static void
309 print_stats (void) 
310 {
311   timer_print_stats ();
312   thread_print_stats ();
313 #ifdef FILESYS
314   disk_print_stats ();
315 #endif
316   console_print_stats ();
317   kbd_print_stats ();
318 #ifdef USERPROG
319   exception_print_stats ();
320 #endif
321 }