}
/* Removes a byte from Q and returns it.
- Q must not be empty if called from an interrupt handler.
- Otherwise, if Q is empty, first sleeps until a byte is
- added. */
+ If Q is empty, sleeps until a byte is added.
+ When called from an interrupt handler, Q must not be empty. */
uint8_t
intq_getc (struct intq *q)
{
}
/* Adds BYTE to the end of Q.
- Q must not be full if called from an interrupt handler.
- Otherwise, if Q is full, first sleeps until a byte is
- removed. */
+ If Q is full, sleeps until a byte is removed.
+ When called from an interrupt handler, Q must not be full. */
void
intq_putc (struct intq *q, uint8_t byte)
{
#include <stdio.h>
#include <string.h>
#include "devices/input.h"
+#include "threads/init.h"
#include "threads/interrupt.h"
#include "threads/io.h"
that we handle elsewhere. */
static const struct keymap invariant_keymap[] =
{
- {0x01, "\033"},
+ {0x01, "\033"}, /* Escape. */
{0x0e, "\b"},
{0x0f, "\tQWERTYUIOP"},
{0x1c, "\r"},
{0x2c, "ZXCVBNM"},
{0x37, "*"},
{0x39, " "},
+ {0x53, "\177"}, /* Delete. */
{0, NULL},
};
/* Ordinary character. */
if (!release)
{
+ /* Reboot if Ctrl+Alt+Del pressed. */
+ if (c == 0177 && ctrl && alt)
+ reboot ();
+
/* Handle Ctrl, Shift.
Note that Ctrl overrides Shift. */
if (ctrl && c >= 0x40 && c < 0x60)
static bool too_many_loops (unsigned loops);
static void busy_wait (int64_t loops);
static void real_time_sleep (int64_t num, int32_t denom);
+static void real_time_delay (int64_t num, int32_t denom);
/* Sets up the timer to interrupt TIMER_FREQ times per second,
and registers the corresponding interrupt. */
return timer_ticks () - then;
}
-/* Suspends execution for approximately TICKS timer ticks. */
+/* Sleeps for approximately TICKS timer ticks. Interrupts must
+ be turned on. */
void
timer_sleep (int64_t ticks)
{
thread_yield ();
}
-/* Suspends execution for approximately MS milliseconds. */
+/* Sleeps for approximately MS milliseconds. Interrupts must be
+ turned on. */
void
timer_msleep (int64_t ms)
{
real_time_sleep (ms, 1000);
}
-/* Suspends execution for approximately US microseconds. */
+/* Sleeps for approximately US microseconds. Interrupts must be
+ turned on. */
void
timer_usleep (int64_t us)
{
real_time_sleep (us, 1000 * 1000);
}
-/* Suspends execution for approximately NS nanoseconds. */
+/* Sleeps for approximately NS nanoseconds. Interrupts must be
+ turned on. */
void
timer_nsleep (int64_t ns)
{
real_time_sleep (ns, 1000 * 1000 * 1000);
}
+/* Busy-waits for approximately MS milliseconds. Interrupts need
+ not be turned on.
+
+ Busy waiting wastes CPU cycles, and busy waiting with
+ interrupts off for the interval between timer ticks or longer
+ will cause timer ticks to be lost. Thus, use timer_msleep()
+ instead if interrupts are enabled. */
+void
+timer_mdelay (int64_t ms)
+{
+ real_time_delay (ms, 1000);
+}
+
+/* Sleeps for approximately US microseconds. Interrupts need not
+ be turned on.
+
+ Busy waiting wastes CPU cycles, and busy waiting with
+ interrupts off for the interval between timer ticks or longer
+ will cause timer ticks to be lost. Thus, use timer_usleep()
+ instead if interrupts are enabled. */
+void
+timer_udelay (int64_t us)
+{
+ real_time_delay (us, 1000 * 1000);
+}
+
+/* Sleeps execution for approximately NS nanoseconds. Interrupts
+ need not be turned on.
+
+ Busy waiting wastes CPU cycles, and busy waiting with
+ interrupts off for the interval between timer ticks or longer
+ will cause timer ticks to be lost. Thus, use timer_nsleep()
+ instead if interrupts are enabled.*/
+void
+timer_ndelay (int64_t ns)
+{
+ real_time_delay (ns, 1000 * 1000 * 1000);
+}
+
/* Prints timer statistics. */
void
timer_print_stats (void)
else
{
/* Otherwise, use a busy-wait loop for more accurate
- sub-tick timing. We scale the numerator and denominator
- down by 1000 to avoid the possibility of overflow. */
- ASSERT (denom % 1000 == 0);
- busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));
+ sub-tick timing. */
+ real_time_delay (num, denom);
}
}
+/* Busy-wait for approximately NUM/DENOM seconds. */
+static void
+real_time_delay (int64_t num, int32_t denom)
+{
+ /* Scale the numerator and denominator down by 1000 to avoid
+ the possibility of overflow. */
+ ASSERT (denom % 1000 == 0);
+ busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));
+}
int64_t timer_ticks (void);
int64_t timer_elapsed (int64_t);
+/* Sleep and yield the CPU to other threads. */
void timer_sleep (int64_t ticks);
void timer_msleep (int64_t milliseconds);
void timer_usleep (int64_t microseconds);
void timer_nsleep (int64_t nanoseconds);
+/* Busy waits. */
+void timer_mdelay (int64_t milliseconds);
+void timer_udelay (int64_t microseconds);
+void timer_ndelay (int64_t nanoseconds);
+
void timer_print_stats (void);
#endif /* devices/timer.h */
host_dev_info hi;
host_eop_info cfg_eop;
bool ignore_device = false;
- int err, sz, txed;
+ int err, sz;
+ unsigned txed;
int config_val;
hi = h->dev->create_dev_channel (h->info, ADDR_DEFAULT, USB_VERSION_1_1);
sz = usb_tx_all (&dev->cfg_eop, cd, dsz,
sizeof (struct config_descriptor), true);
- if (sz < sizeof (struct config_descriptor))
+ if (sz < (int) sizeof (struct config_descriptor))
{
printf ("USB: Did not rx GET descriptor (%d bytes, expected %d)\n", sz,
sizeof (struct config_descriptor));
#pragma pack()
void usb_init (void);
+void usb_storage_init (void);
void usb_register_host (struct usb_host *, host_info info);
int usb_unregister_host (struct usb_host *, host_info info);
static struct queue_head *qh_alloc (struct uhci_info *ui);
static struct uhci_info *uhci_create_info (struct pci_io *io);
-static void uhci_destroy_info (struct uhci_info *ui);
+static void uhci_destroy_info (struct uhci_info *ui) UNUSED;
static host_eop_info uhci_create_eop (host_dev_info hd, int eop, int maxpkt);
static void uhci_remove_eop (host_eop_info hei);
static host_dev_info uhci_create_chan (host_info hi, int dev_addr, int ver);
static void uhci_detect_ports (struct uhci_info *ui);
static int uhci_remove_stalled (struct uhci_info *ui);
-static void uhci_stall_watchdog (struct uhci_info *ui);
+static void uhci_stall_watchdog (struct uhci_info *ui) UNUSED;
static void dump_all_qh (struct uhci_info *ui);
static void dump_qh (struct queue_head *qh);
void uhci_init (void);
-
static struct usb_host uhci_host = {
.name = "UHCI",
.tx_pkt = uhci_tx_pkt,
}
}
-
static void
uhci_destroy_info (struct uhci_info *ui)
{
static void
uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token,
- int eop, void *pkt, int sz, int toggle, bool ls)
+ int eop, void *pkt, int sz, int toggle, bool ls UNUSED)
{
td->buf_ptr = (sz == 0) ? 0 : vtop (pkt);
struct usb_wait w;
int err;
struct uhci_dev_info *ud;
- int n;
ud = ue->ud;
void debug_panic (const char *file, int line, const char *function,
const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN;
void debug_backtrace (void);
+void debug_backtrace_all (void);
#endif
#include <string.h>
#include "threads/init.h"
#include "threads/interrupt.h"
+#include "threads/thread.h"
+#include "threads/switch.h"
+#include "threads/vaddr.h"
#include "devices/serial.h"
/* Halts the OS, printing the source file name, line number, and
power_off ();
for (;;);
}
+
+/* Print call stack of a thread.
+ The thread may be running, ready, or blocked. */
+static void
+print_stacktrace(struct thread *t, void *aux UNUSED)
+{
+ void *retaddr = NULL, **frame = NULL;
+ const char *status = "UNKNOWN";
+
+ switch (t->status) {
+ case THREAD_RUNNING:
+ status = "RUNNING";
+ break;
+
+ case THREAD_READY:
+ status = "READY";
+ break;
+
+ case THREAD_BLOCKED:
+ status = "BLOCKED";
+ break;
+
+ default:
+ break;
+ }
+
+ printf ("Call stack of thread `%s' (status %s):", t->name, status);
+
+ if (t == thread_current())
+ {
+ frame = __builtin_frame_address (1);
+ retaddr = __builtin_return_address (0);
+ }
+ else
+ {
+ /* Retrieve the values of the base and instruction pointers
+ as they were saved when this thread called switch_threads. */
+ struct switch_threads_frame * saved_frame;
+
+ saved_frame = (struct switch_threads_frame *)t->stack;
+
+ /* Skip threads if they have been added to the all threads
+ list, but have never been scheduled.
+ We can identify because their `stack' member either points
+ at the top of their kernel stack page, or the
+ switch_threads_frame's 'eip' member points at switch_entry.
+ See also threads.c. */
+ if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry)
+ {
+ printf (" thread was never scheduled.\n");
+ return;
+ }
+
+ frame = (void **) saved_frame->ebp;
+ retaddr = (void *) saved_frame->eip;
+ }
+
+ printf (" %p", retaddr);
+ for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0])
+ printf (" %p", frame[1]);
+ printf (".\n");
+}
+
+/* Prints call stack of all threads. */
+void
+debug_backtrace_all (void)
+{
+ enum intr_level oldlevel = intr_disable ();
+
+ thread_foreach (print_stacktrace, 0);
+ intr_set_level (oldlevel);
+}
/* -q: Power off after kernel tasks complete? */
bool power_off_when_done;
+/* -r: Reboot after kernel tasks complete? */
+static bool reboot_when_done;
+
static void bss_init (void);
static void paging_init (void);
static void pci_zone_init (void);
run_actions (argv);
/* Finish up. */
+ if (reboot_when_done)
+ reboot ();
+
if (power_off_when_done)
power_off ();
thread_exit ();
usage ();
else if (!strcmp (name, "-q"))
power_off_when_done = true;
+ else if (!strcmp (name, "-r"))
+ reboot_when_done = true;
#ifdef FILESYS
else if (!strcmp (name, "-f"))
format_filesys = true;
" rm FILE Delete FILE.\n"
"Use these actions indirectly via `pintos' -g and -p options:\n"
" extract Untar from scratch disk into file system.\n"
- " get FILE Get FILE from file system into scratch disk.\n"
+ " append FILE Append FILE to tar file on scratch disk.\n"
#endif
"\nOptions:\n"
" -h Print this help message and power off.\n"
" -q Power off VM after actions or on panic.\n"
+ " -r Reboot after actions.\n"
#ifdef FILESYS
" -f Format file system disk during startup.\n"
" -filesys=BDEV Use BDEV for file system instead of default.\n"
}
#endif
+/* Keyboard control register port. */
+#define CONTROL_REG 0x64
+
+/* Reboots the machine via the keyboard controller. */
+void
+reboot (void)
+{
+ int i;
+
+ printf ("Rebooting...\n");
+
+ /* See [kbd] for details on how to program the keyboard
+ * controller. */
+ for (i = 0; i < 100; i++)
+ {
+ int j;
+
+ /* Poll keyboard controller's status byte until
+ * 'input buffer empty' is reported. */
+ for (j = 0; j < 0x10000; j++)
+ {
+ if ((inb (CONTROL_REG) & 0x02) == 0)
+ break;
+ timer_udelay (2);
+ }
+
+ timer_udelay (50);
+
+ /* Pulse bit 0 of the output port P2 of the keyboard controller.
+ * This will reset the CPU. */
+ outb (CONTROL_REG, 0xfe);
+ timer_udelay (50);
+ }
+}
+
/* Powers down the machine we're running on,
as long as we're running on Bochs or QEMU. */
void
extern bool power_off_when_done;
void power_off (void) NO_RETURN;
+void reboot (void);
#endif /* threads/init.h */
#define IRQ_CASCADE0 2
#define IRQ_CASCADE1 9
+/* Programmable Interrupt Controller (PIC) registers.
+ A PC has two PICs, called the master and slave PICs, with the
+ slave attached ("cascaded") to the master IRQ line 2. */
+#define PIC0_CTRL 0x20 /* Master PIC control register address. */
+#define PIC0_DATA 0x21 /* Master PIC data register address. */
+#define PIC1_CTRL 0xa0 /* Slave PIC control register address. */
+#define PIC1_DATA 0xa1 /* Slave PIC data register address. */
+
/* Number of x86 interrupts. */
#define INTR_CNT 256
static void init_pool (struct pool *, void *base, size_t page_cnt,
const char *name);
static bool page_from_pool (const struct pool *, void *page);
-static void page_set_cache(void* page, bool enable_cache);
/* Initializes the page allocator. */
void
that are ready to run but not actually running. */
static struct list ready_list;
+/* List of all processes. Processes are added to this list
+ when they are first scheduled and removed when they exit. */
+static struct list all_list;
+
/* Idle thread. */
static struct thread *idle_thread;
lock_init (&tid_lock);
list_init (&ready_list);
+ list_init (&all_list);
/* Set up a thread structure for the running thread. */
initial_thread = running_thread ();
struct switch_entry_frame *ef;
struct switch_threads_frame *sf;
tid_t tid;
+ enum intr_level old_level;
ASSERT (function != NULL);
init_thread (t, name, priority);
tid = t->tid = allocate_tid ();
+ /* Prepare thread for first run by initializing its stack.
+ Do this atomically so intermediate values for the 'stack'
+ member cannot be observed. */
+ old_level = intr_disable ();
+
/* Stack frame for kernel_thread(). */
kf = alloc_frame (t, sizeof *kf);
kf->eip = NULL;
/* Stack frame for switch_threads(). */
sf = alloc_frame (t, sizeof *sf);
sf->eip = switch_entry;
+ sf->ebp = 0;
+
+ intr_set_level (old_level);
/* Add to run queue. */
thread_unblock (t);
process_exit ();
#endif
- /* Just set our status to dying and schedule another process.
- We will be destroyed during the call to schedule_tail(). */
+ /* Remove thread from all threads list, set our status to dying,
+ and schedule another process. That process will destroy us
+ when it call schedule_tail(). */
intr_disable ();
+ list_remove (&thread_current()->allelem);
thread_current ()->status = THREAD_DYING;
schedule ();
NOT_REACHED ();
intr_set_level (old_level);
}
+/* Invoke function 'func' on all threads, passing along 'aux'.
+ This function must be called with interrupts off. */
+void
+thread_foreach (thread_action_func *func, void *aux)
+{
+ struct list_elem *e;
+
+ ASSERT (intr_get_level () == INTR_OFF);
+
+ for (e = list_begin (&all_list); e != list_end (&all_list);
+ e = list_next (e))
+ {
+ struct thread *t = list_entry (e, struct thread, allelem);
+ func (t, aux);
+ }
+}
+
/* Sets the current thread's priority to NEW_PRIORITY. */
void
thread_set_priority (int new_priority)
t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->magic = THREAD_MAGIC;
+ list_push_back (&all_list, &t->allelem);
}
/* Allocates a SIZE-byte frame at the top of thread T's stack and
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
+ struct list_elem allelem; /* List element for all threads list. */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
void thread_exit (void) NO_RETURN;
void thread_yield (void);
+/* Performs some operation on thread t, given auxiliary data AUX. */
+typedef void thread_action_func (struct thread *t, void *aux);
+void thread_foreach (thread_action_func *, void *);
+
int thread_get_priority (void);
void thread_set_priority (int);
user_shortcut: keys=ctrlaltdel
EOF
print BOCHSRC "gdbstub: enabled=1\n" if $debug eq 'gdb';
- print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none',
- ", time0=0\n";
+ if ($realtime) {
+ print BOCHSRC "clock: sync=realtime\n";
+ } else {
+ print BOCHSRC "clock: sync=none, time0=0\n";
+ }
print BOCHSRC "ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15\n"
if @disks > 2;
print_bochs_disk_line ("ata0-master", $disks[0]);