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