1 | // Console input and output. |
2 | // Input is from the keyboard or serial port. |
3 | // Output is written to the screen and serial port. |
4 | |
5 | #include "types.h" |
6 | #include "defs.h" |
7 | #include "param.h" |
8 | #include "traps.h" |
9 | #include "spinlock.h" |
10 | #include "sleeplock.h" |
11 | #include "fs.h" |
12 | #include "file.h" |
13 | #include "memlayout.h" |
14 | #include "mmu.h" |
15 | #include "proc.h" |
16 | #include "x86.h" |
17 | |
18 | static void consputc(int); |
19 | |
20 | static int panicked = 0; |
21 | |
22 | static struct { |
23 | struct spinlock lock; |
24 | int locking; |
25 | } cons; |
26 | |
27 | static void |
28 | printint(int xx, int base, int sign) |
29 | { |
30 | static char digits[] = "0123456789abcdef" ; |
31 | char buf[16]; |
32 | int i; |
33 | uint x; |
34 | |
35 | if(sign && (sign = xx < 0)) |
36 | x = -xx; |
37 | else |
38 | x = xx; |
39 | |
40 | i = 0; |
41 | do{ |
42 | buf[i++] = digits[x % base]; |
43 | }while((x /= base) != 0); |
44 | |
45 | if(sign) |
46 | buf[i++] = '-'; |
47 | |
48 | while(--i >= 0) |
49 | consputc(buf[i]); |
50 | } |
51 | //PAGEBREAK: 50 |
52 | |
53 | // Print to the console. only understands %d, %x, %p, %s. |
54 | void |
55 | cprintf(char *fmt, ...) |
56 | { |
57 | int i, c, locking; |
58 | uint *argp; |
59 | char *s; |
60 | |
61 | locking = cons.locking; |
62 | if(locking) |
63 | acquire(&cons.lock); |
64 | |
65 | if (fmt == 0) |
66 | panic("null fmt" ); |
67 | |
68 | argp = (uint*)(void*)(&fmt + 1); |
69 | for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ |
70 | if(c != '%'){ |
71 | consputc(c); |
72 | continue; |
73 | } |
74 | c = fmt[++i] & 0xff; |
75 | if(c == 0) |
76 | break; |
77 | switch(c){ |
78 | case 'd': |
79 | printint(*argp++, 10, 1); |
80 | break; |
81 | case 'x': |
82 | case 'p': |
83 | printint(*argp++, 16, 0); |
84 | break; |
85 | case 's': |
86 | if((s = (char*)*argp++) == 0) |
87 | s = "(null)" ; |
88 | for(; *s; s++) |
89 | consputc(*s); |
90 | break; |
91 | case '%': |
92 | consputc('%'); |
93 | break; |
94 | default: |
95 | // Print unknown % sequence to draw attention. |
96 | consputc('%'); |
97 | consputc(c); |
98 | break; |
99 | } |
100 | } |
101 | |
102 | if(locking) |
103 | release(&cons.lock); |
104 | } |
105 | |
106 | void |
107 | panic(char *s) |
108 | { |
109 | int i; |
110 | uint pcs[10]; |
111 | |
112 | cli(); |
113 | cons.locking = 0; |
114 | // use lapiccpunum so that we can call panic from mycpu() |
115 | cprintf("lapicid %d: panic: " , lapicid()); |
116 | cprintf(s); |
117 | cprintf("\n" ); |
118 | getcallerpcs(&s, pcs); |
119 | for(i=0; i<10; i++) |
120 | cprintf(" %p" , pcs[i]); |
121 | panicked = 1; // freeze other CPU |
122 | for(;;) |
123 | ; |
124 | } |
125 | |
126 | //PAGEBREAK: 50 |
127 | #define BACKSPACE 0x100 |
128 | #define CRTPORT 0x3d4 |
129 | static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory |
130 | |
131 | static void |
132 | cgaputc(int c) |
133 | { |
134 | int pos; |
135 | |
136 | // Cursor position: col + 80*row. |
137 | outb(CRTPORT, 14); |
138 | pos = inb(CRTPORT+1) << 8; |
139 | outb(CRTPORT, 15); |
140 | pos |= inb(CRTPORT+1); |
141 | |
142 | if(c == '\n') |
143 | pos += 80 - pos%80; |
144 | else if(c == BACKSPACE){ |
145 | if(pos > 0) --pos; |
146 | } else |
147 | crt[pos++] = (c&0xff) | 0x0700; // black on white |
148 | |
149 | if(pos < 0 || pos > 25*80) |
150 | panic("pos under/overflow" ); |
151 | |
152 | if((pos/80) >= 24){ // Scroll up. |
153 | memmove(crt, crt+80, sizeof(crt[0])*23*80); |
154 | pos -= 80; |
155 | memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos)); |
156 | } |
157 | |
158 | outb(CRTPORT, 14); |
159 | outb(CRTPORT+1, pos>>8); |
160 | outb(CRTPORT, 15); |
161 | outb(CRTPORT+1, pos); |
162 | crt[pos] = ' ' | 0x0700; |
163 | } |
164 | |
165 | void |
166 | consputc(int c) |
167 | { |
168 | if(panicked){ |
169 | cli(); |
170 | for(;;) |
171 | ; |
172 | } |
173 | |
174 | if(c == BACKSPACE){ |
175 | uartputc('\b'); uartputc(' '); uartputc('\b'); |
176 | } else |
177 | uartputc(c); |
178 | cgaputc(c); |
179 | } |
180 | |
181 | #define INPUT_BUF 128 |
182 | struct { |
183 | char buf[INPUT_BUF]; |
184 | uint r; // Read index |
185 | uint w; // Write index |
186 | uint e; // Edit index |
187 | } input; |
188 | |
189 | #define C(x) ((x)-'@') // Control-x |
190 | |
191 | void |
192 | consoleintr(int (*getc)(void)) |
193 | { |
194 | int c, doprocdump = 0; |
195 | |
196 | acquire(&cons.lock); |
197 | while((c = getc()) >= 0){ |
198 | switch(c){ |
199 | case C('P'): // Process listing. |
200 | // procdump() locks cons.lock indirectly; invoke later |
201 | doprocdump = 1; |
202 | break; |
203 | case C('U'): // Kill line. |
204 | while(input.e != input.w && |
205 | input.buf[(input.e-1) % INPUT_BUF] != '\n'){ |
206 | input.e--; |
207 | consputc(BACKSPACE); |
208 | } |
209 | break; |
210 | case C('H'): case '\x7f': // Backspace |
211 | if(input.e != input.w){ |
212 | input.e--; |
213 | consputc(BACKSPACE); |
214 | } |
215 | break; |
216 | default: |
217 | if(c != 0 && input.e-input.r < INPUT_BUF){ |
218 | c = (c == '\r') ? '\n' : c; |
219 | input.buf[input.e++ % INPUT_BUF] = c; |
220 | consputc(c); |
221 | if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ |
222 | input.w = input.e; |
223 | wakeup(&input.r); |
224 | } |
225 | } |
226 | break; |
227 | } |
228 | } |
229 | release(&cons.lock); |
230 | if(doprocdump) { |
231 | procdump(); // now call procdump() wo. cons.lock held |
232 | } |
233 | } |
234 | |
235 | int |
236 | consoleread(struct inode *ip, char *dst, int n) |
237 | { |
238 | uint target; |
239 | int c; |
240 | |
241 | iunlock(ip); |
242 | target = n; |
243 | acquire(&cons.lock); |
244 | while(n > 0){ |
245 | while(input.r == input.w){ |
246 | if(myproc()->killed){ |
247 | release(&cons.lock); |
248 | ilock(ip); |
249 | return -1; |
250 | } |
251 | sleep(&input.r, &cons.lock); |
252 | } |
253 | c = input.buf[input.r++ % INPUT_BUF]; |
254 | if(c == C('D')){ // EOF |
255 | if(n < target){ |
256 | // Save ^D for next time, to make sure |
257 | // caller gets a 0-byte result. |
258 | input.r--; |
259 | } |
260 | break; |
261 | } |
262 | *dst++ = c; |
263 | --n; |
264 | if(c == '\n') |
265 | break; |
266 | } |
267 | release(&cons.lock); |
268 | ilock(ip); |
269 | |
270 | return target - n; |
271 | } |
272 | |
273 | int |
274 | consolewrite(struct inode *ip, char *buf, int n) |
275 | { |
276 | int i; |
277 | |
278 | iunlock(ip); |
279 | acquire(&cons.lock); |
280 | for(i = 0; i < n; i++) |
281 | consputc(buf[i] & 0xff); |
282 | release(&cons.lock); |
283 | ilock(ip); |
284 | |
285 | return n; |
286 | } |
287 | |
288 | void |
289 | consoleinit(void) |
290 | { |
291 | initlock(&cons.lock, "console" ); |
292 | |
293 | devsw[CONSOLE].write = consolewrite; |
294 | devsw[CONSOLE].read = consoleread; |
295 | cons.locking = 1; |
296 | |
297 | ioapicenable(IRQ_KBD, 0); |
298 | } |
299 | |
300 | |