xref: /haiku/src/system/kernel/arch/x86/arch_debug_console.cpp (revision bd6068614473f87449dfa2eaa67fad1527c61e11)
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 
71 static void
72 init_serial_port(uint16 basePort, uint32 baudRate)
73 {
74 	sSerialBasePort = basePort;
75 	sSerialBaudRate = baudRate;
76 
77 	uint16 divisor = (uint16)(115200 / baudRate);
78 
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 */
83 }
84 
85 
86 static void
87 put_char(const char c)
88 {
89 	// wait until the transmitter empty bit is set
90 	while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
91 		asm volatile ("pause;");
92 
93 	out8(c, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
94 }
95 
96 
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.
101  */
102 
103 static int32
104 debug_keyboard_interrupt(void *data)
105 {
106 	static bool controlPressed = false;
107 	static bool altPressed = false;
108 	static bool sysReqPressed = false;
109 	uint8 key;
110 
111 	key = in8(PS2_PORT_DATA);
112 	//dprintf("debug_keyboard_interrupt: key = 0x%x\n", key);
113 
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;
121 
122 		return B_HANDLED_INTERRUPT;
123 	}
124 
125 	switch (key) {
126 		case LEFT_CONTROL:
127 			controlPressed = true;
128 			break;
129 
130 		case LEFT_ALT:
131 		case RIGHT_ALT:
132 			altPressed = true;
133 			break;
134 
135 		case SYS_REQ:
136 			sysReqPressed = true;
137 			break;
138 
139 		case DELETE:
140 			if (controlPressed && altPressed)
141 				arch_cpu_shutdown(true);
142 			break;
143 
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;
151 				}
152 			}
153 			break;
154 	}
155 
156 	return B_HANDLED_INTERRUPT;
157 }
158 
159 
160 //	#pragma mark -
161 
162 
163 void
164 arch_debug_remove_interrupt_handler(uint32 line)
165 {
166 	if (line != INT_PS2_KEYBOARD || !sKeyboardHandlerInstalled)
167 		return;
168 
169 	remove_io_interrupt_handler(INT_PS2_KEYBOARD, &debug_keyboard_interrupt,
170 		NULL);
171 	sKeyboardHandlerInstalled = false;
172 }
173 
174 
175 void
176 arch_debug_install_interrupt_handlers(void)
177 {
178 	install_io_interrupt_handler(INT_PS2_KEYBOARD, &debug_keyboard_interrupt,
179 		NULL, 0);
180 	sKeyboardHandlerInstalled = true;
181 }
182 
183 
184 int
185 arch_debug_blue_screen_try_getchar(void)
186 {
187 	/* polling the keyboard, similar to code in keyboard
188 	 * driver, but without using an interrupt
189 	 */
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;
196 
197 	if (special & 0x80) {
198 		special &= ~0x80;
199 		return '[';
200 	}
201 	if (special != 0) {
202 		key = special;
203 		special = 0;
204 		return key;
205 	}
206 	if (special2 != 0) {
207 		key = special2;
208 		special2 = 0;
209 		return key;
210 	}
211 
212 	uint8 status = in8(PS2_PORT_CTRL);
213 
214 	if ((status & PS2_STATUS_OUTPUT_BUFFER_FULL) == 0) {
215 		// no data in keyboard buffer
216 		return -1;
217 	}
218 
219 	key = in8(PS2_PORT_DATA);
220 
221 	if (status & PS2_STATUS_AUX_DATA) {
222 		// we read mouse data, ignore it
223 		return -1;
224 	}
225 
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;
239 		}
240 	} else {
241 		// key down
242 		switch (key) {
243 			case LEFT_SHIFT:
244 			case RIGHT_SHIFT:
245 				shiftPressed = true;
246 				return -1;
247 
248 			case LEFT_CONTROL:
249 				controlPressed = true;
250 				return -1;
251 
252 			case LEFT_ALT:
253 				altPressed = true;
254 				return -1;
255 
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';
283 
284 
285 			case DELETE:
286 				if (controlPressed && altPressed)
287 					arch_cpu_shutdown(true);
288 
289 				special = 0x80 | '3';
290 				special2 = '~';
291 				return '\x1b';
292 
293 			default:
294 				if (controlPressed) {
295 					char c = kShiftedKeymap[key];
296 					if (c >= 'A' && c <= 'Z')
297 						return 0x1f & c;
298 				}
299 
300 				if (altPressed)
301 					return kAltedKeymap[key];
302 
303 				return shiftPressed
304 					? kShiftedKeymap[key] : kUnshiftedKeymap[key];
305 		}
306 	}
307 
308 	return -1;
309 }
310 
311 
312 char
313 arch_debug_blue_screen_getchar(void)
314 {
315 	while (true) {
316 		int c = arch_debug_blue_screen_try_getchar();
317 		if (c >= 0)
318 			return (char)c;
319 
320 		arch_cpu_pause();
321 	}
322 }
323 
324 
325 int
326 arch_debug_serial_try_getchar(void)
327 {
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;
333 	}
334 
335 	if ((lineStatus & 0x1) == 0)
336 		return -1;
337 
338 	return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER);
339 }
340 
341 
342 char
343 arch_debug_serial_getchar(void)
344 {
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;
351 		}
352 
353 		if ((lineStatus & 0x1) != 0)
354 			break;
355 
356 		arch_cpu_pause();
357 	}
358 
359 	return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER);
360 }
361 
362 
363 static void
364 _arch_debug_serial_putchar(const char c)
365 {
366 	if (c == '\n') {
367 		put_char('\r');
368 		put_char('\n');
369 	} else if (c != '\r')
370 		put_char(c);
371 }
372 
373 void
374 arch_debug_serial_putchar(const char c)
375 {
376 	cpu_status state = 0;
377 	if (!debug_debugger_running()) {
378 		state = disable_interrupts();
379 		acquire_spinlock(&sSerialOutputSpinlock);
380 	}
381 
382 	_arch_debug_serial_putchar(c);
383 
384 	if (!debug_debugger_running()) {
385 		release_spinlock(&sSerialOutputSpinlock);
386 		restore_interrupts(state);
387 	}
388 }
389 
390 
391 void
392 arch_debug_serial_puts(const char *s)
393 {
394 	cpu_status state = 0;
395 	if (!debug_debugger_running()) {
396 		state = disable_interrupts();
397 		acquire_spinlock(&sSerialOutputSpinlock);
398 	}
399 
400 	while (*s != '\0') {
401 		_arch_debug_serial_putchar(*s);
402 		s++;
403 	}
404 
405 	if (!debug_debugger_running()) {
406 		release_spinlock(&sSerialOutputSpinlock);
407 		restore_interrupts(state);
408 	}
409 }
410 
411 
412 void
413 arch_debug_serial_early_boot_message(const char *string)
414 {
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);
419 }
420 
421 
422 status_t
423 arch_debug_console_init(kernel_args *args)
424 {
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];
428 
429 	init_serial_port(sSerialBasePort, sSerialBaudRate);
430 
431 	return B_OK;
432 }
433 
434 
435 status_t
436 arch_debug_console_init_settings(kernel_args *args)
437 {
438 	uint32 baudRate = sSerialBaudRate;
439 	uint16 basePort = sSerialBasePort;
440 	void *handle;
441 
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
458 			}
459 		}
460 
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;
472 			}
473 		}
474 
475 		unload_driver_settings(handle);
476 	}
477 
478 	if (sSerialBasePort == basePort && baudRate == sSerialBaudRate)
479 		return B_OK;
480 
481 	init_serial_port(basePort, baudRate);
482 
483 	return B_OK;
484 }
485