14c707a63681129a66463677d6e7916d80199855
[pintos-anon] / src / lib / kernel / console.c
1 #include <console.h>
2 #include <stdarg.h>
3 #include <stdio.h>
4 #include "devices/serial.h"
5 #include "devices/vga.h"
6 #include "threads/interrupt.h"
7 #include "threads/synch.h"
8
9 static void vprintf_helper (char, void *);
10 static void putchar_unlocked (uint8_t c);
11
12 /* The console lock.
13    Both the vga and serial layers do their own locking, so it's
14    safe to call them at any time.
15    But this lock is useful to prevent simultaneous printf() calls
16    from mixing their output, which looks confusing. */
17 static struct lock console_lock;
18
19 /* It's possible, if you add enough debug output to Pintos, to
20    try to recursively grab console_lock from a single thread.  As
21    a real example, I added a printf() call to palloc_free().
22    Here's a real backtrace that resulted:
23
24    lock_console()
25    vprintf()
26    printf()             - palloc() tries to grab the lock again
27    palloc_free()        
28    schedule_tail()      - another thread dying as we switch threads
29    schedule()
30    thread_yield()
31    intr_handler()       - timer interrupt
32    intr_set_level()
33    serial_putc()
34    putchar_unlocked()
35    putbuf()
36    sys_write()          - one process writing to the console
37    syscall_handler()
38    intr_handler()
39
40    This kind of thing is very difficult to debug, so we avoid the
41    problem by simulating a recursive lock with a depth
42    counter. */
43 static int console_lock_depth;
44
45 /* Initializes the console. */
46 void
47 console_init (void) 
48 {
49   lock_init (&console_lock, "console");
50 }
51
52 /* Acquires the console lock. */
53 static void
54 acquire_console (void) 
55 {
56   if (!intr_context ()) 
57     {
58       if (lock_held_by_current_thread (&console_lock)) 
59         console_lock_depth++; 
60       else
61         lock_acquire (&console_lock); 
62     }
63 }
64
65 /* Releases the console lock. */
66 static void
67 release_console (void) 
68 {
69   if (!intr_context ()) 
70     {
71       if (console_lock_depth > 0)
72         console_lock_depth--;
73       else
74         lock_release (&console_lock); 
75     }
76 }
77
78 /* The standard vprintf() function,
79    which is like printf() but uses a va_list.
80    Writes its output to both vga display and serial port. */
81 int
82 vprintf (const char *format, va_list args) 
83 {
84   int char_cnt = 0;
85
86   acquire_console ();
87   __vprintf (format, args, vprintf_helper, &char_cnt);
88   release_console ();
89
90   return char_cnt;
91 }
92
93 /* Writes string S to the console, followed by a new-line
94    character. */
95 int
96 puts (const char *s) 
97 {
98   acquire_console ();
99   while (*s != '\0')
100     putchar_unlocked (*s++);
101   putchar_unlocked ('\n');
102   release_console ();
103
104   return 0;
105 }
106
107 /* Writes the N characters in BUFFER to the console. */
108 void
109 putbuf (const char *buffer, size_t n) 
110 {
111   acquire_console ();
112   while (n-- > 0)
113     putchar_unlocked (*buffer++);
114   release_console ();
115 }
116
117 /* Writes C to the vga display and serial port. */
118 int
119 putchar (int c) 
120 {
121   acquire_console ();
122   putchar_unlocked (c);
123   release_console ();
124   
125   return c;
126 }
127 \f
128 /* Helper function for vprintf(). */
129 static void
130 vprintf_helper (char c, void *char_cnt_) 
131 {
132   int *char_cnt = char_cnt_;
133   (*char_cnt)++;
134   putchar_unlocked (c);
135 }
136
137 /* Writes C to the vga display and serial port.
138    The caller has already acquired the console lock if
139    appropriate. */
140 static void
141 putchar_unlocked (uint8_t c) 
142 {
143   serial_putc (c);
144   vga_putc (c);
145 }