initialized before thread_init() gets hold of it.
Also allow console output much earlier, by doing initialization
whenever we call into it first and by only trying to take the console
lock after threads have been initialized.
Don't try to take the console lock after the kernel panics, to avoid
getting nailed so hard by bugs in lock_acquire().
Suggested by Godmar Back.
the loader stored it and put it into the @code{ram_pages} variable for
later use.
-Next, @func{thread_init} initializes the thread system. We will defer
-full discussion to our discussion of Pintos threads below. It is
-called so early in initialization because the console, initialized
-just afterward, tries to use locks, and locks in turn require there to be a
-running thread.
-
-Then we initialize the console so that @func{printf} will work.
-@func{main} calls @func{vga_init}, which initializes the VGA text
-display and clears the screen. It also calls @func{serial_init_poll}
-to initialize the first serial port in ``polling mode,'' that is,
-where the kernel busy-waits for the port to be ready for each
-character to be output. (We use polling mode until we're ready to enable
-interrupts, later.) Finally we initialize the console device and
-print a startup message to the console.
-
-@func{main} calls @func{read_command_line} to break the kernel command
+Next, @func{main} calls @func{read_command_line} to break the kernel command
line into arguments, then @func{parse_options} to read any options at
the beginning of the command line. (Actions specified on the
command line execute later.)
-@func{main} calls @func{random_init} to initialize the kernel random
-number generator. If the user specified @option{-rs} on the
-@command{pintos} command line, @func{parse_options} already did
-this, but calling it a second time is harmless.
+@func{thread_init} initializes the thread system. We will defer full
+discussion to our discussion of Pintos threads below. It is called so
+early in initialization because a valid thread structure is a
+prerequisite for acquiring a lock, and lock acquisition in turn is
+important to other Pintos subsystems. Then we initialize the console
+and print a startup message to the console.
The next block of functions we call initialize the kernel's memory
system. @func{palloc_init} sets up the kernel page allocator, which
with the @option{-mlfqs} kernel option. Passing this
option sets @code{thread_mlfqs}, declared in @file{threads/thread.h}, to
true when the options are parsed by @func{parse_options}, which happens
-midway through @func{main}.
+early in @func{main}.
When the 4.4@acronym{BSD} scheduler is enabled, threads no longer
directly control their own priorities. The @var{priority} argument to
priority. When the donations are released, the thread's priority
becomes the one set through the function call. This behavior is checked
by the @code{priority-donate-lower} test.
-
-@item Calling @func{printf} in @func{sema_up} or @func{sema_down} reboots!
-
-@anchor{printf Reboots}
-Yes. These functions are called before @func{printf} is ready to go.
-You could add a global flag initialized to false and set it to true
-just before the first @func{printf} in @func{main}. Then modify
-@func{printf} itself to return immediately if the flag isn't set.
@end table
@node Advanced Scheduler FAQ
Polling mode busy-waits for the serial port to become free
before writing to it. It's slow, but until interrupts have
been initialized it's all we can do. */
-void
-serial_init_poll (void)
+static void
+init_poll (void)
{
ASSERT (mode == UNINIT);
outb (IER_REG, 0); /* Turn off all interrupts. */
{
enum intr_level old_level;
+ if (mode == UNINIT)
+ init_poll ();
ASSERT (mode == POLL);
intr_register_ext (0x20 + 4, serial_interrupt, "serial");
{
enum intr_level old_level = intr_disable ();
- if (mode == POLL)
+ if (mode != QUEUE)
{
/* If we're not set up for interrupt-driven I/O yet,
use dumb polling to transmit a byte. */
+ if (mode == UNINIT)
+ init_poll ();
putc_poll (byte);
}
else
#include <stdint.h>
-void serial_init_poll (void);
void serial_init_queue (void);
void serial_putc (uint8_t);
void serial_flush (void);
static void cls (void);
static void newline (void);
static void move_cursor (void);
+static void find_cursor (size_t *x, size_t *y);
-/* Initializes the VGA text display and clears the screen. */
-void
-vga_init (void)
+/* Initializes the VGA text display. */
+static void
+init (void)
{
- fb = ptov (0xb8000);
- cls ();
+ /* Already initialized? */
+ static bool inited;
+ if (!inited)
+ {
+ fb = ptov (0xb8000);
+ find_cursor (&cx, &cy);
+ inited = true;
+ }
}
/* Writes C to the VGA text display, interpreting control
that might write to the console. */
enum intr_level old_level = intr_disable ();
+ init ();
+
switch (c)
{
case '\n':
outw (0x3d4, 0x0f | (cp << 8));
}
+/* Reads the current hardware cursor position into (*X,*Y). */
+static void
+find_cursor (size_t *x, size_t *y)
+{
+ /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
+ uint16_t cp;
+
+ outb (0x3d4, 0x0e);
+ cp = inb (0x3d5) << 8;
+
+ outb (0x3d4, 0x0f);
+ cp |= inb (0x3d5);
+
+ *x = cp % COL_CNT;
+ *y = cp / COL_CNT;
+}
#ifndef DEVICES_VGA_H
#define DEVICES_VGA_H
-void vga_init (void);
void vga_putc (int);
#endif /* devices/vga.h */
#include <stdio.h>
#include "devices/serial.h"
#include "devices/vga.h"
+#include "threads/init.h"
#include "threads/interrupt.h"
#include "threads/synch.h"
from mixing their output, which looks confusing. */
static struct lock console_lock;
+/* True in ordinary circumstances: we want to use the console
+ lock to avoid mixing output between threads, as explained
+ above.
+
+ False in early boot before the point that locks are functional
+ or the console lock has been initialized, or after a kernel
+ panics. In the former case, taking the lock would cause an
+ assertion failure, which in turn would cause a panic, turning
+ it into the latter case. In the latter case, if it is a buggy
+ lock_acquire() implementation that caused the panic, we'll
+ likely just recurse. */
+static bool use_console_lock;
+
/* It's possible, if you add enough debug output to Pintos, to
try to recursively grab console_lock from a single thread. As
a real example, I added a printf() call to palloc_free().
/* Number of characters written to console. */
static int64_t write_cnt;
-/* Initializes the console. */
+/* Enable console locking. */
void
console_init (void)
{
lock_init (&console_lock);
+ use_console_lock = true;
+}
+
+/* Notifies the console that a kernel panic is underway,
+ which warns it to avoid trying to take the console lock from
+ now on. */
+void
+console_panic (void)
+{
+ use_console_lock = false;
}
/* Prints console statistics. */
static void
acquire_console (void)
{
- if (!intr_context ())
+ if (!intr_context () && use_console_lock)
{
if (lock_held_by_current_thread (&console_lock))
console_lock_depth++;
static void
release_console (void)
{
- if (!intr_context ())
+ if (!intr_context () && use_console_lock)
{
if (console_lock_depth > 0)
console_lock_depth--;
static bool
console_locked_by_current_thread (void)
{
- return intr_context () || lock_held_by_current_thread (&console_lock);
+ return (intr_context ()
+ || !use_console_lock
+ || lock_held_by_current_thread (&console_lock));
}
/* The standard vprintf() function,
#define __LIB_KERNEL_CONSOLE_H
void console_init (void);
+void console_panic (void);
void console_print_stats (void);
#endif /* lib/kernel/console.h */
#include <debug.h>
+#include <console.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
va_list args;
intr_disable ();
+ console_panic ();
level++;
if (level == 1)
*b = t;
}
-/* Initializes the PRNG with the given SEED.
- Does nothing if the PRNG has already been initialized. */
+/* Initializes or reinitializes the PRNG with the given SEED. */
void
random_init (unsigned seed)
{
int i;
uint8_t j;
- if (inited)
- return;
-
for (i = 0; i < 256; i++)
s[i] = i;
for (i = j = 0; i < 256; i++)
{
uint8_t *buf;
- ASSERT (inited);
+ if (!inited)
+ random_init (0);
+
for (buf = buf_; size-- > 0; buf++)
{
uint8_t s_k;
/* Clear BSS and get machine's RAM size. */
ram_init ();
- /* Initialize ourselves as a thread so we can use locks. */
- thread_init ();
-
- /* Initialize the console so we can use printf(). */
- vga_init ();
- serial_init_poll ();
- console_init ();
-
- /* Greet user. */
- printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
-
/* Break command line into arguments and parse options. */
argv = read_command_line ();
argv = parse_options (argv);
- /* Set random seed if parse_options() didn't. */
- random_init (0);
+ /* Initialize ourselves as a thread so we can use locks,
+ then enable console locking. */
+ thread_init ();
+ console_init ();
+
+ /* Greet user. */
+ printf ("Pintos booting with %'zu kB RAM...\n", ram_pages * PGSIZE / 1024);
/* Initialize memory system. */
palloc_init ();
/* If false (default), use round-robin scheduler.
If true, use multi-level feedback queue scheduler.
- Controlled by kernel command-line options "-o mlfqs".
- Note that the command line is not parsed until well after
- thread_init() is called. */
+ Controlled by kernel command-line option "-o mlfqs". */
bool thread_mlfqs;
static void kernel_thread (thread_func *, void *aux);
allocator before trying to create any threads with
thread_create().
- The kernel command line is not parsed until *after* this
- function returns, so that when this function runs,
- thread_mlfqs is always false.
-
It is not safe to call thread_current() until this function
finishes. */
void
}
/* Starts preemptive thread scheduling by enabling interrupts.
- Also creates the idle thread.
-
- By the time this function runs, thread_mlfqs has been properly
- initialized to its final value. */
+ Also creates the idle thread. */
void
thread_start (void)
{
/* If false (default), use round-robin scheduler.
If true, use multi-level feedback queue scheduler.
- Controlled by kernel command-line options "-o mlfqs".
- Note that the command line is not parsed until well after
- thread_init() is called. */
+ Controlled by kernel command-line option "-o mlfqs". */
extern bool thread_mlfqs;
void thread_init (void);