diff options
Diffstat (limited to 'pintos-progos/threads/init.c')
| -rw-r--r-- | pintos-progos/threads/init.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/pintos-progos/threads/init.c b/pintos-progos/threads/init.c new file mode 100644 index 0000000..d8feacd --- /dev/null +++ b/pintos-progos/threads/init.c | |||
| @@ -0,0 +1,453 @@ | |||
| 1 | #include "threads/init.h" | ||
| 2 | #include <console.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <inttypes.h> | ||
| 5 | #include <limits.h> | ||
| 6 | #include <random.h> | ||
| 7 | #include <stddef.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include "devices/kbd.h" | ||
| 12 | #include "devices/input.h" | ||
| 13 | #include "devices/serial.h" | ||
| 14 | #include "devices/shutdown.h" | ||
| 15 | #include "devices/timer.h" | ||
| 16 | #include "devices/vga.h" | ||
| 17 | #include "devices/rtc.h" | ||
| 18 | #include "threads/interrupt.h" | ||
| 19 | #include "threads/io.h" | ||
| 20 | #include "threads/loader.h" | ||
| 21 | #include "threads/malloc.h" | ||
| 22 | #include "threads/palloc.h" | ||
| 23 | #include "threads/pte.h" | ||
| 24 | #include "threads/thread.h" | ||
| 25 | #ifdef USERPROG | ||
| 26 | #include "userprog/process.h" | ||
| 27 | #include "userprog/exception.h" | ||
| 28 | #include "userprog/gdt.h" | ||
| 29 | #include "userprog/syscall.h" | ||
| 30 | #include "userprog/tss.h" | ||
| 31 | #else | ||
| 32 | #include "tests/threads/tests.h" | ||
| 33 | #endif | ||
| 34 | #ifdef FILESYS | ||
| 35 | #include "devices/block.h" | ||
| 36 | #include "devices/ide.h" | ||
| 37 | #include "filesys/filesys.h" | ||
| 38 | #include "filesys/fsutil.h" | ||
| 39 | #endif | ||
| 40 | |||
| 41 | |||
| 42 | /* Page directory with kernel mappings only. */ | ||
| 43 | uint32_t *init_page_dir; | ||
| 44 | |||
| 45 | #ifdef FILESYS | ||
| 46 | /* -f: Format the file system? */ | ||
| 47 | static bool format_filesys; | ||
| 48 | |||
| 49 | /* -filesys, -scratch, -swap: Names of block devices to use, | ||
| 50 | overriding the defaults. */ | ||
| 51 | static const char *filesys_bdev_name; | ||
| 52 | static const char *scratch_bdev_name; | ||
| 53 | #ifdef VM | ||
| 54 | static const char *swap_bdev_name; | ||
| 55 | #endif | ||
| 56 | #endif /* FILESYS */ | ||
| 57 | |||
| 58 | /* -kernel-test: Run kernel test instead of user program */ | ||
| 59 | static bool kernel_test = false; | ||
| 60 | |||
| 61 | /* provide weak kernel test definition if no test is available */ | ||
| 62 | void run_test (const char *param); | ||
| 63 | __attribute__((weak)) | ||
| 64 | void | ||
| 65 | run_test (const char *param UNUSED) | ||
| 66 | { | ||
| 67 | printf("No kernel test linked into kernel\n"); | ||
| 68 | } | ||
| 69 | |||
| 70 | |||
| 71 | /* -ul: Maximum number of pages to put into palloc's user pool. */ | ||
| 72 | static size_t user_page_limit = SIZE_MAX; | ||
| 73 | |||
| 74 | static void bss_init (void); | ||
| 75 | static void paging_init (void); | ||
| 76 | |||
| 77 | static char **read_command_line (void); | ||
| 78 | static char **parse_options (char **argv); | ||
| 79 | static void run_actions (char **argv); | ||
| 80 | static void usage (void); | ||
| 81 | |||
| 82 | #ifdef FILESYS | ||
| 83 | static void locate_block_devices (void); | ||
| 84 | static void locate_block_device (enum block_type, const char *name); | ||
| 85 | #endif | ||
| 86 | |||
| 87 | int main (void) NO_RETURN; | ||
| 88 | |||
| 89 | /* Pintos main program. */ | ||
| 90 | int | ||
| 91 | main (void) | ||
| 92 | { | ||
| 93 | char **argv; | ||
| 94 | |||
| 95 | /* Clear BSS. */ | ||
| 96 | bss_init (); | ||
| 97 | |||
| 98 | /* Break command line into arguments and parse options. */ | ||
| 99 | argv = read_command_line (); | ||
| 100 | argv = parse_options (argv); | ||
| 101 | |||
| 102 | /* Initialize ourselves as a thread so we can use locks, | ||
| 103 | then enable console locking. */ | ||
| 104 | thread_init (); | ||
| 105 | console_init (); | ||
| 106 | |||
| 107 | /* Greet user. */ | ||
| 108 | printf ("Pintos booting with %'"PRIu32" kB RAM...\n", | ||
| 109 | init_ram_pages * PGSIZE / 1024); | ||
| 110 | |||
| 111 | /* Initialize memory system. */ | ||
| 112 | palloc_init (user_page_limit); | ||
| 113 | malloc_init (); | ||
| 114 | paging_init (); | ||
| 115 | |||
| 116 | /* Segmentation. */ | ||
| 117 | #ifdef USERPROG | ||
| 118 | tss_init (); | ||
| 119 | gdt_init (); | ||
| 120 | #endif | ||
| 121 | |||
| 122 | /* Initialize interrupt handlers. */ | ||
| 123 | intr_init (); | ||
| 124 | timer_init (); | ||
| 125 | kbd_init (); | ||
| 126 | input_init (); | ||
| 127 | #ifdef USERPROG | ||
| 128 | exception_init (); | ||
| 129 | syscall_init (); | ||
| 130 | #endif | ||
| 131 | |||
| 132 | /* Start thread scheduler and enable interrupts. */ | ||
| 133 | thread_start (); | ||
| 134 | serial_init_queue (); | ||
| 135 | timer_calibrate (); | ||
| 136 | |||
| 137 | #ifdef FILESYS | ||
| 138 | /* Initialize file system. */ | ||
| 139 | ide_init (); | ||
| 140 | locate_block_devices (); | ||
| 141 | /* kernel tests do not need filesystem */ | ||
| 142 | if (!kernel_test) | ||
| 143 | filesys_init (format_filesys); | ||
| 144 | #endif | ||
| 145 | |||
| 146 | printf ("Boot complete.\n"); | ||
| 147 | |||
| 148 | /* Run actions specified on kernel command line. */ | ||
| 149 | run_actions (argv); | ||
| 150 | |||
| 151 | /* Finish up. */ | ||
| 152 | shutdown (); | ||
| 153 | thread_exit (); | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Clear the "BSS", a segment that should be initialized to | ||
| 157 | zeros. It isn't actually stored on disk or zeroed by the | ||
| 158 | kernel loader, so we have to zero it ourselves. | ||
| 159 | |||
| 160 | The start and end of the BSS segment is recorded by the | ||
| 161 | linker as _start_bss and _end_bss. See kernel.lds. */ | ||
| 162 | static void | ||
| 163 | bss_init (void) | ||
| 164 | { | ||
| 165 | extern char _start_bss, _end_bss; | ||
| 166 | memset (&_start_bss, 0, &_end_bss - &_start_bss); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Populates the base page directory and page table with the | ||
| 170 | kernel virtual mapping, and then sets up the CPU to use the | ||
| 171 | new page directory. Points init_page_dir to the page | ||
| 172 | directory it creates. */ | ||
| 173 | static void | ||
| 174 | paging_init (void) | ||
| 175 | { | ||
| 176 | uint32_t *pd, *pt; | ||
| 177 | size_t page; | ||
| 178 | extern char _start, _end_kernel_text; | ||
| 179 | |||
| 180 | pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 181 | pt = NULL; | ||
| 182 | for (page = 0; page < init_ram_pages; page++) | ||
| 183 | { | ||
| 184 | uintptr_t paddr = page * PGSIZE; | ||
| 185 | char *vaddr = ptov (paddr); | ||
| 186 | size_t pde_idx = pd_no (vaddr); | ||
| 187 | size_t pte_idx = pt_no (vaddr); | ||
| 188 | bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text; | ||
| 189 | |||
| 190 | if (pd[pde_idx] == 0) | ||
| 191 | { | ||
| 192 | pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 193 | pd[pde_idx] = pde_create (pt); | ||
| 194 | } | ||
| 195 | |||
| 196 | pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text); | ||
| 197 | } | ||
| 198 | |||
| 199 | /* Store the physical address of the page directory into CR3 | ||
| 200 | aka PDBR (page directory base register). This activates our | ||
| 201 | new page tables immediately. See [IA32-v2a] "MOV--Move | ||
| 202 | to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address | ||
| 203 | of the Page Directory". */ | ||
| 204 | asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir))); | ||
| 205 | } | ||
| 206 | |||
| 207 | /* Breaks the kernel command line into words and returns them as | ||
| 208 | an argv-like array. */ | ||
| 209 | static char ** | ||
| 210 | read_command_line (void) | ||
| 211 | { | ||
| 212 | static char *argv[LOADER_ARGS_LEN / 2 + 1]; | ||
| 213 | char *p, *end; | ||
| 214 | int argc; | ||
| 215 | int i; | ||
| 216 | |||
| 217 | argc = *(uint32_t *) ptov (LOADER_ARG_CNT); | ||
| 218 | p = ptov (LOADER_ARGS); | ||
| 219 | end = p + LOADER_ARGS_LEN; | ||
| 220 | for (i = 0; i < argc; i++) | ||
| 221 | { | ||
| 222 | if (p >= end) | ||
| 223 | PANIC ("command line arguments overflow"); | ||
| 224 | |||
| 225 | argv[i] = p; | ||
| 226 | p += strnlen (p, end - p) + 1; | ||
| 227 | } | ||
| 228 | argv[argc] = NULL; | ||
| 229 | |||
| 230 | /* Print kernel command line. */ | ||
| 231 | printf ("Kernel command line:"); | ||
| 232 | for (i = 0; i < argc; i++) | ||
| 233 | if (strchr (argv[i], ' ') == NULL) | ||
| 234 | printf (" %s", argv[i]); | ||
| 235 | else | ||
| 236 | printf (" '%s'", argv[i]); | ||
| 237 | printf ("\n"); | ||
| 238 | |||
| 239 | return argv; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* Parses options in ARGV[] | ||
| 243 | and returns the first non-option argument. */ | ||
| 244 | static char ** | ||
| 245 | parse_options (char **argv) | ||
| 246 | { | ||
| 247 | for (; *argv != NULL && **argv == '-'; argv++) | ||
| 248 | { | ||
| 249 | char *save_ptr; | ||
| 250 | char *name = strtok_r (*argv, "=", &save_ptr); | ||
| 251 | char *value = strtok_r (NULL, "", &save_ptr); | ||
| 252 | |||
| 253 | #ifndef USERPROG | ||
| 254 | kernel_test = true; | ||
| 255 | #endif | ||
| 256 | if (!strcmp (name, "-h")) | ||
| 257 | usage (); | ||
| 258 | else if (!strcmp (name, "-q")) | ||
| 259 | shutdown_configure (SHUTDOWN_POWER_OFF); | ||
| 260 | else if (!strcmp (name, "-r")) | ||
| 261 | shutdown_configure (SHUTDOWN_REBOOT); | ||
| 262 | #ifdef FILESYS | ||
| 263 | else if (!strcmp (name, "-f")) | ||
| 264 | format_filesys = true; | ||
| 265 | else if (!strcmp (name, "-filesys")) | ||
| 266 | filesys_bdev_name = value; | ||
| 267 | else if (!strcmp (name, "-scratch")) | ||
| 268 | scratch_bdev_name = value; | ||
| 269 | #ifdef VM | ||
| 270 | else if (!strcmp (name, "-swap")) | ||
| 271 | swap_bdev_name = value; | ||
| 272 | #endif | ||
| 273 | #endif | ||
| 274 | else if (!strcmp (name, "-rs")) | ||
| 275 | random_init (atoi (value)); | ||
| 276 | else if (!strcmp (name, "-mlfqs")) | ||
| 277 | thread_mlfqs = true; | ||
| 278 | #ifdef USERPROG | ||
| 279 | else if (!strcmp (name, "-kernel-test")) | ||
| 280 | kernel_test = true; | ||
| 281 | else if (!strcmp (name, "-ul")) | ||
| 282 | user_page_limit = atoi (value); | ||
| 283 | #endif | ||
| 284 | else | ||
| 285 | PANIC ("unknown option `%s' (use -h for help)", name); | ||
| 286 | } | ||
| 287 | |||
| 288 | /* Initialize the random number generator based on the system | ||
| 289 | time. This has no effect if an "-rs" option was specified. | ||
| 290 | |||
| 291 | When running under Bochs, this is not enough by itself to | ||
| 292 | get a good seed value, because the pintos script sets the | ||
| 293 | initial time to a predictable value, not to the local time, | ||
| 294 | for reproducibility. To fix this, give the "-r" option to | ||
| 295 | the pintos script to request real-time execution. */ | ||
| 296 | random_init (rtc_get_time ()); | ||
| 297 | |||
| 298 | return argv; | ||
| 299 | } | ||
| 300 | |||
| 301 | /* Runs the task specified in ARGV[1]. */ | ||
| 302 | static void | ||
| 303 | run_task (char **argv) | ||
| 304 | { | ||
| 305 | const char *task = argv[1]; | ||
| 306 | |||
| 307 | printf ("Executing '%s':\n", task); | ||
| 308 | #ifdef USERPROG | ||
| 309 | if (kernel_test) | ||
| 310 | run_test (task); | ||
| 311 | else | ||
| 312 | process_wait (process_execute (task)); | ||
| 313 | #else | ||
| 314 | run_test (task); | ||
| 315 | #endif | ||
| 316 | printf ("Execution of '%s' complete.\n", task); | ||
| 317 | } | ||
| 318 | |||
| 319 | /* Executes all of the actions specified in ARGV[] | ||
| 320 | up to the null pointer sentinel. */ | ||
| 321 | static void | ||
| 322 | run_actions (char **argv) | ||
| 323 | { | ||
| 324 | /* An action. */ | ||
| 325 | struct action | ||
| 326 | { | ||
| 327 | char *name; /* Action name. */ | ||
| 328 | int argc; /* # of args, including action name. */ | ||
| 329 | void (*function) (char **argv); /* Function to execute action. */ | ||
| 330 | }; | ||
| 331 | |||
| 332 | /* Table of supported actions. */ | ||
| 333 | static const struct action actions[] = | ||
| 334 | { | ||
| 335 | {"run", 2, run_task}, | ||
| 336 | #ifdef FILESYS | ||
| 337 | {"ls", 1, fsutil_ls}, | ||
| 338 | {"cat", 2, fsutil_cat}, | ||
| 339 | {"rm", 2, fsutil_rm}, | ||
| 340 | {"extract", 1, fsutil_extract}, | ||
| 341 | {"append", 2, fsutil_append}, | ||
| 342 | #endif | ||
| 343 | {NULL, 0, NULL}, | ||
| 344 | }; | ||
| 345 | |||
| 346 | while (*argv != NULL) | ||
| 347 | { | ||
| 348 | const struct action *a; | ||
| 349 | int i; | ||
| 350 | |||
| 351 | /* Find action name. */ | ||
| 352 | for (a = actions; ; a++) | ||
| 353 | if (a->name == NULL) | ||
| 354 | PANIC ("unknown action `%s' (use -h for help)", *argv); | ||
| 355 | else if (!strcmp (*argv, a->name)) | ||
| 356 | break; | ||
| 357 | |||
| 358 | /* Check for required arguments. */ | ||
| 359 | for (i = 1; i < a->argc; i++) | ||
| 360 | if (argv[i] == NULL) | ||
| 361 | PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1); | ||
| 362 | |||
| 363 | /* Invoke action and advance. */ | ||
| 364 | a->function (argv); | ||
| 365 | argv += a->argc; | ||
| 366 | } | ||
| 367 | |||
| 368 | } | ||
| 369 | |||
| 370 | /* Prints a kernel command line help message and powers off the | ||
| 371 | machine. */ | ||
| 372 | static void | ||
| 373 | usage (void) | ||
| 374 | { | ||
| 375 | printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n" | ||
| 376 | "Options must precede actions.\n" | ||
| 377 | "Actions are executed in the order specified.\n" | ||
| 378 | "\nAvailable actions:\n" | ||
| 379 | #ifdef USERPROG | ||
| 380 | " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n" | ||
| 381 | #else | ||
| 382 | " run TEST Run TEST.\n" | ||
| 383 | #endif | ||
| 384 | #ifdef FILESYS | ||
| 385 | " ls List files in the root directory.\n" | ||
| 386 | " cat FILE Print FILE to the console.\n" | ||
| 387 | " rm FILE Delete FILE.\n" | ||
| 388 | "Use these actions indirectly via `pintos' -g and -p options:\n" | ||
| 389 | " extract Untar from scratch device into file system.\n" | ||
| 390 | " append FILE Append FILE to tar file on scratch device.\n" | ||
| 391 | #endif | ||
| 392 | "\nOptions:\n" | ||
| 393 | " -h Print this help message and power off.\n" | ||
| 394 | " -q Power off VM after actions or on panic.\n" | ||
| 395 | " -r Reboot after actions.\n" | ||
| 396 | #ifdef FILESYS | ||
| 397 | " -f Format file system device during startup.\n" | ||
| 398 | " -filesys=BDEV Use BDEV for file system instead of default.\n" | ||
| 399 | " -scratch=BDEV Use BDEV for scratch instead of default.\n" | ||
| 400 | #ifdef VM | ||
| 401 | " -swap=BDEV Use BDEV for swap instead of default.\n" | ||
| 402 | #endif | ||
| 403 | #endif | ||
| 404 | " -rs=SEED Set random number seed to SEED.\n" | ||
| 405 | " -mlfqs Use multi-level feedback queue scheduler.\n" | ||
| 406 | #ifdef USERPROG | ||
| 407 | " -ul=COUNT Limit user memory to COUNT pages.\n" | ||
| 408 | #endif | ||
| 409 | ); | ||
| 410 | shutdown_power_off (); | ||
| 411 | } | ||
| 412 | |||
| 413 | #ifdef FILESYS | ||
| 414 | /* Figure out what block devices to cast in the various Pintos roles. */ | ||
| 415 | static void | ||
| 416 | locate_block_devices (void) | ||
| 417 | { | ||
| 418 | locate_block_device (BLOCK_FILESYS, filesys_bdev_name); | ||
| 419 | locate_block_device (BLOCK_SCRATCH, scratch_bdev_name); | ||
| 420 | #ifdef VM | ||
| 421 | locate_block_device (BLOCK_SWAP, swap_bdev_name); | ||
| 422 | #endif | ||
| 423 | } | ||
| 424 | |||
| 425 | /* Figures out what block device to use for the given ROLE: the | ||
| 426 | block device with the given NAME, if NAME is non-null, | ||
| 427 | otherwise the first block device in probe order of type | ||
| 428 | ROLE. */ | ||
| 429 | static void | ||
| 430 | locate_block_device (enum block_type role, const char *name) | ||
| 431 | { | ||
| 432 | struct block *block = NULL; | ||
| 433 | |||
| 434 | if (name != NULL) | ||
| 435 | { | ||
| 436 | block = block_get_by_name (name); | ||
| 437 | if (block == NULL) | ||
| 438 | PANIC ("No such block device \"%s\"", name); | ||
| 439 | } | ||
| 440 | else | ||
| 441 | { | ||
| 442 | for (block = block_first (); block != NULL; block = block_next (block)) | ||
| 443 | if (block_type (block) == role) | ||
| 444 | break; | ||
| 445 | } | ||
| 446 | |||
| 447 | if (block != NULL) | ||
| 448 | { | ||
| 449 | printf ("%s: using %s\n", block_type_name (role), block_name (block)); | ||
| 450 | block_set_role (role, block); | ||
| 451 | } | ||
| 452 | } | ||
| 453 | #endif | ||
