diff options
Diffstat (limited to 'pintos-progos/threads')
27 files changed, 3819 insertions, 0 deletions
diff --git a/pintos-progos/threads/.gitignore b/pintos-progos/threads/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/pintos-progos/threads/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/pintos-progos/threads/Make.vars b/pintos-progos/threads/Make.vars new file mode 100644 index 0000000..310c240 --- /dev/null +++ b/pintos-progos/threads/Make.vars | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel $(TEST_SUBDIRS) | ||
| 5 | TEST_SUBDIRS = tests/threads | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/threads/Grading | ||
| 7 | SIMULATOR = --bochs | ||
diff --git a/pintos-progos/threads/Makefile b/pintos-progos/threads/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/pintos-progos/threads/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/pintos-progos/threads/flags.h b/pintos-progos/threads/flags.h new file mode 100644 index 0000000..5654ac7 --- /dev/null +++ b/pintos-progos/threads/flags.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef THREADS_FLAGS_H | ||
| 2 | #define THREADS_FLAGS_H | ||
| 3 | |||
| 4 | /* EFLAGS Register. */ | ||
| 5 | #define FLAG_MBS 0x00000002 /* Must be set. */ | ||
| 6 | #define FLAG_IF 0x00000200 /* Interrupt Flag. */ | ||
| 7 | |||
| 8 | #endif /* threads/flags.h */ | ||
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 | ||
diff --git a/pintos-progos/threads/init.h b/pintos-progos/threads/init.h new file mode 100644 index 0000000..8a3df90 --- /dev/null +++ b/pintos-progos/threads/init.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef THREADS_INIT_H | ||
| 2 | #define THREADS_INIT_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdint.h> | ||
| 8 | |||
| 9 | /* Page directory with kernel mappings only. */ | ||
| 10 | extern uint32_t *init_page_dir; | ||
| 11 | |||
| 12 | #endif /* threads/init.h */ | ||
diff --git a/pintos-progos/threads/interrupt.c b/pintos-progos/threads/interrupt.c new file mode 100644 index 0000000..e3b90dc --- /dev/null +++ b/pintos-progos/threads/interrupt.c | |||
| @@ -0,0 +1,438 @@ | |||
| 1 | #include "threads/interrupt.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <stdint.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "threads/flags.h" | ||
| 7 | #include "threads/intr-stubs.h" | ||
| 8 | #include "threads/io.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "threads/vaddr.h" | ||
| 11 | #include "devices/timer.h" | ||
| 12 | |||
| 13 | /* Programmable Interrupt Controller (PIC) registers. | ||
| 14 | A PC has two PICs, called the master and slave PICs, with the | ||
| 15 | slave attached ("cascaded") to the master IRQ line 2. */ | ||
| 16 | #define PIC0_CTRL 0x20 /* Master PIC control register address. */ | ||
| 17 | #define PIC0_DATA 0x21 /* Master PIC data register address. */ | ||
| 18 | #define PIC1_CTRL 0xa0 /* Slave PIC control register address. */ | ||
| 19 | #define PIC1_DATA 0xa1 /* Slave PIC data register address. */ | ||
| 20 | |||
| 21 | /* Number of x86 interrupts. */ | ||
| 22 | #define INTR_CNT 256 | ||
| 23 | |||
| 24 | /* The Interrupt Descriptor Table (IDT). The format is fixed by | ||
| 25 | the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor | ||
| 26 | Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By | ||
| 27 | Exception- or Interrupt-Handler Procedure". */ | ||
| 28 | static uint64_t idt[INTR_CNT]; | ||
| 29 | |||
| 30 | /* Interrupt handler functions for each interrupt. */ | ||
| 31 | static intr_handler_func *intr_handlers[INTR_CNT]; | ||
| 32 | |||
| 33 | /* Names for each interrupt, for debugging purposes. */ | ||
| 34 | static const char *intr_names[INTR_CNT]; | ||
| 35 | |||
| 36 | /* Number of unexpected interrupts for each vector. An | ||
| 37 | unexpected interrupt is one that has no registered handler. */ | ||
| 38 | static unsigned int unexpected_cnt[INTR_CNT]; | ||
| 39 | |||
| 40 | /* External interrupts are those generated by devices outside the | ||
| 41 | CPU, such as the timer. External interrupts run with | ||
| 42 | interrupts turned off, so they never nest, nor are they ever | ||
| 43 | pre-empted. Handlers for external interrupts also may not | ||
| 44 | sleep, although they may invoke intr_yield_on_return() to | ||
| 45 | request that a new process be scheduled just before the | ||
| 46 | interrupt returns. */ | ||
| 47 | static bool in_external_intr; /* Are we processing an external interrupt? */ | ||
| 48 | static bool yield_on_return; /* Should we yield on interrupt return? */ | ||
| 49 | |||
| 50 | /* Programmable Interrupt Controller helpers. */ | ||
| 51 | static void pic_init (void); | ||
| 52 | static void pic_end_of_interrupt (int irq); | ||
| 53 | |||
| 54 | /* Interrupt Descriptor Table helpers. */ | ||
| 55 | static uint64_t make_intr_gate (void (*) (void), int dpl); | ||
| 56 | static uint64_t make_trap_gate (void (*) (void), int dpl); | ||
| 57 | static inline uint64_t make_idtr_operand (uint16_t limit, void *base); | ||
| 58 | |||
| 59 | /* Interrupt handlers. */ | ||
| 60 | void intr_handler (struct intr_frame *args); | ||
| 61 | static void unexpected_interrupt (const struct intr_frame *); | ||
| 62 | |||
| 63 | /* Returns the current interrupt status. */ | ||
| 64 | enum intr_level | ||
| 65 | intr_get_level (void) | ||
| 66 | { | ||
| 67 | uint32_t flags; | ||
| 68 | |||
| 69 | /* Push the flags register on the processor stack, then pop the | ||
| 70 | value off the stack into `flags'. See [IA32-v2b] "PUSHF" | ||
| 71 | and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware | ||
| 72 | Interrupts". */ | ||
| 73 | asm volatile ("pushfl; popl %0" : "=g" (flags)); | ||
| 74 | |||
| 75 | return flags & FLAG_IF ? INTR_ON : INTR_OFF; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Enables or disables interrupts as specified by LEVEL and | ||
| 79 | returns the previous interrupt status. */ | ||
| 80 | enum intr_level | ||
| 81 | intr_set_level (enum intr_level level) | ||
| 82 | { | ||
| 83 | return level == INTR_ON ? intr_enable () : intr_disable (); | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Enables interrupts and returns the previous interrupt status. */ | ||
| 87 | enum intr_level | ||
| 88 | intr_enable (void) | ||
| 89 | { | ||
| 90 | enum intr_level old_level = intr_get_level (); | ||
| 91 | ASSERT (!intr_context ()); | ||
| 92 | |||
| 93 | /* Enable interrupts by setting the interrupt flag. | ||
| 94 | |||
| 95 | See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable | ||
| 96 | Hardware Interrupts". */ | ||
| 97 | asm volatile ("sti"); | ||
| 98 | |||
| 99 | return old_level; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Disables interrupts and returns the previous interrupt status. */ | ||
| 103 | enum intr_level | ||
| 104 | intr_disable (void) | ||
| 105 | { | ||
| 106 | enum intr_level old_level = intr_get_level (); | ||
| 107 | |||
| 108 | /* Disable interrupts by clearing the interrupt flag. | ||
| 109 | See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable | ||
| 110 | Hardware Interrupts". */ | ||
| 111 | asm volatile ("cli" : : : "memory"); | ||
| 112 | |||
| 113 | return old_level; | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Initializes the interrupt system. */ | ||
| 117 | void | ||
| 118 | intr_init (void) | ||
| 119 | { | ||
| 120 | uint64_t idtr_operand; | ||
| 121 | int i; | ||
| 122 | |||
| 123 | /* Initialize interrupt controller. */ | ||
| 124 | pic_init (); | ||
| 125 | |||
| 126 | /* Initialize IDT. */ | ||
| 127 | for (i = 0; i < INTR_CNT; i++) | ||
| 128 | idt[i] = make_intr_gate (intr_stubs[i], 0); | ||
| 129 | |||
| 130 | /* Load IDT register. | ||
| 131 | See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt | ||
| 132 | Descriptor Table (IDT)". */ | ||
| 133 | idtr_operand = make_idtr_operand (sizeof idt - 1, idt); | ||
| 134 | asm volatile ("lidt %0" : : "m" (idtr_operand)); | ||
| 135 | |||
| 136 | /* Initialize intr_names. */ | ||
| 137 | for (i = 0; i < INTR_CNT; i++) | ||
| 138 | intr_names[i] = "unknown"; | ||
| 139 | intr_names[0] = "#DE Divide Error"; | ||
| 140 | intr_names[1] = "#DB Debug Exception"; | ||
| 141 | intr_names[2] = "NMI Interrupt"; | ||
| 142 | intr_names[3] = "#BP Breakpoint Exception"; | ||
| 143 | intr_names[4] = "#OF Overflow Exception"; | ||
| 144 | intr_names[5] = "#BR BOUND Range Exceeded Exception"; | ||
| 145 | intr_names[6] = "#UD Invalid Opcode Exception"; | ||
| 146 | intr_names[7] = "#NM Device Not Available Exception"; | ||
| 147 | intr_names[8] = "#DF Double Fault Exception"; | ||
| 148 | intr_names[9] = "Coprocessor Segment Overrun"; | ||
| 149 | intr_names[10] = "#TS Invalid TSS Exception"; | ||
| 150 | intr_names[11] = "#NP Segment Not Present"; | ||
| 151 | intr_names[12] = "#SS Stack Fault Exception"; | ||
| 152 | intr_names[13] = "#GP General Protection Exception"; | ||
| 153 | intr_names[14] = "#PF Page-Fault Exception"; | ||
| 154 | intr_names[16] = "#MF x87 FPU Floating-Point Error"; | ||
| 155 | intr_names[17] = "#AC Alignment Check Exception"; | ||
| 156 | intr_names[18] = "#MC Machine-Check Exception"; | ||
| 157 | intr_names[19] = "#XF SIMD Floating-Point Exception"; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Registers interrupt VEC_NO to invoke HANDLER with descriptor | ||
| 161 | privilege level DPL. Names the interrupt NAME for debugging | ||
| 162 | purposes. The interrupt handler will be invoked with | ||
| 163 | interrupt status set to LEVEL. */ | ||
| 164 | static void | ||
| 165 | register_handler (uint8_t vec_no, int dpl, enum intr_level level, | ||
| 166 | intr_handler_func *handler, const char *name) | ||
| 167 | { | ||
| 168 | ASSERT (intr_handlers[vec_no] == NULL); | ||
| 169 | if (level == INTR_ON) | ||
| 170 | idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl); | ||
| 171 | else | ||
| 172 | idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl); | ||
| 173 | intr_handlers[vec_no] = handler; | ||
| 174 | intr_names[vec_no] = name; | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Registers external interrupt VEC_NO to invoke HANDLER, which | ||
| 178 | is named NAME for debugging purposes. The handler will | ||
| 179 | execute with interrupts disabled. */ | ||
| 180 | void | ||
| 181 | intr_register_ext (uint8_t vec_no, intr_handler_func *handler, | ||
| 182 | const char *name) | ||
| 183 | { | ||
| 184 | ASSERT (vec_no >= 0x20 && vec_no <= 0x2f); | ||
| 185 | register_handler (vec_no, 0, INTR_OFF, handler, name); | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Registers internal interrupt VEC_NO to invoke HANDLER, which | ||
| 189 | is named NAME for debugging purposes. The interrupt handler | ||
| 190 | will be invoked with interrupt status LEVEL. | ||
| 191 | |||
| 192 | The handler will have descriptor privilege level DPL, meaning | ||
| 193 | that it can be invoked intentionally when the processor is in | ||
| 194 | the DPL or lower-numbered ring. In practice, DPL==3 allows | ||
| 195 | user mode to invoke the interrupts and DPL==0 prevents such | ||
| 196 | invocation. Faults and exceptions that occur in user mode | ||
| 197 | still cause interrupts with DPL==0 to be invoked. See | ||
| 198 | [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1 | ||
| 199 | "Accessing Nonconforming Code Segments" for further | ||
| 200 | discussion. */ | ||
| 201 | void | ||
| 202 | intr_register_int (uint8_t vec_no, int dpl, enum intr_level level, | ||
| 203 | intr_handler_func *handler, const char *name) | ||
| 204 | { | ||
| 205 | ASSERT (vec_no < 0x20 || vec_no > 0x2f); | ||
| 206 | register_handler (vec_no, dpl, level, handler, name); | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Returns true during processing of an external interrupt | ||
| 210 | and false at all other times. */ | ||
| 211 | bool | ||
| 212 | intr_context (void) | ||
| 213 | { | ||
| 214 | return in_external_intr; | ||
| 215 | } | ||
| 216 | |||
| 217 | /* During processing of an external interrupt, directs the | ||
| 218 | interrupt handler to yield to a new process just before | ||
| 219 | returning from the interrupt. May not be called at any other | ||
| 220 | time. */ | ||
| 221 | void | ||
| 222 | intr_yield_on_return (void) | ||
| 223 | { | ||
| 224 | ASSERT (intr_context ()); | ||
| 225 | yield_on_return = true; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* 8259A Programmable Interrupt Controller. */ | ||
| 229 | |||
| 230 | /* Initializes the PICs. Refer to [8259A] for details. | ||
| 231 | |||
| 232 | By default, interrupts 0...15 delivered by the PICs will go to | ||
| 233 | interrupt vectors 0...15. Those vectors are also used for CPU | ||
| 234 | traps and exceptions, so we reprogram the PICs so that | ||
| 235 | interrupts 0...15 are delivered to interrupt vectors 32...47 | ||
| 236 | (0x20...0x2f) instead. */ | ||
| 237 | static void | ||
| 238 | pic_init (void) | ||
| 239 | { | ||
| 240 | /* Mask all interrupts on both PICs. */ | ||
| 241 | outb (PIC0_DATA, 0xff); | ||
| 242 | outb (PIC1_DATA, 0xff); | ||
| 243 | |||
| 244 | /* Initialize master. */ | ||
| 245 | outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */ | ||
| 246 | outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */ | ||
| 247 | outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */ | ||
| 248 | outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */ | ||
| 249 | |||
| 250 | /* Initialize slave. */ | ||
| 251 | outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */ | ||
| 252 | outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */ | ||
| 253 | outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */ | ||
| 254 | outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */ | ||
| 255 | |||
| 256 | /* Unmask all interrupts. */ | ||
| 257 | outb (PIC0_DATA, 0x00); | ||
| 258 | outb (PIC1_DATA, 0x00); | ||
| 259 | } | ||
| 260 | |||
| 261 | /* Sends an end-of-interrupt signal to the PIC for the given IRQ. | ||
| 262 | If we don't acknowledge the IRQ, it will never be delivered to | ||
| 263 | us again, so this is important. */ | ||
| 264 | static void | ||
| 265 | pic_end_of_interrupt (int irq) | ||
| 266 | { | ||
| 267 | ASSERT (irq >= 0x20 && irq < 0x30); | ||
| 268 | |||
| 269 | /* Acknowledge master PIC. */ | ||
| 270 | outb (0x20, 0x20); | ||
| 271 | |||
| 272 | /* Acknowledge slave PIC if this is a slave interrupt. */ | ||
| 273 | if (irq >= 0x28) | ||
| 274 | outb (0xa0, 0x20); | ||
| 275 | } | ||
| 276 | |||
| 277 | /* Creates an gate that invokes FUNCTION. | ||
| 278 | |||
| 279 | The gate has descriptor privilege level DPL, meaning that it | ||
| 280 | can be invoked intentionally when the processor is in the DPL | ||
| 281 | or lower-numbered ring. In practice, DPL==3 allows user mode | ||
| 282 | to call into the gate and DPL==0 prevents such calls. Faults | ||
| 283 | and exceptions that occur in user mode still cause gates with | ||
| 284 | DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege | ||
| 285 | Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments" | ||
| 286 | for further discussion. | ||
| 287 | |||
| 288 | TYPE must be either 14 (for an interrupt gate) or 15 (for a | ||
| 289 | trap gate). The difference is that entering an interrupt gate | ||
| 290 | disables interrupts, but entering a trap gate does not. See | ||
| 291 | [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or | ||
| 292 | Interrupt-Handler Procedure" for discussion. */ | ||
| 293 | static uint64_t | ||
| 294 | make_gate (void (*function) (void), int dpl, int type) | ||
| 295 | { | ||
| 296 | uint32_t e0, e1; | ||
| 297 | |||
| 298 | ASSERT (function != NULL); | ||
| 299 | ASSERT (dpl >= 0 && dpl <= 3); | ||
| 300 | ASSERT (type >= 0 && type <= 15); | ||
| 301 | |||
| 302 | e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */ | ||
| 303 | | (SEL_KCSEG << 16)); /* Target code segment. */ | ||
| 304 | |||
| 305 | e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */ | ||
| 306 | | (1 << 15) /* Present. */ | ||
| 307 | | ((uint32_t) dpl << 13) /* Descriptor privilege level. */ | ||
| 308 | | (0 << 12) /* System. */ | ||
| 309 | | ((uint32_t) type << 8)); /* Gate type. */ | ||
| 310 | |||
| 311 | return e0 | ((uint64_t) e1 << 32); | ||
| 312 | } | ||
| 313 | |||
| 314 | /* Creates an interrupt gate that invokes FUNCTION with the given | ||
| 315 | DPL. */ | ||
| 316 | static uint64_t | ||
| 317 | make_intr_gate (void (*function) (void), int dpl) | ||
| 318 | { | ||
| 319 | return make_gate (function, dpl, 14); | ||
| 320 | } | ||
| 321 | |||
| 322 | /* Creates a trap gate that invokes FUNCTION with the given | ||
| 323 | DPL. */ | ||
| 324 | static uint64_t | ||
| 325 | make_trap_gate (void (*function) (void), int dpl) | ||
| 326 | { | ||
| 327 | return make_gate (function, dpl, 15); | ||
| 328 | } | ||
| 329 | |||
| 330 | /* Returns a descriptor that yields the given LIMIT and BASE when | ||
| 331 | used as an operand for the LIDT instruction. */ | ||
| 332 | static inline uint64_t | ||
| 333 | make_idtr_operand (uint16_t limit, void *base) | ||
| 334 | { | ||
| 335 | return limit | ((uint64_t) (uint32_t) base << 16); | ||
| 336 | } | ||
| 337 | |||
| 338 | /* Interrupt handlers. */ | ||
| 339 | |||
| 340 | /* Handler for all interrupts, faults, and exceptions. This | ||
| 341 | function is called by the assembly language interrupt stubs in | ||
| 342 | intr-stubs.S. FRAME describes the interrupt and the | ||
| 343 | interrupted thread's registers. */ | ||
| 344 | void | ||
| 345 | intr_handler (struct intr_frame *frame) | ||
| 346 | { | ||
| 347 | bool external; | ||
| 348 | intr_handler_func *handler; | ||
| 349 | |||
| 350 | /* External interrupts are special. | ||
| 351 | We only handle one at a time (so interrupts must be off) | ||
| 352 | and they need to be acknowledged on the PIC (see below). | ||
| 353 | An external interrupt handler cannot sleep. */ | ||
| 354 | external = frame->vec_no >= 0x20 && frame->vec_no < 0x30; | ||
| 355 | if (external) | ||
| 356 | { | ||
| 357 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 358 | ASSERT (!intr_context ()); | ||
| 359 | |||
| 360 | in_external_intr = true; | ||
| 361 | yield_on_return = false; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* Invoke the interrupt's handler. */ | ||
| 365 | handler = intr_handlers[frame->vec_no]; | ||
| 366 | if (handler != NULL) | ||
| 367 | handler (frame); | ||
| 368 | else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f) | ||
| 369 | { | ||
| 370 | /* There is no handler, but this interrupt can trigger | ||
| 371 | spuriously due to a hardware fault or hardware race | ||
| 372 | condition. Ignore it. */ | ||
| 373 | } | ||
| 374 | else | ||
| 375 | unexpected_interrupt (frame); | ||
| 376 | |||
| 377 | /* Complete the processing of an external interrupt. */ | ||
| 378 | if (external) | ||
| 379 | { | ||
| 380 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 381 | ASSERT (intr_context ()); | ||
| 382 | |||
| 383 | in_external_intr = false; | ||
| 384 | pic_end_of_interrupt (frame->vec_no); | ||
| 385 | |||
| 386 | if (yield_on_return) | ||
| 387 | thread_yield (); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | /* Handles an unexpected interrupt with interrupt frame F. An | ||
| 392 | unexpected interrupt is one that has no registered handler. */ | ||
| 393 | static void | ||
| 394 | unexpected_interrupt (const struct intr_frame *f) | ||
| 395 | { | ||
| 396 | /* Count the number so far. */ | ||
| 397 | unsigned int n = ++unexpected_cnt[f->vec_no]; | ||
| 398 | |||
| 399 | /* If the number is a power of 2, print a message. This rate | ||
| 400 | limiting means that we get information about an uncommon | ||
| 401 | unexpected interrupt the first time and fairly often after | ||
| 402 | that, but one that occurs many times will not overwhelm the | ||
| 403 | console. */ | ||
| 404 | if ((n & (n - 1)) == 0) | ||
| 405 | printf ("Unexpected interrupt %#04x (%s)\n", | ||
| 406 | f->vec_no, intr_names[f->vec_no]); | ||
| 407 | } | ||
| 408 | |||
| 409 | /* Dumps interrupt frame F to the console, for debugging. */ | ||
| 410 | void | ||
| 411 | intr_dump_frame (const struct intr_frame *f) | ||
| 412 | { | ||
| 413 | uint32_t cr2; | ||
| 414 | |||
| 415 | /* Store current value of CR2 into `cr2'. | ||
| 416 | CR2 is the linear address of the last page fault. | ||
| 417 | See [IA32-v2a] "MOV--Move to/from Control Registers" and | ||
| 418 | [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception | ||
| 419 | (#PF)". */ | ||
| 420 | asm ("movl %%cr2, %0" : "=r" (cr2)); | ||
| 421 | |||
| 422 | printf ("Interrupt %#04x (%s) at eip=%p\n", | ||
| 423 | f->vec_no, intr_names[f->vec_no], f->eip); | ||
| 424 | printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code); | ||
| 425 | printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n", | ||
| 426 | f->eax, f->ebx, f->ecx, f->edx); | ||
| 427 | printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n", | ||
| 428 | f->esi, f->edi, (uint32_t) f->esp, f->ebp); | ||
| 429 | printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n", | ||
| 430 | f->cs, f->ds, f->es, f->ss); | ||
| 431 | } | ||
| 432 | |||
| 433 | /* Returns the name of interrupt VEC. */ | ||
| 434 | const char * | ||
| 435 | intr_name (uint8_t vec) | ||
| 436 | { | ||
| 437 | return intr_names[vec]; | ||
| 438 | } | ||
diff --git a/pintos-progos/threads/interrupt.h b/pintos-progos/threads/interrupt.h new file mode 100644 index 0000000..d43e06d --- /dev/null +++ b/pintos-progos/threads/interrupt.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | #ifndef THREADS_INTERRUPT_H | ||
| 2 | #define THREADS_INTERRUPT_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Interrupts on or off? */ | ||
| 8 | enum intr_level | ||
| 9 | { | ||
| 10 | INTR_OFF, /* Interrupts disabled. */ | ||
| 11 | INTR_ON /* Interrupts enabled. */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | enum intr_level intr_get_level (void); | ||
| 15 | enum intr_level intr_set_level (enum intr_level); | ||
| 16 | enum intr_level intr_enable (void); | ||
| 17 | enum intr_level intr_disable (void); | ||
| 18 | |||
| 19 | /* Interrupt stack frame. */ | ||
| 20 | struct intr_frame | ||
| 21 | { | ||
| 22 | /* Pushed by intr_entry in intr-stubs.S. | ||
| 23 | These are the interrupted task's saved registers. */ | ||
| 24 | uint32_t edi; /* Saved EDI. */ | ||
| 25 | uint32_t esi; /* Saved ESI. */ | ||
| 26 | uint32_t ebp; /* Saved EBP. */ | ||
| 27 | uint32_t esp_dummy; /* Not used. */ | ||
| 28 | uint32_t ebx; /* Saved EBX. */ | ||
| 29 | uint32_t edx; /* Saved EDX. */ | ||
| 30 | uint32_t ecx; /* Saved ECX. */ | ||
| 31 | uint32_t eax; /* Saved EAX. */ | ||
| 32 | uint16_t gs, :16; /* Saved GS segment register. */ | ||
| 33 | uint16_t fs, :16; /* Saved FS segment register. */ | ||
| 34 | uint16_t es, :16; /* Saved ES segment register. */ | ||
| 35 | uint16_t ds, :16; /* Saved DS segment register. */ | ||
| 36 | |||
| 37 | /* Pushed by intrNN_stub in intr-stubs.S. */ | ||
| 38 | uint32_t vec_no; /* Interrupt vector number. */ | ||
| 39 | |||
| 40 | /* Sometimes pushed by the CPU, | ||
| 41 | otherwise for consistency pushed as 0 by intrNN_stub. | ||
| 42 | The CPU puts it just under `eip', but we move it here. */ | ||
| 43 | uint32_t error_code; /* Error code. */ | ||
| 44 | |||
| 45 | /* Pushed by intrNN_stub in intr-stubs.S. | ||
| 46 | This frame pointer eases interpretation of backtraces. */ | ||
| 47 | void *frame_pointer; /* Saved EBP (frame pointer). */ | ||
| 48 | |||
| 49 | /* Pushed by the CPU. | ||
| 50 | These are the interrupted task's saved registers. */ | ||
| 51 | void (*eip) (void); /* Next instruction to execute. */ | ||
| 52 | uint16_t cs, :16; /* Code segment for eip. */ | ||
| 53 | uint32_t eflags; /* Saved CPU flags. */ | ||
| 54 | void *esp; /* Saved stack pointer. */ | ||
| 55 | uint16_t ss, :16; /* Data segment for esp. */ | ||
| 56 | }; | ||
| 57 | |||
| 58 | typedef void intr_handler_func (struct intr_frame *); | ||
| 59 | |||
| 60 | void intr_init (void); | ||
| 61 | void intr_register_ext (uint8_t vec, intr_handler_func *, const char *name); | ||
| 62 | void intr_register_int (uint8_t vec, int dpl, enum intr_level, | ||
| 63 | intr_handler_func *, const char *name); | ||
| 64 | bool intr_context (void); | ||
| 65 | void intr_yield_on_return (void); | ||
| 66 | |||
| 67 | void intr_dump_frame (const struct intr_frame *); | ||
| 68 | const char *intr_name (uint8_t vec); | ||
| 69 | |||
| 70 | #endif /* threads/interrupt.h */ | ||
diff --git a/pintos-progos/threads/intr-stubs.S b/pintos-progos/threads/intr-stubs.S new file mode 100644 index 0000000..adb674e --- /dev/null +++ b/pintos-progos/threads/intr-stubs.S | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | .text | ||
| 4 | |||
| 5 | /* Main interrupt entry point. | ||
| 6 | |||
| 7 | An internal or external interrupt starts in one of the | ||
| 8 | intrNN_stub routines, which push the `struct intr_frame' | ||
| 9 | frame_pointer, error_code, and vec_no members on the stack, | ||
| 10 | then jump here. | ||
| 11 | |||
| 12 | We save the rest of the `struct intr_frame' members to the | ||
| 13 | stack, set up some registers as needed by the kernel, and then | ||
| 14 | call intr_handler(), which actually handles the interrupt. | ||
| 15 | |||
| 16 | We "fall through" to intr_exit to return from the interrupt. | ||
| 17 | */ | ||
| 18 | .func intr_entry | ||
| 19 | intr_entry: | ||
| 20 | /* Save caller's registers. */ | ||
| 21 | pushl %ds | ||
| 22 | pushl %es | ||
| 23 | pushl %fs | ||
| 24 | pushl %gs | ||
| 25 | pushal | ||
| 26 | |||
| 27 | /* Set up kernel environment. */ | ||
| 28 | cld /* String instructions go upward. */ | ||
| 29 | mov $SEL_KDSEG, %eax /* Initialize segment registers. */ | ||
| 30 | mov %eax, %ds | ||
| 31 | mov %eax, %es | ||
| 32 | leal 56(%esp), %ebp /* Set up frame pointer. */ | ||
| 33 | |||
| 34 | /* Call interrupt handler. */ | ||
| 35 | pushl %esp | ||
| 36 | .globl intr_handler | ||
| 37 | call intr_handler | ||
| 38 | addl $4, %esp | ||
| 39 | .endfunc | ||
| 40 | |||
| 41 | /* Interrupt exit. | ||
| 42 | |||
| 43 | Restores the caller's registers, discards extra data on the | ||
| 44 | stack, and returns to the caller. | ||
| 45 | |||
| 46 | This is a separate function because it is called directly when | ||
| 47 | we launch a new user process (see start_process() in | ||
| 48 | userprog/process.c). */ | ||
| 49 | .globl intr_exit | ||
| 50 | .func intr_exit | ||
| 51 | intr_exit: | ||
| 52 | /* Restore caller's registers. */ | ||
| 53 | popal | ||
| 54 | popl %gs | ||
| 55 | popl %fs | ||
| 56 | popl %es | ||
| 57 | popl %ds | ||
| 58 | |||
| 59 | /* Discard `struct intr_frame' vec_no, error_code, | ||
| 60 | frame_pointer members. */ | ||
| 61 | addl $12, %esp | ||
| 62 | |||
| 63 | /* Return to caller. */ | ||
| 64 | iret | ||
| 65 | .endfunc | ||
| 66 | |||
| 67 | /* Interrupt stubs. | ||
| 68 | |||
| 69 | This defines 256 fragments of code, named `intr00_stub' | ||
| 70 | through `intrff_stub', each of which is used as the entry | ||
| 71 | point for the corresponding interrupt vector. It also puts | ||
| 72 | the address of each of these functions in the correct spot in | ||
| 73 | `intr_stubs', an array of function pointers. | ||
| 74 | |||
| 75 | Most of the stubs do this: | ||
| 76 | |||
| 77 | 1. Push %ebp on the stack (frame_pointer in `struct intr_frame'). | ||
| 78 | |||
| 79 | 2. Push 0 on the stack (error_code). | ||
| 80 | |||
| 81 | 3. Push the interrupt number on the stack (vec_no). | ||
| 82 | |||
| 83 | The CPU pushes an extra "error code" on the stack for a few | ||
| 84 | interrupts. Because we want %ebp to be where the error code | ||
| 85 | is, we follow a different path: | ||
| 86 | |||
| 87 | 1. Push a duplicate copy of the error code on the stack. | ||
| 88 | |||
| 89 | 2. Replace the original copy of the error code by %ebp. | ||
| 90 | |||
| 91 | 3. Push the interrupt number on the stack. */ | ||
| 92 | |||
| 93 | .data | ||
| 94 | .globl intr_stubs | ||
| 95 | intr_stubs: | ||
| 96 | |||
| 97 | /* This implements steps 1 and 2, described above, in the common | ||
| 98 | case where we just push a 0 error code. */ | ||
| 99 | #define zero \ | ||
| 100 | pushl %ebp; \ | ||
| 101 | pushl $0 | ||
| 102 | |||
| 103 | /* This implements steps 1 and 2, described above, in the case | ||
| 104 | where the CPU already pushed an error code. */ | ||
| 105 | #define REAL \ | ||
| 106 | pushl (%esp); \ | ||
| 107 | movl %ebp, 4(%esp) | ||
| 108 | |||
| 109 | /* Emits a stub for interrupt vector NUMBER. | ||
| 110 | TYPE is `zero', for the case where we push a 0 error code, | ||
| 111 | or `REAL', if the CPU pushes an error code for us. */ | ||
| 112 | #define STUB(NUMBER, TYPE) \ | ||
| 113 | .text; \ | ||
| 114 | .func intr##NUMBER##_stub; \ | ||
| 115 | intr##NUMBER##_stub: \ | ||
| 116 | TYPE; \ | ||
| 117 | push $0x##NUMBER; \ | ||
| 118 | jmp intr_entry; \ | ||
| 119 | .endfunc; \ | ||
| 120 | \ | ||
| 121 | .data; \ | ||
| 122 | .long intr##NUMBER##_stub; | ||
| 123 | |||
| 124 | /* All the stubs. */ | ||
| 125 | STUB(00, zero) STUB(01, zero) STUB(02, zero) STUB(03, zero) | ||
| 126 | STUB(04, zero) STUB(05, zero) STUB(06, zero) STUB(07, zero) | ||
| 127 | STUB(08, REAL) STUB(09, zero) STUB(0a, REAL) STUB(0b, REAL) | ||
| 128 | STUB(0c, zero) STUB(0d, REAL) STUB(0e, REAL) STUB(0f, zero) | ||
| 129 | |||
| 130 | STUB(10, zero) STUB(11, REAL) STUB(12, zero) STUB(13, zero) | ||
| 131 | STUB(14, zero) STUB(15, zero) STUB(16, zero) STUB(17, zero) | ||
| 132 | STUB(18, REAL) STUB(19, zero) STUB(1a, REAL) STUB(1b, REAL) | ||
| 133 | STUB(1c, zero) STUB(1d, REAL) STUB(1e, REAL) STUB(1f, zero) | ||
| 134 | |||
| 135 | STUB(20, zero) STUB(21, zero) STUB(22, zero) STUB(23, zero) | ||
| 136 | STUB(24, zero) STUB(25, zero) STUB(26, zero) STUB(27, zero) | ||
| 137 | STUB(28, zero) STUB(29, zero) STUB(2a, zero) STUB(2b, zero) | ||
| 138 | STUB(2c, zero) STUB(2d, zero) STUB(2e, zero) STUB(2f, zero) | ||
| 139 | |||
| 140 | STUB(30, zero) STUB(31, zero) STUB(32, zero) STUB(33, zero) | ||
| 141 | STUB(34, zero) STUB(35, zero) STUB(36, zero) STUB(37, zero) | ||
| 142 | STUB(38, zero) STUB(39, zero) STUB(3a, zero) STUB(3b, zero) | ||
| 143 | STUB(3c, zero) STUB(3d, zero) STUB(3e, zero) STUB(3f, zero) | ||
| 144 | |||
| 145 | STUB(40, zero) STUB(41, zero) STUB(42, zero) STUB(43, zero) | ||
| 146 | STUB(44, zero) STUB(45, zero) STUB(46, zero) STUB(47, zero) | ||
| 147 | STUB(48, zero) STUB(49, zero) STUB(4a, zero) STUB(4b, zero) | ||
| 148 | STUB(4c, zero) STUB(4d, zero) STUB(4e, zero) STUB(4f, zero) | ||
| 149 | |||
| 150 | STUB(50, zero) STUB(51, zero) STUB(52, zero) STUB(53, zero) | ||
| 151 | STUB(54, zero) STUB(55, zero) STUB(56, zero) STUB(57, zero) | ||
| 152 | STUB(58, zero) STUB(59, zero) STUB(5a, zero) STUB(5b, zero) | ||
| 153 | STUB(5c, zero) STUB(5d, zero) STUB(5e, zero) STUB(5f, zero) | ||
| 154 | |||
| 155 | STUB(60, zero) STUB(61, zero) STUB(62, zero) STUB(63, zero) | ||
| 156 | STUB(64, zero) STUB(65, zero) STUB(66, zero) STUB(67, zero) | ||
| 157 | STUB(68, zero) STUB(69, zero) STUB(6a, zero) STUB(6b, zero) | ||
| 158 | STUB(6c, zero) STUB(6d, zero) STUB(6e, zero) STUB(6f, zero) | ||
| 159 | |||
| 160 | STUB(70, zero) STUB(71, zero) STUB(72, zero) STUB(73, zero) | ||
| 161 | STUB(74, zero) STUB(75, zero) STUB(76, zero) STUB(77, zero) | ||
| 162 | STUB(78, zero) STUB(79, zero) STUB(7a, zero) STUB(7b, zero) | ||
| 163 | STUB(7c, zero) STUB(7d, zero) STUB(7e, zero) STUB(7f, zero) | ||
| 164 | |||
| 165 | STUB(80, zero) STUB(81, zero) STUB(82, zero) STUB(83, zero) | ||
| 166 | STUB(84, zero) STUB(85, zero) STUB(86, zero) STUB(87, zero) | ||
| 167 | STUB(88, zero) STUB(89, zero) STUB(8a, zero) STUB(8b, zero) | ||
| 168 | STUB(8c, zero) STUB(8d, zero) STUB(8e, zero) STUB(8f, zero) | ||
| 169 | |||
| 170 | STUB(90, zero) STUB(91, zero) STUB(92, zero) STUB(93, zero) | ||
| 171 | STUB(94, zero) STUB(95, zero) STUB(96, zero) STUB(97, zero) | ||
| 172 | STUB(98, zero) STUB(99, zero) STUB(9a, zero) STUB(9b, zero) | ||
| 173 | STUB(9c, zero) STUB(9d, zero) STUB(9e, zero) STUB(9f, zero) | ||
| 174 | |||
| 175 | STUB(a0, zero) STUB(a1, zero) STUB(a2, zero) STUB(a3, zero) | ||
| 176 | STUB(a4, zero) STUB(a5, zero) STUB(a6, zero) STUB(a7, zero) | ||
| 177 | STUB(a8, zero) STUB(a9, zero) STUB(aa, zero) STUB(ab, zero) | ||
| 178 | STUB(ac, zero) STUB(ad, zero) STUB(ae, zero) STUB(af, zero) | ||
| 179 | |||
| 180 | STUB(b0, zero) STUB(b1, zero) STUB(b2, zero) STUB(b3, zero) | ||
| 181 | STUB(b4, zero) STUB(b5, zero) STUB(b6, zero) STUB(b7, zero) | ||
| 182 | STUB(b8, zero) STUB(b9, zero) STUB(ba, zero) STUB(bb, zero) | ||
| 183 | STUB(bc, zero) STUB(bd, zero) STUB(be, zero) STUB(bf, zero) | ||
| 184 | |||
| 185 | STUB(c0, zero) STUB(c1, zero) STUB(c2, zero) STUB(c3, zero) | ||
| 186 | STUB(c4, zero) STUB(c5, zero) STUB(c6, zero) STUB(c7, zero) | ||
| 187 | STUB(c8, zero) STUB(c9, zero) STUB(ca, zero) STUB(cb, zero) | ||
| 188 | STUB(cc, zero) STUB(cd, zero) STUB(ce, zero) STUB(cf, zero) | ||
| 189 | |||
| 190 | STUB(d0, zero) STUB(d1, zero) STUB(d2, zero) STUB(d3, zero) | ||
| 191 | STUB(d4, zero) STUB(d5, zero) STUB(d6, zero) STUB(d7, zero) | ||
| 192 | STUB(d8, zero) STUB(d9, zero) STUB(da, zero) STUB(db, zero) | ||
| 193 | STUB(dc, zero) STUB(dd, zero) STUB(de, zero) STUB(df, zero) | ||
| 194 | |||
| 195 | STUB(e0, zero) STUB(e1, zero) STUB(e2, zero) STUB(e3, zero) | ||
| 196 | STUB(e4, zero) STUB(e5, zero) STUB(e6, zero) STUB(e7, zero) | ||
| 197 | STUB(e8, zero) STUB(e9, zero) STUB(ea, zero) STUB(eb, zero) | ||
| 198 | STUB(ec, zero) STUB(ed, zero) STUB(ee, zero) STUB(ef, zero) | ||
| 199 | |||
| 200 | STUB(f0, zero) STUB(f1, zero) STUB(f2, zero) STUB(f3, zero) | ||
| 201 | STUB(f4, zero) STUB(f5, zero) STUB(f6, zero) STUB(f7, zero) | ||
| 202 | STUB(f8, zero) STUB(f9, zero) STUB(fa, zero) STUB(fb, zero) | ||
| 203 | STUB(fc, zero) STUB(fd, zero) STUB(fe, zero) STUB(ff, zero) | ||
diff --git a/pintos-progos/threads/intr-stubs.h b/pintos-progos/threads/intr-stubs.h new file mode 100644 index 0000000..9ceba15 --- /dev/null +++ b/pintos-progos/threads/intr-stubs.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #ifndef THREADS_INTR_STUBS_H | ||
| 2 | #define THREADS_INTR_STUBS_H | ||
| 3 | |||
| 4 | /* Interrupt stubs. | ||
| 5 | |||
| 6 | These are little snippets of code in intr-stubs.S, one for | ||
| 7 | each of the 256 possible x86 interrupts. Each one does a | ||
| 8 | little bit of stack manipulation, then jumps to intr_entry(). | ||
| 9 | See intr-stubs.S for more information. | ||
| 10 | |||
| 11 | This array points to each of the interrupt stub entry points | ||
| 12 | so that intr_init() can easily find them. */ | ||
| 13 | typedef void intr_stub_func (void); | ||
| 14 | extern intr_stub_func *intr_stubs[256]; | ||
| 15 | |||
| 16 | /* Interrupt return path. */ | ||
| 17 | void intr_exit (void); | ||
| 18 | |||
| 19 | #endif /* threads/intr-stubs.h */ | ||
diff --git a/pintos-progos/threads/io.h b/pintos-progos/threads/io.h new file mode 100644 index 0000000..30d52da --- /dev/null +++ b/pintos-progos/threads/io.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | #ifndef THREADS_IO_H | ||
| 2 | #define THREADS_IO_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Reads and returns a byte from PORT. */ | ||
| 8 | static inline uint8_t | ||
| 9 | inb (uint16_t port) | ||
| 10 | { | ||
| 11 | /* See [IA32-v2a] "IN". */ | ||
| 12 | uint8_t data; | ||
| 13 | asm volatile ("inb %w1, %b0" : "=a" (data) : "Nd" (port)); | ||
| 14 | return data; | ||
| 15 | } | ||
| 16 | |||
| 17 | /* Reads CNT bytes from PORT, one after another, and stores them | ||
| 18 | into the buffer starting at ADDR. */ | ||
| 19 | static inline void | ||
| 20 | insb (uint16_t port, void *addr, size_t cnt) | ||
| 21 | { | ||
| 22 | /* See [IA32-v2a] "INS". */ | ||
| 23 | asm volatile ("rep insb" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); | ||
| 24 | } | ||
| 25 | |||
| 26 | /* Reads and returns 16 bits from PORT. */ | ||
| 27 | static inline uint16_t | ||
| 28 | inw (uint16_t port) | ||
| 29 | { | ||
| 30 | uint16_t data; | ||
| 31 | /* See [IA32-v2a] "IN". */ | ||
| 32 | asm volatile ("inw %w1, %w0" : "=a" (data) : "Nd" (port)); | ||
| 33 | return data; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Reads CNT 16-bit (halfword) units from PORT, one after | ||
| 37 | another, and stores them into the buffer starting at ADDR. */ | ||
| 38 | static inline void | ||
| 39 | insw (uint16_t port, void *addr, size_t cnt) | ||
| 40 | { | ||
| 41 | /* See [IA32-v2a] "INS". */ | ||
| 42 | asm volatile ("rep insw" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); | ||
| 43 | } | ||
| 44 | |||
| 45 | /* Reads and returns 32 bits from PORT. */ | ||
| 46 | static inline uint32_t | ||
| 47 | inl (uint16_t port) | ||
| 48 | { | ||
| 49 | /* See [IA32-v2a] "IN". */ | ||
| 50 | uint32_t data; | ||
| 51 | asm volatile ("inl %w1, %0" : "=a" (data) : "Nd" (port)); | ||
| 52 | return data; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* Reads CNT 32-bit (word) units from PORT, one after another, | ||
| 56 | and stores them into the buffer starting at ADDR. */ | ||
| 57 | static inline void | ||
| 58 | insl (uint16_t port, void *addr, size_t cnt) | ||
| 59 | { | ||
| 60 | /* See [IA32-v2a] "INS". */ | ||
| 61 | asm volatile ("rep insl" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Writes byte DATA to PORT. */ | ||
| 65 | static inline void | ||
| 66 | outb (uint16_t port, uint8_t data) | ||
| 67 | { | ||
| 68 | /* See [IA32-v2b] "OUT". */ | ||
| 69 | asm volatile ("outb %b0, %w1" : : "a" (data), "Nd" (port)); | ||
| 70 | } | ||
| 71 | |||
| 72 | /* Writes to PORT each byte of data in the CNT-byte buffer | ||
| 73 | starting at ADDR. */ | ||
| 74 | static inline void | ||
| 75 | outsb (uint16_t port, const void *addr, size_t cnt) | ||
| 76 | { | ||
| 77 | /* See [IA32-v2b] "OUTS". */ | ||
| 78 | asm volatile ("rep outsb" : "+S" (addr), "+c" (cnt) : "d" (port)); | ||
| 79 | } | ||
| 80 | |||
| 81 | /* Writes the 16-bit DATA to PORT. */ | ||
| 82 | static inline void | ||
| 83 | outw (uint16_t port, uint16_t data) | ||
| 84 | { | ||
| 85 | /* See [IA32-v2b] "OUT". */ | ||
| 86 | asm volatile ("outw %w0, %w1" : : "a" (data), "Nd" (port)); | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Writes to PORT each 16-bit unit (halfword) of data in the | ||
| 90 | CNT-halfword buffer starting at ADDR. */ | ||
| 91 | static inline void | ||
| 92 | outsw (uint16_t port, const void *addr, size_t cnt) | ||
| 93 | { | ||
| 94 | /* See [IA32-v2b] "OUTS". */ | ||
| 95 | asm volatile ("rep outsw" : "+S" (addr), "+c" (cnt) : "d" (port)); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Writes the 32-bit DATA to PORT. */ | ||
| 99 | static inline void | ||
| 100 | outl (uint16_t port, uint32_t data) | ||
| 101 | { | ||
| 102 | /* See [IA32-v2b] "OUT". */ | ||
| 103 | asm volatile ("outl %0, %w1" : : "a" (data), "Nd" (port)); | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Writes to PORT each 32-bit unit (word) of data in the CNT-word | ||
| 107 | buffer starting at ADDR. */ | ||
| 108 | static inline void | ||
| 109 | outsl (uint16_t port, const void *addr, size_t cnt) | ||
| 110 | { | ||
| 111 | /* See [IA32-v2b] "OUTS". */ | ||
| 112 | asm volatile ("rep outsl" : "+S" (addr), "+c" (cnt) : "d" (port)); | ||
| 113 | } | ||
| 114 | |||
| 115 | #endif /* threads/io.h */ | ||
diff --git a/pintos-progos/threads/kernel.lds.S b/pintos-progos/threads/kernel.lds.S new file mode 100644 index 0000000..19082d5 --- /dev/null +++ b/pintos-progos/threads/kernel.lds.S | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | OUTPUT_FORMAT("elf32-i386") | ||
| 4 | OUTPUT_ARCH("i386") | ||
| 5 | ENTRY(start) /* Kernel starts at "start" symbol. */ | ||
| 6 | SECTIONS | ||
| 7 | { | ||
| 8 | /* Specify the kernel base address. */ | ||
| 9 | _start = LOADER_PHYS_BASE + LOADER_KERN_BASE; | ||
| 10 | |||
| 11 | /* Make room for the ELF headers. */ | ||
| 12 | . = _start + SIZEOF_HEADERS; | ||
| 13 | |||
| 14 | /* Kernel starts with code, followed by read-only data and writable data. */ | ||
| 15 | .text : { *(.start) *(.text) } = 0x90 | ||
| 16 | .rodata : { *(.rodata) *(.rodata.*) | ||
| 17 | . = ALIGN(0x1000); | ||
| 18 | _end_kernel_text = .; } | ||
| 19 | .data : { *(.data) | ||
| 20 | _signature = .; LONG(0xaa55aa55) } | ||
| 21 | |||
| 22 | /* BSS (zero-initialized data) is after everything else. */ | ||
| 23 | _start_bss = .; | ||
| 24 | .bss : { *(.bss) } | ||
| 25 | _end_bss = .; | ||
| 26 | |||
| 27 | _end = .; | ||
| 28 | |||
| 29 | ASSERT (_end - _start <= 512K, "Kernel image is too big.") | ||
| 30 | } | ||
diff --git a/pintos-progos/threads/loader.S b/pintos-progos/threads/loader.S new file mode 100644 index 0000000..dd87ea1 --- /dev/null +++ b/pintos-progos/threads/loader.S | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | #### Kernel loader. | ||
| 4 | |||
| 5 | #### This code should be stored in the first sector of a hard disk. | ||
| 6 | #### When the BIOS runs, it loads this code at physical address | ||
| 7 | #### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it, | ||
| 8 | #### in real mode. The loader loads the kernel into memory and jumps | ||
| 9 | #### to its entry point, which is the start function in start.S. | ||
| 10 | #### | ||
| 11 | #### The BIOS passes in the drive that the loader was read from as | ||
| 12 | #### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives | ||
| 13 | #### numbered 0x80, 0x81, ... We want to support booting a kernel on | ||
| 14 | #### a different drive from the loader, so we don't take advantage of | ||
| 15 | #### this. | ||
| 16 | |||
| 17 | # Runs in real mode, which is a 16-bit segment. | ||
| 18 | .code16 | ||
| 19 | |||
| 20 | # Set up segment registers. | ||
| 21 | # Set stack to grow downward from 60 kB (after boot, the kernel | ||
| 22 | # continues to use this stack for its initial thread). | ||
| 23 | |||
| 24 | sub %ax, %ax | ||
| 25 | mov %ax, %ds | ||
| 26 | mov %ax, %ss | ||
| 27 | mov $0xf000, %esp | ||
| 28 | |||
| 29 | # Configure serial port so we can report progress without connected VGA. | ||
| 30 | # See [IntrList] for details. | ||
| 31 | sub %dx, %dx # Serial port 0. | ||
| 32 | mov $0xe3, %al # 9600 bps, N-8-1. | ||
| 33 | # AH is already 0 (Initialize Port). | ||
| 34 | int $0x14 # Destroys AX. | ||
| 35 | |||
| 36 | call puts | ||
| 37 | .string "PiLo" | ||
| 38 | |||
| 39 | #### Read the partition table on each system hard disk and scan for a | ||
| 40 | #### partition of type 0x20, which is the type that we use for a | ||
| 41 | #### Pintos kernel. | ||
| 42 | #### | ||
| 43 | #### Read [Partitions] for a description of the partition table format | ||
| 44 | #### that we parse. | ||
| 45 | #### | ||
| 46 | #### We print out status messages to show the disk and partition being | ||
| 47 | #### scanned, e.g. hda1234 as we scan four partitions on the first | ||
| 48 | #### hard disk. | ||
| 49 | |||
| 50 | mov $0x80, %dl # Hard disk 0. | ||
| 51 | read_mbr: | ||
| 52 | sub %ebx, %ebx # Sector 0. | ||
| 53 | mov $0x2000, %ax # Use 0x20000 for buffer. | ||
| 54 | mov %ax, %es | ||
| 55 | call read_sector | ||
| 56 | jc no_such_drive | ||
| 57 | |||
| 58 | # Print hd[a-z]. | ||
| 59 | call puts | ||
| 60 | .string " hd" | ||
| 61 | mov %dl, %al | ||
| 62 | add $'a' - 0x80, %al | ||
| 63 | call putc | ||
| 64 | |||
| 65 | # Check for MBR signature--if not present, it's not a | ||
| 66 | # partitioned hard disk. | ||
| 67 | cmpw $0xaa55, %es:510 | ||
| 68 | jne next_drive | ||
| 69 | |||
| 70 | mov $446, %si # Offset of partition table entry 1. | ||
| 71 | mov $'1', %al | ||
| 72 | check_partition: | ||
| 73 | # Is it an unused partition? | ||
| 74 | cmpl $0, %es:(%si) | ||
| 75 | je next_partition | ||
| 76 | |||
| 77 | # Print [1-4]. | ||
| 78 | call putc | ||
| 79 | |||
| 80 | # Is it a Pintos kernel partition? | ||
| 81 | cmpb $0x20, %es:4(%si) | ||
| 82 | jne next_partition | ||
| 83 | |||
| 84 | # Is it a bootable partition? | ||
| 85 | cmpb $0x80, %es:(%si) | ||
| 86 | je load_kernel | ||
| 87 | |||
| 88 | next_partition: | ||
| 89 | # No match for this partition, go on to the next one. | ||
| 90 | add $16, %si # Offset to next partition table entry. | ||
| 91 | inc %al | ||
| 92 | cmp $510, %si | ||
| 93 | jb check_partition | ||
| 94 | |||
| 95 | next_drive: | ||
| 96 | # No match on this drive, go on to the next one. | ||
| 97 | inc %dl | ||
| 98 | jnc read_mbr | ||
| 99 | |||
| 100 | no_such_drive: | ||
| 101 | no_boot_partition: | ||
| 102 | # Didn't find a Pintos kernel partition anywhere, give up. | ||
| 103 | call puts | ||
| 104 | .string "\rNot found\r" | ||
| 105 | |||
| 106 | # Notify BIOS that boot failed. See [IntrList]. | ||
| 107 | int $0x18 | ||
| 108 | |||
| 109 | #### We found a kernel. The kernel's drive is in DL. The partition | ||
| 110 | #### table entry for the kernel's partition is at ES:SI. Our job now | ||
| 111 | #### is to read the kernel from disk and jump to its start address. | ||
| 112 | |||
| 113 | load_kernel: | ||
| 114 | call puts | ||
| 115 | .string "\rLoading" | ||
| 116 | |||
| 117 | # Figure out number of sectors to read. A Pintos kernel is | ||
| 118 | # just an ELF format object, which doesn't have an | ||
| 119 | # easy-to-read field to identify its own size (see [ELF1]). | ||
| 120 | # But we limit Pintos kernels to 512 kB for other reasons, so | ||
| 121 | # it's easy enough to just read the entire contents of the | ||
| 122 | # partition or 512 kB from disk, whichever is smaller. | ||
| 123 | mov %es:12(%si), %ecx # EBP = number of sectors | ||
| 124 | cmp $1024, %ecx # Cap size at 512 kB | ||
| 125 | jbe 1f | ||
| 126 | mov $1024, %cx | ||
| 127 | 1: | ||
| 128 | |||
| 129 | mov %es:8(%si), %ebx # EBX = first sector | ||
| 130 | mov $0x2000, %ax # Start load address: 0x20000 | ||
| 131 | |||
| 132 | next_sector: | ||
| 133 | # Read one sector into memory. | ||
| 134 | mov %ax, %es # ES:0000 -> load address | ||
| 135 | call read_sector | ||
| 136 | jc read_failed | ||
| 137 | |||
| 138 | # Print '.' as progress indicator once every 16 sectors == 8 kB. | ||
| 139 | test $15, %bl | ||
| 140 | jnz 1f | ||
| 141 | call puts | ||
| 142 | .string "." | ||
| 143 | 1: | ||
| 144 | |||
| 145 | # Advance memory pointer and disk sector. | ||
| 146 | add $0x20, %ax | ||
| 147 | inc %bx | ||
| 148 | loop next_sector | ||
| 149 | |||
| 150 | call puts | ||
| 151 | .string "\r" | ||
| 152 | |||
| 153 | #### Transfer control to the kernel that we loaded. We read the start | ||
| 154 | #### address out of the ELF header (see [ELF1]) and convert it from a | ||
| 155 | #### 32-bit linear address into a 16:16 segment:offset address for | ||
| 156 | #### real mode, then jump to the converted address. The 80x86 doesn't | ||
| 157 | #### have an instruction to jump to an absolute segment:offset kept in | ||
| 158 | #### registers, so in fact we store the address in a temporary memory | ||
| 159 | #### location, then jump indirectly through that location. To save 4 | ||
| 160 | #### bytes in the loader, we reuse 4 bytes of the loader's code for | ||
| 161 | #### this temporary pointer. | ||
| 162 | |||
| 163 | mov $0x2000, %ax | ||
| 164 | mov %ax, %es | ||
| 165 | mov %es:0x18, %dx | ||
| 166 | mov %dx, start | ||
| 167 | movw $0x2000, start + 2 | ||
| 168 | ljmp *start | ||
| 169 | |||
| 170 | read_failed: | ||
| 171 | start: | ||
| 172 | # Disk sector read failed. | ||
| 173 | call puts | ||
| 174 | 1: .string "\rBad read\r" | ||
| 175 | |||
| 176 | # Notify BIOS that boot failed. See [IntrList]. | ||
| 177 | int $0x18 | ||
| 178 | |||
| 179 | #### Print string subroutine. To save space in the loader, this | ||
| 180 | #### subroutine takes its null-terminated string argument from the | ||
| 181 | #### code stream just after the call, and then returns to the byte | ||
| 182 | #### just after the terminating null. This subroutine preserves all | ||
| 183 | #### general-purpose registers. | ||
| 184 | |||
| 185 | puts: xchg %si, %ss:(%esp) | ||
| 186 | push %ax | ||
| 187 | next_char: | ||
| 188 | mov %cs:(%si), %al | ||
| 189 | inc %si | ||
| 190 | test %al, %al | ||
| 191 | jz 1f | ||
| 192 | call putc | ||
| 193 | jmp next_char | ||
| 194 | 1: pop %ax | ||
| 195 | xchg %si, %ss:(%esp) | ||
| 196 | ret | ||
| 197 | |||
| 198 | #### Character output subroutine. Prints the character in AL to the | ||
| 199 | #### VGA display and serial port 0, using BIOS services (see | ||
| 200 | #### [IntrList]). Preserves all general-purpose registers. | ||
| 201 | #### | ||
| 202 | #### If called upon to output a carriage return, this subroutine | ||
| 203 | #### automatically supplies the following line feed. | ||
| 204 | |||
| 205 | putc: pusha | ||
| 206 | |||
| 207 | 1: sub %bh, %bh # Page 0. | ||
| 208 | mov $0x0e, %ah # Teletype output service. | ||
| 209 | int $0x10 | ||
| 210 | |||
| 211 | mov $0x01, %ah # Serial port output service. | ||
| 212 | sub %dx, %dx # Serial port 0. | ||
| 213 | 2: int $0x14 # Destroys AH. | ||
| 214 | test $0x80, %ah # Output timed out? | ||
| 215 | jz 3f | ||
| 216 | movw $0x9090, 2b # Turn "int $0x14" above into NOPs. | ||
| 217 | |||
| 218 | 3: | ||
| 219 | cmp $'\r', %al | ||
| 220 | jne popa_ret | ||
| 221 | mov $'\n', %al | ||
| 222 | jmp 1b | ||
| 223 | |||
| 224 | #### Sector read subroutine. Takes a drive number in DL (0x80 = hard | ||
| 225 | #### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and | ||
| 226 | #### reads the specified sector into memory at ES:0000. Returns with | ||
| 227 | #### carry set on error, clear otherwise. Preserves all | ||
| 228 | #### general-purpose registers. | ||
| 229 | |||
| 230 | read_sector: | ||
| 231 | pusha | ||
| 232 | sub %ax, %ax | ||
| 233 | push %ax # LBA sector number [48:63] | ||
| 234 | push %ax # LBA sector number [32:47] | ||
| 235 | push %ebx # LBA sector number [0:31] | ||
| 236 | push %es # Buffer segment | ||
| 237 | push %ax # Buffer offset (always 0) | ||
| 238 | push $1 # Number of sectors to read | ||
| 239 | push $16 # Packet size | ||
| 240 | mov $0x42, %ah # Extended read | ||
| 241 | mov %sp, %si # DS:SI -> packet | ||
| 242 | int $0x13 # Error code in CF | ||
| 243 | popa # Pop 16 bytes, preserve flags | ||
| 244 | popa_ret: | ||
| 245 | popa | ||
| 246 | ret # Error code still in CF | ||
| 247 | |||
| 248 | #### Command-line arguments and their count. | ||
| 249 | #### This is written by the `pintos' utility and read by the kernel. | ||
| 250 | #### The loader itself does not do anything with the command line. | ||
| 251 | .org LOADER_ARG_CNT - LOADER_BASE | ||
| 252 | .fill LOADER_ARG_CNT_LEN, 1, 0 | ||
| 253 | |||
| 254 | .org LOADER_ARGS - LOADER_BASE | ||
| 255 | .fill LOADER_ARGS_LEN, 1, 0 | ||
| 256 | |||
| 257 | #### Partition table. | ||
| 258 | .org LOADER_PARTS - LOADER_BASE | ||
| 259 | .fill LOADER_PARTS_LEN, 1, 0 | ||
| 260 | |||
| 261 | #### Boot-sector signature for BIOS inspection. | ||
| 262 | .org LOADER_SIG - LOADER_BASE | ||
| 263 | .word 0xaa55 | ||
diff --git a/pintos-progos/threads/loader.h b/pintos-progos/threads/loader.h new file mode 100644 index 0000000..1bfe111 --- /dev/null +++ b/pintos-progos/threads/loader.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #ifndef THREADS_LOADER_H | ||
| 2 | #define THREADS_LOADER_H | ||
| 3 | |||
| 4 | /* Constants fixed by the PC BIOS. */ | ||
| 5 | #define LOADER_BASE 0x7c00 /* Physical address of loader's base. */ | ||
| 6 | #define LOADER_END 0x7e00 /* Physical address of end of loader. */ | ||
| 7 | |||
| 8 | /* Physical address of kernel base. */ | ||
| 9 | #define LOADER_KERN_BASE 0x20000 /* 128 kB. */ | ||
| 10 | |||
| 11 | /* Kernel virtual address at which all physical memory is mapped. | ||
| 12 | Must be aligned on a 4 MB boundary. */ | ||
| 13 | #define LOADER_PHYS_BASE 0xc0000000 /* 3 GB. */ | ||
| 14 | |||
| 15 | /* Important loader physical addresses. */ | ||
| 16 | #define LOADER_SIG (LOADER_END - LOADER_SIG_LEN) /* 0xaa55 BIOS signature. */ | ||
| 17 | #define LOADER_PARTS (LOADER_SIG - LOADER_PARTS_LEN) /* Partition table. */ | ||
| 18 | #define LOADER_ARGS (LOADER_PARTS - LOADER_ARGS_LEN) /* Command-line args. */ | ||
| 19 | #define LOADER_ARG_CNT (LOADER_ARGS - LOADER_ARG_CNT_LEN) /* Number of args. */ | ||
| 20 | |||
| 21 | /* Sizes of loader data structures. */ | ||
| 22 | #define LOADER_SIG_LEN 2 | ||
| 23 | #define LOADER_PARTS_LEN 64 | ||
| 24 | #define LOADER_ARGS_LEN 128 | ||
| 25 | #define LOADER_ARG_CNT_LEN 4 | ||
| 26 | |||
| 27 | /* GDT selectors defined by loader. | ||
| 28 | More selectors are defined by userprog/gdt.h. */ | ||
| 29 | #define SEL_NULL 0x00 /* Null selector. */ | ||
| 30 | #define SEL_KCSEG 0x08 /* Kernel code selector. */ | ||
| 31 | #define SEL_KDSEG 0x10 /* Kernel data selector. */ | ||
| 32 | |||
| 33 | #ifndef __ASSEMBLER__ | ||
| 34 | #include <stdint.h> | ||
| 35 | |||
| 36 | /* Amount of physical memory, in 4 kB pages. */ | ||
| 37 | extern uint32_t init_ram_pages; | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #endif /* threads/loader.h */ | ||
diff --git a/pintos-progos/threads/malloc.c b/pintos-progos/threads/malloc.c new file mode 100644 index 0000000..f6f803b --- /dev/null +++ b/pintos-progos/threads/malloc.c | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | #include "threads/malloc.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <list.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "threads/palloc.h" | ||
| 9 | #include "threads/synch.h" | ||
| 10 | #include "threads/vaddr.h" | ||
| 11 | |||
| 12 | /* A simple implementation of malloc(). | ||
| 13 | |||
| 14 | The size of each request, in bytes, is rounded up to a power | ||
| 15 | of 2 and assigned to the "descriptor" that manages blocks of | ||
| 16 | that size. The descriptor keeps a list of free blocks. If | ||
| 17 | the free list is nonempty, one of its blocks is used to | ||
| 18 | satisfy the request. | ||
| 19 | |||
| 20 | Otherwise, a new page of memory, called an "arena", is | ||
| 21 | obtained from the page allocator (if none is available, | ||
| 22 | malloc() returns a null pointer). The new arena is divided | ||
| 23 | into blocks, all of which are added to the descriptor's free | ||
| 24 | list. Then we return one of the new blocks. | ||
| 25 | |||
| 26 | When we free a block, we add it to its descriptor's free list. | ||
| 27 | But if the arena that the block was in now has no in-use | ||
| 28 | blocks, we remove all of the arena's blocks from the free list | ||
| 29 | and give the arena back to the page allocator. | ||
| 30 | |||
| 31 | We can't handle blocks bigger than 2 kB using this scheme, | ||
| 32 | because they're too big to fit in a single page with a | ||
| 33 | descriptor. We handle those by allocating contiguous pages | ||
| 34 | with the page allocator and sticking the allocation size at | ||
| 35 | the beginning of the allocated block's arena header. */ | ||
| 36 | |||
| 37 | /* Descriptor. */ | ||
| 38 | struct desc | ||
| 39 | { | ||
| 40 | size_t block_size; /* Size of each element in bytes. */ | ||
| 41 | size_t blocks_per_arena; /* Number of blocks in an arena. */ | ||
| 42 | struct list free_list; /* List of free blocks. */ | ||
| 43 | struct lock lock; /* Lock. */ | ||
| 44 | }; | ||
| 45 | |||
| 46 | /* Magic number for detecting arena corruption. */ | ||
| 47 | #define ARENA_MAGIC 0x9a548eed | ||
| 48 | |||
| 49 | /* Arena. */ | ||
| 50 | struct arena | ||
| 51 | { | ||
| 52 | unsigned magic; /* Always set to ARENA_MAGIC. */ | ||
| 53 | struct desc *desc; /* Owning descriptor, null for big block. */ | ||
| 54 | size_t free_cnt; /* Free blocks; pages in big block. */ | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* Free block. */ | ||
| 58 | struct block | ||
| 59 | { | ||
| 60 | struct list_elem free_elem; /* Free list element. */ | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* Our set of descriptors. */ | ||
| 64 | static struct desc descs[10]; /* Descriptors. */ | ||
| 65 | static size_t desc_cnt; /* Number of descriptors. */ | ||
| 66 | |||
| 67 | static struct arena *block_to_arena (struct block *); | ||
| 68 | static struct block *arena_to_block (struct arena *, size_t idx); | ||
| 69 | |||
| 70 | /* Initializes the malloc() descriptors. */ | ||
| 71 | void | ||
| 72 | malloc_init (void) | ||
| 73 | { | ||
| 74 | size_t block_size; | ||
| 75 | |||
| 76 | for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2) | ||
| 77 | { | ||
| 78 | struct desc *d = &descs[desc_cnt++]; | ||
| 79 | ASSERT (desc_cnt <= sizeof descs / sizeof *descs); | ||
| 80 | d->block_size = block_size; | ||
| 81 | d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size; | ||
| 82 | list_init (&d->free_list); | ||
| 83 | lock_init (&d->lock); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Obtains and returns a new block of at least SIZE bytes. | ||
| 88 | Returns a null pointer if memory is not available. */ | ||
| 89 | void * | ||
| 90 | malloc (size_t size) | ||
| 91 | { | ||
| 92 | struct desc *d; | ||
| 93 | struct block *b; | ||
| 94 | struct arena *a; | ||
| 95 | |||
| 96 | /* A null pointer satisfies a request for 0 bytes. */ | ||
| 97 | if (size == 0) | ||
| 98 | return NULL; | ||
| 99 | |||
| 100 | /* Find the smallest descriptor that satisfies a SIZE-byte | ||
| 101 | request. */ | ||
| 102 | for (d = descs; d < descs + desc_cnt; d++) | ||
| 103 | if (d->block_size >= size) | ||
| 104 | break; | ||
| 105 | if (d == descs + desc_cnt) | ||
| 106 | { | ||
| 107 | /* SIZE is too big for any descriptor. | ||
| 108 | Allocate enough pages to hold SIZE plus an arena. */ | ||
| 109 | size_t page_cnt = DIV_ROUND_UP (size + sizeof *a, PGSIZE); | ||
| 110 | a = palloc_get_multiple (0, page_cnt); | ||
| 111 | if (a == NULL) | ||
| 112 | return NULL; | ||
| 113 | |||
| 114 | /* Initialize the arena to indicate a big block of PAGE_CNT | ||
| 115 | pages, and return it. */ | ||
| 116 | a->magic = ARENA_MAGIC; | ||
| 117 | a->desc = NULL; | ||
| 118 | a->free_cnt = page_cnt; | ||
| 119 | return a + 1; | ||
| 120 | } | ||
| 121 | |||
| 122 | lock_acquire (&d->lock); | ||
| 123 | |||
| 124 | /* If the free list is empty, create a new arena. */ | ||
| 125 | if (list_empty (&d->free_list)) | ||
| 126 | { | ||
| 127 | size_t i; | ||
| 128 | |||
| 129 | /* Allocate a page. */ | ||
| 130 | a = palloc_get_page (0); | ||
| 131 | if (a == NULL) | ||
| 132 | { | ||
| 133 | lock_release (&d->lock); | ||
| 134 | return NULL; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Initialize arena and add its blocks to the free list. */ | ||
| 138 | a->magic = ARENA_MAGIC; | ||
| 139 | a->desc = d; | ||
| 140 | a->free_cnt = d->blocks_per_arena; | ||
| 141 | for (i = 0; i < d->blocks_per_arena; i++) | ||
| 142 | { | ||
| 143 | struct block *b = arena_to_block (a, i); | ||
| 144 | list_push_back (&d->free_list, &b->free_elem); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /* Get a block from free list and return it. */ | ||
| 149 | b = list_entry (list_pop_front (&d->free_list), struct block, free_elem); | ||
| 150 | a = block_to_arena (b); | ||
| 151 | a->free_cnt--; | ||
| 152 | lock_release (&d->lock); | ||
| 153 | return b; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Allocates and return A times B bytes initialized to zeroes. | ||
| 157 | Returns a null pointer if memory is not available. */ | ||
| 158 | void * | ||
| 159 | calloc (size_t a, size_t b) | ||
| 160 | { | ||
| 161 | void *p; | ||
| 162 | size_t size; | ||
| 163 | |||
| 164 | /* Calculate block size and make sure it fits in size_t. */ | ||
| 165 | size = a * b; | ||
| 166 | if (size < a || size < b) | ||
| 167 | return NULL; | ||
| 168 | |||
| 169 | /* Allocate and zero memory. */ | ||
| 170 | p = malloc (size); | ||
| 171 | if (p != NULL) | ||
| 172 | memset (p, 0, size); | ||
| 173 | |||
| 174 | return p; | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Returns the number of bytes allocated for BLOCK. */ | ||
| 178 | static size_t | ||
| 179 | block_size (void *block) | ||
| 180 | { | ||
| 181 | struct block *b = block; | ||
| 182 | struct arena *a = block_to_arena (b); | ||
| 183 | struct desc *d = a->desc; | ||
| 184 | |||
| 185 | return d != NULL ? d->block_size : PGSIZE * a->free_cnt - pg_ofs (block); | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly | ||
| 189 | moving it in the process. | ||
| 190 | If successful, returns the new block; on failure, returns a | ||
| 191 | null pointer. | ||
| 192 | A call with null OLD_BLOCK is equivalent to malloc(NEW_SIZE). | ||
| 193 | A call with zero NEW_SIZE is equivalent to free(OLD_BLOCK). */ | ||
| 194 | void * | ||
| 195 | realloc (void *old_block, size_t new_size) | ||
| 196 | { | ||
| 197 | if (new_size == 0) | ||
| 198 | { | ||
| 199 | free (old_block); | ||
| 200 | return NULL; | ||
| 201 | } | ||
| 202 | else | ||
| 203 | { | ||
| 204 | void *new_block = malloc (new_size); | ||
| 205 | if (old_block != NULL && new_block != NULL) | ||
| 206 | { | ||
| 207 | size_t old_size = block_size (old_block); | ||
| 208 | size_t min_size = new_size < old_size ? new_size : old_size; | ||
| 209 | memcpy (new_block, old_block, min_size); | ||
| 210 | free (old_block); | ||
| 211 | } | ||
| 212 | return new_block; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | /* Frees block P, which must have been previously allocated with | ||
| 217 | malloc(), calloc(), or realloc(). */ | ||
| 218 | void | ||
| 219 | free (void *p) | ||
| 220 | { | ||
| 221 | if (p != NULL) | ||
| 222 | { | ||
| 223 | struct block *b = p; | ||
| 224 | struct arena *a = block_to_arena (b); | ||
| 225 | struct desc *d = a->desc; | ||
| 226 | |||
| 227 | if (d != NULL) | ||
| 228 | { | ||
| 229 | /* It's a normal block. We handle it here. */ | ||
| 230 | |||
| 231 | #ifndef NDEBUG | ||
| 232 | /* Clear the block to help detect use-after-free bugs. */ | ||
| 233 | memset (b, 0xcc, d->block_size); | ||
| 234 | #endif | ||
| 235 | |||
| 236 | lock_acquire (&d->lock); | ||
| 237 | |||
| 238 | /* Add block to free list. */ | ||
| 239 | list_push_front (&d->free_list, &b->free_elem); | ||
| 240 | |||
| 241 | /* If the arena is now entirely unused, free it. */ | ||
| 242 | if (++a->free_cnt >= d->blocks_per_arena) | ||
| 243 | { | ||
| 244 | size_t i; | ||
| 245 | |||
| 246 | ASSERT (a->free_cnt == d->blocks_per_arena); | ||
| 247 | for (i = 0; i < d->blocks_per_arena; i++) | ||
| 248 | { | ||
| 249 | struct block *b = arena_to_block (a, i); | ||
| 250 | list_remove (&b->free_elem); | ||
| 251 | } | ||
| 252 | palloc_free_page (a); | ||
| 253 | } | ||
| 254 | |||
| 255 | lock_release (&d->lock); | ||
| 256 | } | ||
| 257 | else | ||
| 258 | { | ||
| 259 | /* It's a big block. Free its pages. */ | ||
| 260 | palloc_free_multiple (a, a->free_cnt); | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | /* Returns the arena that block B is inside. */ | ||
| 267 | static struct arena * | ||
| 268 | block_to_arena (struct block *b) | ||
| 269 | { | ||
| 270 | struct arena *a = pg_round_down (b); | ||
| 271 | |||
| 272 | /* Check that the arena is valid. */ | ||
| 273 | ASSERT (a != NULL); | ||
| 274 | ASSERT (a->magic == ARENA_MAGIC); | ||
| 275 | |||
| 276 | /* Check that the block is properly aligned for the arena. */ | ||
| 277 | ASSERT (a->desc == NULL | ||
| 278 | || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0); | ||
| 279 | ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a); | ||
| 280 | |||
| 281 | return a; | ||
| 282 | } | ||
| 283 | |||
| 284 | /* Returns the (IDX - 1)'th block within arena A. */ | ||
| 285 | static struct block * | ||
| 286 | arena_to_block (struct arena *a, size_t idx) | ||
| 287 | { | ||
| 288 | ASSERT (a != NULL); | ||
| 289 | ASSERT (a->magic == ARENA_MAGIC); | ||
| 290 | ASSERT (idx < a->desc->blocks_per_arena); | ||
| 291 | return (struct block *) ((uint8_t *) a | ||
| 292 | + sizeof *a | ||
| 293 | + idx * a->desc->block_size); | ||
| 294 | } | ||
diff --git a/pintos-progos/threads/malloc.h b/pintos-progos/threads/malloc.h new file mode 100644 index 0000000..bc55d36 --- /dev/null +++ b/pintos-progos/threads/malloc.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #ifndef THREADS_MALLOC_H | ||
| 2 | #define THREADS_MALLOC_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | |||
| 7 | void malloc_init (void); | ||
| 8 | void *malloc (size_t) __attribute__ ((malloc)); | ||
| 9 | void *calloc (size_t, size_t) __attribute__ ((malloc)); | ||
| 10 | void *realloc (void *, size_t); | ||
| 11 | void free (void *); | ||
| 12 | |||
| 13 | #endif /* threads/malloc.h */ | ||
diff --git a/pintos-progos/threads/palloc.c b/pintos-progos/threads/palloc.c new file mode 100644 index 0000000..5c7ef2f --- /dev/null +++ b/pintos-progos/threads/palloc.c | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | #include "threads/palloc.h" | ||
| 2 | #include <bitmap.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <inttypes.h> | ||
| 5 | #include <round.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdint.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include "threads/loader.h" | ||
| 11 | #include "threads/synch.h" | ||
| 12 | #include "threads/vaddr.h" | ||
| 13 | |||
| 14 | /* Page allocator. Hands out memory in page-size (or | ||
| 15 | page-multiple) chunks. See malloc.h for an allocator that | ||
| 16 | hands out smaller chunks. | ||
| 17 | |||
| 18 | System memory is divided into two "pools" called the kernel | ||
| 19 | and user pools. The user pool is for user (virtual) memory | ||
| 20 | pages, the kernel pool for everything else. The idea here is | ||
| 21 | that the kernel needs to have memory for its own operations | ||
| 22 | even if user processes are swapping like mad. | ||
| 23 | |||
| 24 | By default, half of system RAM is given to the kernel pool and | ||
| 25 | half to the user pool. That should be huge overkill for the | ||
| 26 | kernel pool, but that's just fine for demonstration purposes. */ | ||
| 27 | |||
| 28 | /* A memory pool. */ | ||
| 29 | struct pool | ||
| 30 | { | ||
| 31 | struct lock lock; /* Mutual exclusion. */ | ||
| 32 | struct bitmap *used_map; /* Bitmap of free pages. */ | ||
| 33 | uint8_t *base; /* Base of pool. */ | ||
| 34 | }; | ||
| 35 | |||
| 36 | /* Two pools: one for kernel data, one for user pages. */ | ||
| 37 | static struct pool kernel_pool, user_pool; | ||
| 38 | |||
| 39 | static void init_pool (struct pool *, void *base, size_t page_cnt, | ||
| 40 | const char *name); | ||
| 41 | static bool page_from_pool (const struct pool *, void *page); | ||
| 42 | |||
| 43 | /* Initializes the page allocator. At most USER_PAGE_LIMIT | ||
| 44 | pages are put into the user pool. */ | ||
| 45 | void | ||
| 46 | palloc_init (size_t user_page_limit) | ||
| 47 | { | ||
| 48 | /* Free memory starts at 1 MB and runs to the end of RAM. */ | ||
| 49 | uint8_t *free_start = ptov (1024 * 1024); | ||
| 50 | uint8_t *free_end = ptov (init_ram_pages * PGSIZE); | ||
| 51 | size_t free_pages = (free_end - free_start) / PGSIZE; | ||
| 52 | size_t user_pages = free_pages / 2; | ||
| 53 | size_t kernel_pages; | ||
| 54 | if (user_pages > user_page_limit) | ||
| 55 | user_pages = user_page_limit; | ||
| 56 | kernel_pages = free_pages - user_pages; | ||
| 57 | |||
| 58 | /* Give half of memory to kernel, half to user. */ | ||
| 59 | init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool"); | ||
| 60 | init_pool (&user_pool, free_start + kernel_pages * PGSIZE, | ||
| 61 | user_pages, "user pool"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Obtains and returns a group of PAGE_CNT contiguous free pages. | ||
| 65 | If PAL_USER is set, the pages are obtained from the user pool, | ||
| 66 | otherwise from the kernel pool. If PAL_ZERO is set in FLAGS, | ||
| 67 | then the pages are filled with zeros. If too few pages are | ||
| 68 | available, returns a null pointer, unless PAL_ASSERT is set in | ||
| 69 | FLAGS, in which case the kernel panics. */ | ||
| 70 | void * | ||
| 71 | palloc_get_multiple (enum palloc_flags flags, size_t page_cnt) | ||
| 72 | { | ||
| 73 | struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool; | ||
| 74 | void *pages; | ||
| 75 | size_t page_idx; | ||
| 76 | |||
| 77 | if (page_cnt == 0) | ||
| 78 | return NULL; | ||
| 79 | |||
| 80 | lock_acquire (&pool->lock); | ||
| 81 | page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false); | ||
| 82 | lock_release (&pool->lock); | ||
| 83 | |||
| 84 | if (page_idx != BITMAP_ERROR) | ||
| 85 | pages = pool->base + PGSIZE * page_idx; | ||
| 86 | else | ||
| 87 | pages = NULL; | ||
| 88 | |||
| 89 | if (pages != NULL) | ||
| 90 | { | ||
| 91 | if (flags & PAL_ZERO) | ||
| 92 | memset (pages, 0, PGSIZE * page_cnt); | ||
| 93 | } | ||
| 94 | else | ||
| 95 | { | ||
| 96 | if (flags & PAL_ASSERT) | ||
| 97 | PANIC ("palloc_get: out of pages"); | ||
| 98 | } | ||
| 99 | |||
| 100 | return pages; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Obtains a single free page and returns its kernel virtual | ||
| 104 | address. | ||
| 105 | If PAL_USER is set, the page is obtained from the user pool, | ||
| 106 | otherwise from the kernel pool. If PAL_ZERO is set in FLAGS, | ||
| 107 | then the page is filled with zeros. If no pages are | ||
| 108 | available, returns a null pointer, unless PAL_ASSERT is set in | ||
| 109 | FLAGS, in which case the kernel panics. */ | ||
| 110 | void * | ||
| 111 | palloc_get_page (enum palloc_flags flags) | ||
| 112 | { | ||
| 113 | return palloc_get_multiple (flags, 1); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Frees the PAGE_CNT pages starting at PAGES. */ | ||
| 117 | void | ||
| 118 | palloc_free_multiple (void *pages, size_t page_cnt) | ||
| 119 | { | ||
| 120 | struct pool *pool; | ||
| 121 | size_t page_idx; | ||
| 122 | |||
| 123 | ASSERT (pg_ofs (pages) == 0); | ||
| 124 | if (pages == NULL || page_cnt == 0) | ||
| 125 | return; | ||
| 126 | |||
| 127 | if (page_from_pool (&kernel_pool, pages)) | ||
| 128 | pool = &kernel_pool; | ||
| 129 | else if (page_from_pool (&user_pool, pages)) | ||
| 130 | pool = &user_pool; | ||
| 131 | else | ||
| 132 | NOT_REACHED (); | ||
| 133 | |||
| 134 | page_idx = pg_no (pages) - pg_no (pool->base); | ||
| 135 | |||
| 136 | #ifndef NDEBUG | ||
| 137 | memset (pages, 0xcc, PGSIZE * page_cnt); | ||
| 138 | #endif | ||
| 139 | |||
| 140 | ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt)); | ||
| 141 | bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false); | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Frees the page at PAGE. */ | ||
| 145 | void | ||
| 146 | palloc_free_page (void *page) | ||
| 147 | { | ||
| 148 | palloc_free_multiple (page, 1); | ||
| 149 | } | ||
| 150 | |||
| 151 | /* List all used pages */ | ||
| 152 | void | ||
| 153 | palloc_dump_used_pages () | ||
| 154 | { | ||
| 155 | struct pool *pool; | ||
| 156 | int pool_index; | ||
| 157 | for (pool_index = 0; pool_index < 2; pool_index++) { | ||
| 158 | pool = (pool_index == 0) ? &kernel_pool : &user_pool; | ||
| 159 | printf("%s Pool at %p\n", (pool_index == 0) ? "kernel" : "user", pool->base); | ||
| 160 | size_t start = 0, index; | ||
| 161 | while ((index = bitmap_scan(pool->used_map,start,1,true)) != BITMAP_ERROR) { | ||
| 162 | printf(" - %p\n",pool->base + (PGSIZE * index)); | ||
| 163 | start = index + 1; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Initializes pool P as starting at START and ending at END, | ||
| 169 | naming it NAME for debugging purposes. */ | ||
| 170 | static void | ||
| 171 | init_pool (struct pool *p, void *base, size_t page_cnt, const char *name) | ||
| 172 | { | ||
| 173 | /* We'll put the pool's used_map at its base. | ||
| 174 | Calculate the space needed for the bitmap | ||
| 175 | and subtract it from the pool's size. */ | ||
| 176 | size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE); | ||
| 177 | if (bm_pages > page_cnt) | ||
| 178 | PANIC ("Not enough memory in %s for bitmap.", name); | ||
| 179 | page_cnt -= bm_pages; | ||
| 180 | |||
| 181 | printf ("%zu pages available in %s.\n", page_cnt, name); | ||
| 182 | |||
| 183 | /* Initialize the pool. */ | ||
| 184 | lock_init (&p->lock); | ||
| 185 | p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE); | ||
| 186 | p->base = base + bm_pages * PGSIZE; | ||
| 187 | } | ||
| 188 | |||
| 189 | /* Returns true if PAGE was allocated from POOL, | ||
| 190 | false otherwise. */ | ||
| 191 | static bool | ||
| 192 | page_from_pool (const struct pool *pool, void *page) | ||
| 193 | { | ||
| 194 | size_t page_no = pg_no (page); | ||
| 195 | size_t start_page = pg_no (pool->base); | ||
| 196 | size_t end_page = start_page + bitmap_size (pool->used_map); | ||
| 197 | |||
| 198 | return page_no >= start_page && page_no < end_page; | ||
| 199 | } | ||
diff --git a/pintos-progos/threads/palloc.h b/pintos-progos/threads/palloc.h new file mode 100644 index 0000000..d7f4e0b --- /dev/null +++ b/pintos-progos/threads/palloc.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef THREADS_PALLOC_H | ||
| 2 | #define THREADS_PALLOC_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | /* How to allocate pages. */ | ||
| 7 | enum palloc_flags | ||
| 8 | { | ||
| 9 | PAL_ASSERT = 001, /* Panic on failure. */ | ||
| 10 | PAL_ZERO = 002, /* Zero page contents. */ | ||
| 11 | PAL_USER = 004 /* User page. */ | ||
| 12 | }; | ||
| 13 | void palloc_init (size_t user_page_limit); | ||
| 14 | void *palloc_get_page (enum palloc_flags); | ||
| 15 | void *palloc_get_multiple (enum palloc_flags, size_t page_cnt); | ||
| 16 | void palloc_free_page (void *); | ||
| 17 | void palloc_free_multiple (void *, size_t page_cnt); | ||
| 18 | void palloc_dump_used_pages (void); | ||
| 19 | |||
| 20 | #endif /* threads/palloc.h */ | ||
diff --git a/pintos-progos/threads/pte.h b/pintos-progos/threads/pte.h new file mode 100644 index 0000000..1660727 --- /dev/null +++ b/pintos-progos/threads/pte.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | #ifndef THREADS_PTE_H | ||
| 2 | #define THREADS_PTE_H | ||
| 3 | |||
| 4 | #include "threads/vaddr.h" | ||
| 5 | |||
| 6 | /* Functions and macros for working with x86 hardware page | ||
| 7 | tables. | ||
| 8 | |||
| 9 | See vaddr.h for more generic functions and macros for virtual | ||
| 10 | addresses. | ||
| 11 | |||
| 12 | Virtual addresses are structured as follows: | ||
| 13 | |||
| 14 | 31 22 21 12 11 0 | ||
| 15 | +----------------------+----------------------+----------------------+ | ||
| 16 | | Page Directory Index | Page Table Index | Page Offset | | ||
| 17 | +----------------------+----------------------+----------------------+ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* Page table index (bits 12:21). */ | ||
| 21 | #define PTSHIFT PGBITS /* First page table bit. */ | ||
| 22 | #define PTBITS 10 /* Number of page table bits. */ | ||
| 23 | #define PTSPAN (1 << PTBITS << PGBITS) /* Bytes covered by a page table. */ | ||
| 24 | #define PTMASK BITMASK(PTSHIFT, PTBITS) /* Page table bits (12:21). */ | ||
| 25 | |||
| 26 | /* Page directory index (bits 22:31). */ | ||
| 27 | #define PDSHIFT (PTSHIFT + PTBITS) /* First page directory bit. */ | ||
| 28 | #define PDBITS 10 /* Number of page dir bits. */ | ||
| 29 | #define PDMASK BITMASK(PDSHIFT, PDBITS) /* Page directory bits (22:31). */ | ||
| 30 | |||
| 31 | /* Obtains page table index from a virtual address. */ | ||
| 32 | static inline unsigned pt_no (const void *va) { | ||
| 33 | return ((uintptr_t) va & PTMASK) >> PTSHIFT; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Obtains page directory index from a virtual address. */ | ||
| 37 | static inline uintptr_t pd_no (const void *va) { | ||
| 38 | return (uintptr_t) va >> PDSHIFT; | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Page directory and page table entries. | ||
| 42 | |||
| 43 | For more information see the section on page tables in the | ||
| 44 | Pintos reference guide chapter, or [IA32-v3a] 3.7.6 | ||
| 45 | "Page-Directory and Page-Table Entries". | ||
| 46 | |||
| 47 | PDEs and PTEs share a common format: | ||
| 48 | |||
| 49 | 31 12 11 0 | ||
| 50 | +------------------------------------+------------------------+ | ||
| 51 | | Physical Address | Flags | | ||
| 52 | +------------------------------------+------------------------+ | ||
| 53 | |||
| 54 | In a PDE, the physical address points to a page table. | ||
| 55 | In a PTE, the physical address points to a data or code page. | ||
| 56 | The important flags are listed below. | ||
| 57 | When a PDE or PTE is not "present", the other flags are | ||
| 58 | ignored. | ||
| 59 | A PDE or PTE that is initialized to 0 will be interpreted as | ||
| 60 | "not present", which is just fine. */ | ||
| 61 | #define PTE_FLAGS 0x00000fff /* Flag bits. */ | ||
| 62 | #define PTE_ADDR 0xfffff000 /* Address bits. */ | ||
| 63 | #define PTE_AVL 0x00000e00 /* Bits available for OS use. */ | ||
| 64 | #define PTE_P 0x1 /* 1=present, 0=not present. */ | ||
| 65 | #define PTE_W 0x2 /* 1=read/write, 0=read-only. */ | ||
| 66 | #define PTE_U 0x4 /* 1=user/kernel, 0=kernel only. */ | ||
| 67 | #define PTE_A 0x20 /* 1=accessed, 0=not acccessed. */ | ||
| 68 | #define PTE_D 0x40 /* 1=dirty, 0=not dirty (PTEs only). */ | ||
| 69 | |||
| 70 | /* Returns a PDE that points to page table PT. */ | ||
| 71 | static inline uint32_t pde_create (uint32_t *pt) { | ||
| 72 | ASSERT (pg_ofs (pt) == 0); | ||
| 73 | return vtop (pt) | PTE_U | PTE_P | PTE_W; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Returns a pointer to the page table that page directory entry | ||
| 77 | PDE, which must "present", points to. */ | ||
| 78 | static inline uint32_t *pde_get_pt (uint32_t pde) { | ||
| 79 | ASSERT (pde & PTE_P); | ||
| 80 | return ptov (pde & PTE_ADDR); | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Returns a PTE that points to PAGE. | ||
| 84 | The PTE's page is readable. | ||
| 85 | If WRITABLE is true then it will be writable as well. | ||
| 86 | The page will be usable only by ring 0 code (the kernel). */ | ||
| 87 | static inline uint32_t pte_create_kernel (void *page, bool writable) { | ||
| 88 | ASSERT (pg_ofs (page) == 0); | ||
| 89 | return vtop (page) | PTE_P | (writable ? PTE_W : 0); | ||
| 90 | } | ||
| 91 | |||
| 92 | /* Returns a PTE that points to PAGE. | ||
| 93 | The PTE's page is readable. | ||
| 94 | If WRITABLE is true then it will be writable as well. | ||
| 95 | The page will be usable by both user and kernel code. */ | ||
| 96 | static inline uint32_t pte_create_user (void *page, bool writable) { | ||
| 97 | return pte_create_kernel (page, writable) | PTE_U; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Returns a pointer to the page that page table entry PTE points | ||
| 101 | to. */ | ||
| 102 | static inline void *pte_get_page (uint32_t pte) { | ||
| 103 | return ptov (pte & PTE_ADDR); | ||
| 104 | } | ||
| 105 | |||
| 106 | #endif /* threads/pte.h */ | ||
| 107 | |||
diff --git a/pintos-progos/threads/start.S b/pintos-progos/threads/start.S new file mode 100644 index 0000000..29ffa7a --- /dev/null +++ b/pintos-progos/threads/start.S | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | #### Kernel startup code. | ||
| 4 | |||
| 5 | #### The loader (in loader.S) loads the kernel at physical address | ||
| 6 | #### 0x20000 (128 kB) and jumps to "start", defined here. This code | ||
| 7 | #### switches from real mode to 32-bit protected mode and calls | ||
| 8 | #### main(). | ||
| 9 | |||
| 10 | /* Flags in control register 0. */ | ||
| 11 | #define CR0_PE 0x00000001 /* Protection Enable. */ | ||
| 12 | #define CR0_EM 0x00000004 /* (Floating-point) Emulation. */ | ||
| 13 | #define CR0_PG 0x80000000 /* Paging. */ | ||
| 14 | #define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */ | ||
| 15 | |||
| 16 | .section .start | ||
| 17 | |||
| 18 | # The following code runs in real mode, which is a 16-bit code segment. | ||
| 19 | .code16 | ||
| 20 | |||
| 21 | .func start | ||
| 22 | .globl start | ||
| 23 | start: | ||
| 24 | |||
| 25 | # The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000, | ||
| 26 | # but we should initialize the other segment registers. | ||
| 27 | |||
| 28 | mov $0x2000, %ax | ||
| 29 | mov %ax, %ds | ||
| 30 | mov %ax, %es | ||
| 31 | |||
| 32 | # Set string instructions to go upward. | ||
| 33 | cld | ||
| 34 | |||
| 35 | #### Get memory size, via interrupt 15h function 88h (see [IntrList]), | ||
| 36 | #### which returns AX = (kB of physical memory) - 1024. This only | ||
| 37 | #### works for memory sizes <= 65 MB, which should be fine for our | ||
| 38 | #### purposes. We cap memory at 64 MB because that's all we prepare | ||
| 39 | #### page tables for, below. | ||
| 40 | |||
| 41 | movb $0x88, %ah | ||
| 42 | int $0x15 | ||
| 43 | addl $1024, %eax # Total kB memory | ||
| 44 | cmp $0x10000, %eax # Cap at 64 MB | ||
| 45 | jbe 1f | ||
| 46 | mov $0x10000, %eax | ||
| 47 | 1: shrl $2, %eax # Total 4 kB pages | ||
| 48 | addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000 | ||
| 49 | |||
| 50 | #### Enable A20. Address line 20 is tied low when the machine boots, | ||
| 51 | #### which prevents addressing memory about 1 MB. This code fixes it. | ||
| 52 | |||
| 53 | # Poll status register while busy. | ||
| 54 | |||
| 55 | 1: inb $0x64, %al | ||
| 56 | testb $0x2, %al | ||
| 57 | jnz 1b | ||
| 58 | |||
| 59 | # Send command for writing output port. | ||
| 60 | |||
| 61 | movb $0xd1, %al | ||
| 62 | outb %al, $0x64 | ||
| 63 | |||
| 64 | # Poll status register while busy. | ||
| 65 | |||
| 66 | 1: inb $0x64, %al | ||
| 67 | testb $0x2, %al | ||
| 68 | jnz 1b | ||
| 69 | |||
| 70 | # Enable A20 line. | ||
| 71 | |||
| 72 | movb $0xdf, %al | ||
| 73 | outb %al, $0x60 | ||
| 74 | |||
| 75 | # Poll status register while busy. | ||
| 76 | |||
| 77 | 1: inb $0x64, %al | ||
| 78 | testb $0x2, %al | ||
| 79 | jnz 1b | ||
| 80 | |||
| 81 | #### Create temporary page directory and page table and set page | ||
| 82 | #### directory base register. | ||
| 83 | |||
| 84 | # Create page directory at 0xf000 (60 kB) and fill with zeroes. | ||
| 85 | mov $0xf00, %ax | ||
| 86 | mov %ax, %es | ||
| 87 | subl %eax, %eax | ||
| 88 | subl %edi, %edi | ||
| 89 | movl $0x400, %ecx | ||
| 90 | rep stosl | ||
| 91 | |||
| 92 | # Add PDEs to point to page tables for the first 64 MB of RAM. | ||
| 93 | # Also add identical PDEs starting at LOADER_PHYS_BASE. | ||
| 94 | # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" | ||
| 95 | # for a description of the bits in %eax. | ||
| 96 | |||
| 97 | movl $0x10007, %eax | ||
| 98 | movl $0x11, %ecx | ||
| 99 | subl %edi, %edi | ||
| 100 | 1: movl %eax, %es:(%di) | ||
| 101 | movl %eax, %es:LOADER_PHYS_BASE >> 20(%di) | ||
| 102 | addw $4, %di | ||
| 103 | addl $0x1000, %eax | ||
| 104 | loop 1b | ||
| 105 | |||
| 106 | # Set up page tables for one-to-map linear to physical map for the | ||
| 107 | # first 64 MB of RAM. | ||
| 108 | # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" | ||
| 109 | # for a description of the bits in %eax. | ||
| 110 | |||
| 111 | movw $0x1000, %ax | ||
| 112 | movw %ax, %es | ||
| 113 | movl $0x7, %eax | ||
| 114 | movl $0x4000, %ecx | ||
| 115 | subl %edi, %edi | ||
| 116 | 1: movl %eax, %es:(%di) | ||
| 117 | addw $4, %di | ||
| 118 | addl $0x1000, %eax | ||
| 119 | loop 1b | ||
| 120 | |||
| 121 | # Set page directory base register. | ||
| 122 | |||
| 123 | movl $0xf000, %eax | ||
| 124 | movl %eax, %cr3 | ||
| 125 | |||
| 126 | #### Switch to protected mode. | ||
| 127 | |||
| 128 | # First, disable interrupts. We won't set up the IDT until we get | ||
| 129 | # into C code, so any interrupt would blow us away. | ||
| 130 | |||
| 131 | cli | ||
| 132 | |||
| 133 | # Protected mode requires a GDT, so point the GDTR to our GDT. | ||
| 134 | # We need a data32 prefix to ensure that all 32 bits of the GDT | ||
| 135 | # descriptor are loaded (default is to load only 24 bits). | ||
| 136 | # The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit | ||
| 137 | # relocations. | ||
| 138 | |||
| 139 | data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000 | ||
| 140 | |||
| 141 | # Then we turn on the following bits in CR0: | ||
| 142 | # PE (Protect Enable): this turns on protected mode. | ||
| 143 | # PG (Paging): turns on paging. | ||
| 144 | # WP (Write Protect): if unset, ring 0 code ignores | ||
| 145 | # write-protect bits in page tables (!). | ||
| 146 | # EM (Emulation): forces floating-point instructions to trap. | ||
| 147 | # We don't support floating point. | ||
| 148 | |||
| 149 | movl %cr0, %eax | ||
| 150 | orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax | ||
| 151 | movl %eax, %cr0 | ||
| 152 | |||
| 153 | # We're now in protected mode in a 16-bit segment. The CPU still has | ||
| 154 | # the real-mode code segment cached in %cs's segment descriptor. We | ||
| 155 | # need to reload %cs, and the easiest way is to use a far jump. | ||
| 156 | # Because we're not running in a 32-bit segment the data32 prefix is | ||
| 157 | # needed to jump to a 32-bit offset in the target segment. | ||
| 158 | |||
| 159 | data32 ljmp $SEL_KCSEG, $1f | ||
| 160 | |||
| 161 | # We're now in protected mode in a 32-bit segment. | ||
| 162 | # Let the assembler know. | ||
| 163 | |||
| 164 | .code32 | ||
| 165 | |||
| 166 | # Reload all the other segment registers and the stack pointer to | ||
| 167 | # point into our new GDT. | ||
| 168 | |||
| 169 | 1: mov $SEL_KDSEG, %ax | ||
| 170 | mov %ax, %ds | ||
| 171 | mov %ax, %es | ||
| 172 | mov %ax, %fs | ||
| 173 | mov %ax, %gs | ||
| 174 | mov %ax, %ss | ||
| 175 | addl $LOADER_PHYS_BASE, %esp | ||
| 176 | movl $0, %ebp # Null-terminate main()'s backtrace | ||
| 177 | |||
| 178 | #### Call main(). | ||
| 179 | |||
| 180 | call main | ||
| 181 | |||
| 182 | # main() shouldn't ever return. If it does, spin. | ||
| 183 | |||
| 184 | 1: jmp 1b | ||
| 185 | .endfunc | ||
| 186 | |||
| 187 | #### GDT | ||
| 188 | |||
| 189 | .align 8 | ||
| 190 | gdt: | ||
| 191 | .quad 0x0000000000000000 # Null segment. Not used by CPU. | ||
| 192 | .quad 0x00cf9a000000ffff # System code, base 0, limit 4 GB. | ||
| 193 | .quad 0x00cf92000000ffff # System data, base 0, limit 4 GB. | ||
| 194 | |||
| 195 | gdtdesc: | ||
| 196 | .word gdtdesc - gdt - 1 # Size of the GDT, minus 1 byte. | ||
| 197 | .long gdt # Address of the GDT. | ||
| 198 | |||
| 199 | #### Physical memory size in 4 kB pages. This is exported to the rest | ||
| 200 | #### of the kernel. | ||
| 201 | .globl init_ram_pages | ||
| 202 | init_ram_pages: | ||
| 203 | .long 0 | ||
| 204 | |||
diff --git a/pintos-progos/threads/switch.S b/pintos-progos/threads/switch.S new file mode 100644 index 0000000..feca86c --- /dev/null +++ b/pintos-progos/threads/switch.S | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #include "threads/switch.h" | ||
| 2 | |||
| 3 | #### struct thread *switch_threads (struct thread *cur, struct thread *next); | ||
| 4 | #### | ||
| 5 | #### Switches from CUR, which must be the running thread, to NEXT, | ||
| 6 | #### which must also be running switch_threads(), returning CUR in | ||
| 7 | #### NEXT's context. | ||
| 8 | #### | ||
| 9 | #### This function works by assuming that the thread we're switching | ||
| 10 | #### into is also running switch_threads(). Thus, all it has to do is | ||
| 11 | #### preserve a few registers on the stack, then switch stacks and | ||
| 12 | #### restore the registers. As part of switching stacks we record the | ||
| 13 | #### current stack pointer in CUR's thread structure. | ||
| 14 | |||
| 15 | .globl switch_threads | ||
| 16 | .func switch_threads | ||
| 17 | switch_threads: | ||
| 18 | # Save caller's register state. | ||
| 19 | # | ||
| 20 | # Note that the SVR4 ABI allows us to destroy %eax, %ecx, %edx, | ||
| 21 | # but requires us to preserve %ebx, %ebp, %esi, %edi. See | ||
| 22 | # [SysV-ABI-386] pages 3-11 and 3-12 for details. | ||
| 23 | # | ||
| 24 | # This stack frame must match the one set up by thread_create() | ||
| 25 | # in size. | ||
| 26 | pushl %ebx | ||
| 27 | pushl %ebp | ||
| 28 | pushl %esi | ||
| 29 | pushl %edi | ||
| 30 | |||
| 31 | # Get offsetof (struct thread, stack). | ||
| 32 | .globl thread_stack_ofs | ||
| 33 | mov thread_stack_ofs, %edx | ||
| 34 | |||
| 35 | # Save current stack pointer to old thread's stack, if any. | ||
| 36 | movl SWITCH_CUR(%esp), %eax | ||
| 37 | movl %esp, (%eax,%edx,1) | ||
| 38 | |||
| 39 | # Restore stack pointer from new thread's stack. | ||
| 40 | movl SWITCH_NEXT(%esp), %ecx | ||
| 41 | movl (%ecx,%edx,1), %esp | ||
| 42 | |||
| 43 | # Restore caller's register state. | ||
| 44 | popl %edi | ||
| 45 | popl %esi | ||
| 46 | popl %ebp | ||
| 47 | popl %ebx | ||
| 48 | ret | ||
| 49 | .endfunc | ||
| 50 | |||
| 51 | .globl switch_entry | ||
| 52 | .func switch_entry | ||
| 53 | switch_entry: | ||
| 54 | # Discard switch_threads() arguments. | ||
| 55 | addl $8, %esp | ||
| 56 | |||
| 57 | # Call thread_schedule_tail(prev). | ||
| 58 | pushl %eax | ||
| 59 | .globl thread_schedule_tail | ||
| 60 | call thread_schedule_tail | ||
| 61 | addl $4, %esp | ||
| 62 | |||
| 63 | # Start thread proper. | ||
| 64 | ret | ||
| 65 | .endfunc | ||
diff --git a/pintos-progos/threads/switch.h b/pintos-progos/threads/switch.h new file mode 100644 index 0000000..cc156b6 --- /dev/null +++ b/pintos-progos/threads/switch.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef THREADS_SWITCH_H | ||
| 2 | #define THREADS_SWITCH_H | ||
| 3 | |||
| 4 | #ifndef __ASSEMBLER__ | ||
| 5 | /* switch_thread()'s stack frame. */ | ||
| 6 | struct switch_threads_frame | ||
| 7 | { | ||
| 8 | uint32_t edi; /* 0: Saved %edi. */ | ||
| 9 | uint32_t esi; /* 4: Saved %esi. */ | ||
| 10 | uint32_t ebp; /* 8: Saved %ebp. */ | ||
| 11 | uint32_t ebx; /* 12: Saved %ebx. */ | ||
| 12 | void (*eip) (void); /* 16: Return address. */ | ||
| 13 | struct thread *cur; /* 20: switch_threads()'s CUR argument. */ | ||
| 14 | struct thread *next; /* 24: switch_threads()'s NEXT argument. */ | ||
| 15 | }; | ||
| 16 | |||
| 17 | /* Switches from CUR, which must be the running thread, to NEXT, | ||
| 18 | which must also be running switch_threads(), returning CUR in | ||
| 19 | NEXT's context. */ | ||
| 20 | struct thread *switch_threads (struct thread *cur, struct thread *next); | ||
| 21 | |||
| 22 | /* Stack frame for switch_entry(). */ | ||
| 23 | struct switch_entry_frame | ||
| 24 | { | ||
| 25 | void (*eip) (void); | ||
| 26 | }; | ||
| 27 | |||
| 28 | void switch_entry (void); | ||
| 29 | |||
| 30 | /* Pops the CUR and NEXT arguments off the stack, for use in | ||
| 31 | initializing threads. */ | ||
| 32 | void switch_thunk (void); | ||
| 33 | #endif | ||
| 34 | |||
| 35 | /* Offsets used by switch.S. */ | ||
| 36 | #define SWITCH_CUR 20 | ||
| 37 | #define SWITCH_NEXT 24 | ||
| 38 | |||
| 39 | #endif /* threads/switch.h */ | ||
diff --git a/pintos-progos/threads/synch.c b/pintos-progos/threads/synch.c new file mode 100644 index 0000000..317c68a --- /dev/null +++ b/pintos-progos/threads/synch.c | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | /* This file is derived from source code for the Nachos | ||
| 2 | instructional operating system. The Nachos copyright notice | ||
| 3 | is reproduced in full below. */ | ||
| 4 | |||
| 5 | /* Copyright (c) 1992-1996 The Regents of the University of California. | ||
| 6 | All rights reserved. | ||
| 7 | |||
| 8 | Permission to use, copy, modify, and distribute this software | ||
| 9 | and its documentation for any purpose, without fee, and | ||
| 10 | without written agreement is hereby granted, provided that the | ||
| 11 | above copyright notice and the following two paragraphs appear | ||
| 12 | in all copies of this software. | ||
| 13 | |||
| 14 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO | ||
| 15 | ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR | ||
| 16 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE | ||
| 17 | AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA | ||
| 18 | HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 19 | |||
| 20 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | ||
| 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
| 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| 23 | PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" | ||
| 24 | BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO | ||
| 25 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR | ||
| 26 | MODIFICATIONS. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include "threads/synch.h" | ||
| 30 | #include <stdio.h> | ||
| 31 | #include <string.h> | ||
| 32 | #include "threads/interrupt.h" | ||
| 33 | #include "threads/thread.h" | ||
| 34 | |||
| 35 | /* Initializes semaphore SEMA to VALUE. A semaphore is a | ||
| 36 | nonnegative integer along with two atomic operators for | ||
| 37 | manipulating it: | ||
| 38 | |||
| 39 | - down or "P": wait for the value to become positive, then | ||
| 40 | decrement it. | ||
| 41 | |||
| 42 | - up or "V": increment the value (and wake up one waiting | ||
| 43 | thread, if any). */ | ||
| 44 | void | ||
| 45 | sema_init (struct semaphore *sema, unsigned value) | ||
| 46 | { | ||
| 47 | ASSERT (sema != NULL); | ||
| 48 | |||
| 49 | sema->value = value; | ||
| 50 | list_init (&sema->waiters); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Down or "P" operation on a semaphore. Waits for SEMA's value | ||
| 54 | to become positive and then atomically decrements it. | ||
| 55 | |||
| 56 | This function may sleep, so it must not be called within an | ||
| 57 | interrupt handler. This function may be called with | ||
| 58 | interrupts disabled, but if it sleeps then the next scheduled | ||
| 59 | thread will probably turn interrupts back on. */ | ||
| 60 | void | ||
| 61 | sema_down (struct semaphore *sema) | ||
| 62 | { | ||
| 63 | enum intr_level old_level; | ||
| 64 | |||
| 65 | ASSERT (sema != NULL); | ||
| 66 | ASSERT (!intr_context ()); | ||
| 67 | |||
| 68 | old_level = intr_disable (); | ||
| 69 | while (sema->value == 0) | ||
| 70 | { | ||
| 71 | list_push_back (&sema->waiters, &thread_current ()->elem); | ||
| 72 | thread_block (); | ||
| 73 | } | ||
| 74 | sema->value--; | ||
| 75 | intr_set_level (old_level); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Down or "P" operation on a semaphore, but only if the | ||
| 79 | semaphore is not already 0. Returns true if the semaphore is | ||
| 80 | decremented, false otherwise. | ||
| 81 | |||
| 82 | This function may be called from an interrupt handler. */ | ||
| 83 | bool | ||
| 84 | sema_try_down (struct semaphore *sema) | ||
| 85 | { | ||
| 86 | enum intr_level old_level; | ||
| 87 | bool success; | ||
| 88 | |||
| 89 | ASSERT (sema != NULL); | ||
| 90 | |||
| 91 | old_level = intr_disable (); | ||
| 92 | if (sema->value > 0) | ||
| 93 | { | ||
| 94 | sema->value--; | ||
| 95 | success = true; | ||
| 96 | } | ||
| 97 | else | ||
| 98 | success = false; | ||
| 99 | intr_set_level (old_level); | ||
| 100 | |||
| 101 | return success; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* Up or "V" operation on a semaphore. Increments SEMA's value | ||
| 105 | and wakes up one thread of those waiting for SEMA, if any. | ||
| 106 | |||
| 107 | This function may be called from an interrupt handler. */ | ||
| 108 | void | ||
| 109 | sema_up (struct semaphore *sema) | ||
| 110 | { | ||
| 111 | enum intr_level old_level; | ||
| 112 | |||
| 113 | ASSERT (sema != NULL); | ||
| 114 | |||
| 115 | old_level = intr_disable (); | ||
| 116 | if (!list_empty (&sema->waiters)) | ||
| 117 | thread_unblock (list_entry (list_pop_front (&sema->waiters), | ||
| 118 | struct thread, elem)); | ||
| 119 | sema->value++; | ||
| 120 | intr_set_level (old_level); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void sema_test_helper (void *sema_); | ||
| 124 | |||
| 125 | /* Self-test for semaphores that makes control "ping-pong" | ||
| 126 | between a pair of threads. Insert calls to printf() to see | ||
| 127 | what's going on. */ | ||
| 128 | void | ||
| 129 | sema_self_test (void) | ||
| 130 | { | ||
| 131 | struct semaphore sema[2]; | ||
| 132 | int i; | ||
| 133 | |||
| 134 | printf ("Testing semaphores..."); | ||
| 135 | sema_init (&sema[0], 0); | ||
| 136 | sema_init (&sema[1], 0); | ||
| 137 | thread_create ("sema-test", PRI_DEFAULT, sema_test_helper, &sema); | ||
| 138 | for (i = 0; i < 10; i++) | ||
| 139 | { | ||
| 140 | sema_up (&sema[0]); | ||
| 141 | sema_down (&sema[1]); | ||
| 142 | } | ||
| 143 | printf ("done.\n"); | ||
| 144 | } | ||
| 145 | |||
| 146 | /* Thread function used by sema_self_test(). */ | ||
| 147 | static void | ||
| 148 | sema_test_helper (void *sema_) | ||
| 149 | { | ||
| 150 | struct semaphore *sema = sema_; | ||
| 151 | int i; | ||
| 152 | |||
| 153 | for (i = 0; i < 10; i++) | ||
| 154 | { | ||
| 155 | sema_down (&sema[0]); | ||
| 156 | sema_up (&sema[1]); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Initializes LOCK. A lock can be held by at most a single | ||
| 161 | thread at any given time. Our locks are not "recursive", that | ||
| 162 | is, it is an error for the thread currently holding a lock to | ||
| 163 | try to acquire that lock. | ||
| 164 | |||
| 165 | A lock is a specialization of a semaphore with an initial | ||
| 166 | value of 1. The difference between a lock and such a | ||
| 167 | semaphore is twofold. First, a semaphore can have a value | ||
| 168 | greater than 1, but a lock can only be owned by a single | ||
| 169 | thread at a time. Second, a semaphore does not have an owner, | ||
| 170 | meaning that one thread can "down" the semaphore and then | ||
| 171 | another one "up" it, but with a lock the same thread must both | ||
| 172 | acquire and release it. When these restrictions prove | ||
| 173 | onerous, it's a good sign that a semaphore should be used, | ||
| 174 | instead of a lock. */ | ||
| 175 | void | ||
| 176 | lock_init (struct lock *lock) | ||
| 177 | { | ||
| 178 | ASSERT (lock != NULL); | ||
| 179 | |||
| 180 | lock->holder = NULL; | ||
| 181 | sema_init (&lock->semaphore, 1); | ||
| 182 | } | ||
| 183 | |||
| 184 | /* Acquires LOCK, sleeping until it becomes available if | ||
| 185 | necessary. The lock must not already be held by the current | ||
| 186 | thread. | ||
| 187 | |||
| 188 | This function may sleep, so it must not be called within an | ||
| 189 | interrupt handler. This function may be called with | ||
| 190 | interrupts disabled, but interrupts will be turned back on if | ||
| 191 | we need to sleep. */ | ||
| 192 | void | ||
| 193 | lock_acquire (struct lock *lock) | ||
| 194 | { | ||
| 195 | ASSERT (lock != NULL); | ||
| 196 | ASSERT (!intr_context ()); | ||
| 197 | ASSERT (!lock_held_by_current_thread (lock)); | ||
| 198 | |||
| 199 | sema_down (&lock->semaphore); | ||
| 200 | lock->holder = thread_current (); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* Tries to acquires LOCK and returns true if successful or false | ||
| 204 | on failure. The lock must not already be held by the current | ||
| 205 | thread. | ||
| 206 | |||
| 207 | This function will not sleep, so it may be called within an | ||
| 208 | interrupt handler. */ | ||
| 209 | bool | ||
| 210 | lock_try_acquire (struct lock *lock) | ||
| 211 | { | ||
| 212 | bool success; | ||
| 213 | |||
| 214 | ASSERT (lock != NULL); | ||
| 215 | ASSERT (!lock_held_by_current_thread (lock)); | ||
| 216 | |||
| 217 | success = sema_try_down (&lock->semaphore); | ||
| 218 | if (success) | ||
| 219 | lock->holder = thread_current (); | ||
| 220 | return success; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* Releases LOCK, which must be owned by the current thread. | ||
| 224 | |||
| 225 | An interrupt handler cannot acquire a lock, so it does not | ||
| 226 | make sense to try to release a lock within an interrupt | ||
| 227 | handler. */ | ||
| 228 | void | ||
| 229 | lock_release (struct lock *lock) | ||
| 230 | { | ||
| 231 | ASSERT (lock != NULL); | ||
| 232 | ASSERT (lock_held_by_current_thread (lock)); | ||
| 233 | |||
| 234 | lock->holder = NULL; | ||
| 235 | sema_up (&lock->semaphore); | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Returns true if the current thread holds LOCK, false | ||
| 239 | otherwise. (Note that testing whether some other thread holds | ||
| 240 | a lock would be racy.) */ | ||
| 241 | bool | ||
| 242 | lock_held_by_current_thread (const struct lock *lock) | ||
| 243 | { | ||
| 244 | ASSERT (lock != NULL); | ||
| 245 | |||
| 246 | return lock->holder == thread_current (); | ||
| 247 | } | ||
| 248 | |||
| 249 | /* One semaphore in a list. */ | ||
| 250 | struct semaphore_elem | ||
| 251 | { | ||
| 252 | struct list_elem elem; /* List element. */ | ||
| 253 | struct semaphore semaphore; /* This semaphore. */ | ||
| 254 | }; | ||
| 255 | |||
| 256 | /* Initializes condition variable COND. A condition variable | ||
| 257 | allows one piece of code to signal a condition and cooperating | ||
| 258 | code to receive the signal and act upon it. */ | ||
| 259 | void | ||
| 260 | cond_init (struct condition *cond) | ||
| 261 | { | ||
| 262 | ASSERT (cond != NULL); | ||
| 263 | |||
| 264 | list_init (&cond->waiters); | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Atomically releases LOCK and waits for COND to be signaled by | ||
| 268 | some other piece of code. After COND is signaled, LOCK is | ||
| 269 | reacquired before returning. LOCK must be held before calling | ||
| 270 | this function. | ||
| 271 | |||
| 272 | The monitor implemented by this function is "Mesa" style, not | ||
| 273 | "Hoare" style, that is, sending and receiving a signal are not | ||
| 274 | an atomic operation. Thus, typically the caller must recheck | ||
| 275 | the condition after the wait completes and, if necessary, wait | ||
| 276 | again. | ||
| 277 | |||
| 278 | A given condition variable is associated with only a single | ||
| 279 | lock, but one lock may be associated with any number of | ||
| 280 | condition variables. That is, there is a one-to-many mapping | ||
| 281 | from locks to condition variables. | ||
| 282 | |||
| 283 | This function may sleep, so it must not be called within an | ||
| 284 | interrupt handler. This function may be called with | ||
| 285 | interrupts disabled, but interrupts will be turned back on if | ||
| 286 | we need to sleep. */ | ||
| 287 | void | ||
| 288 | cond_wait (struct condition *cond, struct lock *lock) | ||
| 289 | { | ||
| 290 | struct semaphore_elem waiter; | ||
| 291 | |||
| 292 | ASSERT (cond != NULL); | ||
| 293 | ASSERT (lock != NULL); | ||
| 294 | ASSERT (!intr_context ()); | ||
| 295 | ASSERT (lock_held_by_current_thread (lock)); | ||
| 296 | |||
| 297 | sema_init (&waiter.semaphore, 0); | ||
| 298 | list_push_back (&cond->waiters, &waiter.elem); | ||
| 299 | lock_release (lock); | ||
| 300 | sema_down (&waiter.semaphore); | ||
| 301 | lock_acquire (lock); | ||
| 302 | } | ||
| 303 | |||
| 304 | /* If any threads are waiting on COND (protected by LOCK), then | ||
| 305 | this function signals one of them to wake up from its wait. | ||
| 306 | LOCK must be held before calling this function. | ||
| 307 | |||
| 308 | An interrupt handler cannot acquire a lock, so it does not | ||
| 309 | make sense to try to signal a condition variable within an | ||
| 310 | interrupt handler. */ | ||
| 311 | void | ||
| 312 | cond_signal (struct condition *cond, struct lock *lock UNUSED) | ||
| 313 | { | ||
| 314 | ASSERT (cond != NULL); | ||
| 315 | ASSERT (lock != NULL); | ||
| 316 | ASSERT (!intr_context ()); | ||
| 317 | ASSERT (lock_held_by_current_thread (lock)); | ||
| 318 | |||
| 319 | if (!list_empty (&cond->waiters)) | ||
| 320 | sema_up (&list_entry (list_pop_front (&cond->waiters), | ||
| 321 | struct semaphore_elem, elem)->semaphore); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Wakes up all threads, if any, waiting on COND (protected by | ||
| 325 | LOCK). LOCK must be held before calling this function. | ||
| 326 | |||
| 327 | An interrupt handler cannot acquire a lock, so it does not | ||
| 328 | make sense to try to signal a condition variable within an | ||
| 329 | interrupt handler. */ | ||
| 330 | void | ||
| 331 | cond_broadcast (struct condition *cond, struct lock *lock) | ||
| 332 | { | ||
| 333 | ASSERT (cond != NULL); | ||
| 334 | ASSERT (lock != NULL); | ||
| 335 | |||
| 336 | while (!list_empty (&cond->waiters)) | ||
| 337 | cond_signal (cond, lock); | ||
| 338 | } | ||
diff --git a/pintos-progos/threads/synch.h b/pintos-progos/threads/synch.h new file mode 100644 index 0000000..a19e88b --- /dev/null +++ b/pintos-progos/threads/synch.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #ifndef THREADS_SYNCH_H | ||
| 2 | #define THREADS_SYNCH_H | ||
| 3 | |||
| 4 | #include <list.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | |||
| 7 | /* A counting semaphore. */ | ||
| 8 | struct semaphore | ||
| 9 | { | ||
| 10 | unsigned value; /* Current value. */ | ||
| 11 | struct list waiters; /* List of waiting threads. */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | void sema_init (struct semaphore *, unsigned value); | ||
| 15 | void sema_down (struct semaphore *); | ||
| 16 | bool sema_try_down (struct semaphore *); | ||
| 17 | void sema_up (struct semaphore *); | ||
| 18 | void sema_self_test (void); | ||
| 19 | |||
| 20 | /* Lock. */ | ||
| 21 | struct lock | ||
| 22 | { | ||
| 23 | struct thread *holder; /* Thread holding lock (for debugging). */ | ||
| 24 | struct semaphore semaphore; /* Binary semaphore controlling access. */ | ||
| 25 | }; | ||
| 26 | |||
| 27 | void lock_init (struct lock *); | ||
| 28 | void lock_acquire (struct lock *); | ||
| 29 | bool lock_try_acquire (struct lock *); | ||
| 30 | void lock_release (struct lock *); | ||
| 31 | bool lock_held_by_current_thread (const struct lock *); | ||
| 32 | |||
| 33 | /* Condition variable. */ | ||
| 34 | struct condition | ||
| 35 | { | ||
| 36 | struct list waiters; /* List of waiting threads. */ | ||
| 37 | }; | ||
| 38 | |||
| 39 | void cond_init (struct condition *); | ||
| 40 | void cond_wait (struct condition *, struct lock *); | ||
| 41 | void cond_signal (struct condition *, struct lock *); | ||
| 42 | void cond_broadcast (struct condition *, struct lock *); | ||
| 43 | |||
| 44 | /* Optimization barrier. | ||
| 45 | |||
| 46 | The compiler will not reorder operations across an | ||
| 47 | optimization barrier. See "Optimization Barriers" in the | ||
| 48 | reference guide for more information.*/ | ||
| 49 | #define barrier() asm volatile ("" : : : "memory") | ||
| 50 | |||
| 51 | #endif /* threads/synch.h */ | ||
diff --git a/pintos-progos/threads/thread.c b/pintos-progos/threads/thread.c new file mode 100644 index 0000000..845f059 --- /dev/null +++ b/pintos-progos/threads/thread.c | |||
| @@ -0,0 +1,594 @@ | |||
| 1 | #include "threads/thread.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include <random.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include "threads/flags.h" | ||
| 8 | #include "threads/interrupt.h" | ||
| 9 | #include "threads/intr-stubs.h" | ||
| 10 | #include "threads/palloc.h" | ||
| 11 | #include "threads/switch.h" | ||
| 12 | #include "threads/synch.h" | ||
| 13 | #include "threads/vaddr.h" | ||
| 14 | #ifdef USERPROG | ||
| 15 | #include "userprog/process.h" | ||
| 16 | #endif | ||
| 17 | |||
| 18 | /* Random value for struct thread's `magic' member. | ||
| 19 | Used to detect stack overflow. See the big comment at the top | ||
| 20 | of thread.h for details. */ | ||
| 21 | #define THREAD_MAGIC 0xcd6abf4b | ||
| 22 | |||
| 23 | /* List of processes in THREAD_READY state, that is, processes | ||
| 24 | that are ready to run but not actually running. */ | ||
| 25 | static struct list ready_list; | ||
| 26 | |||
| 27 | /* List of all processes. Processes are added to this list | ||
| 28 | when they are first scheduled and removed when they exit. */ | ||
| 29 | static struct list all_list; | ||
| 30 | |||
| 31 | /* Idle thread. */ | ||
| 32 | static struct thread *idle_thread; | ||
| 33 | |||
| 34 | /* Initial thread, the thread running init.c:main(). */ | ||
| 35 | static struct thread *initial_thread; | ||
| 36 | |||
| 37 | /* Lock used by allocate_tid(). */ | ||
| 38 | static struct lock tid_lock; | ||
| 39 | |||
| 40 | /* Stack frame for kernel_thread(). */ | ||
| 41 | struct kernel_thread_frame | ||
| 42 | { | ||
| 43 | void *eip; /* Return address. */ | ||
| 44 | thread_func *function; /* Function to call. */ | ||
| 45 | void *aux; /* Auxiliary data for function. */ | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* Statistics. */ | ||
| 49 | static long long idle_ticks; /* # of timer ticks spent idle. */ | ||
| 50 | static long long kernel_ticks; /* # of timer ticks in kernel threads. */ | ||
| 51 | static long long user_ticks; /* # of timer ticks in user programs. */ | ||
| 52 | |||
| 53 | /* Scheduling. */ | ||
| 54 | #define TIME_SLICE 4 /* # of timer ticks to give each thread. */ | ||
| 55 | static unsigned thread_ticks; /* # of timer ticks since last yield. */ | ||
| 56 | |||
| 57 | /* If false (default), use round-robin scheduler. | ||
| 58 | If true, use multi-level feedback queue scheduler. | ||
| 59 | Controlled by kernel command-line option "-o mlfqs". */ | ||
| 60 | bool thread_mlfqs; | ||
| 61 | |||
| 62 | static void kernel_thread (thread_func *, void *aux); | ||
| 63 | |||
| 64 | static void idle (void *aux UNUSED); | ||
| 65 | static struct thread *running_thread (void); | ||
| 66 | static struct thread *next_thread_to_run (void); | ||
| 67 | static void init_thread (struct thread *, const char *name, int priority); | ||
| 68 | static bool is_thread (struct thread *) UNUSED; | ||
| 69 | static void *alloc_frame (struct thread *, size_t size); | ||
| 70 | static void schedule (void); | ||
| 71 | void thread_schedule_tail (struct thread *prev); | ||
| 72 | static tid_t allocate_tid (void); | ||
| 73 | |||
| 74 | /* Initializes the threading system by transforming the code | ||
| 75 | that's currently running into a thread. This can't work in | ||
| 76 | general and it is possible in this case only because loader.S | ||
| 77 | was careful to put the bottom of the stack at a page boundary. | ||
| 78 | |||
| 79 | Also initializes the run queue and the tid lock. | ||
| 80 | |||
| 81 | After calling this function, be sure to initialize the page | ||
| 82 | allocator before trying to create any threads with | ||
| 83 | thread_create(). | ||
| 84 | |||
| 85 | It is not safe to call thread_current() until this function | ||
| 86 | finishes. */ | ||
| 87 | void | ||
| 88 | thread_init (void) | ||
| 89 | { | ||
| 90 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 91 | |||
| 92 | lock_init (&tid_lock); | ||
| 93 | list_init (&ready_list); | ||
| 94 | list_init (&all_list); | ||
| 95 | |||
| 96 | #ifdef USERPROG | ||
| 97 | process_init (); | ||
| 98 | #endif | ||
| 99 | |||
| 100 | /* Set up a thread structure for the running thread. */ | ||
| 101 | initial_thread = running_thread (); | ||
| 102 | init_thread (initial_thread, "main", PRI_DEFAULT); | ||
| 103 | initial_thread->status = THREAD_RUNNING; | ||
| 104 | initial_thread->tid = allocate_tid (); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* Starts preemptive thread scheduling by enabling interrupts. | ||
| 108 | Also creates the idle thread. */ | ||
| 109 | void | ||
| 110 | thread_start (void) | ||
| 111 | { | ||
| 112 | /* Create the idle thread. */ | ||
| 113 | struct semaphore idle_started; | ||
| 114 | sema_init (&idle_started, 0); | ||
| 115 | thread_create ("idle", PRI_MIN, idle, &idle_started); | ||
| 116 | |||
| 117 | /* Start preemptive thread scheduling. */ | ||
| 118 | intr_enable (); | ||
| 119 | |||
| 120 | /* Wait for the idle thread to initialize idle_thread. */ | ||
| 121 | sema_down (&idle_started); | ||
| 122 | } | ||
| 123 | |||
| 124 | /* Called by the timer interrupt handler at each timer tick. | ||
| 125 | Thus, this function runs in an external interrupt context. */ | ||
| 126 | void | ||
| 127 | thread_tick (void) | ||
| 128 | { | ||
| 129 | struct thread *t = thread_current (); | ||
| 130 | |||
| 131 | /* Update statistics. */ | ||
| 132 | if (t == idle_thread) | ||
| 133 | idle_ticks++; | ||
| 134 | #ifdef USERPROG | ||
| 135 | else if (t->pagedir != NULL) | ||
| 136 | user_ticks++; | ||
| 137 | #endif | ||
| 138 | else | ||
| 139 | kernel_ticks++; | ||
| 140 | |||
| 141 | /* Enforce preemption. */ | ||
| 142 | if (++thread_ticks >= TIME_SLICE) | ||
| 143 | intr_yield_on_return (); | ||
| 144 | } | ||
| 145 | |||
| 146 | /* Prints thread statistics. */ | ||
| 147 | void | ||
| 148 | thread_print_stats (void) | ||
| 149 | { | ||
| 150 | printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n", | ||
| 151 | idle_ticks, kernel_ticks, user_ticks); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* Creates a new kernel thread named NAME with the given initial | ||
| 155 | PRIORITY, which executes FUNCTION passing AUX as the argument, | ||
| 156 | and adds it to the ready queue. Returns the thread identifier | ||
| 157 | for the new thread, or TID_ERROR if creation fails. | ||
| 158 | |||
| 159 | If thread_start() has been called, then the new thread may be | ||
| 160 | scheduled before thread_create() returns. It could even exit | ||
| 161 | before thread_create() returns. Contrariwise, the original | ||
| 162 | thread may run for any amount of time before the new thread is | ||
| 163 | scheduled. Use a semaphore or some other form of | ||
| 164 | synchronization if you need to ensure ordering. | ||
| 165 | |||
| 166 | The code provided sets the new thread's `priority' member to | ||
| 167 | PRIORITY, but no actual priority scheduling is implemented. | ||
| 168 | Priority scheduling is the goal of Problem 1-3. */ | ||
| 169 | tid_t | ||
| 170 | thread_create (const char *name, int priority, | ||
| 171 | thread_func *function, void *aux) | ||
| 172 | { | ||
| 173 | struct thread *t; | ||
| 174 | struct kernel_thread_frame *kf; | ||
| 175 | struct switch_entry_frame *ef; | ||
| 176 | struct switch_threads_frame *sf; | ||
| 177 | tid_t tid; | ||
| 178 | enum intr_level old_level; | ||
| 179 | |||
| 180 | ASSERT (function != NULL); | ||
| 181 | |||
| 182 | /* Allocate thread. */ | ||
| 183 | t = palloc_get_page (PAL_ZERO); | ||
| 184 | if (t == NULL) | ||
| 185 | return TID_ERROR; | ||
| 186 | |||
| 187 | /* Initialize thread. */ | ||
| 188 | init_thread (t, name, priority); | ||
| 189 | tid = t->tid = allocate_tid (); | ||
| 190 | |||
| 191 | /* Prepare thread for first run by initializing its stack. | ||
| 192 | Do this atomically so intermediate values for the 'stack' | ||
| 193 | member cannot be observed. */ | ||
| 194 | old_level = intr_disable (); | ||
| 195 | |||
| 196 | /* Stack frame for kernel_thread(). */ | ||
| 197 | kf = alloc_frame (t, sizeof *kf); | ||
| 198 | kf->eip = NULL; | ||
| 199 | kf->function = function; | ||
| 200 | kf->aux = aux; | ||
| 201 | |||
| 202 | /* Stack frame for switch_entry(). */ | ||
| 203 | ef = alloc_frame (t, sizeof *ef); | ||
| 204 | ef->eip = (void (*) (void)) kernel_thread; | ||
| 205 | |||
| 206 | /* Stack frame for switch_threads(). */ | ||
| 207 | sf = alloc_frame (t, sizeof *sf); | ||
| 208 | sf->eip = switch_entry; | ||
| 209 | sf->ebp = 0; | ||
| 210 | |||
| 211 | intr_set_level (old_level); | ||
| 212 | |||
| 213 | /* Add to run queue. */ | ||
| 214 | thread_unblock (t); | ||
| 215 | |||
| 216 | return tid; | ||
| 217 | } | ||
| 218 | |||
| 219 | /* Puts the current thread to sleep. It will not be scheduled | ||
| 220 | again until awoken by thread_unblock(). | ||
| 221 | |||
| 222 | This function must be called with interrupts turned off. It | ||
| 223 | is usually a better idea to use one of the synchronization | ||
| 224 | primitives in synch.h. */ | ||
| 225 | void | ||
| 226 | thread_block (void) | ||
| 227 | { | ||
| 228 | ASSERT (!intr_context ()); | ||
| 229 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 230 | |||
| 231 | thread_current ()->status = THREAD_BLOCKED; | ||
| 232 | schedule (); | ||
| 233 | } | ||
| 234 | |||
| 235 | /* Transitions a blocked thread T to the ready-to-run state. | ||
| 236 | This is an error if T is not blocked. (Use thread_yield() to | ||
| 237 | make the running thread ready.) | ||
| 238 | |||
| 239 | This function does not preempt the running thread. This can | ||
| 240 | be important: if the caller had disabled interrupts itself, | ||
| 241 | it may expect that it can atomically unblock a thread and | ||
| 242 | update other data. */ | ||
| 243 | void | ||
| 244 | thread_unblock (struct thread *t) | ||
| 245 | { | ||
| 246 | enum intr_level old_level; | ||
| 247 | |||
| 248 | ASSERT (is_thread (t)); | ||
| 249 | |||
| 250 | old_level = intr_disable (); | ||
| 251 | ASSERT (t->status == THREAD_BLOCKED); | ||
| 252 | list_push_back (&ready_list, &t->elem); | ||
| 253 | t->status = THREAD_READY; | ||
| 254 | intr_set_level (old_level); | ||
| 255 | } | ||
| 256 | |||
| 257 | /* Returns the name of the running thread. */ | ||
| 258 | const char * | ||
| 259 | thread_name (void) | ||
| 260 | { | ||
| 261 | return thread_current ()->name; | ||
| 262 | } | ||
| 263 | |||
| 264 | /* Returns the running thread. | ||
| 265 | This is running_thread() plus a couple of sanity checks. | ||
| 266 | See the big comment at the top of thread.h for details. */ | ||
| 267 | struct thread * | ||
| 268 | thread_current (void) | ||
| 269 | { | ||
| 270 | struct thread *t = running_thread (); | ||
| 271 | |||
| 272 | /* Make sure T is really a thread. | ||
| 273 | If either of these assertions fire, then your thread may | ||
| 274 | have overflowed its stack. Each thread has less than 4 kB | ||
| 275 | of stack, so a few big automatic arrays or moderate | ||
| 276 | recursion can cause stack overflow. */ | ||
| 277 | ASSERT (is_thread (t)); | ||
| 278 | ASSERT (t->status == THREAD_RUNNING); | ||
| 279 | |||
| 280 | return t; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* Returns the running thread's tid. */ | ||
| 284 | tid_t | ||
| 285 | thread_tid (void) | ||
| 286 | { | ||
| 287 | return thread_current ()->tid; | ||
| 288 | } | ||
| 289 | |||
| 290 | /* Deschedules the current thread and destroys it. Never | ||
| 291 | returns to the caller. */ | ||
| 292 | void | ||
| 293 | thread_exit (void) | ||
| 294 | { | ||
| 295 | ASSERT (!intr_context ()); | ||
| 296 | |||
| 297 | #ifdef USERPROG | ||
| 298 | process_exit (); | ||
| 299 | #endif | ||
| 300 | |||
| 301 | /* Remove thread from all threads list, set our status to dying, | ||
| 302 | and schedule another process. That process will destroy us | ||
| 303 | when it calls thread_schedule_tail(). */ | ||
| 304 | intr_disable (); | ||
| 305 | list_remove (&thread_current()->allelem); | ||
| 306 | thread_current ()->status = THREAD_DYING; | ||
| 307 | schedule (); | ||
| 308 | NOT_REACHED (); | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Yields the CPU. The current thread is not put to sleep and | ||
| 312 | may be scheduled again immediately at the scheduler's whim. */ | ||
| 313 | void | ||
| 314 | thread_yield (void) | ||
| 315 | { | ||
| 316 | struct thread *cur = thread_current (); | ||
| 317 | enum intr_level old_level; | ||
| 318 | |||
| 319 | ASSERT (!intr_context ()); | ||
| 320 | |||
| 321 | old_level = intr_disable (); | ||
| 322 | if (cur != idle_thread) | ||
| 323 | list_push_back (&ready_list, &cur->elem); | ||
| 324 | cur->status = THREAD_READY; | ||
| 325 | schedule (); | ||
| 326 | intr_set_level (old_level); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Invoke function 'func' on all threads, passing along 'aux'. | ||
| 330 | This function must be called with interrupts off. */ | ||
| 331 | void | ||
| 332 | thread_foreach (thread_action_func *func, void *aux) | ||
| 333 | { | ||
| 334 | struct list_elem *e; | ||
| 335 | |||
| 336 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 337 | |||
| 338 | for (e = list_begin (&all_list); e != list_end (&all_list); | ||
| 339 | e = list_next (e)) | ||
| 340 | { | ||
| 341 | struct thread *t = list_entry (e, struct thread, allelem); | ||
| 342 | func (t, aux); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | /* Sets the current thread's priority to NEW_PRIORITY. */ | ||
| 347 | void | ||
| 348 | thread_set_priority (int new_priority) | ||
| 349 | { | ||
| 350 | thread_current ()->priority = new_priority; | ||
| 351 | } | ||
| 352 | |||
| 353 | /* Returns the current thread's priority. */ | ||
| 354 | int | ||
| 355 | thread_get_priority (void) | ||
| 356 | { | ||
| 357 | return thread_current ()->priority; | ||
| 358 | } | ||
| 359 | |||
| 360 | /* Sets the current thread's nice value to NICE. */ | ||
| 361 | void | ||
| 362 | thread_set_nice (int nice UNUSED) | ||
| 363 | { | ||
| 364 | /* Not yet implemented. */ | ||
| 365 | } | ||
| 366 | |||
| 367 | /* Returns the current thread's nice value. */ | ||
| 368 | int | ||
| 369 | thread_get_nice (void) | ||
| 370 | { | ||
| 371 | /* Not yet implemented. */ | ||
| 372 | return 0; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* Returns 100 times the system load average. */ | ||
| 376 | int | ||
| 377 | thread_get_load_avg (void) | ||
| 378 | { | ||
| 379 | /* Not yet implemented. */ | ||
| 380 | return 0; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* Returns 100 times the current thread's recent_cpu value. */ | ||
| 384 | int | ||
| 385 | thread_get_recent_cpu (void) | ||
| 386 | { | ||
| 387 | /* Not yet implemented. */ | ||
| 388 | return 0; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* Idle thread. Executes when no other thread is ready to run. | ||
| 392 | |||
| 393 | The idle thread is initially put on the ready list by | ||
| 394 | thread_start(). It will be scheduled once initially, at which | ||
| 395 | point it initializes idle_thread, "up"s the semaphore passed | ||
| 396 | to it to enable thread_start() to continue, and immediately | ||
| 397 | blocks. After that, the idle thread never appears in the | ||
| 398 | ready list. It is returned by next_thread_to_run() as a | ||
| 399 | special case when the ready list is empty. */ | ||
| 400 | static void | ||
| 401 | idle (void *idle_started_ UNUSED) | ||
| 402 | { | ||
| 403 | struct semaphore *idle_started = idle_started_; | ||
| 404 | idle_thread = thread_current (); | ||
| 405 | sema_up (idle_started); | ||
| 406 | |||
| 407 | for (;;) | ||
| 408 | { | ||
| 409 | /* Let someone else run. */ | ||
| 410 | intr_disable (); | ||
| 411 | thread_block (); | ||
| 412 | |||
| 413 | /* Re-enable interrupts and wait for the next one. | ||
| 414 | |||
| 415 | The `sti' instruction disables interrupts until the | ||
| 416 | completion of the next instruction, so these two | ||
| 417 | instructions are executed atomically. This atomicity is | ||
| 418 | important; otherwise, an interrupt could be handled | ||
| 419 | between re-enabling interrupts and waiting for the next | ||
| 420 | one to occur, wasting as much as one clock tick worth of | ||
| 421 | time. | ||
| 422 | |||
| 423 | See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a] | ||
| 424 | 7.11.1 "HLT Instruction". */ | ||
| 425 | asm volatile ("sti; hlt" : : : "memory"); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | /* Function used as the basis for a kernel thread. */ | ||
| 430 | static void | ||
| 431 | kernel_thread (thread_func *function, void *aux) | ||
| 432 | { | ||
| 433 | ASSERT (function != NULL); | ||
| 434 | |||
| 435 | intr_enable (); /* The scheduler runs with interrupts off. */ | ||
| 436 | function (aux); /* Execute the thread function. */ | ||
| 437 | thread_exit (); /* If function() returns, kill the thread. */ | ||
| 438 | } | ||
| 439 | |||
| 440 | /* Returns the running thread. */ | ||
| 441 | struct thread * | ||
| 442 | running_thread (void) | ||
| 443 | { | ||
| 444 | uint32_t *esp; | ||
| 445 | |||
| 446 | /* Copy the CPU's stack pointer into `esp', and then round that | ||
| 447 | down to the start of a page. Because `struct thread' is | ||
| 448 | always at the beginning of a page and the stack pointer is | ||
| 449 | somewhere in the middle, this locates the curent thread. */ | ||
| 450 | asm ("mov %%esp, %0" : "=g" (esp)); | ||
| 451 | return pg_round_down (esp); | ||
| 452 | } | ||
| 453 | |||
| 454 | /* Returns true if T appears to point to a valid thread. */ | ||
| 455 | static bool | ||
| 456 | is_thread (struct thread *t) | ||
| 457 | { | ||
| 458 | return t != NULL && t->magic == THREAD_MAGIC; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* Does basic initialization of T as a blocked thread named | ||
| 462 | NAME. */ | ||
| 463 | static void | ||
| 464 | init_thread (struct thread *t, const char *name, int priority) | ||
| 465 | { | ||
| 466 | ASSERT (t != NULL); | ||
| 467 | ASSERT (PRI_MIN <= priority && priority <= PRI_MAX); | ||
| 468 | ASSERT (name != NULL); | ||
| 469 | |||
| 470 | memset (t, 0, sizeof *t); | ||
| 471 | t->status = THREAD_BLOCKED; | ||
| 472 | strlcpy (t->name, name, sizeof t->name); | ||
| 473 | t->stack = (uint8_t *) t + PGSIZE; | ||
| 474 | t->priority = priority; | ||
| 475 | t->magic = THREAD_MAGIC; | ||
| 476 | list_push_back (&all_list, &t->allelem); | ||
| 477 | #ifdef USERPROG | ||
| 478 | list_init(&t->children); | ||
| 479 | #endif | ||
| 480 | } | ||
| 481 | |||
| 482 | /* Allocates a SIZE-byte frame at the top of thread T's stack and | ||
| 483 | returns a pointer to the frame's base. */ | ||
| 484 | static void * | ||
| 485 | alloc_frame (struct thread *t, size_t size) | ||
| 486 | { | ||
| 487 | /* Stack data is always allocated in word-size units. */ | ||
| 488 | ASSERT (is_thread (t)); | ||
| 489 | ASSERT (size % sizeof (uint32_t) == 0); | ||
| 490 | |||
| 491 | t->stack -= size; | ||
| 492 | return t->stack; | ||
| 493 | } | ||
| 494 | |||
| 495 | /* Chooses and returns the next thread to be scheduled. Should | ||
| 496 | return a thread from the run queue, unless the run queue is | ||
| 497 | empty. (If the running thread can continue running, then it | ||
| 498 | will be in the run queue.) If the run queue is empty, return | ||
| 499 | idle_thread. */ | ||
| 500 | static struct thread * | ||
| 501 | next_thread_to_run (void) | ||
| 502 | { | ||
| 503 | if (list_empty (&ready_list)) | ||
| 504 | return idle_thread; | ||
| 505 | else | ||
| 506 | return list_entry (list_pop_front (&ready_list), struct thread, elem); | ||
| 507 | } | ||
| 508 | |||
| 509 | /* Completes a thread switch by activating the new thread's page | ||
| 510 | tables, and, if the previous thread is dying, destroying it. | ||
| 511 | |||
| 512 | At this function's invocation, we just switched from thread | ||
| 513 | PREV, the new thread is already running, and interrupts are | ||
| 514 | still disabled. This function is normally invoked by | ||
| 515 | thread_schedule() as its final action before returning, but | ||
| 516 | the first time a thread is scheduled it is called by | ||
| 517 | switch_entry() (see switch.S). | ||
| 518 | |||
| 519 | It's not safe to call printf() until the thread switch is | ||
| 520 | complete. In practice that means that printf()s should be | ||
| 521 | added at the end of the function. | ||
| 522 | |||
| 523 | After this function and its caller returns, the thread switch | ||
| 524 | is complete. */ | ||
| 525 | void | ||
| 526 | thread_schedule_tail (struct thread *prev) | ||
| 527 | { | ||
| 528 | struct thread *cur = running_thread (); | ||
| 529 | |||
| 530 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 531 | |||
| 532 | /* Mark us as running. */ | ||
| 533 | cur->status = THREAD_RUNNING; | ||
| 534 | |||
| 535 | /* Start new time slice. */ | ||
| 536 | thread_ticks = 0; | ||
| 537 | |||
| 538 | #ifdef USERPROG | ||
| 539 | /* Activate the new address space. */ | ||
| 540 | process_activate (); | ||
| 541 | #endif | ||
| 542 | |||
| 543 | /* If the thread we switched from is dying, destroy its struct | ||
| 544 | thread. This must happen late so that thread_exit() doesn't | ||
| 545 | pull out the rug under itself. (We don't free | ||
| 546 | initial_thread because its memory was not obtained via | ||
| 547 | palloc().) */ | ||
| 548 | if (prev != NULL && prev->status == THREAD_DYING && prev != initial_thread) | ||
| 549 | { | ||
| 550 | ASSERT (prev != cur); | ||
| 551 | palloc_free_page (prev); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | /* Schedules a new process. At entry, interrupts must be off and | ||
| 556 | the running process's state must have been changed from | ||
| 557 | running to some other state. This function finds another | ||
| 558 | thread to run and switches to it. | ||
| 559 | |||
| 560 | It's not safe to call printf() until thread_schedule_tail() | ||
| 561 | has completed. */ | ||
| 562 | static void | ||
| 563 | schedule (void) | ||
| 564 | { | ||
| 565 | struct thread *cur = running_thread (); | ||
| 566 | struct thread *next = next_thread_to_run (); | ||
| 567 | struct thread *prev = NULL; | ||
| 568 | |||
| 569 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 570 | ASSERT (cur->status != THREAD_RUNNING); | ||
| 571 | ASSERT (is_thread (next)); | ||
| 572 | |||
| 573 | if (cur != next) | ||
| 574 | prev = switch_threads (cur, next); | ||
| 575 | thread_schedule_tail (prev); | ||
| 576 | } | ||
| 577 | |||
| 578 | /* Returns a tid to use for a new thread. */ | ||
| 579 | static tid_t | ||
| 580 | allocate_tid (void) | ||
| 581 | { | ||
| 582 | static tid_t next_tid = 1; | ||
| 583 | tid_t tid; | ||
| 584 | |||
| 585 | lock_acquire (&tid_lock); | ||
| 586 | tid = next_tid++; | ||
| 587 | lock_release (&tid_lock); | ||
| 588 | |||
| 589 | return tid; | ||
| 590 | } | ||
| 591 | |||
| 592 | /* Offset of `stack' member within `struct thread'. | ||
| 593 | Used by switch.S, which can't figure it out on its own. */ | ||
| 594 | uint32_t thread_stack_ofs = offsetof (struct thread, stack); | ||
diff --git a/pintos-progos/threads/thread.h b/pintos-progos/threads/thread.h new file mode 100644 index 0000000..eebf42c --- /dev/null +++ b/pintos-progos/threads/thread.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | #ifndef THREADS_THREAD_H | ||
| 2 | #define THREADS_THREAD_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <list.h> | ||
| 6 | #include <stdint.h> | ||
| 7 | #include "threads/synch.h" | ||
| 8 | |||
| 9 | /* States in a thread's life cycle. */ | ||
| 10 | enum thread_status | ||
| 11 | { | ||
| 12 | THREAD_RUNNING, /* Running thread. */ | ||
| 13 | THREAD_READY, /* Not running but ready to run. */ | ||
| 14 | THREAD_BLOCKED, /* Waiting for an event to trigger. */ | ||
| 15 | THREAD_DYING /* About to be destroyed. */ | ||
| 16 | }; | ||
| 17 | |||
| 18 | /* Thread identifier type. | ||
| 19 | You can redefine this to whatever type you like. */ | ||
| 20 | typedef int tid_t; | ||
| 21 | #define TID_ERROR ((tid_t) -1) /* Error value for tid_t. */ | ||
| 22 | |||
| 23 | /* Thread priorities. */ | ||
| 24 | #define PRI_MIN 0 /* Lowest priority. */ | ||
| 25 | #define PRI_DEFAULT 31 /* Default priority. */ | ||
| 26 | #define PRI_MAX 63 /* Highest priority. */ | ||
| 27 | |||
| 28 | /* A kernel thread or user process. | ||
| 29 | |||
| 30 | Each thread structure is stored in its own 4 kB page. The | ||
| 31 | thread structure itself sits at the very bottom of the page | ||
| 32 | (at offset 0). The rest of the page is reserved for the | ||
| 33 | thread's kernel stack, which grows downward from the top of | ||
| 34 | the page (at offset 4 kB). Here's an illustration: | ||
| 35 | |||
| 36 | 4 kB +---------------------------------+ | ||
| 37 | | kernel stack | | ||
| 38 | | | | | ||
| 39 | | | | | ||
| 40 | | V | | ||
| 41 | | grows downward | | ||
| 42 | | | | ||
| 43 | | | | ||
| 44 | | | | ||
| 45 | | | | ||
| 46 | | | | ||
| 47 | | | | ||
| 48 | | | | ||
| 49 | | | | ||
| 50 | +---------------------------------+ | ||
| 51 | | magic | | ||
| 52 | | : | | ||
| 53 | | : | | ||
| 54 | | name | | ||
| 55 | | status | | ||
| 56 | 0 kB +---------------------------------+ | ||
| 57 | |||
| 58 | The upshot of this is twofold: | ||
| 59 | |||
| 60 | 1. First, `struct thread' must not be allowed to grow too | ||
| 61 | big. If it does, then there will not be enough room for | ||
| 62 | the kernel stack. Our base `struct thread' is only a | ||
| 63 | few bytes in size. It probably should stay well under 1 | ||
| 64 | kB. | ||
| 65 | |||
| 66 | 2. Second, kernel stacks must not be allowed to grow too | ||
| 67 | large. If a stack overflows, it will corrupt the thread | ||
| 68 | state. Thus, kernel functions should not allocate large | ||
| 69 | structures or arrays as non-static local variables. Use | ||
| 70 | dynamic allocation with malloc() or palloc_get_page() | ||
| 71 | instead. | ||
| 72 | |||
| 73 | The first symptom of either of these problems will probably be | ||
| 74 | an assertion failure in thread_current(), which checks that | ||
| 75 | the `magic' member of the running thread's `struct thread' is | ||
| 76 | set to THREAD_MAGIC. Stack overflow will normally change this | ||
| 77 | value, triggering the assertion. */ | ||
| 78 | /* The `elem' member has a dual purpose. It can be an element in | ||
| 79 | the run queue (thread.c), or it can be an element in a | ||
| 80 | semaphore wait list (synch.c). It can be used these two ways | ||
| 81 | only because they are mutually exclusive: only a thread in the | ||
| 82 | ready state is on the run queue, whereas only a thread in the | ||
| 83 | blocked state is on a semaphore wait list. */ | ||
| 84 | struct thread | ||
| 85 | { | ||
| 86 | /* Owned by thread.c. */ | ||
| 87 | tid_t tid; /* Thread identifier. */ | ||
| 88 | enum thread_status status; /* Thread state. */ | ||
| 89 | char name[16]; /* Name (for debugging purposes). */ | ||
| 90 | uint8_t *stack; /* Saved stack pointer. */ | ||
| 91 | int priority; /* Priority. */ | ||
| 92 | struct list_elem allelem; /* List element for all threads list. */ | ||
| 93 | |||
| 94 | /* Shared between thread.c and synch.c. */ | ||
| 95 | struct list_elem elem; /* List element. */ | ||
| 96 | |||
| 97 | #ifdef USERPROG | ||
| 98 | /* Owned by userprog/process.c */ | ||
| 99 | struct process* process; /* Process Structure */ | ||
| 100 | struct list children; /* Threads can hold processes, but not vice versa */ | ||
| 101 | uint32_t *pagedir; /* Page directory. */ | ||
| 102 | #endif | ||
| 103 | |||
| 104 | /* Owned by thread.c. */ | ||
| 105 | unsigned magic; /* Detects stack overflow. */ | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* If false (default), use round-robin scheduler. | ||
| 109 | If true, use multi-level feedback queue scheduler. | ||
| 110 | Controlled by kernel command-line option "-o mlfqs". */ | ||
| 111 | extern bool thread_mlfqs; | ||
| 112 | |||
| 113 | void thread_init (void); | ||
| 114 | void thread_start (void); | ||
| 115 | |||
| 116 | void thread_tick (void); | ||
| 117 | void thread_print_stats (void); | ||
| 118 | |||
| 119 | typedef void thread_func (void *aux); | ||
| 120 | tid_t thread_create (const char *name, int priority, thread_func *, void *); | ||
| 121 | |||
| 122 | void thread_block (void); | ||
| 123 | void thread_unblock (struct thread *); | ||
| 124 | |||
| 125 | struct thread *thread_current (void); | ||
| 126 | tid_t thread_tid (void); | ||
| 127 | const char *thread_name (void); | ||
| 128 | |||
| 129 | void thread_exit (void) NO_RETURN; | ||
| 130 | void thread_yield (void); | ||
| 131 | |||
| 132 | /* Performs some operation on thread t, given auxiliary data AUX. */ | ||
| 133 | typedef void thread_action_func (struct thread *t, void *aux); | ||
| 134 | void thread_foreach (thread_action_func *, void *); | ||
| 135 | |||
| 136 | int thread_get_priority (void); | ||
| 137 | void thread_set_priority (int); | ||
| 138 | |||
| 139 | int thread_get_nice (void); | ||
| 140 | void thread_set_nice (int); | ||
| 141 | int thread_get_recent_cpu (void); | ||
| 142 | int thread_get_load_avg (void); | ||
| 143 | |||
| 144 | #endif /* threads/thread.h */ | ||
diff --git a/pintos-progos/threads/vaddr.h b/pintos-progos/threads/vaddr.h new file mode 100644 index 0000000..184c824 --- /dev/null +++ b/pintos-progos/threads/vaddr.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | #ifndef THREADS_VADDR_H | ||
| 2 | #define THREADS_VADDR_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | |||
| 8 | #include "threads/loader.h" | ||
| 9 | |||
| 10 | /* Functions and macros for working with virtual addresses. | ||
| 11 | |||
| 12 | See pte.h for functions and macros specifically for x86 | ||
| 13 | hardware page tables. */ | ||
| 14 | |||
| 15 | #define BITMASK(SHIFT, CNT) (((1ul << (CNT)) - 1) << (SHIFT)) | ||
| 16 | |||
| 17 | /* Page offset (bits 0:12). */ | ||
| 18 | #define PGSHIFT 0 /* Index of first offset bit. */ | ||
| 19 | #define PGBITS 12 /* Number of offset bits. */ | ||
| 20 | #define PGSIZE (1 << PGBITS) /* Bytes in a page. */ | ||
| 21 | #define PGMASK BITMASK(PGSHIFT, PGBITS) /* Page offset bits (0:12). */ | ||
| 22 | |||
| 23 | /* Offset within a page. */ | ||
| 24 | static inline unsigned pg_ofs (const void *va) { | ||
| 25 | return (uintptr_t) va & PGMASK; | ||
| 26 | } | ||
| 27 | |||
| 28 | /* Virtual page number. */ | ||
| 29 | static inline uintptr_t pg_no (const void *va) { | ||
| 30 | return (uintptr_t) va >> PGBITS; | ||
| 31 | } | ||
| 32 | |||
| 33 | /* Round up to nearest page boundary. */ | ||
| 34 | static inline void *pg_round_up (const void *va) { | ||
| 35 | return (void *) (((uintptr_t) va + PGSIZE - 1) & ~PGMASK); | ||
| 36 | } | ||
| 37 | |||
| 38 | /* Round down to nearest page boundary. */ | ||
| 39 | static inline void *pg_round_down (const void *va) { | ||
| 40 | return (void *) ((uintptr_t) va & ~PGMASK); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Base address of the 1:1 physical-to-virtual mapping. Physical | ||
| 44 | memory is mapped starting at this virtual address. Thus, | ||
| 45 | physical address 0 is accessible at PHYS_BASE, physical | ||
| 46 | address address 0x1234 at (uint8_t *) PHYS_BASE + 0x1234, and | ||
| 47 | so on. | ||
| 48 | |||
| 49 | This address also marks the end of user programs' address | ||
| 50 | space. Up to this point in memory, user programs are allowed | ||
| 51 | to map whatever they like. At this point and above, the | ||
| 52 | virtual address space belongs to the kernel. */ | ||
| 53 | #define PHYS_BASE ((void *) LOADER_PHYS_BASE) | ||
| 54 | |||
| 55 | /* Returns true if VADDR is a user virtual address. */ | ||
| 56 | static inline bool | ||
| 57 | is_user_vaddr (const void *vaddr) | ||
| 58 | { | ||
| 59 | return vaddr < PHYS_BASE; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* Returns true if VADDR is a kernel virtual address. */ | ||
| 63 | static inline bool | ||
| 64 | is_kernel_vaddr (const void *vaddr) | ||
| 65 | { | ||
| 66 | return vaddr >= PHYS_BASE; | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Returns kernel virtual address at which physical address PADDR | ||
| 70 | is mapped. */ | ||
| 71 | static inline void * | ||
| 72 | ptov (uintptr_t paddr) | ||
| 73 | { | ||
| 74 | ASSERT ((void *) paddr < PHYS_BASE); | ||
| 75 | |||
| 76 | return (void *) (paddr + PHYS_BASE); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Returns physical address at which kernel virtual address VADDR | ||
| 80 | is mapped. */ | ||
| 81 | static inline uintptr_t | ||
| 82 | vtop (const void *vaddr) | ||
| 83 | { | ||
| 84 | ASSERT (is_kernel_vaddr (vaddr)); | ||
| 85 | |||
| 86 | return (uintptr_t) vaddr - (uintptr_t) PHYS_BASE; | ||
| 87 | } | ||
| 88 | |||
| 89 | #endif /* threads/vaddr.h */ | ||
