429 lines
11 KiB
C
Executable file
429 lines
11 KiB
C
Executable file
#include "threads/init.h"
|
||
#include <console.h>
|
||
#include <debug.h>
|
||
#include <inttypes.h>
|
||
#include <limits.h>
|
||
#include <random.h>
|
||
#include <stddef.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#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
|
||
/* -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 */
|
||
|
||
/* -ul: Maximum number of pages to put into palloc's user pool. */
|
||
static size_t user_page_limit = SIZE_MAX;
|
||
|
||
static void bss_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);
|
||
|
||
#ifdef FILESYS
|
||
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)
|
||
{
|
||
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 ();
|
||
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. */
|
||
ide_init ();
|
||
locate_block_devices ();
|
||
filesys_init (format_filesys);
|
||
#endif
|
||
|
||
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.
|
||
|
||
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)
|
||
{
|
||
extern char _start_bss, _end_bss;
|
||
memset (&_start_bss, 0, &_end_bss - &_start_bss);
|
||
}
|
||
|
||
/* 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)
|
||
{
|
||
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)));
|
||
}
|
||
|
||
/* 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 ();
|
||
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);
|
||
}
|
||
|
||
/* 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
|
||
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},
|
||
{"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;
|
||
}
|
||
|
||
}
|
||
|
||
/* 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 ();
|
||
}
|
||
|
||
#ifdef FILESYS
|
||
/* Figure out what block devices to cast in the various Pintos roles. */
|
||
static void
|
||
locate_block_devices (void)
|
||
{
|
||
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
|
||
}
|
||
|
||
/* 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)
|
||
{
|
||
struct block *block = NULL;
|
||
|
||
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
|