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