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