xref: /haiku/src/system/kernel/arch/x86/arch_debug_console.cpp (revision 7a74a5df454197933bc6e80a542102362ee98703)
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.
6  *
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"
12 
13 #include <KernelExport.h>
14 #include <driver_settings.h>
15 #include <int.h>
16 
17 #include <arch/cpu.h>
18 #include <arch/debug_console.h>
19 #include <boot/stage2.h>
20 #include <debug.h>
21 
22 #include <string.h>
23 #include <stdlib.h>
24 
25 
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,
36 };
37 
38 enum keycodes {
39 	LEFT_SHIFT		= 42,
40 	RIGHT_SHIFT		= 54,
41 
42 	LEFT_CONTROL	= 29,
43 
44 	LEFT_ALT		= 56,
45 	RIGHT_ALT		= 58,
46 
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,
55 
56 	DELETE			= 83,
57 	SYS_REQ			= 84,
58 	F12				= 88,
59 };
60 
61 
62 static const uint32 kSerialBaudRate = 115200;
63 static uint16 sSerialBasePort = 0x3f8;
64 	// COM1 is the default debug output port
65 
66 static bool sKeyboardHandlerInstalled = false;
67 
68 static spinlock sSerialOutputSpinlock = B_SPINLOCK_INITIALIZER;
69 
70 
71 static void
72 init_serial_port(uint16 basePort, uint32 baudRate)
73 {
74 	sSerialBasePort = basePort;
75 
76 	uint16 divisor = (uint16)(115200 / baudRate);
77 
78 	out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL);	/* set divisor latch access bit */
79 	out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW);
80 	out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH);
81 	out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);		/* 8N1 */
82 }
83 
84 
85 static void
86 put_char(const char c)
87 {
88 	// wait until the transmitter empty bit is set
89 	while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
90 		asm volatile ("pause;");
91 
92 	out8(c, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
93 }
94 
95 
96 /**	Minimal keyboard handler to be able to get into the debugger and
97  *	reboot the machine before the input_server is up and running.
98  *	It is added as soon as interrupts become available, and removed
99  *	again if anything else requests the interrupt 1.
100  */
101 
102 static int32
103 debug_keyboard_interrupt(void *data)
104 {
105 	static bool controlPressed = false;
106 	static bool altPressed = false;
107 	static bool sysReqPressed = false;
108 	uint8 key;
109 
110 	key = in8(PS2_PORT_DATA);
111 	//dprintf("debug_keyboard_interrupt: key = 0x%x\n", key);
112 
113 	if (key & 0x80) {
114 		if (key == LEFT_CONTROL)
115 			controlPressed = false;
116 		else if (key == LEFT_ALT)
117 			altPressed = false;
118 		else if (key == SYS_REQ)
119 			sysReqPressed = false;
120 
121 		return B_HANDLED_INTERRUPT;
122 	}
123 
124 	switch (key) {
125 		case LEFT_CONTROL:
126 			controlPressed = true;
127 			break;
128 
129 		case LEFT_ALT:
130 		case RIGHT_ALT:
131 			altPressed = true;
132 			break;
133 
134 		case SYS_REQ:
135 			sysReqPressed = true;
136 			break;
137 
138 		case DELETE:
139 			if (controlPressed && altPressed)
140 				arch_cpu_shutdown(true);
141 			break;
142 
143 		default:
144 			if (altPressed && sysReqPressed) {
145 				if (debug_emergency_key_pressed(kUnshiftedKeymap[key])) {
146 					// we probably have lost some keys, so reset our key states
147 					controlPressed = false;
148 					sysReqPressed = false;
149 					altPressed = false;
150 				}
151 			}
152 			break;
153 	}
154 
155 	return B_HANDLED_INTERRUPT;
156 }
157 
158 
159 //	#pragma mark -
160 
161 
162 void
163 arch_debug_remove_interrupt_handler(uint32 line)
164 {
165 	if (line != INT_PS2_KEYBOARD || !sKeyboardHandlerInstalled)
166 		return;
167 
168 	remove_io_interrupt_handler(INT_PS2_KEYBOARD, &debug_keyboard_interrupt,
169 		NULL);
170 	sKeyboardHandlerInstalled = false;
171 }
172 
173 
174 void
175 arch_debug_install_interrupt_handlers(void)
176 {
177 	install_io_interrupt_handler(INT_PS2_KEYBOARD, &debug_keyboard_interrupt,
178 		NULL, 0);
179 	sKeyboardHandlerInstalled = true;
180 }
181 
182 
183 int
184 arch_debug_blue_screen_try_getchar(void)
185 {
186 	/* polling the keyboard, similar to code in keyboard
187 	 * driver, but without using an interrupt
188 	 */
189 	static bool shiftPressed = false;
190 	static bool controlPressed = false;
191 	static bool altPressed = false;
192 	static uint8 special = 0;
193 	static uint8 special2 = 0;
194 	uint8 key = 0;
195 
196 	if (special & 0x80) {
197 		special &= ~0x80;
198 		return '[';
199 	}
200 	if (special != 0) {
201 		key = special;
202 		special = 0;
203 		return key;
204 	}
205 	if (special2 != 0) {
206 		key = special2;
207 		special2 = 0;
208 		return key;
209 	}
210 
211 	uint8 status = in8(PS2_PORT_CTRL);
212 
213 	if ((status & PS2_STATUS_OUTPUT_BUFFER_FULL) == 0) {
214 		// no data in keyboard buffer
215 		return -1;
216 	}
217 
218 	key = in8(PS2_PORT_DATA);
219 
220 	if (status & PS2_STATUS_AUX_DATA) {
221 		// we read mouse data, ignore it
222 		return -1;
223 	}
224 
225 	if (key & 0x80) {
226 		// key up
227 		switch (key & ~0x80) {
228 			case LEFT_SHIFT:
229 			case RIGHT_SHIFT:
230 				shiftPressed = false;
231 				return -1;
232 			case LEFT_CONTROL:
233 				controlPressed = false;
234 				return -1;
235 			case LEFT_ALT:
236 				altPressed = false;
237 				return -1;
238 		}
239 	} else {
240 		// key down
241 		switch (key) {
242 			case LEFT_SHIFT:
243 			case RIGHT_SHIFT:
244 				shiftPressed = true;
245 				return -1;
246 
247 			case LEFT_CONTROL:
248 				controlPressed = true;
249 				return -1;
250 
251 			case LEFT_ALT:
252 				altPressed = true;
253 				return -1;
254 
255 			// start escape sequence for cursor movement
256 			case CURSOR_UP:
257 				special = 0x80 | 'A';
258 				return '\x1b';
259 			case CURSOR_DOWN:
260 				special = 0x80 | 'B';
261 				return '\x1b';
262 			case CURSOR_RIGHT:
263 				special = 0x80 | 'C';
264 				return '\x1b';
265 			case CURSOR_LEFT:
266 				special = 0x80 | 'D';
267 				return '\x1b';
268 			case CURSOR_HOME:
269 				special = 0x80 | 'H';
270 				return '\x1b';
271 			case CURSOR_END:
272 				special = 0x80 | 'F';
273 				return '\x1b';
274 			case PAGE_UP:
275 				special = 0x80 | '5';
276 				special2 = '~';
277 				return '\x1b';
278 			case PAGE_DOWN:
279 				special = 0x80 | '6';
280 				special2 = '~';
281 				return '\x1b';
282 
283 
284 			case DELETE:
285 				if (controlPressed && altPressed)
286 					arch_cpu_shutdown(true);
287 
288 				special = 0x80 | '3';
289 				special2 = '~';
290 				return '\x1b';
291 
292 			default:
293 				if (controlPressed) {
294 					char c = kShiftedKeymap[key];
295 					if (c >= 'A' && c <= 'Z')
296 						return 0x1f & c;
297 				}
298 
299 				if (altPressed)
300 					return kAltedKeymap[key];
301 
302 				return shiftPressed
303 					? kShiftedKeymap[key] : kUnshiftedKeymap[key];
304 		}
305 	}
306 
307 	return -1;
308 }
309 
310 
311 char
312 arch_debug_blue_screen_getchar(void)
313 {
314 	while (true) {
315 		int c = arch_debug_blue_screen_try_getchar();
316 		if (c >= 0)
317 			return (char)c;
318 
319 		PAUSE();
320 	}
321 }
322 
323 
324 int
325 arch_debug_serial_try_getchar(void)
326 {
327 	uint8 lineStatus = in8(sSerialBasePort + SERIAL_LINE_STATUS);
328 	if (lineStatus == 0xff) {
329 		// The "data available" bit is set, but also all error bits. Likely we
330 		// don't have a valid I/O port.
331 		return -1;
332 	}
333 
334 	if ((lineStatus & 0x1) == 0)
335 		return -1;
336 
337 	return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER);
338 }
339 
340 
341 char
342 arch_debug_serial_getchar(void)
343 {
344 	while (true) {
345 		uint8 lineStatus = in8(sSerialBasePort + SERIAL_LINE_STATUS);
346 		if (lineStatus == 0xff) {
347 			// The "data available" bit is set, but also all error bits. Likely
348 			// we don't have a valid I/O port.
349 			return 0;
350 		}
351 
352 		if ((lineStatus & 0x1) != 0)
353 			break;
354 
355 		PAUSE();
356 	}
357 
358 	return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER);
359 }
360 
361 
362 static void
363 _arch_debug_serial_putchar(const char c)
364 {
365 	if (c == '\n') {
366 		put_char('\r');
367 		put_char('\n');
368 	} else if (c != '\r')
369 		put_char(c);
370 }
371 
372 void
373 arch_debug_serial_putchar(const char c)
374 {
375 	cpu_status state = 0;
376 	if (!debug_debugger_running()) {
377 		state = disable_interrupts();
378 		acquire_spinlock(&sSerialOutputSpinlock);
379 	}
380 
381 	_arch_debug_serial_putchar(c);
382 
383 	if (!debug_debugger_running()) {
384 		release_spinlock(&sSerialOutputSpinlock);
385 		restore_interrupts(state);
386 	}
387 }
388 
389 
390 void
391 arch_debug_serial_puts(const char *s)
392 {
393 	cpu_status state = 0;
394 	if (!debug_debugger_running()) {
395 		state = disable_interrupts();
396 		acquire_spinlock(&sSerialOutputSpinlock);
397 	}
398 
399 	while (*s != '\0') {
400 		_arch_debug_serial_putchar(*s);
401 		s++;
402 	}
403 
404 	if (!debug_debugger_running()) {
405 		release_spinlock(&sSerialOutputSpinlock);
406 		restore_interrupts(state);
407 	}
408 }
409 
410 
411 void
412 arch_debug_serial_early_boot_message(const char *string)
413 {
414 	// this function will only be called in fatal situations
415 	// ToDo: also enable output via text console?!
416 	arch_debug_console_init(NULL);
417 	arch_debug_serial_puts(string);
418 }
419 
420 
421 status_t
422 arch_debug_console_init(kernel_args *args)
423 {
424 	// only use the port if we could find one, else use the standard port
425 	if (args != NULL && args->platform_args.serial_base_ports[0] != 0)
426 		sSerialBasePort = args->platform_args.serial_base_ports[0];
427 
428 	init_serial_port(sSerialBasePort, kSerialBaudRate);
429 
430 	return B_OK;
431 }
432 
433 
434 status_t
435 arch_debug_console_init_settings(kernel_args *args)
436 {
437 	uint32 baudRate = kSerialBaudRate;
438 	uint16 basePort = sSerialBasePort;
439 	void *handle;
440 
441 	// get debug settings
442 	handle = load_driver_settings("kernel");
443 	if (handle != NULL) {
444 		const char *value = get_driver_parameter(handle, "serial_debug_port",
445 			NULL, NULL);
446 		if (value != NULL) {
447 			int32 number = strtol(value, NULL, 0);
448 			if (number >= MAX_SERIAL_PORTS) {
449 				// use as port number directly
450 				basePort = number;
451 			} else if (number >= 0) {
452 				// use as index into port array
453 				if (args->platform_args.serial_base_ports[number] != 0)
454 					basePort = args->platform_args.serial_base_ports[number];
455 			} else {
456 				// ignore value and use default
457 			}
458 		}
459 
460 		value = get_driver_parameter(handle, "serial_debug_speed", NULL, NULL);
461 		if (value != NULL) {
462 			int32 number = strtol(value, NULL, 0);
463 			switch (number) {
464 				case 9600:
465 				case 19200:
466 				case 38400:
467 				case 57600:
468 				case 115200:
469 				//case 230400:
470 					baudRate = number;
471 			}
472 		}
473 
474 		unload_driver_settings(handle);
475 	}
476 
477 	if (sSerialBasePort == basePort && baudRate == kSerialBaudRate)
478 		return B_OK;
479 
480 	init_serial_port(sSerialBasePort, kSerialBaudRate);
481 
482 	return B_OK;
483 }
484