00001 #include <console.h> 00002 #include <stdarg.h> 00003 #include <stdio.h> 00004 #include "devices/serial.h" 00005 #include "devices/vga.h" 00006 #include "threads/init.h" 00007 #include "threads/interrupt.h" 00008 #include "threads/synch.h" 00009 00010 static void vprintf_helper (char, void *); 00011 static void putchar_have_lock (uint8_t c); 00012 00013 /* The console lock. 00014 Both the vga and serial layers do their own locking, so it's 00015 safe to call them at any time. 00016 But this lock is useful to prevent simultaneous printf() calls 00017 from mixing their output, which looks confusing. */ 00018 static struct lock console_lock; 00019 00020 /* True in ordinary circumstances: we want to use the console 00021 lock to avoid mixing output between threads, as explained 00022 above. 00023 00024 False in early boot before the point that locks are functional 00025 or the console lock has been initialized, or after a kernel 00026 panics. In the former case, taking the lock would cause an 00027 assertion failure, which in turn would cause a panic, turning 00028 it into the latter case. In the latter case, if it is a buggy 00029 lock_acquire() implementation that caused the panic, we'll 00030 likely just recurse. */ 00031 static bool use_console_lock; 00032 00033 /* It's possible, if you add enough debug output to Pintos, to 00034 try to recursively grab console_lock from a single thread. As 00035 a real example, I added a printf() call to palloc_free(). 00036 Here's a real backtrace that resulted: 00037 00038 lock_console() 00039 vprintf() 00040 printf() - palloc() tries to grab the lock again 00041 palloc_free() 00042 schedule_tail() - another thread dying as we switch threads 00043 schedule() 00044 thread_yield() 00045 intr_handler() - timer interrupt 00046 intr_set_level() 00047 serial_putc() 00048 putchar_have_lock() 00049 putbuf() 00050 sys_write() - one process writing to the console 00051 syscall_handler() 00052 intr_handler() 00053 00054 This kind of thing is very difficult to debug, so we avoid the 00055 problem by simulating a recursive lock with a depth 00056 counter. */ 00057 static int console_lock_depth; 00058 00059 /* Number of characters written to console. */ 00060 static int64_t write_cnt; 00061 00062 /* Enable console locking. */ 00063 void 00064 console_init (void) 00065 { 00066 lock_init (&console_lock); 00067 use_console_lock = true; 00068 } 00069 00070 /* Notifies the console that a kernel panic is underway, 00071 which warns it to avoid trying to take the console lock from 00072 now on. */ 00073 void 00074 console_panic (void) 00075 { 00076 use_console_lock = false; 00077 } 00078 00079 /* Prints console statistics. */ 00080 void 00081 console_print_stats (void) 00082 { 00083 printf ("Console: %lld characters output\n", write_cnt); 00084 } 00085 00086 /* Acquires the console lock. */ 00087 static void 00088 acquire_console (void) 00089 { 00090 if (!intr_context () && use_console_lock) 00091 { 00092 if (lock_held_by_current_thread (&console_lock)) 00093 console_lock_depth++; 00094 else 00095 lock_acquire (&console_lock); 00096 } 00097 } 00098 00099 /* Releases the console lock. */ 00100 static void 00101 release_console (void) 00102 { 00103 if (!intr_context () && use_console_lock) 00104 { 00105 if (console_lock_depth > 0) 00106 console_lock_depth--; 00107 else 00108 lock_release (&console_lock); 00109 } 00110 } 00111 00112 /* Returns true if the current thread has the console lock, 00113 false otherwise. */ 00114 static bool 00115 console_locked_by_current_thread (void) 00116 { 00117 return (intr_context () 00118 || !use_console_lock 00119 || lock_held_by_current_thread (&console_lock)); 00120 } 00121 00122 /* The standard vprintf() function, 00123 which is like printf() but uses a va_list. 00124 Writes its output to both vga display and serial port. */ 00125 int 00126 vprintf (const char *format, va_list args) 00127 { 00128 int char_cnt = 0; 00129 00130 acquire_console (); 00131 __vprintf (format, args, vprintf_helper, &char_cnt); 00132 release_console (); 00133 00134 return char_cnt; 00135 } 00136 00137 /* Writes string S to the console, followed by a new-line 00138 character. */ 00139 int 00140 puts (const char *s) 00141 { 00142 acquire_console (); 00143 while (*s != '\0') 00144 putchar_have_lock (*s++); 00145 putchar_have_lock ('\n'); 00146 release_console (); 00147 00148 return 0; 00149 } 00150 00151 /* Writes the N characters in BUFFER to the console. */ 00152 void 00153 putbuf (const char *buffer, size_t n) 00154 { 00155 acquire_console (); 00156 while (n-- > 0) 00157 putchar_have_lock (*buffer++); 00158 release_console (); 00159 } 00160 00161 /* Writes C to the vga display and serial port. */ 00162 int 00163 putchar (int c) 00164 { 00165 acquire_console (); 00166 putchar_have_lock (c); 00167 release_console (); 00168 00169 return c; 00170 } 00171 00172 /* Helper function for vprintf(). */ 00173 static void 00174 vprintf_helper (char c, void *char_cnt_) 00175 { 00176 int *char_cnt = char_cnt_; 00177 (*char_cnt)++; 00178 putchar_have_lock (c); 00179 } 00180 00181 /* Writes C to the vga display and serial port. 00182 The caller has already acquired the console lock if 00183 appropriate. */ 00184 static void 00185 putchar_have_lock (uint8_t c) 00186 { 00187 ASSERT (console_locked_by_current_thread ()); 00188 write_cnt++; 00189 serial_putc (c); 00190 vga_putc (c); 00191 }