1/*
2 * Copyright 2004-2007, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "debug.h"
8
9#include <limits.h>
10#include <string.h>
11
12#include <boot/platform.h>
13#include <boot/stage2.h>
14#include <boot/stdio.h>
15#include <kernel.h>
16#include <util/ring_buffer.h>
17
18#include "keyboard.h"
19#include "mmu.h"
20#include "serial.h"
21
22
23//#define PRINT_TIME_STAMPS
24	// Define to print a TSC timestamp before each line of output.
25
26
27static const char* const kDebugSyslogSignature = "Haiku syslog";
28
29static char sBuffer[16384];
30static uint32 sBufferPosition;
31
32static ring_buffer* sDebugSyslogBuffer = NULL;
33static bool sPostCleanup = false;
34
35
36static void
37syslog_write(const char* buffer, size_t length)
38{
39	if (sPostCleanup && sDebugSyslogBuffer != NULL) {
40		ring_buffer_write(sDebugSyslogBuffer, (const uint8*)buffer, length);
41	} else if (sBufferPosition + length < sizeof(sBuffer)) {
42		memcpy(sBuffer + sBufferPosition, buffer, length);
43		sBufferPosition += length;
44	}
45}
46
47
48static void
49dprintf_args(const char *format, va_list args)
50{
51	char buffer[512];
52	int length = vsnprintf(buffer, sizeof(buffer), format, args);
53	if (length == 0)
54		return;
55
56	if (length >= (int)sizeof(buffer))
57		length = sizeof(buffer) - 1;
58
59#ifdef PRINT_TIME_STAMPS
60	static bool sNewLine = true;
61
62	if (sNewLine) {
63		char timeBuffer[32];
64		snprintf(timeBuffer, sizeof(timeBuffer), "[%" B_PRIu64 "] ", __rdtsc());
65		syslog_write(timeBuffer, strlen(timeBuffer));
66		serial_puts(timeBuffer, strlen(timeBuffer));
67	}
68
69	sNewLine = buffer[length - 1] == '\n';
70#endif	// PRINT_TIME_STAMPS
71
72	syslog_write(buffer, length);
73	serial_puts(buffer, length);
74
75	if (platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT)
76		fprintf(stderr, "%s", buffer);
77}
78
79
80// #pragma mark -
81
82
83/*!	This works only after console_init() was called.
84*/
85void
86panic(const char *format, ...)
87{
88	va_list list;
89
90	platform_switch_to_text_mode();
91
92	puts("*** PANIC ***");
93
94	va_start(list, format);
95	vprintf(format, list);
96	va_end(list);
97
98	puts("\nPress key to reboot.");
99
100	clear_key_buffer();
101	wait_for_key();
102	platform_exit();
103}
104
105
106void
107dprintf(const char *format, ...)
108{
109	va_list args;
110
111	va_start(args, format);
112	dprintf_args(format, args);
113	va_end(args);
114}
115
116
117void
118kprintf(const char *format, ...)
119{
120	va_list args;
121
122	va_start(args, format);
123
124	// print to console, if available
125	if (stdout != NULL)
126		vfprintf(stdout, format, args);
127
128	// always print to serial line
129	dprintf_args(format, args);
130
131	va_end(args);
132}
133
134
135// #pragma mark -
136
137
138void
139debug_init_post_mmu(void)
140{
141	// allocate 1 MB memory at 63 MB
142	addr_t base = 63 * 1024 * 1024;
143	size_t size = 1024 * 1024;
144	if (!mmu_allocate_physical(base, size))
145		return;
146
147	void* buffer = (void*)mmu_map_physical_memory(base, size,
148		kDefaultPageFlags);
149	if (buffer == NULL)
150		return;
151
152	// check whether there's a previous syslog we can recover
153	size_t signatureLength = strlen(kDebugSyslogSignature);
154	bool recover = memcmp(buffer, kDebugSyslogSignature, signatureLength) == 0;
155
156	size -= signatureLength;
157	buffer = (uint8*)buffer + ROUNDUP(signatureLength, sizeof(void*));
158
159	sDebugSyslogBuffer = create_ring_buffer_etc(buffer, size,
160		recover ? RING_BUFFER_INIT_FROM_BUFFER : 0);
161
162	gKernelArgs.debug_output = sDebugSyslogBuffer;
163	gKernelArgs.debug_size = sDebugSyslogBuffer->size;
164}
165
166
167void
168debug_cleanup(void)
169{
170	if (sDebugSyslogBuffer != NULL) {
171		// If desired, store the debug syslog data from the previous session for
172		// the kernel.
173		size_t bytesReadable = 0;
174		if (gKernelArgs.previous_debug_size != 0) {
175			bytesReadable = ring_buffer_readable(sDebugSyslogBuffer);
176			gKernelArgs.previous_debug_size = bytesReadable;
177		}
178
179		if (bytesReadable != 0) {
180			if (uint8* buffer = (uint8*)kernel_args_malloc(bytesReadable)) {
181				ring_buffer_read(sDebugSyslogBuffer, buffer, bytesReadable);
182				gKernelArgs.previous_debug_output = buffer;
183			} else
184				gKernelArgs.previous_debug_size = 0;
185		}
186
187		// Prepare the debug syslog buffer for this session.
188		size_t signatureLength = strlen(kDebugSyslogSignature);
189		void* buffer
190			= (void*)ROUNDDOWN((addr_t)sDebugSyslogBuffer, B_PAGE_SIZE);
191
192		if (gKernelArgs.keep_debug_output_buffer) {
193			// copy the output gathered so far into the ring buffer
194			ring_buffer_clear(sDebugSyslogBuffer);
195			ring_buffer_write(sDebugSyslogBuffer, (uint8*)sBuffer,
196				sBufferPosition);
197
198			memcpy(buffer, kDebugSyslogSignature, signatureLength);
199		} else {
200			// clear the signature
201			memset(buffer, 0, signatureLength);
202		}
203	} else
204		gKernelArgs.keep_debug_output_buffer = false;
205
206	if (!gKernelArgs.keep_debug_output_buffer) {
207		gKernelArgs.debug_output = kernel_args_malloc(sBufferPosition);
208		if (gKernelArgs.debug_output != NULL) {
209			memcpy(gKernelArgs.debug_output, sBuffer, sBufferPosition);
210			gKernelArgs.debug_size = sBufferPosition;
211		}
212	}
213
214	sPostCleanup = true;
215}
216
217
218char*
219platform_debug_get_log_buffer(size_t* _size)
220{
221	if (_size != NULL)
222		*_size = sizeof(sBuffer);
223
224	return sBuffer;
225}
226