headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / arch / x86 / arch_debug_console.cpp
blob30a1762737e131d618676175c22190c90c356dbc
1 /*
2 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de
3 * Copyright 2001, Rob Judd <judd@ob-wan.com>
4 * Copyright 2002, Marcus Overhagen <marcus@overhagen.de>
5 * Distributed under the terms of the MIT License.
7 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
8 * Distributed under the terms of the NewOS License.
9 */
10 #include "debugger_keymaps.h"
11 #include "ps2_defs.h"
13 #include <KernelExport.h>
14 #include <driver_settings.h>
15 #include <int.h>
17 #include <arch/cpu.h>
18 #include <arch/debug_console.h>
19 #include <boot/stage2.h>
20 #include <debug.h>
22 #include <string.h>
23 #include <stdlib.h>
26 enum serial_register_offsets {
27 SERIAL_TRANSMIT_BUFFER = 0,
28 SERIAL_RECEIVE_BUFFER = 0,
29 SERIAL_DIVISOR_LATCH_LOW = 0,
30 SERIAL_DIVISOR_LATCH_HIGH = 1,
31 SERIAL_FIFO_CONTROL = 2,
32 SERIAL_LINE_CONTROL = 3,
33 SERIAL_MODEM_CONTROL = 4,
34 SERIAL_LINE_STATUS = 5,
35 SERIAL_MODEM_STATUS = 6,
38 enum keycodes {
39 LEFT_SHIFT = 42,
40 RIGHT_SHIFT = 54,
42 LEFT_CONTROL = 29,
44 LEFT_ALT = 56,
45 RIGHT_ALT = 58,
47 CURSOR_LEFT = 75,
48 CURSOR_RIGHT = 77,
49 CURSOR_UP = 72,
50 CURSOR_DOWN = 80,
51 CURSOR_HOME = 71,
52 CURSOR_END = 79,
53 PAGE_UP = 73,
54 PAGE_DOWN = 81,
56 DELETE = 83,
57 SYS_REQ = 84,
58 F12 = 88,
62 static uint32 sSerialBaudRate = 115200;
63 static uint16 sSerialBasePort = 0x3f8;
64 // COM1 is the default debug output port
66 static bool sKeyboardHandlerInstalled = false;
68 static spinlock sSerialOutputSpinlock = B_SPINLOCK_INITIALIZER;
71 static void
72 init_serial_port(uint16 basePort, uint32 baudRate)
74 sSerialBasePort = basePort;
75 sSerialBaudRate = baudRate;
77 uint16 divisor = (uint16)(115200 / baudRate);
79 out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL); /* set divisor latch access bit */
80 out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW);
81 out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH);
82 out8(3, sSerialBasePort + SERIAL_LINE_CONTROL); /* 8N1 */
86 static void
87 put_char(const char c)
89 // wait until the transmitter empty bit is set
90 while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
91 asm volatile ("pause;");
93 out8(c, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
97 /** Minimal keyboard handler to be able to get into the debugger and
98 * reboot the machine before the input_server is up and running.
99 * It is added as soon as interrupts become available, and removed
100 * again if anything else requests the interrupt 1.
103 static int32
104 debug_keyboard_interrupt(void *data)
106 static bool controlPressed = false;
107 static bool altPressed = false;
108 static bool sysReqPressed = false;
109 uint8 key;
111 key = in8(PS2_PORT_DATA);
112 //dprintf("debug_keyboard_interrupt: key = 0x%x\n", key);
114 if (key & 0x80) {
115 if (key == LEFT_CONTROL)
116 controlPressed = false;
117 else if (key == LEFT_ALT)
118 altPressed = false;
119 else if (key == SYS_REQ)
120 sysReqPressed = false;
122 return B_HANDLED_INTERRUPT;
125 switch (key) {
126 case LEFT_CONTROL:
127 controlPressed = true;
128 break;
130 case LEFT_ALT:
131 case RIGHT_ALT:
132 altPressed = true;
133 break;
135 case SYS_REQ:
136 sysReqPressed = true;
137 break;
139 case DELETE:
140 if (controlPressed && altPressed)
141 arch_cpu_shutdown(true);
142 break;
144 default:
145 if (altPressed && sysReqPressed) {
146 if (debug_emergency_key_pressed(kUnshiftedKeymap[key])) {
147 // we probably have lost some keys, so reset our key states
148 controlPressed = false;
149 sysReqPressed = false;
150 altPressed = false;
153 break;
156 return B_HANDLED_INTERRUPT;
160 // #pragma mark -
163 void
164 arch_debug_remove_interrupt_handler(uint32 line)
166 if (line != INT_PS2_KEYBOARD || !sKeyboardHandlerInstalled)
167 return;
169 remove_io_interrupt_handler(INT_PS2_KEYBOARD, &debug_keyboard_interrupt,
170 NULL);
171 sKeyboardHandlerInstalled = false;
175 void
176 arch_debug_install_interrupt_handlers(void)
178 install_io_interrupt_handler(INT_PS2_KEYBOARD, &debug_keyboard_interrupt,
179 NULL, 0);
180 sKeyboardHandlerInstalled = true;
185 arch_debug_blue_screen_try_getchar(void)
187 /* polling the keyboard, similar to code in keyboard
188 * driver, but without using an interrupt
190 static bool shiftPressed = false;
191 static bool controlPressed = false;
192 static bool altPressed = false;
193 static uint8 special = 0;
194 static uint8 special2 = 0;
195 uint8 key = 0;
197 if (special & 0x80) {
198 special &= ~0x80;
199 return '[';
201 if (special != 0) {
202 key = special;
203 special = 0;
204 return key;
206 if (special2 != 0) {
207 key = special2;
208 special2 = 0;
209 return key;
212 uint8 status = in8(PS2_PORT_CTRL);
214 if ((status & PS2_STATUS_OUTPUT_BUFFER_FULL) == 0) {
215 // no data in keyboard buffer
216 return -1;
219 key = in8(PS2_PORT_DATA);
221 if (status & PS2_STATUS_AUX_DATA) {
222 // we read mouse data, ignore it
223 return -1;
226 if (key & 0x80) {
227 // key up
228 switch (key & ~0x80) {
229 case LEFT_SHIFT:
230 case RIGHT_SHIFT:
231 shiftPressed = false;
232 return -1;
233 case LEFT_CONTROL:
234 controlPressed = false;
235 return -1;
236 case LEFT_ALT:
237 altPressed = false;
238 return -1;
240 } else {
241 // key down
242 switch (key) {
243 case LEFT_SHIFT:
244 case RIGHT_SHIFT:
245 shiftPressed = true;
246 return -1;
248 case LEFT_CONTROL:
249 controlPressed = true;
250 return -1;
252 case LEFT_ALT:
253 altPressed = true;
254 return -1;
256 // start escape sequence for cursor movement
257 case CURSOR_UP:
258 special = 0x80 | 'A';
259 return '\x1b';
260 case CURSOR_DOWN:
261 special = 0x80 | 'B';
262 return '\x1b';
263 case CURSOR_RIGHT:
264 special = 0x80 | 'C';
265 return '\x1b';
266 case CURSOR_LEFT:
267 special = 0x80 | 'D';
268 return '\x1b';
269 case CURSOR_HOME:
270 special = 0x80 | 'H';
271 return '\x1b';
272 case CURSOR_END:
273 special = 0x80 | 'F';
274 return '\x1b';
275 case PAGE_UP:
276 special = 0x80 | '5';
277 special2 = '~';
278 return '\x1b';
279 case PAGE_DOWN:
280 special = 0x80 | '6';
281 special2 = '~';
282 return '\x1b';
285 case DELETE:
286 if (controlPressed && altPressed)
287 arch_cpu_shutdown(true);
289 special = 0x80 | '3';
290 special2 = '~';
291 return '\x1b';
293 default:
294 if (controlPressed) {
295 char c = kShiftedKeymap[key];
296 if (c >= 'A' && c <= 'Z')
297 return 0x1f & c;
300 if (altPressed)
301 return kAltedKeymap[key];
303 return shiftPressed
304 ? kShiftedKeymap[key] : kUnshiftedKeymap[key];
308 return -1;
312 char
313 arch_debug_blue_screen_getchar(void)
315 while (true) {
316 int c = arch_debug_blue_screen_try_getchar();
317 if (c >= 0)
318 return (char)c;
320 arch_cpu_pause();
326 arch_debug_serial_try_getchar(void)
328 uint8 lineStatus = in8(sSerialBasePort + SERIAL_LINE_STATUS);
329 if (lineStatus == 0xff) {
330 // The "data available" bit is set, but also all error bits. Likely we
331 // don't have a valid I/O port.
332 return -1;
335 if ((lineStatus & 0x1) == 0)
336 return -1;
338 return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER);
342 char
343 arch_debug_serial_getchar(void)
345 while (true) {
346 uint8 lineStatus = in8(sSerialBasePort + SERIAL_LINE_STATUS);
347 if (lineStatus == 0xff) {
348 // The "data available" bit is set, but also all error bits. Likely
349 // we don't have a valid I/O port.
350 return 0;
353 if ((lineStatus & 0x1) != 0)
354 break;
356 arch_cpu_pause();
359 return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER);
363 static void
364 _arch_debug_serial_putchar(const char c)
366 if (c == '\n') {
367 put_char('\r');
368 put_char('\n');
369 } else if (c != '\r')
370 put_char(c);
373 void
374 arch_debug_serial_putchar(const char c)
376 cpu_status state = 0;
377 if (!debug_debugger_running()) {
378 state = disable_interrupts();
379 acquire_spinlock(&sSerialOutputSpinlock);
382 _arch_debug_serial_putchar(c);
384 if (!debug_debugger_running()) {
385 release_spinlock(&sSerialOutputSpinlock);
386 restore_interrupts(state);
391 void
392 arch_debug_serial_puts(const char *s)
394 cpu_status state = 0;
395 if (!debug_debugger_running()) {
396 state = disable_interrupts();
397 acquire_spinlock(&sSerialOutputSpinlock);
400 while (*s != '\0') {
401 _arch_debug_serial_putchar(*s);
402 s++;
405 if (!debug_debugger_running()) {
406 release_spinlock(&sSerialOutputSpinlock);
407 restore_interrupts(state);
412 void
413 arch_debug_serial_early_boot_message(const char *string)
415 // this function will only be called in fatal situations
416 // ToDo: also enable output via text console?!
417 arch_debug_console_init(NULL);
418 arch_debug_serial_puts(string);
422 status_t
423 arch_debug_console_init(kernel_args *args)
425 // only use the port if we could find one, else use the standard port
426 if (args != NULL && args->platform_args.serial_base_ports[0] != 0)
427 sSerialBasePort = args->platform_args.serial_base_ports[0];
429 init_serial_port(sSerialBasePort, sSerialBaudRate);
431 return B_OK;
435 status_t
436 arch_debug_console_init_settings(kernel_args *args)
438 uint32 baudRate = sSerialBaudRate;
439 uint16 basePort = sSerialBasePort;
440 void *handle;
442 // get debug settings
443 handle = load_driver_settings("kernel");
444 if (handle != NULL) {
445 const char *value = get_driver_parameter(handle, "serial_debug_port",
446 NULL, NULL);
447 if (value != NULL) {
448 int32 number = strtol(value, NULL, 0);
449 if (number >= MAX_SERIAL_PORTS) {
450 // use as port number directly
451 basePort = number;
452 } else if (number >= 0) {
453 // use as index into port array
454 if (args->platform_args.serial_base_ports[number] != 0)
455 basePort = args->platform_args.serial_base_ports[number];
456 } else {
457 // ignore value and use default
461 value = get_driver_parameter(handle, "serial_debug_speed", NULL, NULL);
462 if (value != NULL) {
463 int32 number = strtol(value, NULL, 0);
464 switch (number) {
465 case 9600:
466 case 19200:
467 case 38400:
468 case 57600:
469 case 115200:
470 //case 230400:
471 baudRate = number;
475 unload_driver_settings(handle);
478 if (sSerialBasePort == basePort && baudRate == sSerialBaudRate)
479 return B_OK;
481 init_serial_port(basePort, baudRate);
483 return B_OK;