X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pintos-anon;a=blobdiff_plain;f=src%2Fthreads%2Finit.c;h=cebec2c817f5954b9accf9558dbd36822c1fb8f4;hp=79cd3d0309bcb7823c3aa2ef7d48055782df1047;hb=a03618133f7df0954802a470a4bee7674f7aed45;hpb=8063d8f3b3a778e9a15eff66dfd1b08652bae523 diff --git a/src/threads/init.c b/src/threads/init.c index 79cd3d0..cebec2c 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -1,199 +1,429 @@ -#include "init.h" -#include -#include +#include "threads/init.h" +#include +#include +#include #include -#include "debug.h" -#include "interrupt.h" -#include "io.h" -#include "kbd.h" -#include "lib.h" -#include "loader.h" -#include "malloc.h" -#include "mmu.h" -#include "paging.h" -#include "palloc.h" -#include "random.h" -#include "serial.h" -#include "thread.h" -#include "timer.h" -#include "vga.h" +#include +#include +#include +#include +#include +#include "devices/kbd.h" +#include "devices/input.h" +#include "devices/serial.h" +#include "devices/shutdown.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/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/block.h" +#include "devices/ide.h" +#include "filesys/filesys.h" +#include "filesys/fsutil.h" +#endif + +/* Page directory with kernel mappings only. */ +uint32_t *init_page_dir; + #ifdef FILESYS -#include "filesys.h" -#include "disk.h" +/* -f: Format the file system? */ +static bool format_filesys; + +/* -filesys, -scratch, -swap: Names of block devices to use, + overriding the defaults. */ +static const char *filesys_bdev_name; +static const char *scratch_bdev_name; +#ifdef VM +static const char *swap_bdev_name; #endif +#endif /* FILESYS */ -/* Size of kernel static code and data, in 4 kB pages. */ -size_t kernel_pages; +/* -ul: Maximum number of pages to put into palloc's user pool. */ +static size_t user_page_limit = SIZE_MAX; -/* Amount of physical memory, in 4 kB pages. */ -size_t ram_pages; +static void bss_init (void); +static void paging_init (void); -static void ram_init (void); -static void gdt_init (void); -static void argv_init (void); -void power_off (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 -main_thread (void *aux UNUSED) -{ #ifdef FILESYS - disk_init (); - filesys_init (true); - filesys_self_test (); +static void locate_block_devices (void); +static void locate_block_device (enum block_type, const char *name); #endif -} +int main (void) NO_RETURN; + +/* Pintos main program. */ int main (void) { - struct thread *t; - - /* Initialize components needed by printk() very early. */ - ram_init (); - vga_init (); - serial_init (); - printk ("Booting cnachos86...\n"); - - /* Calculate how much RAM the kernel uses, and find out from - the bootloader how much RAM this machine has. */ - printk ("ram: detected %'d kB main memory.\n", ram_pages * 4); - - /* Memory from the end of the kernel through the end of memory - is free. Give it to the page allocator. */ - palloc_init (ptov (LOADER_KERN_BASE + kernel_pages * PGSIZE), - ptov (ram_pages * PGSIZE)); - paging_init (); - gdt_init (); + char **argv; + + /* Clear BSS. */ + bss_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 (); + console_init (); + + /* Greet user. */ + printf ("Pintos booting with %'"PRIu32" kB RAM...\n", + init_ram_pages * PGSIZE / 1024); + /* Initialize memory system. */ + palloc_init (user_page_limit); malloc_init (); - random_init (); + paging_init (); - argv_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 - thread_init (); + /* Start thread scheduler and enable interrupts. */ + thread_start (); + serial_init_queue (); + timer_calibrate (); - t = thread_create ("main", main_thread, NULL); - thread_start (t); +#ifdef FILESYS + /* Initialize file system. */ + ide_init (); + locate_block_devices (); + filesys_init (format_filesys); +#endif - printk ("Done!\n"); - return 0; + printf ("Boot complete.\n"); + + /* Run actions specified on kernel command line. */ + run_actions (argv); + + /* Finish up. */ + shutdown (); + thread_exit (); } + +/* Clear the "BSS", 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. -static uint64_t -make_seg_desc (uint32_t base, - uint32_t limit, - enum seg_system system, - enum seg_type type, - int dpl, - enum seg_granularity granularity) + The start and end of the BSS segment is recorded by the + linker as _start_bss and _end_bss. See kernel.lds. */ +static void +bss_init (void) { - uint32_t e0 = ((limit & 0xffff) /* Limit 15:0. */ - | (base << 16)); /* Base 15:0. */ - uint32_t e1 = (((base >> 16) & 0xff) /* Base 23:16. */ - | (system << 12) /* 0=system, 1=code/data. */ - | (type << 8) /* Segment type. */ - | (dpl << 13) /* Descriptor privilege. */ - | (1 << 15) /* Present. */ - | (limit & 0xf0000) /* Limit 16:19. */ - | (1 << 22) /* 32-bit segment. */ - | (granularity << 23) /* Byte/page granularity. */ - | (base & 0xff000000)); /* Base 31:24. */ - return e0 | ((uint64_t) e1 << 32); + extern char _start_bss, _end_bss; + memset (&_start_bss, 0, &_end_bss - &_start_bss); } -static uint64_t -make_code_desc (int dpl) +/* 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 init_page_dir to the page + directory it creates. */ +static void +paging_init (void) { - return make_seg_desc (0, 0xfffff, SYS_CODE_DATA, TYPE_CODE | TYPE_READABLE, - dpl, GRAN_PAGE); + uint32_t *pd, *pt; + size_t page; + extern char _start, _end_kernel_text; + + pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO); + pt = NULL; + for (page = 0; page < init_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 (init_page_dir))); } -static uint64_t -make_data_desc (int dpl) +/* Breaks the kernel command line into words and returns them as + an argv-like array. */ +static char ** +read_command_line (void) { - return make_seg_desc (0, 0xfffff, SYS_CODE_DATA, TYPE_WRITABLE, - dpl, GRAN_PAGE); + 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; } -static uint64_t -make_tss_desc (void *vaddr) +/* Parses options in ARGV[] + and returns the first non-option argument. */ +static char ** +parse_options (char **argv) { - return make_seg_desc ((uint32_t) vaddr, - 0x67, SYS_SYSTEM, TYPE_TSS_32_A, 0, GRAN_BYTE); -} + 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")) + shutdown_configure (SHUTDOWN_POWER_OFF); + else if (!strcmp (name, "-r")) + shutdown_configure (SHUTDOWN_REBOOT); +#ifdef FILESYS + else if (!strcmp (name, "-f")) + format_filesys = true; + else if (!strcmp (name, "-filesys")) + filesys_bdev_name = value; + else if (!strcmp (name, "-scratch")) + scratch_bdev_name = value; +#ifdef VM + else if (!strcmp (name, "-swap")) + swap_bdev_name = value; +#endif +#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); + } -uint64_t gdt[SEL_CNT]; + /* Initialize the random number generator based on the system + time. This has no effect if an "-rs" option was specified. -struct tss *tss; + 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; +} -/* Sets up a proper GDT. The bootstrap loader's GDT didn't - include user-mode selectors or a TSS. */ +/* Runs the task specified in ARGV[1]. */ static void -gdt_init (void) +run_task (char **argv) { - uint64_t gdtr_operand; - - /* Our TSS is never used in a call gate or task gate, so only a - few fields of it are ever referenced, and those are the only - ones we initialize. */ - tss = palloc_get (PAL_ASSERT | PAL_ZERO); - tss->esp0 = (uint32_t) ptov(0x20000); - tss->ss0 = SEL_KDSEG; - tss->bitmap = 0xdfff; - - /* Initialize GDT. */ - gdt[SEL_NULL / sizeof *gdt] = 0; - gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0); - gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0); - gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3); - gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3); - gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss); - - /* Load GDTR, TR. */ - gdtr_operand = make_dtr_operand (sizeof gdt - 1, gdt); - asm volatile ("lgdt %0" :: "m" (gdtr_operand)); - asm volatile ("ltr %w0" :: "r" (SEL_TSS)); + 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 -ram_init (void) +run_actions (char **argv) { - /* Start and end of kernel image, - and start and end of BSS segment. - These are created by kernel.lds. */ - extern char _start, _end; - extern char _start_bss, _end_bss; + /* An action. */ + struct action + { + char *name; /* Action name. */ + int argc; /* # of args, including action name. */ + void (*function) (char **argv); /* Function to execute action. */ + }; - /* 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. */ - memset (&_start_bss, 0, &_end_bss - &_start_bss); + /* 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}, + {"extract", 1, fsutil_extract}, + {"append", 2, fsutil_append}, +#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; + } + +} - /* Calculate how much RAM the kernel uses, - and find out from the bootloader how much RAM this machine - has. */ - kernel_pages = (&_end - &_start + 4095) / 4096; - ram_pages = *(uint32_t *) ptov (LOADER_BASE + LOADER_RAM_PAGES); +/* 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" + " extract Untar from scratch device into file system.\n" + " append FILE Append FILE to tar file on scratch device.\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" +#ifdef FILESYS + " -f Format file system device during startup.\n" + " -filesys=BDEV Use BDEV for file system instead of default.\n" + " -scratch=BDEV Use BDEV for scratch instead of default.\n" +#ifdef VM + " -swap=BDEV Use BDEV for swap instead of default.\n" +#endif +#endif + " -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 + ); + shutdown_power_off (); } -void -argv_init (void) +#ifdef FILESYS +/* Figure out what block devices to cast in the various Pintos roles. */ +static void +locate_block_devices (void) { - char *cmd_line = ptov (LOADER_BASE + LOADER_CMD_LINE); + locate_block_device (BLOCK_FILESYS, filesys_bdev_name); + locate_block_device (BLOCK_SCRATCH, scratch_bdev_name); +#ifdef VM + locate_block_device (BLOCK_SWAP, swap_bdev_name); +#endif } - -void -power_off (void) + +/* Figures out what block device to use for the given ROLE: the + block device with the given NAME, if NAME is non-null, + otherwise the first block device in probe order of type + ROLE. */ +static void +locate_block_device (enum block_type role, const char *name) { - const char s[] = "Shutdown"; - const char *p; + struct block *block = NULL; - printk ("Powering off...\n"); - for (p = s; *p != '\0'; p++) - outb (0x8900, *p); - for (;;); + if (name != NULL) + { + block = block_get_by_name (name); + if (block == NULL) + PANIC ("No such block device \"%s\"", name); + } + else + { + for (block = block_first (); block != NULL; block = block_next (block)) + if (block_type (block) == role) + break; + } + + if (block != NULL) + { + printf ("%s: using %s\n", block_type_name (role), block_name (block)); + block_set_role (role, block); + } } +#endif