diff options
Diffstat (limited to 'src/threads/init.c')
| -rw-r--r-- | src/threads/init.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/src/threads/init.c b/src/threads/init.c new file mode 100644 index 0000000..43f491f --- /dev/null +++ b/src/threads/init.c @@ -0,0 +1,429 @@ +#include "threads/init.h" +#include <console.h> +#include <debug.h> +#include <limits.h> +#include <random.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "devices/kbd.h" +#include "devices/input.h" +#include "devices/serial.h" +#include "devices/timer.h" +#include "devices/vga.h" +#include "threads/interrupt.h" +#include "threads/io.h" +#include "threads/loader.h" +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "threads/pte.h" +#include "threads/thread.h" +#ifdef USERPROG +#include "userprog/process.h" +#include "userprog/exception.h" +#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" +#include "filesys/filesys.h" +#include "filesys/fsutil.h" +#endif + +/* Amount of physical memory, in 4 kB pages. */ +size_t ram_pages; + +/* Page directory with kernel mappings only. */ +uint32_t *base_page_dir; + +#ifdef FILESYS +/* -f: Format the file system? */ +static bool format_filesys; +#endif + +/* -q: Power off after kernel tasks complete? */ +bool power_off_when_done = false; +/* -Q: Force power off by klaar@ida... */ +bool force_off_when_done = false; +/* -tcf: Simulate failure in thread_create klaar@ida... */ +int thread_create_limit = 0; /* infinite */ + +static void ram_init (void); +static void paging_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) +{ + 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 (); + process_init (); + console_init (); + + /* Greet user. */ + printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024); + + /* Initialize memory system. */ + palloc_init (); + malloc_init (); + paging_init (); + + /* Segmentation. */ +#ifdef USERPROG + tss_init (); + gdt_init (); +#endif + + /* Initialize interrupt handlers. */ + intr_init (); + timer_init (); + kbd_init (); + input_init (); +#ifdef USERPROG + exception_init (); + syscall_init (); +#endif + + /* Start thread scheduler and enable interrupts. */ + thread_start (); + serial_init_queue (); + timer_calibrate (); + +#ifdef FILESYS + /* Initialize file system. */ + disk_init (); + filesys_init (format_filesys); +#endif + + printf ("Boot complete.\n"); + + /* Run actions specified on kernel command line. */ + run_actions (argv); + + /* Finish up. */ + if (power_off_when_done) + power_off (); + + thread_exit (); +} + +/* Clear BSS and obtain RAM size from loader. */ +static void +ram_init (void) +{ + /* The "BSS" is a segment that should be initialized to zeros. + It isn't actually stored on disk or zeroed by the kernel + loader, so we have to zero it ourselves. + + The start and end of the BSS segment is recorded by the + linker as _start_bss and _end_bss. See kernel.lds. */ + extern char _start_bss, _end_bss; + memset (&_start_bss, 0, &_end_bss - &_start_bss); + + /* Get RAM size from loader. See loader.S. */ + ram_pages = *(uint32_t *) ptov (LOADER_RAM_PGS); +} + +/* Populates the base page directory and page table with the + kernel virtual mapping, and then sets up the CPU to use the + new page directory. Points base_page_dir to the page + directory it creates. + + At the time this function is called, the active page table + (set up by loader.S) only maps the first 4 MB of RAM, so we + should not try to use extravagant amounts of memory. + Fortunately, there is no need to do so. */ +static void +paging_init (void) +{ + uint32_t *pd, *pt; + size_t page; + extern char _start, _end_kernel_text; + + 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; + 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_page (PAL_ASSERT | PAL_ZERO); + pd[pde_idx] = pde_create (pt); + } + + pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text); + } + + /* 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))); +} + +/* Breaks the kernel command line into words and returns them as + an argv-like array. */ +static char ** +read_command_line (void) +{ + static char *argv[LOADER_ARGS_LEN / 2 + 1]; + char *p, *end; + int argc; + int i; + + argc = *(uint32_t *) ptov (LOADER_ARG_CNT); + p = ptov (LOADER_ARGS); + end = p + LOADER_ARGS_LEN; + for (i = 0; i < argc; i++) + { + if (p >= end) + PANIC ("command line arguments overflow"); + + argv[i] = p; + p += strnlen (p, end - p) + 1; + } + argv[argc] = NULL; + + /* Print kernel command line. */ + printf ("Kernel command line:"); + for (i = 0; i < argc; i++) + 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 (), power_off_when_done = force_off_when_done = true; + else if (!strcmp (name, "-q")) + power_off_when_done = true; + else if (!strcmp (name, "-Q")) // klaar@ida + power_off_when_done = force_off_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); + else if (!strcmp (name, "-fl")) // klaar@ida + free_page_limit = atoi (value); + else if (!strcmp (name, "-tcl")) // klaar@ida + thread_create_limit = atoi (value); +#endif + else + PANIC ("unknown option `%s' (use -h for help)", name); + } + + 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 + 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 + {"ls", 1, fsutil_ls}, + {"cat", 2, fsutil_cat}, + {"rm", 2, fsutil_rm}, + {"put", 2, fsutil_put}, + {"get", 2, fsutil_get}, +#endif + {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 + " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n" +#else + " run TEST Run TEST.\n" +#endif +#ifdef FILESYS + " 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" + " put FILE Put FILE into file system from scratch disk.\n" + " get FILE Get FILE from file system into 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" + " -q Force off VM after actions or on panic.\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" + " -fl=COUNT Limit free memory to COUNT pages.\n" + " -tcl=N Fail at call N to thread_create.\n" +#endif + ); + + /* klaar@ida disabled due to threads and locks not initialized + * yet... and power_off() now use locks. + */ +// power_off (); +} + + +/* Powers down the machine we're running on, + as long as we're running on Bochs or QEMU. */ +void +power_off (void) +{ + const char s[] = "Shutdown"; + const char *p; + + printf ("# Preparing to power off...\n"); + DEBUG_thread_poweroff_check( force_off_when_done ); + +#ifdef FILESYS + filesys_done (); +#endif + + print_stats (); + + printf ("Powering off...\n"); + serial_flush (); + + /* klaar,filst 2014-01 + ACPI shutdown should use: + outw(PM1a_CNT, SLP_TYPa | SLP_EN ); + Gathering of the corect values for the parameters is not easy. + This works for QEMU and Bochs. It's not portable. + */ + outw(0xB004, 0x2000); + + /* This is APM Shutdown */ + 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 +} |
