X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pintos-anon;a=blobdiff_plain;f=src%2Fthreads%2Finit.c;h=ac9460307d3d2b3d5596713bedb8c040db65bf0c;hp=0d9c69984603eaaa6a69bf4fac58c4af9e54ce54;hb=cc5c971c3cc498d528a2f74f4dc2f8e27a690311;hpb=f0612244c44f4b4f0bc79e3fc882e9f74bd4a3f4 diff --git a/src/threads/init.c b/src/threads/init.c index 0d9c699..ac94603 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -9,16 +9,17 @@ #include #include #include "devices/kbd.h" +#include "devices/input.h" #include "devices/serial.h" #include "devices/timer.h" #include "devices/vga.h" +#include "devices/rtc.h" #include "threads/interrupt.h" #include "threads/io.h" #include "threads/loader.h" #include "threads/malloc.h" -#include "threads/mmu.h" #include "threads/palloc.h" -#include "threads/test.h" +#include "threads/pte.h" #include "threads/thread.h" #ifdef USERPROG #include "userprog/process.h" @@ -26,6 +27,8 @@ #include "userprog/gdt.h" #include "userprog/syscall.h" #include "userprog/tss.h" +#else +#include "tests/threads/tests.h" #endif #ifdef FILESYS #include "devices/disk.h" @@ -40,56 +43,66 @@ size_t ram_pages; uint32_t *base_page_dir; #ifdef FILESYS -/* -f: Format the filesystem? */ +/* -f: Format the file system? */ static bool format_filesys; #endif -#ifdef USERPROG -/* -ex: Initial program to run. */ -static char *initial_program; -#endif +/* -q: Power off after kernel tasks complete? */ +bool power_off_when_done; -/* -q: Power off after running requested actions? */ -static bool do_power_off; +/* -r: Reboot after kernel tasks complete? */ +static bool reboot_when_done; static void ram_init (void); static void paging_init (void); -static void argv_init (void); + +static char **read_command_line (void); +static char **parse_options (char **argv); +static void run_actions (char **argv); +static void usage (void); + +static void print_stats (void); + int main (void) NO_RETURN; +/* Pintos main program. */ int main (void) { - /* Initialize everything needed for printf() first. */ + char **argv; + + /* Clear BSS and get machine's RAM size. */ ram_init (); + + /* Break command line into arguments and parse options. */ + argv = read_command_line (); + argv = parse_options (argv); + + /* Initialize ourselves as a thread so we can use locks, + then enable console locking. */ thread_init (); - vga_init (); - serial_init_poll (); - console_init (); + console_init (); /* Greet user. */ - printf ("Pintos booting with %'zd kB RAM...\n", ram_pages * (PGSIZE / 1024)); - - /* Parse command line. */ - argv_init (); + printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024); - /* Initialize memory system, segments, paging. */ + /* Initialize memory system. */ palloc_init (); + malloc_init (); paging_init (); + + /* Segmentation. */ #ifdef USERPROG tss_init (); gdt_init (); #endif - malloc_init (); - - /* Set random seed if argv_init() didn't. */ - random_init (0); /* Initialize interrupt handlers. */ intr_init (); timer_init (); kbd_init (); + input_init (); #ifdef USERPROG exception_init (); syscall_init (); @@ -98,31 +111,25 @@ main (void) /* Start thread scheduler and enable interrupts. */ thread_start (); serial_init_queue (); + timer_calibrate (); #ifdef FILESYS - /* Initialize filesystem. */ + /* Initialize file system. */ disk_init (); filesys_init (format_filesys); - fsutil_run (); #endif printf ("Boot complete.\n"); -#ifdef USERPROG - /* Run a user program. */ - if (initial_program != NULL) - { - printf ("\nExecuting '%s':\n", initial_program); - process_execute (initial_program); - } -#else - test (); -#endif + /* Run actions specified on kernel command line. */ + run_actions (argv); - if (do_power_off) - power_off (); + /* Finish up. */ + if (reboot_when_done) + reboot (); - /* Terminate this thread. */ + if (power_off_when_done) + power_off (); thread_exit (); } @@ -140,7 +147,7 @@ ram_init (void) memset (&_start_bss, 0, &_end_bss - &_start_bss); /* Get RAM size from loader. See loader.S. */ - ram_pages = *(uint32_t *) ptov (LOADER_RAM_PAGES); + ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS); } /* Populates the base page directory and page table with the @@ -157,116 +164,252 @@ paging_init (void) { uint32_t *pd, *pt; size_t page; + extern char _start, _end_kernel_text; - pd = base_page_dir = palloc_get (PAL_ASSERT | PAL_ZERO); + pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO); pt = NULL; for (page = 0; page < ram_pages; page++) { uintptr_t paddr = page * PGSIZE; - void *vaddr = ptov (paddr); + char *vaddr = ptov (paddr); size_t pde_idx = pd_no (vaddr); size_t pte_idx = pt_no (vaddr); + bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text; if (pd[pde_idx] == 0) { - pt = palloc_get (PAL_ASSERT | PAL_ZERO); + pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); pd[pde_idx] = pde_create (pt); } - pt[pte_idx] = pte_create_kernel (vaddr, true); + pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text); } - asm volatile ("movl %0,%%cr3" :: "r" (vtop (base_page_dir))); + /* Store the physical address of the page directory into CR3 + aka PDBR (page directory base register). This activates our + new page tables immediately. See [IA32-v2a] "MOV--Move + to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address + of the Page Directory". */ + asm volatile ("movl %0, %%cr3" : : "r" (vtop (base_page_dir))); } -/* Parses the command line. */ -static void -argv_init (void) +/* Breaks the kernel command line into words and returns them as + an argv-like array. */ +static char ** +read_command_line (void) { - char *cmd_line, *pos; - char *argv[LOADER_CMD_LINE_LEN / 2 + 2]; - int argc = 0; + static char *argv[LOADER_ARGS_LEN / 2 + 1]; + char *p, *end; + int argc; int i; - /* The command line is made up of null terminated strings - followed by an empty string. Break it up into words. */ - cmd_line = pos = ptov (LOADER_CMD_LINE); - printf ("Kernel command line:"); - while (pos < cmd_line + LOADER_CMD_LINE_LEN) + argc = *(uint32_t *) ptov (LOADER_ARG_CNT); + p = ptov (LOADER_ARGS); + end = p + LOADER_ARGS_LEN; + for (i = 0; i < argc; i++) { - ASSERT (argc < LOADER_CMD_LINE_LEN / 2); - if (*pos == '\0') - break; - argv[argc++] = pos; - printf (" %s", pos); - pos = strchr (pos, '\0') + 1; + if (p >= end) + PANIC ("command line arguments overflow"); + + argv[i] = p; + p += strnlen (p, end - p) + 1; } - printf ("\n"); - argv[argc] = ""; - argv[argc + 1] = ""; + argv[argc] = NULL; - /* Parse the words. */ + /* Print kernel command line. */ + printf ("Kernel command line:"); for (i = 0; i < argc; i++) - if (!strcmp (argv[i], "-rs")) - random_init (atoi (argv[++i])); - else if (!strcmp (argv[i], "-d")) - debug_enable (argv[++i]); - else if (!strcmp (argv[i], "-q")) - do_power_off = true; + if (strchr (argv[i], ' ') == NULL) + printf (" %s", argv[i]); + else + printf (" '%s'", argv[i]); + printf ("\n"); + + return argv; +} + +/* Parses options in ARGV[] + and returns the first non-option argument. */ +static char ** +parse_options (char **argv) +{ + for (; *argv != NULL && **argv == '-'; argv++) + { + char *save_ptr; + char *name = strtok_r (*argv, "=", &save_ptr); + char *value = strtok_r (NULL, "", &save_ptr); + + if (!strcmp (name, "-h")) + usage (); + else if (!strcmp (name, "-q")) + power_off_when_done = true; + else if (!strcmp (name, "-r")) + reboot_when_done = true; +#ifdef FILESYS + else if (!strcmp (name, "-f")) + format_filesys = true; +#endif + else if (!strcmp (name, "-rs")) + random_init (atoi (value)); + else if (!strcmp (name, "-mlfqs")) + thread_mlfqs = true; +#ifdef USERPROG + else if (!strcmp (name, "-ul")) + user_page_limit = atoi (value); +#endif + else + PANIC ("unknown option `%s' (use -h for help)", name); + } + + /* Initialize the random number generator based on the system + time. This has no effect if an "-rs" option was specified. + + When running under Bochs, this is not enough by itself to + get a good seed value, because the pintos script sets the + initial time to a predictable value, not to the local time, + for reproducibility. To fix this, give the "-r" option to + the pintos script to request real-time execution. */ + random_init (rtc_get_time ()); + + return argv; +} + +/* Runs the task specified in ARGV[1]. */ +static void +run_task (char **argv) +{ + const char *task = argv[1]; + + printf ("Executing '%s':\n", task); #ifdef USERPROG - else if (!strcmp (argv[i], "-ex")) - initial_program = argv[++i]; + process_wait (process_execute (task)); +#else + run_test (task); #endif + printf ("Execution of '%s' complete.\n", task); +} + +/* Executes all of the actions specified in ARGV[] + up to the null pointer sentinel. */ +static void +run_actions (char **argv) +{ + /* An action. */ + struct action + { + char *name; /* Action name. */ + int argc; /* # of args, including action name. */ + void (*function) (char **argv); /* Function to execute action. */ + }; + + /* Table of supported actions. */ + static const struct action actions[] = + { + {"run", 2, run_task}, #ifdef FILESYS - else if (!strcmp (argv[i], "-f")) - format_filesys = true; - else if (!strcmp (argv[i], "-ci")) - { - fsutil_copyin_file = argv[++i]; - fsutil_copyin_size = atoi (argv[++i]); - } - else if (!strcmp (argv[i], "-co")) - fsutil_copyout_file = argv[++i]; - else if (!strcmp (argv[i], "-p")) - fsutil_print_file = argv[++i]; - else if (!strcmp (argv[i], "-r")) - fsutil_remove_file = argv[++i]; - else if (!strcmp (argv[i], "-ls")) - fsutil_list_files = true; - else if (!strcmp (argv[i], "-D")) - fsutil_dump_filesys = true; + {"ls", 1, fsutil_ls}, + {"cat", 2, fsutil_cat}, + {"rm", 2, fsutil_rm}, + {"extract", 1, fsutil_extract}, + {"append", 2, fsutil_append}, #endif - else if (!strcmp (argv[i], "-u")) - { - printf ( - "Kernel options:\n" - " -rs SEED Seed random seed to SEED.\n" - " -d CLASS[,...] Enable the given classes of debug messages.\n" + {NULL, 0, NULL}, + }; + + while (*argv != NULL) + { + const struct action *a; + int i; + + /* Find action name. */ + for (a = actions; ; a++) + if (a->name == NULL) + PANIC ("unknown action `%s' (use -h for help)", *argv); + else if (!strcmp (*argv, a->name)) + break; + + /* Check for required arguments. */ + for (i = 1; i < a->argc; i++) + if (argv[i] == NULL) + PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1); + + /* Invoke action and advance. */ + a->function (argv); + argv += a->argc; + } + +} + +/* Prints a kernel command line help message and powers off the + machine. */ +static void +usage (void) +{ + printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n" + "Options must precede actions.\n" + "Actions are executed in the order specified.\n" + "\nAvailable actions:\n" #ifdef USERPROG - " -ex 'PROG [ARG...]' Run PROG, passing the optional arguments.\n" + " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n" +#else + " run TEST Run TEST.\n" #endif #ifdef FILESYS - " -f Format the filesystem disk (hdb or hd0:1).\n" - " -ci FILENAME SIZE Copy SIZE bytes from the scratch disk (hdc\n" - " or hd1:0) into the filesystem as FILENAME\n" - " -co FILENAME Copy FILENAME to the scratch disk, with\n" - " size at start of sector 0 and data afterward\n" - " -p FILENAME Print the contents of FILENAME\n" - " -r FILENAME Delete FILENAME\n" - " -ls List the files in the filesystem\n" - " -D Dump complete filesystem contents\n" + " ls List files in the root directory.\n" + " cat FILE Print FILE to the console.\n" + " rm FILE Delete FILE.\n" + "Use these actions indirectly via `pintos' -g and -p options:\n" + " extract Untar from scratch disk into file system.\n" + " append FILE Append FILE to tar file on scratch disk.\n" +#endif + "\nOptions:\n" + " -h Print this help message and power off.\n" + " -q Power off VM after actions or on panic.\n" + " -r Reboot after actions.\n" + " -f Format file system disk during startup.\n" + " -rs=SEED Set random number seed to SEED.\n" + " -mlfqs Use multi-level feedback queue scheduler.\n" +#ifdef USERPROG + " -ul=COUNT Limit user memory to COUNT pages.\n" #endif - " -q Power off after doing requested actions.\n" - " -u Print this help message and power off.\n" ); - power_off (); - } - else - PANIC ("unknown option `%s' (use -u for help)", argv[i]); + power_off (); +} + + +/* Reboots the machine we're running on. */ +void +reboot (void) +{ + int i; + + printf ("Rebooting...\n"); + + /* based on reboot.c code by Osamu Tomita + * See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html */ + for (i = 0; i < 100; i++) { + int j; + + /* Poll keyboard controller's status byte until + * 'input buffer empty' is reported, so it's ok to write */ + for (j = 0; j < 0x10000; j++) + { + if ((inb (0x64) & 0x02) == 0) + break; + timer_udelay (2); + } + + timer_udelay (50); + + /* Pulse bit 0 of the output port P2 of the keyboard controller. + * This will reset the CPU. */ + outb (0x64, 0xfe); + timer_udelay (50); + } } /* Powers down the machine we're running on, - as long as we're running on Bochs or qemu. */ + as long as we're running on Bochs or QEMU. */ void power_off (void) { @@ -277,8 +420,30 @@ power_off (void) filesys_done (); #endif + print_stats (); + printf ("Powering off...\n"); + serial_flush (); + for (p = s; *p != '\0'; p++) outb (0x8900, *p); + asm volatile ("cli; hlt" : : : "memory"); + printf ("still running...\n"); for (;;); } + +/* Print statistics about Pintos execution. */ +static void +print_stats (void) +{ + timer_print_stats (); + thread_print_stats (); +#ifdef FILESYS + disk_print_stats (); +#endif + console_print_stats (); + kbd_print_stats (); +#ifdef USERPROG + exception_print_stats (); +#endif +}