1 #include "threads/init.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 "devices/rtc.h"
17 #include "threads/interrupt.h"
18 #include "threads/io.h"
19 #include "threads/loader.h"
20 #include "threads/malloc.h"
21 #include "threads/palloc.h"
22 #include "threads/pte.h"
23 #include "threads/thread.h"
25 #include "userprog/process.h"
26 #include "userprog/exception.h"
27 #include "userprog/gdt.h"
28 #include "userprog/syscall.h"
29 #include "userprog/tss.h"
31 #include "tests/threads/tests.h"
34 #include "devices/disk.h"
35 #include "filesys/filesys.h"
36 #include "filesys/fsutil.h"
39 /* Amount of physical memory, in 4 kB pages. */
42 /* Page directory with kernel mappings only. */
43 uint32_t *init_page_dir;
46 /* -f: Format the file system? */
47 static bool format_filesys;
50 /* -q: Power off after kernel tasks complete? */
51 bool power_off_when_done;
53 /* -r: Reboot after kernel tasks complete? */
54 static bool reboot_when_done;
56 /* -ul: Maximum number of pages to put into palloc's user pool. */
57 static size_t user_page_limit = SIZE_MAX;
59 static void ram_init (void);
60 static void paging_init (void);
62 static char **read_command_line (void);
63 static char **parse_options (char **argv);
64 static void run_actions (char **argv);
65 static void usage (void);
67 static void print_stats (void);
70 int main (void) NO_RETURN;
72 /* Pintos main program. */
78 /* Clear BSS and get machine's RAM size. */
81 /* Break command line into arguments and parse options. */
82 argv = read_command_line ();
83 argv = parse_options (argv);
85 /* Initialize ourselves as a thread so we can use locks,
86 then enable console locking. */
91 printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
93 /* Initialize memory system. */
94 palloc_init (user_page_limit);
104 /* Initialize interrupt handlers. */
114 /* Start thread scheduler and enable interrupts. */
116 serial_init_queue ();
120 /* Initialize file system. */
122 filesys_init (format_filesys);
125 printf ("Boot complete.\n");
127 /* Run actions specified on kernel command line. */
131 if (reboot_when_done)
134 if (power_off_when_done)
139 /* Clear BSS and obtain RAM size from loader. */
143 /* The "BSS" is a segment that should be initialized to zeros.
144 It isn't actually stored on disk or zeroed by the kernel
145 loader, so we have to zero it ourselves.
147 The start and end of the BSS segment is recorded by the
148 linker as _start_bss and _end_bss. See kernel.lds. */
149 extern char _start_bss, _end_bss;
150 memset (&_start_bss, 0, &_end_bss - &_start_bss);
152 /* Get RAM size from loader. See loader.S. */
153 ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS);
156 /* Populates the base page directory and page table with the
157 kernel virtual mapping, and then sets up the CPU to use the
158 new page directory. Points init_page_dir to the page
159 directory it creates.
161 At the time this function is called, the active page table
162 (set up by loader.S) only maps the first 4 MB of RAM, so we
163 should not try to use extravagant amounts of memory.
164 Fortunately, there is no need to do so. */
170 extern char _start, _end_kernel_text;
172 pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
174 for (page = 0; page < ram_pages; page++)
176 uintptr_t paddr = page * PGSIZE;
177 char *vaddr = ptov (paddr);
178 size_t pde_idx = pd_no (vaddr);
179 size_t pte_idx = pt_no (vaddr);
180 bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
182 if (pd[pde_idx] == 0)
184 pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
185 pd[pde_idx] = pde_create (pt);
188 pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
191 /* Store the physical address of the page directory into CR3
192 aka PDBR (page directory base register). This activates our
193 new page tables immediately. See [IA32-v2a] "MOV--Move
194 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
195 of the Page Directory". */
196 asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
199 /* Breaks the kernel command line into words and returns them as
200 an argv-like array. */
202 read_command_line (void)
204 static char *argv[LOADER_ARGS_LEN / 2 + 1];
209 argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
210 p = ptov (LOADER_ARGS);
211 end = p + LOADER_ARGS_LEN;
212 for (i = 0; i < argc; i++)
215 PANIC ("command line arguments overflow");
218 p += strnlen (p, end - p) + 1;
222 /* Print kernel command line. */
223 printf ("Kernel command line:");
224 for (i = 0; i < argc; i++)
225 if (strchr (argv[i], ' ') == NULL)
226 printf (" %s", argv[i]);
228 printf (" '%s'", argv[i]);
234 /* Parses options in ARGV[]
235 and returns the first non-option argument. */
237 parse_options (char **argv)
239 for (; *argv != NULL && **argv == '-'; argv++)
242 char *name = strtok_r (*argv, "=", &save_ptr);
243 char *value = strtok_r (NULL, "", &save_ptr);
245 if (!strcmp (name, "-h"))
247 else if (!strcmp (name, "-q"))
248 power_off_when_done = true;
249 else if (!strcmp (name, "-r"))
250 reboot_when_done = true;
252 else if (!strcmp (name, "-f"))
253 format_filesys = true;
255 else if (!strcmp (name, "-rs"))
256 random_init (atoi (value));
257 else if (!strcmp (name, "-mlfqs"))
260 else if (!strcmp (name, "-ul"))
261 user_page_limit = atoi (value);
264 PANIC ("unknown option `%s' (use -h for help)", name);
267 /* Initialize the random number generator based on the system
268 time. This has no effect if an "-rs" option was specified.
270 When running under Bochs, this is not enough by itself to
271 get a good seed value, because the pintos script sets the
272 initial time to a predictable value, not to the local time,
273 for reproducibility. To fix this, give the "-r" option to
274 the pintos script to request real-time execution. */
275 random_init (rtc_get_time ());
280 /* Runs the task specified in ARGV[1]. */
282 run_task (char **argv)
284 const char *task = argv[1];
286 printf ("Executing '%s':\n", task);
288 process_wait (process_execute (task));
292 printf ("Execution of '%s' complete.\n", task);
295 /* Executes all of the actions specified in ARGV[]
296 up to the null pointer sentinel. */
298 run_actions (char **argv)
303 char *name; /* Action name. */
304 int argc; /* # of args, including action name. */
305 void (*function) (char **argv); /* Function to execute action. */
308 /* Table of supported actions. */
309 static const struct action actions[] =
311 {"run", 2, run_task},
313 {"ls", 1, fsutil_ls},
314 {"cat", 2, fsutil_cat},
315 {"rm", 2, fsutil_rm},
316 {"extract", 1, fsutil_extract},
317 {"append", 2, fsutil_append},
322 while (*argv != NULL)
324 const struct action *a;
327 /* Find action name. */
328 for (a = actions; ; a++)
330 PANIC ("unknown action `%s' (use -h for help)", *argv);
331 else if (!strcmp (*argv, a->name))
334 /* Check for required arguments. */
335 for (i = 1; i < a->argc; i++)
337 PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
339 /* Invoke action and advance. */
346 /* Prints a kernel command line help message and powers off the
351 printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
352 "Options must precede actions.\n"
353 "Actions are executed in the order specified.\n"
354 "\nAvailable actions:\n"
356 " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
358 " run TEST Run TEST.\n"
361 " ls List files in the root directory.\n"
362 " cat FILE Print FILE to the console.\n"
363 " rm FILE Delete FILE.\n"
364 "Use these actions indirectly via `pintos' -g and -p options:\n"
365 " extract Untar from scratch disk into file system.\n"
366 " append FILE Append FILE to tar file on scratch disk.\n"
369 " -h Print this help message and power off.\n"
370 " -q Power off VM after actions or on panic.\n"
371 " -r Reboot after actions.\n"
372 " -f Format file system disk during startup.\n"
373 " -rs=SEED Set random number seed to SEED.\n"
374 " -mlfqs Use multi-level feedback queue scheduler.\n"
376 " -ul=COUNT Limit user memory to COUNT pages.\n"
382 /* Keyboard control register port. */
383 #define CONTROL_REG 0x64
385 /* Reboots the machine via the keyboard controller. */
391 printf ("Rebooting...\n");
393 /* See [kbd] for details on how to program the keyboard
395 for (i = 0; i < 100; i++)
399 /* Poll keyboard controller's status byte until
400 * 'input buffer empty' is reported. */
401 for (j = 0; j < 0x10000; j++)
403 if ((inb (CONTROL_REG) & 0x02) == 0)
410 /* Pulse bit 0 of the output port P2 of the keyboard controller.
411 * This will reset the CPU. */
412 outb (CONTROL_REG, 0xfe);
417 /* Powers down the machine we're running on,
418 as long as we're running on Bochs or QEMU. */
422 const char s[] = "Shutdown";
431 printf ("Powering off...\n");
434 for (p = s; *p != '\0'; p++)
436 asm volatile ("cli; hlt" : : : "memory");
437 printf ("still running...\n");
441 /* Print statistics about Pintos execution. */
445 timer_print_stats ();
446 thread_print_stats ();
450 console_print_stats ();
453 exception_print_stats ();