1 #include "threads/init.h"
11 #include "devices/kbd.h"
12 #include "devices/input.h"
13 #include "devices/pci.h"
14 #include "devices/usb.h"
15 #include "devices/serial.h"
16 #include "devices/timer.h"
17 #include "devices/vga.h"
18 #include "devices/rtc.h"
19 #include "threads/interrupt.h"
20 #include "threads/io.h"
21 #include "threads/loader.h"
22 #include "threads/malloc.h"
23 #include "threads/palloc.h"
24 #include "threads/pte.h"
25 #include "threads/thread.h"
27 #include "userprog/process.h"
28 #include "userprog/exception.h"
29 #include "userprog/gdt.h"
30 #include "userprog/syscall.h"
31 #include "userprog/tss.h"
33 #include "tests/threads/tests.h"
36 #include "devices/block.h"
37 #include "devices/ide.h"
38 #include "filesys/filesys.h"
39 #include "filesys/fsutil.h"
42 /* Page directory with kernel mappings only. */
43 uint32_t *base_page_dir;
44 bool base_page_dir_initialized = 0;
47 /* -f: Format the file system? */
48 static bool format_filesys;
50 /* -filesys, -scratch, -swap: Names of block devices to use,
51 overriding the defaults. */
52 static const char *filesys_bdev_name;
53 static const char *scratch_bdev_name;
55 static const char *swap_bdev_name;
59 /* -q: Power off after kernel tasks complete? */
60 bool power_off_when_done;
62 static void bss_init (void);
63 static void paging_init (void);
64 static void pci_zone_init (void);
66 static char **read_command_line (void);
67 static char **parse_options (char **argv);
68 static void run_actions (char **argv);
69 static void usage (void);
72 static void locate_block_devices (void);
73 static void locate_block_device (enum block_type, const char *name);
76 static void print_stats (void);
78 int main (void) NO_RETURN;
80 /* Pintos main program. */
89 /* Break command line into arguments and parse options. */
90 argv = read_command_line ();
91 argv = parse_options (argv);
93 /* Initialize ourselves as a thread so we can use locks,
94 then enable console locking. */
99 printf ("Pintos booting with %'"PRIu32" kB RAM...\n",
100 ram_pages * PGSIZE / 1024);
102 /* Initialize memory system. */
113 /* Initialize interrupt handlers. */
124 /* Start thread scheduler and enable interrupts. */
126 serial_init_queue ();
131 /* Initialize file system. */
134 locate_block_devices ();
135 filesys_init (format_filesys);
138 printf ("Boot complete.\n");
140 /* Run actions specified on kernel command line. */
144 if (power_off_when_done)
149 /* Clear the "BSS", a segment that should be initialized to
150 zeros. It isn't actually stored on disk or zeroed by the
151 kernel loader, so we have to zero it ourselves.
153 The start and end of the BSS segment is recorded by the
154 linker as _start_bss and _end_bss. See kernel.lds. */
158 extern char _start_bss, _end_bss;
159 memset (&_start_bss, 0, &_end_bss - &_start_bss);
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. */
171 extern char _start, _end_kernel_text;
173 pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
175 for (page = 0; page < ram_pages; page++)
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;
183 if (pd[pde_idx] == 0)
185 pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
186 pd[pde_idx] = pde_create_kernel (pt);
189 pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
194 /* Store the physical address of the page directory into CR3
195 aka PDBR (page directory base register). This activates our
196 new page tables immediately. See [IA32-v2a] "MOV--Move
197 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
198 of the Page Directory". */
199 asm volatile ("movl %0, %%cr3" : : "r" (vtop (base_page_dir)));
201 base_page_dir_initialized = 1;
204 /* initialize PCI zone at PCI_ADDR_ZONE_BEGIN - PCI_ADDR_ZONE_END*/
209 for (i = 0; i < PCI_ADDR_ZONE_PDES; i++)
211 size_t pde_idx = pd_no ((void *) PCI_ADDR_ZONE_BEGIN) + i;
215 pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
216 pde = pde_create_kernel (pt);
217 base_page_dir[pde_idx] = pde;
221 /* Breaks the kernel command line into words and returns them as
222 an argv-like array. */
224 read_command_line (void)
226 static char *argv[LOADER_ARGS_LEN / 2 + 1];
231 argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
232 p = ptov (LOADER_ARGS);
233 end = p + LOADER_ARGS_LEN;
234 for (i = 0; i < argc; i++)
237 PANIC ("command line arguments overflow");
240 p += strnlen (p, end - p) + 1;
244 /* Print kernel command line. */
245 printf ("Kernel command line:");
246 for (i = 0; i < argc; i++)
247 if (strchr (argv[i], ' ') == NULL)
248 printf (" %s", argv[i]);
250 printf (" '%s'", argv[i]);
256 /* Parses options in ARGV[]
257 and returns the first non-option argument. */
259 parse_options (char **argv)
261 for (; *argv != NULL && **argv == '-'; argv++)
264 char *name = strtok_r (*argv, "=", &save_ptr);
265 char *value = strtok_r (NULL, "", &save_ptr);
267 if (!strcmp (name, "-h"))
269 else if (!strcmp (name, "-q"))
270 power_off_when_done = true;
272 else if (!strcmp (name, "-f"))
273 format_filesys = true;
274 else if (!strcmp (name, "-filesys"))
275 filesys_bdev_name = value;
276 else if (!strcmp (name, "-scratch"))
277 scratch_bdev_name = value;
279 else if (!strcmp (name, "-swap"))
280 swap_bdev_name = value;
283 else if (!strcmp (name, "-rs"))
284 random_init (atoi (value));
285 else if (!strcmp (name, "-mlfqs"))
288 else if (!strcmp (name, "-ul"))
289 user_page_limit = atoi (value);
292 PANIC ("unknown option `%s' (use -h for help)", name);
295 /* Initialize the random number generator based on the system
296 time. This has no effect if an "-rs" option was specified.
298 When running under Bochs, this is not enough by itself to
299 get a good seed value, because the pintos script sets the
300 initial time to a predictable value, not to the local time,
301 for reproducibility. To fix this, give the "-r" option to
302 the pintos script to request real-time execution. */
303 random_init (rtc_get_time ());
308 /* Runs the task specified in ARGV[1]. */
310 run_task (char **argv)
312 const char *task = argv[1];
314 printf ("Executing '%s':\n", task);
316 process_wait (process_execute (task));
320 printf ("Execution of '%s' complete.\n", task);
323 /* Executes all of the actions specified in ARGV[]
324 up to the null pointer sentinel. */
326 run_actions (char **argv)
331 char *name; /* Action name. */
332 int argc; /* # of args, including action name. */
333 void (*function) (char **argv); /* Function to execute action. */
336 /* Table of supported actions. */
337 static const struct action actions[] =
339 {"run", 2, run_task},
341 {"ls", 1, fsutil_ls},
342 {"cat", 2, fsutil_cat},
343 {"rm", 2, fsutil_rm},
344 {"extract", 1, fsutil_extract},
345 {"append", 2, fsutil_append},
350 while (*argv != NULL)
352 const struct action *a;
355 /* Find action name. */
356 for (a = actions; ; a++)
358 PANIC ("unknown action `%s' (use -h for help)", *argv);
359 else if (!strcmp (*argv, a->name))
362 /* Check for required arguments. */
363 for (i = 1; i < a->argc; i++)
365 PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
367 /* Invoke action and advance. */
374 /* Prints a kernel command line help message and powers off the
379 printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
380 "Options must precede actions.\n"
381 "Actions are executed in the order specified.\n"
382 "\nAvailable actions:\n"
384 " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
386 " run TEST Run TEST.\n"
389 " ls List files in the root directory.\n"
390 " cat FILE Print FILE to the console.\n"
391 " rm FILE Delete FILE.\n"
392 "Use these actions indirectly via `pintos' -g and -p options:\n"
393 " extract Untar from scratch disk into file system.\n"
394 " get FILE Get FILE from file system into scratch disk.\n"
397 " -h Print this help message and power off.\n"
398 " -q Power off VM after actions or on panic.\n"
400 " -f Format file system disk during startup.\n"
401 " -filesys=BDEV Use BDEV for file system instead of default.\n"
402 " -scratch=BDEV Use BDEV for scratch instead of default.\n"
404 " -swap=BDEV Use BDEV for swap instead of default.\n"
407 " -rs=SEED Set random number seed to SEED.\n"
408 " -mlfqs Use multi-level feedback queue scheduler.\n"
410 " -ul=COUNT Limit user memory to COUNT pages.\n"
417 /* Figure out what disks to cast in the various Pintos roles. */
419 locate_block_devices (void)
421 locate_block_device (BLOCK_FILESYS, filesys_bdev_name);
422 locate_block_device (BLOCK_SCRATCH, scratch_bdev_name);
424 locate_block_device (BLOCK_SWAP, swap_bdev_name);
428 /* Figures out what block device to use for the given ROLE: the
429 block device with the given NAME, if NAME is non-null,
430 otherwise the first block device in probe order of type
433 locate_block_device (enum block_type role, const char *name)
435 struct block *block = NULL;
439 block = block_get_by_name (name);
441 PANIC ("No such block device \"%s\"", name);
445 for (block = block_first (); block != NULL; block = block_next (block))
446 if (block_type (block) == role)
452 printf ("%s: using %s\n", block_type_name (role), block_name (block));
453 block_set_role (role, block);
458 /* Powers down the machine we're running on,
459 as long as we're running on Bochs or QEMU. */
463 const char s[] = "Shutdown";
472 printf ("Powering off...\n");
475 for (p = s; *p != '\0'; p++)
477 asm volatile ("cli; hlt" : : : "memory");
478 printf ("still running...\n");
482 /* Print statistics about Pintos execution. */
486 timer_print_stats ();
487 thread_print_stats ();
489 block_print_stats ();
491 console_print_stats ();
494 exception_print_stats ();