PKUOS - Pintos
Pintos source browser for PKU Operating System 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? */
46static bool format_filesys;
47
48/** -filesys, -scratch, -swap: Names of block devices to use,
49 overriding the defaults. */
50static const char *filesys_bdev_name;
51static const char *scratch_bdev_name;
52#ifdef VM
53static const char *swap_bdev_name;
54#endif
55#endif /**< FILESYS */
56
57/** -ul: Maximum number of pages to put into palloc's user pool. */
58static size_t user_page_limit = SIZE_MAX;
59
60static void bss_init (void);
61static void paging_init (void);
62
63static char **read_command_line (void);
64static char **parse_options (char **argv);
65static void run_actions (char **argv);
66static void usage (void);
67
68#ifdef FILESYS
69static void locate_block_devices (void);
70static void locate_block_device (enum block_type, const char *name);
71#endif
72
74
75/** Pintos main entry point. */
76int
77pintos_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
115 syscall_init ();
116#endif
117
118 /* Start thread scheduler and enable interrupts. */
119 thread_start ();
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. */
150static void
151bss_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. */
161static 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. */
197static 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. */
232static char **
233parse_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"))
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]. */
285static void
286run_task (char **argv)
287{
288 const char *task = argv[1];
289
290 printf ("Executing '%s':\n", task);
291#ifdef USERPROG
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. */
301static void
302run_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. */
352static void
353usage (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. */
395static void
396locate_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. */
409static void
410locate_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
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
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
const char * block_type_name(enum block_type type)
Returns a human-readable name for the given block device TYPE.
Definition: block.c:35
void block_set_role(enum block_type role, struct block *block)
Assigns BLOCK the given ROLE.
Definition: block.c:62
const char * block_name(struct block *block)
Returns BLOCK's name (e.g.
Definition: block.c:151
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
block_type
Type of a block device.
Definition: block.h:27
@ BLOCK_SWAP
Swap.
Definition: block.h:32
@ BLOCK_FILESYS
File system.
Definition: block.h:30
@ BLOCK_SCRATCH
Scratch.
Definition: block.h:31
#define PANIC(...)
Halts the OS, printing the source file name, line number, and function name, plus a user-specific mes...
Definition: debug.h:14
#define NO_RETURN
Definition: debug.h:8
void _start(int argc, char *argv[])
Definition: entry.c:7
void exception_init(void)
Registers handlers for interrupts that can be caused by user programs.
Definition: exception.c:30
void filesys_init(bool format)
Initializes the file system module.
Definition: filesys.c:18
void fsutil_cat(char **argv)
Prints the contents of file ARGV[1] to the system console as hex and ASCII.
Definition: fsutil.c:34
void fsutil_ls(char **argv UNUSED)
List files in the root directory.
Definition: fsutil.c:16
void fsutil_append(char **argv)
Copies file FILE_NAME from the file system to the scratch device, in ustar format.
Definition: fsutil.c:167
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
void fsutil_rm(char **argv)
Deletes file ARGV[1].
Definition: fsutil.c:61
void gdt_init(void)
Sets up a proper GDT.
Definition: gdt.c:36
void ide_init(void)
Initialize the disk subsystem and detect disks.
Definition: ide.c:102
static void run_actions(char **argv)
Executes all of the actions specified in ARGV[] up to the null pointer sentinel.
Definition: init.c:302
uint32_t * init_page_dir
Page directory with kernel mappings only.
Definition: init.c:42
int pintos_init(void)
Pintos main entry point.
Definition: init.c:73
static char ** parse_options(char **argv)
Parses options in ARGV[] and returns the first non-option argument.
Definition: init.c:233
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
static void bss_init(void)
Clear the "BSS", a segment that should be initialized to zeros.
Definition: init.c:151
static void usage(void)
Prints a kernel command line help message and powers off the machine.
Definition: init.c:353
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
static size_t user_page_limit
-ul: Maximum number of pages to put into palloc's user pool.
Definition: init.c:58
static void run_task(char **argv)
Runs the task specified in ARGV[1].
Definition: init.c:286
void input_init(void)
Initializes the input buffer.
Definition: input.c:11
char * name[]
Definition: insult.c:47
void intr_init(void)
Initializes the interrupt system.
Definition: interrupt.c:118
#define PRIu32
Definition: inttypes.h:23
void kbd_init(void)
Initializes the keyboard.
Definition: kbd.c:31
void console_init(void)
Enable console locking.
Definition: console.c:64
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
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
uint32_t init_ram_pages
Amount of physical memory, in 4 kB pages.
#define LOADER_ARGS
Command-line args.
Definition: loader.h:18
#define LOADER_ARGS_LEN
Definition: loader.h:24
#define LOADER_ARG_CNT
Number of args.
Definition: loader.h:19
void malloc_init(void)
Initializes the malloc() descriptors.
Definition: malloc.c:72
void * palloc_get_page(enum palloc_flags flags)
Obtains a single free page and returns its kernel virtual address.
Definition: palloc.c:111
void palloc_init(size_t user_page_limit)
Initializes the page allocator.
Definition: palloc.c:46
@ PAL_ZERO
Zero page contents.
Definition: palloc.h:10
@ PAL_ASSERT
Panic on failure.
Definition: palloc.h:9
tid_t process_execute(const char *file_name)
Starts a new thread running a user program loaded from FILENAME.
Definition: process.c:29
int process_wait(tid_t child_tid UNUSED)
Waits for thread TID to die and returns its exit status.
Definition: process.c:89
static unsigned pt_no(const void *va)
Obtains page table index from a virtual address.
Definition: pte.h:32
static uint32_t pde_create(uint32_t *pt)
Returns a PDE that points to page table PT.
Definition: pte.h:71
static uintptr_t pd_no(const void *va)
Obtains page directory index from a virtual address.
Definition: pte.h:37
static uint32_t pte_create_kernel(void *page, bool writable)
Returns a PTE that points to PAGE.
Definition: pte.h:87
void random_init(unsigned seed)
Initializes or reinitializes the PRNG with the given SEED.
Definition: random.c:34
time_t rtc_get_time(void)
Returns number of seconds since Unix epoch of January 1,.
Definition: rtc.c:43
void serial_init_queue(void)
Initializes the serial port device for queued interrupt-driven I/O.
Definition: serial.c:82
void shutdown(void)
Shuts down the machine in the way configured by shutdown_configure().
Definition: shutdown.c:29
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
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
@ SHUTDOWN_POWER_OFF
Power off the machine (if possible).
Definition: shutdown.h:10
@ SHUTDOWN_REBOOT
Reboot the machine (if possible).
Definition: shutdown.h:11
#define NULL
Definition: stddef.h:4
uint32_t uintptr_t
Definition: stdint.h:36
#define SIZE_MAX
lib/stdint.h
Definition: stdint.h:49
unsigned int uint32_t
Definition: stdint.h:26
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
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
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
char * strtok_r(char *s, const char *delimiters, char **save_ptr)
Breaks a string into tokens separated by DELIMITERS.
Definition: string.c:235
int strcmp(const char *a_, const char *b_)
Finds the first differing characters in strings A and B.
Definition: string.c:73
A block device.
Definition: block.c:10
A linked list element.
Definition: list.c:23
void run_test(const char *name)
Runs the test named NAME.
Definition: tests.c:47
void thread_start(void)
Starts preemptive thread scheduling by enabling interrupts.
Definition: thread.c:106
bool thread_mlfqs
If false (default), use round-robin scheduler.
Definition: thread.c:60
void thread_exit(void)
Deschedules the current thread and destroys it.
Definition: thread.c:281
void thread_init(void)
void timer_init(void)
Sets up the timer to interrupt TIMER_FREQ times per second, and registers the corresponding interrupt...
Definition: timer.c:36
void timer_calibrate(void)
Calibrates loops_per_tick, used to implement brief delays.
Definition: timer.c:44
void tss_init(void)
Initializes the kernel TSS.
Definition: tss.c:80
void syscall_init(void)
userprog/syscall.h
Definition: syscall.c:10
static uintptr_t vtop(const void *vaddr)
Returns physical address at which kernel virtual address VADDR is mapped.
Definition: vaddr.h:82
#define PGSIZE
Bytes in a page.
Definition: vaddr.h:20
static void * ptov(uintptr_t paddr)
Returns kernel virtual address at which physical address PADDR is mapped.
Definition: vaddr.h:72