From 8fff557107b6a3efec0000ce98904450c1de3648 Mon Sep 17 00:00:00 2001 From: Godmar Back Date: Thu, 28 Aug 2008 04:33:40 +0000 Subject: [PATCH] merged in all changes done to the trunk up to Aug 28 --- src/devices/intq.c | 10 +++--- src/devices/kbd.c | 8 ++++- src/devices/timer.c | 67 +++++++++++++++++++++++++++++++----- src/devices/timer.h | 6 ++++ src/devices/usb.c | 5 +-- src/devices/usb.h | 1 + src/devices/usb_uhci.c | 9 ++--- src/lib/debug.h | 1 + src/lib/kernel/debug.c | 75 +++++++++++++++++++++++++++++++++++++++++ src/threads/init.c | 46 ++++++++++++++++++++++++- src/threads/init.h | 1 + src/threads/interrupt.c | 8 +++++ src/threads/palloc.c | 1 - src/threads/thread.c | 38 +++++++++++++++++++-- src/threads/thread.h | 5 +++ src/utils/pintos | 7 ++-- 16 files changed, 259 insertions(+), 29 deletions(-) diff --git a/src/devices/intq.c b/src/devices/intq.c index 028dca4..40b23ae 100644 --- a/src/devices/intq.c +++ b/src/devices/intq.c @@ -32,9 +32,8 @@ intq_full (const struct intq *q) } /* 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) { @@ -56,9 +55,8 @@ 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) { diff --git a/src/devices/kbd.c b/src/devices/kbd.c index 4d7dfdf..1aa71f5 100644 --- a/src/devices/kbd.c +++ b/src/devices/kbd.c @@ -4,6 +4,7 @@ #include #include #include "devices/input.h" +#include "threads/init.h" #include "threads/interrupt.h" #include "threads/io.h" @@ -53,7 +54,7 @@ struct keymap that we handle elsewhere. */ static const struct keymap invariant_keymap[] = { - {0x01, "\033"}, + {0x01, "\033"}, /* Escape. */ {0x0e, "\b"}, {0x0f, "\tQWERTYUIOP"}, {0x1c, "\r"}, @@ -61,6 +62,7 @@ static const struct keymap invariant_keymap[] = {0x2c, "ZXCVBNM"}, {0x37, "*"}, {0x39, " "}, + {0x53, "\177"}, /* Delete. */ {0, NULL}, }; @@ -131,6 +133,10 @@ keyboard_interrupt (struct intr_frame *args UNUSED) /* 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) diff --git a/src/devices/timer.c b/src/devices/timer.c index a83a588..c97667f 100644 --- a/src/devices/timer.c +++ b/src/devices/timer.c @@ -28,6 +28,7 @@ static intr_handler_func timer_interrupt; 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. */ @@ -84,7 +85,8 @@ timer_elapsed (int64_t then) 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) { @@ -95,27 +97,69 @@ 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) @@ -187,10 +231,17 @@ real_time_sleep (int64_t num, int32_t denom) 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)); +} diff --git a/src/devices/timer.h b/src/devices/timer.h index 45a3f72..cd3d6bb 100644 --- a/src/devices/timer.h +++ b/src/devices/timer.h @@ -13,11 +13,17 @@ void timer_calibrate (void); 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 */ diff --git a/src/devices/usb.c b/src/devices/usb.c index b885eb1..1ac802f 100644 --- a/src/devices/usb.c +++ b/src/devices/usb.c @@ -260,7 +260,8 @@ usb_configure_default (struct host *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); @@ -431,7 +432,7 @@ usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz) 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)); diff --git a/src/devices/usb.h b/src/devices/usb.h index 4ecce3b..f82762f 100644 --- a/src/devices/usb.h +++ b/src/devices/usb.h @@ -185,6 +185,7 @@ struct usb_setup_pkt #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); diff --git a/src/devices/usb_uhci.c b/src/devices/usb_uhci.c index 77b8333..9cd4d69 100644 --- a/src/devices/usb_uhci.c +++ b/src/devices/usb_uhci.c @@ -225,7 +225,7 @@ static void qh_free (struct uhci_info *ui, struct queue_head *qh); 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); @@ -253,7 +253,7 @@ static void uhci_irq (void *uhci_data); 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); @@ -262,7 +262,6 @@ static void dump_regs (struct uhci_info *ui); void uhci_init (void); - static struct usb_host uhci_host = { .name = "UHCI", .tx_pkt = uhci_tx_pkt, @@ -433,7 +432,6 @@ dump_regs (struct uhci_info *ui) } } - static void uhci_destroy_info (struct uhci_info *ui) { @@ -586,7 +584,7 @@ token_to_pid (int token) 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); @@ -643,7 +641,6 @@ uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt, struct usb_wait w; int err; struct uhci_dev_info *ud; - int n; ud = ue->ud; diff --git a/src/lib/debug.h b/src/lib/debug.h index 3218ab6..888ab7b 100644 --- a/src/lib/debug.h +++ b/src/lib/debug.h @@ -16,6 +16,7 @@ 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 diff --git a/src/lib/kernel/debug.c b/src/lib/kernel/debug.c index 93c3952..2280195 100644 --- a/src/lib/kernel/debug.c +++ b/src/lib/kernel/debug.c @@ -7,6 +7,9 @@ #include #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 @@ -46,3 +49,75 @@ debug_panic (const char *file, int line, const char *function, 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); +} diff --git a/src/threads/init.c b/src/threads/init.c index 66bc867..c60f79e 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -59,6 +59,9 @@ static const char *swap_bdev_name; /* -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); @@ -141,6 +144,9 @@ main (void) run_actions (argv); /* Finish up. */ + if (reboot_when_done) + reboot (); + if (power_off_when_done) power_off (); thread_exit (); @@ -268,6 +274,8 @@ parse_options (char **argv) 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; @@ -391,11 +399,12 @@ usage (void) " 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" @@ -455,6 +464,41 @@ locate_block_device (enum block_type role, const char *name) } #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 diff --git a/src/threads/init.h b/src/threads/init.h index 863d1de..36b4187 100644 --- a/src/threads/init.h +++ b/src/threads/init.h @@ -13,5 +13,6 @@ extern uint32_t *base_page_dir; extern bool power_off_when_done; void power_off (void) NO_RETURN; +void reboot (void); #endif /* threads/init.h */ diff --git a/src/threads/interrupt.c b/src/threads/interrupt.c index 27f4708..24539e6 100644 --- a/src/threads/interrupt.c +++ b/src/threads/interrupt.c @@ -20,6 +20,14 @@ #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 diff --git a/src/threads/palloc.c b/src/threads/palloc.c index ddbfeda..46bfefc 100644 --- a/src/threads/palloc.c +++ b/src/threads/palloc.c @@ -42,7 +42,6 @@ size_t user_page_limit = SIZE_MAX; 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 diff --git a/src/threads/thread.c b/src/threads/thread.c index 92d1aa8..2532463 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -24,6 +24,10 @@ 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; @@ -87,6 +91,7 @@ thread_init (void) 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 (); @@ -166,6 +171,7 @@ thread_create (const char *name, int priority, struct switch_entry_frame *ef; struct switch_threads_frame *sf; tid_t tid; + enum intr_level old_level; ASSERT (function != NULL); @@ -178,6 +184,11 @@ thread_create (const char *name, int priority, 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; @@ -191,6 +202,9 @@ thread_create (const char *name, int priority, /* 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); @@ -280,9 +294,11 @@ thread_exit (void) 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 (); @@ -306,6 +322,23 @@ thread_yield (void) 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) @@ -436,6 +469,7 @@ init_thread (struct thread *t, const char *name, int 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 diff --git a/src/threads/thread.h b/src/threads/thread.h index 0039560..7965c06 100644 --- a/src/threads/thread.h +++ b/src/threads/thread.h @@ -88,6 +88,7 @@ struct thread 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. */ @@ -125,6 +126,10 @@ const char *thread_name (void); 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); diff --git a/src/utils/pintos b/src/utils/pintos index a62b192..d42669c 100755 --- a/src/utils/pintos +++ b/src/utils/pintos @@ -585,8 +585,11 @@ panic: action=fatal 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]); -- 2.30.2