CS318 - Pintos
Pintos source browser for JHU CS318 course
init.c
Go to the documentation of this file.
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 /** Page directory with kernel mappings only. */
43 
44 #ifdef FILESYS
45 /** -f: Format the file system? */
46 static bool format_filesys;
47 
48 /** -filesys, -scratch, -swap: Names of block devices to use,
49  overriding the defaults. */
50 static const char *filesys_bdev_name;
51 static const char *scratch_bdev_name;
52 #ifdef VM
53 static const char *swap_bdev_name;
54 #endif
55 #endif /**< FILESYS */
56 
57 /** -ul: Maximum number of pages to put into palloc's user pool. */
58 static size_t user_page_limit = SIZE_MAX;
59 
60 static void bss_init (void);
61 static void paging_init (void);
62 
63 static char **read_command_line (void);
64 static char **parse_options (char **argv);
65 static void run_actions (char **argv);
66 static void usage (void);
67 
68 #ifdef FILESYS
69 static void locate_block_devices (void);
70 static void locate_block_device (enum block_type, const char *name);
71 #endif
72 
74 
75 /** Pintos main entry point. */
76 int
77 pintos_init (void)
78 {
79  char **argv;
80 
81  /* Clear BSS. */
82  bss_init ();
83 
84  /* Break command line into arguments and parse options. */
85  argv = read_command_line ();
86  argv = parse_options (argv);
87 
88  /* Initialize ourselves as a thread so we can use locks,
89  then enable console locking. */
90  thread_init ();
91  console_init ();
92 
93  /* Greet user. */
94  printf ("Pintos booting with %'"PRIu32" kB RAM...\n",
95  init_ram_pages * PGSIZE / 1024);
96 
97  /* Initialize memory system. */
99  malloc_init ();
100  paging_init ();
101 
102  /* Segmentation. */
103 #ifdef USERPROG
104  tss_init ();
105  gdt_init ();
106 #endif
107 
108  /* Initialize interrupt handlers. */
109  intr_init ();
110  timer_init ();
111  kbd_init ();
112  input_init ();
113 #ifdef USERPROG
114  exception_init ();
115  syscall_init ();
116 #endif
117 
118  /* Start thread scheduler and enable interrupts. */
119  thread_start ();
121  timer_calibrate ();
122 
123 #ifdef FILESYS
124  /* Initialize file system. */
125  ide_init ();
126  locate_block_devices ();
127  filesys_init (format_filesys);
128 #endif
129 
130  printf ("Boot complete.\n");
131 
132  if (*argv != NULL) {
133  /* Run actions specified on kernel command line. */
134  run_actions (argv);
135  } else {
136  // TODO: no command line passed to kernel. Run interactively
137  }
138 
139  /* Finish up. */
140  shutdown ();
141  thread_exit ();
142 }
143 
144 /** Clear the "BSS", a segment that should be initialized to
145  zeros. It isn't actually stored on disk or zeroed by the
146  kernel loader, so we have to zero it ourselves.
147 
148  The start and end of the BSS segment is recorded by the
149  linker as _start_bss and _end_bss. See kernel.lds. */
150 static void
151 bss_init (void)
152 {
153  extern char _start_bss, _end_bss;
154  memset (&_start_bss, 0, &_end_bss - &_start_bss);
155 }
156 
157 /** Populates the base page directory and page table with the
158  kernel virtual mapping, and then sets up the CPU to use the
159  new page directory. Points init_page_dir to the page
160  directory it creates. */
161 static void
163 {
164  uint32_t *pd, *pt;
165  size_t page;
166  extern char _start, _end_kernel_text;
167 
169  pt = NULL;
170  for (page = 0; page < init_ram_pages; page++)
171  {
172  uintptr_t paddr = page * PGSIZE;
173  char *vaddr = ptov (paddr);
174  size_t pde_idx = pd_no (vaddr);
175  size_t pte_idx = pt_no (vaddr);
176  bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
177 
178  if (pd[pde_idx] == 0)
179  {
181  pd[pde_idx] = pde_create (pt);
182  }
183 
184  pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
185  }
186 
187  /* Store the physical address of the page directory into CR3
188  aka PDBR (page directory base register). This activates our
189  new page tables immediately. See [IA32-v2a] "MOV--Move
190  to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
191  of the Page Directory". */
192  asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
193 }
194 
195 /** Breaks the kernel command line into words and returns them as
196  an argv-like array. */
197 static char **
199 {
200  static char *argv[LOADER_ARGS_LEN / 2 + 1];
201  char *p, *end;
202  int argc;
203  int i;
204 
205  argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
206  p = ptov (LOADER_ARGS);
207  end = p + LOADER_ARGS_LEN;
208  for (i = 0; i < argc; i++)
209  {
210  if (p >= end)
211  PANIC ("command line arguments overflow");
212 
213  argv[i] = p;
214  p += strnlen (p, end - p) + 1;
215  }
216  argv[argc] = NULL;
217 
218  /* Print kernel command line. */
219  printf ("Kernel command line:");
220  for (i = 0; i < argc; i++)
221  if (strchr (argv[i], ' ') == NULL)
222  printf (" %s", argv[i]);
223  else
224  printf (" '%s'", argv[i]);
225  printf ("\n");
226 
227  return argv;
228 }
229 
230 /** Parses options in ARGV[]
231  and returns the first non-option argument. */
232 static char **
233 parse_options (char **argv)
234 {
235  for (; *argv != NULL && **argv == '-'; argv++)
236  {
237  char *save_ptr;
238  char *name = strtok_r (*argv, "=", &save_ptr);
239  char *value = strtok_r (NULL, "", &save_ptr);
240 
241  if (!strcmp (name, "-h"))
242  usage ();
243  else if (!strcmp (name, "-q"))
245  else if (!strcmp (name, "-r"))
247 #ifdef FILESYS
248  else if (!strcmp (name, "-f"))
249  format_filesys = true;
250  else if (!strcmp (name, "-filesys"))
251  filesys_bdev_name = value;
252  else if (!strcmp (name, "-scratch"))
253  scratch_bdev_name = value;
254 #ifdef VM
255  else if (!strcmp (name, "-swap"))
256  swap_bdev_name = value;
257 #endif
258 #endif
259  else if (!strcmp (name, "-rs"))
260  random_init (atoi (value));
261  else if (!strcmp (name, "-mlfqs"))
262  thread_mlfqs = true;
263 #ifdef USERPROG
264  else if (!strcmp (name, "-ul"))
266 #endif
267  else
268  PANIC ("unknown option `%s' (use -h for help)", name);
269  }
270 
271  /* Initialize the random number generator based on the system
272  time. This has no effect if an "-rs" option was specified.
273 
274  When running under Bochs, this is not enough by itself to
275  get a good seed value, because the pintos script sets the
276  initial time to a predictable value, not to the local time,
277  for reproducibility. To fix this, give the "-r" option to
278  the pintos script to request real-time execution. */
280 
281  return argv;
282 }
283 
284 /** Runs the task specified in ARGV[1]. */
285 static void
286 run_task (char **argv)
287 {
288  const char *task = argv[1];
289 
290  printf ("Executing '%s':\n", task);
291 #ifdef USERPROG
292  process_wait (process_execute (task));
293 #else
294  run_test (task);
295 #endif
296  printf ("Execution of '%s' complete.\n", task);
297 }
298 
299 /** Executes all of the actions specified in ARGV[]
300  up to the null pointer sentinel. */
301 static void
302 run_actions (char **argv)
303 {
304  /* An action. */
305  struct action
306  {
307  char *name; /**< Action name. */
308  int argc; /**< # of args, including action name. */
309  void (*function) (char **argv); /**< Function to execute action. */
310  };
311 
312  /* Table of supported actions. */
313  static const struct action actions[] =
314  {
315  {"run", 2, run_task},
316 #ifdef FILESYS
317  {"ls", 1, fsutil_ls},
318  {"cat", 2, fsutil_cat},
319  {"rm", 2, fsutil_rm},
320  {"extract", 1, fsutil_extract},
321  {"append", 2, fsutil_append},
322 #endif
323  {NULL, 0, NULL},
324  };
325 
326  while (*argv != NULL)
327  {
328  const struct action *a;
329  int i;
330 
331  /* Find action name. */
332  for (a = actions; ; a++)
333  if (a->name == NULL)
334  PANIC ("unknown action `%s' (use -h for help)", *argv);
335  else if (!strcmp (*argv, a->name))
336  break;
337 
338  /* Check for required arguments. */
339  for (i = 1; i < a->argc; i++)
340  if (argv[i] == NULL)
341  PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
342 
343  /* Invoke action and advance. */
344  a->function (argv);
345  argv += a->argc;
346  }
347 
348 }
349 
350 /** Prints a kernel command line help message and powers off the
351  machine. */
352 static void
353 usage (void)
354 {
355  printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
356  "Options must precede actions.\n"
357  "Actions are executed in the order specified.\n"
358  "\nAvailable actions:\n"
359 #ifdef USERPROG
360  " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
361 #else
362  " run TEST Run TEST.\n"
363 #endif
364 #ifdef FILESYS
365  " ls List files in the root directory.\n"
366  " cat FILE Print FILE to the console.\n"
367  " rm FILE Delete FILE.\n"
368  "Use these actions indirectly via `pintos' -g and -p options:\n"
369  " extract Untar from scratch device into file system.\n"
370  " append FILE Append FILE to tar file on scratch device.\n"
371 #endif
372  "\nOptions:\n"
373  " -h Print this help message and power off.\n"
374  " -q Power off VM after actions or on panic.\n"
375  " -r Reboot after actions.\n"
376 #ifdef FILESYS
377  " -f Format file system device during startup.\n"
378  " -filesys=BDEV Use BDEV for file system instead of default.\n"
379  " -scratch=BDEV Use BDEV for scratch instead of default.\n"
380 #ifdef VM
381  " -swap=BDEV Use BDEV for swap instead of default.\n"
382 #endif
383 #endif
384  " -rs=SEED Set random number seed to SEED.\n"
385  " -mlfqs Use multi-level feedback queue scheduler.\n"
386 #ifdef USERPROG
387  " -ul=COUNT Limit user memory to COUNT pages.\n"
388 #endif
389  );
391 }
392 
393 #ifdef FILESYS
394 /** Figure out what block devices to cast in the various Pintos roles. */
395 static void
396 locate_block_devices (void)
397 {
398  locate_block_device (BLOCK_FILESYS, filesys_bdev_name);
399  locate_block_device (BLOCK_SCRATCH, scratch_bdev_name);
400 #ifdef VM
401  locate_block_device (BLOCK_SWAP, swap_bdev_name);
402 #endif
403 }
404 
405 /** Figures out what block device to use for the given ROLE: the
406  block device with the given NAME, if NAME is non-null,
407  otherwise the first block device in probe order of type
408  ROLE. */
409 static void
410 locate_block_device (enum block_type role, const char *name)
411 {
412  struct block *block = NULL;
413 
414  if (name != NULL)
415  {
417  if (block == NULL)
418  PANIC ("No such block device \"%s\"", name);
419  }
420  else
421  {
422  for (block = block_first (); block != NULL; block = block_next (block))
423  if (block_type (block) == role)
424  break;
425  }
426 
427  if (block != NULL)
428  {
429  printf ("%s: using %s\n", block_type_name (role), block_name (block));
430  block_set_role (role, block);
431  }
432 }
433 #endif
fsutil_rm
void fsutil_rm(char **argv)
Deletes file ARGV[1].
Definition: fsutil.c:61
name
char * name[]
Definition: insult.c:47
NO_RETURN
#define NO_RETURN
Definition: debug.h:8
block.h
filesys_init
void filesys_init(bool format)
Initializes the file system module.
Definition: filesys.c:18
strchr
char * strchr(const char *string, int c_)
Finds and returns the first occurrence of C in STRING, or a null pointer if C does not appear in STRI...
Definition: string.c:113
pte_create_kernel
static uint32_t pte_create_kernel(void *page, bool writable)
Returns a PTE that points to PAGE.
Definition: pte.h:87
gdt.h
BLOCK_SWAP
Swap.
Definition: block.h:32
PAL_ZERO
Zero page contents.
Definition: palloc.h:10
strtok_r
char * strtok_r(char *s, const char *delimiters, char **save_ptr)
Breaks a string into tokens separated by DELIMITERS.
Definition: string.c:235
thread_start
void thread_start(void)
Starts preemptive thread scheduling by enabling interrupts.
Definition: thread.c:106
vtop
static uintptr_t vtop(const void *vaddr)
Returns physical address at which kernel virtual address VADDR is mapped.
Definition: vaddr.h:82
NULL
#define NULL
Definition: stddef.h:4
filesys.h
user_page_limit
static size_t user_page_limit
-ul: Maximum number of pages to put into palloc's user pool.
Definition: init.c:58
string.h
kbd.h
init_ram_pages
uint32_t init_ram_pages
Amount of physical memory, in 4 kB pages.
strcmp
int strcmp(const char *a_, const char *b_)
Finds the first differing characters in strings A and B.
Definition: string.c:73
run_actions
static void run_actions(char **argv)
Executes all of the actions specified in ARGV[] up to the null pointer sentinel.
Definition: init.c:302
ptov
static void * ptov(uintptr_t paddr)
Returns kernel virtual address at which physical address PADDR is mapped.
Definition: vaddr.h:72
atoi
int atoi(const char *s)
Converts a string representation of a signed decimal integer in S into an ‘int’, which is returned.
Definition: stdlib.c:10
syscall_init
void syscall_init(void)
userprog/syscall.h
Definition: syscall.c:10
vga.h
PRIu32
#define PRIu32
Definition: inttypes.h:23
read_command_line
static char ** read_command_line(void)
Breaks the kernel command line into words and returns them as an argv-like array.
Definition: init.c:198
run_task
static void run_task(char **argv)
Runs the task specified in ARGV[1].
Definition: init.c:286
fsutil_append
void fsutil_append(char **argv)
Copies file FILE_NAME from the file system to the scratch device, in ustar format.
Definition: fsutil.c:167
console.h
PGSIZE
#define PGSIZE
Bytes in a page.
Definition: vaddr.h:20
pte.h
syscall.h
palloc_get_page
void * palloc_get_page(enum palloc_flags flags)
Obtains a single free page and returns its kernel virtual address.
Definition: palloc.c:111
shutdown.h
process_wait
int process_wait(tid_t child_tid UNUSED)
Waits for thread TID to die and returns its exit status.
Definition: process.c:89
PANIC
#define PANIC(...)
Halts the OS, printing the source file name, line number, and function name, plus a user-specific mes...
Definition: debug.h:14
pt_no
static unsigned pt_no(const void *va)
Obtains page table index from a virtual address.
Definition: pte.h:32
PAL_ASSERT
Panic on failure.
Definition: palloc.h:9
random.h
limits.h
process.h
interrupt.h
uint32_t
unsigned int uint32_t
Definition: stdint.h:26
usage
static void usage(void)
Prints a kernel command line help message and powers off the machine.
Definition: init.c:353
fsutil.h
init.h
random_init
void random_init(unsigned seed)
Initializes or reinitializes the PRNG with the given SEED.
Definition: random.c:34
printf
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
shutdown_configure
void shutdown_configure(enum shutdown_type type)
Sets TYPE as the way that machine will shut down when Pintos execution is complete.
Definition: shutdown.c:50
parse_options
static char ** parse_options(char **argv)
Parses options in ARGV[] and returns the first non-option argument.
Definition: init.c:233
rtc_get_time
time_t rtc_get_time(void)
Returns number of seconds since Unix epoch of January 1,.
Definition: rtc.c:43
timer.h
SIZE_MAX
#define SIZE_MAX
lib/stdint.h
Definition: stdint.h:49
thread_init
void thread_init(void)
fsutil_cat
void fsutil_cat(char **argv)
Prints the contents of file ARGV[1] to the system console as hex and ASCII.
Definition: fsutil.c:34
shutdown_power_off
void shutdown_power_off(void)
Powers down the machine we're running on, as long as we're running on Bochs or QEMU.
Definition: shutdown.c:88
block_name
const char * block_name(struct block *block)
Returns BLOCK's name (e.g.
Definition: block.c:151
arc4::i
uint8_t i
Definition: arc4.h:11
ide_init
void ide_init(void)
Initialize the disk subsystem and detect disks.
Definition: ide.c:102
malloc.h
palloc.h
BLOCK_SCRATCH
Scratch.
Definition: block.h:31
ide.h
block_get_by_name
struct block * block_get_by_name(const char *name)
Returns the block device with the given NAME, or a null pointer if no block device has that name.
Definition: block.c:87
block
A block device.
Definition: block.c:9
serial.h
LOADER_ARGS_LEN
#define LOADER_ARGS_LEN
Definition: loader.h:24
process_execute
tid_t process_execute(const char *file_name)
Starts a new thread running a user program loaded from FILENAME.
Definition: process.c:29
strnlen
size_t strnlen(const char *string, size_t maxlen)
If STRING is less than MAXLEN characters in length, returns its actual length.
Definition: string.c:307
block_first
struct block * block_first(void)
Returns the first block device in kernel probe order, or a null pointer if no block devices are regis...
Definition: block.c:71
thread_exit
void thread_exit(void)
Deschedules the current thread and destroys it.
Definition: thread.c:281
block_next
struct block * block_next(struct block *block)
Returns the block device following BLOCK in kernel probe order, or a null pointer if BLOCK is the las...
Definition: block.c:79
value
A linked list element.
Definition: list.c:22
fsutil_extract
void fsutil_extract(char **argv UNUSED)
Extracts a ustar-format tar archive from the scratch block device into the Pintos file system.
Definition: fsutil.c:73
gdt_init
void gdt_init(void)
Sets up a proper GDT.
Definition: gdt.c:36
console_init
void console_init(void)
Enable console locking.
Definition: console.c:64
paging_init
static void paging_init(void)
Populates the base page directory and page table with the kernel virtual mapping, and then sets up th...
Definition: init.c:162
LOADER_ARGS
#define LOADER_ARGS
Command-line args.
Definition: loader.h:18
bss_init
static void bss_init(void)
Clear the "BSS", a segment that should be initialized to zeros.
Definition: init.c:151
pd_no
static uintptr_t pd_no(const void *va)
Obtains page directory index from a virtual address.
Definition: pte.h:37
memset
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
timer_calibrate
void timer_calibrate(void)
Calibrates loops_per_tick, used to implement brief delays.
Definition: timer.c:44
io.h
init_page_dir
uint32_t * init_page_dir
Page directory with kernel mappings only.
Definition: init.c:42
thread_mlfqs
bool thread_mlfqs
If false (default), use round-robin scheduler.
Definition: thread.c:60
block_type_name
const char * block_type_name(enum block_type type)
Returns a human-readable name for the given block device TYPE.
Definition: block.c:35
tests.h
loader.h
pde_create
static uint32_t pde_create(uint32_t *pt)
Returns a PDE that points to page table PT.
Definition: pte.h:71
block_type
block_type
Type of a block device.
Definition: block.h:26
SHUTDOWN_POWER_OFF
Power off the machine (if possible).
Definition: shutdown.h:10
tss_init
void tss_init(void)
Initializes the kernel TSS.
Definition: tss.c:80
rtc.h
pintos_init
int pintos_init(void)
Pintos main entry point.
Definition: init.c:73
uintptr_t
uint32_t uintptr_t
Definition: stdint.h:36
exception.h
serial_init_queue
void serial_init_queue(void)
Initializes the serial port device for queued interrupt-driven I/O.
Definition: serial.c:82
BLOCK_FILESYS
File system.
Definition: block.h:30
intr_init
void intr_init(void)
Initializes the interrupt system.
Definition: interrupt.c:118
stdlib.h
palloc_init
void palloc_init(size_t user_page_limit)
Initializes the page allocator.
Definition: palloc.c:46
timer_init
void timer_init(void)
Sets up the timer to interrupt TIMER_FREQ times per second, and registers the corresponding interrupt...
Definition: timer.c:36
SHUTDOWN_REBOOT
Reboot the machine (if possible).
Definition: shutdown.h:11
shutdown
void shutdown(void)
Shuts down the machine in the way configured by shutdown_configure().
Definition: shutdown.c:29
block_set_role
void block_set_role(enum block_type role, struct block *block)
Assigns BLOCK the given ROLE.
Definition: block.c:62
thread.h
inttypes.h
stddef.h
tss.h
kbd_init
void kbd_init(void)
Initializes the keyboard.
Definition: kbd.c:31
input.h
run_test
void run_test(const char *name)
Runs the test named NAME.
Definition: tests.c:47
debug.h
fsutil_ls
void fsutil_ls(char **argv UNUSED)
List files in the root directory.
Definition: fsutil.c:16
LOADER_ARG_CNT
#define LOADER_ARG_CNT
Number of args.
Definition: loader.h:19
malloc_init
void malloc_init(void)
Initializes the malloc() descriptors.
Definition: malloc.c:72
input_init
void input_init(void)
Initializes the input buffer.
Definition: input.c:11
_start
void _start(int argc, char *argv[])
Definition: entry.c:7
exception_init
void exception_init(void)
Registers handlers for interrupts that can be caused by user programs.
Definition: exception.c:30