1 /*
2  * Copyright 2018 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/dlog.h"
10 
11 #include <stdbool.h>
12 #include <stddef.h>
13 
14 #include "hf/ffa.h"
15 #include "hf/spinlock.h"
16 #include "hf/std.h"
17 #include "hf/stdout.h"
18 
19 /* Keep macro alignment */
20 /* clang-format off */
21 
22 #define FLAG_SPACE 0x01
23 #define FLAG_ZERO  0x02
24 #define FLAG_MINUS 0x04
25 #define FLAG_PLUS  0x08
26 #define FLAG_ALT   0x10
27 #define FLAG_UPPER 0x20
28 #define FLAG_NEG   0x40
29 
30 #define DLOG_MAX_STRING_LENGTH 64
31 
32 /* clang-format on */
33 
34 static bool dlog_lock_enabled = false;
35 static struct spinlock sl = SPINLOCK_INIT;
36 
37 /*
38  * These global variables for the log buffer are not static because a test needs
39  * to access them directly.
40  */
41 size_t dlog_buffer_offset;
42 char dlog_buffer[DLOG_BUFFER_SIZE];
43 
44 /**
45  * Takes the lock, if it is enabled.
46  */
lock(void)47 static void lock(void)
48 {
49 	if (dlog_lock_enabled) {
50 		sl_lock(&sl);
51 	}
52 }
53 
54 /**
55  * Releases the lock, if it is enabled.
56  */
unlock(void)57 static void unlock(void)
58 {
59 	if (dlog_lock_enabled) {
60 		sl_unlock(&sl);
61 	}
62 }
63 
64 /**
65  * Enables the lock protecting the serial device.
66  */
dlog_enable_lock(void)67 void dlog_enable_lock(void)
68 {
69 	dlog_lock_enabled = true;
70 }
71 
dlog_putchar(char c)72 static void dlog_putchar(char c)
73 {
74 	dlog_buffer[dlog_buffer_offset] = c;
75 	dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
76 	stdout_putchar(c);
77 }
78 
79 /**
80  * Prints a raw string to the debug log and returns its length.
81  */
print_raw_string(const char * str)82 static size_t print_raw_string(const char *str)
83 {
84 	const char *c = str;
85 
86 	while (*c != '\0') {
87 		dlog_putchar(*c++);
88 	}
89 
90 	return c - str;
91 }
92 
93 /**
94  * Prints a formatted string to the debug log. The format includes a minimum
95  * width, the fill character, and flags (whether to align to left or right).
96  *
97  * str is the full string, while suffix is a pointer within str that indicates
98  * where the suffix begins. This is used when printing right-aligned numbers
99  * with a zero fill; for example, -10 with width 4 should be padded to -010,
100  * so suffix would point to index one of the "-10" string .
101  */
print_string(const char * str,const char * suffix,size_t width,int flags,char fill)102 static void print_string(const char *str, const char *suffix, size_t width,
103 			 int flags, char fill)
104 {
105 	size_t len = suffix - str;
106 
107 	/* Print the string up to the beginning of the suffix. */
108 	while (str != suffix) {
109 		dlog_putchar(*str++);
110 	}
111 
112 	if (flags & FLAG_MINUS) {
113 		/* Left-aligned. Print suffix, then print padding if needed. */
114 		len += print_raw_string(suffix);
115 		while (len < width) {
116 			dlog_putchar(' ');
117 			len++;
118 		}
119 		return;
120 	}
121 
122 	/* Fill until we reach the desired length. */
123 	len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
124 	while (len < width) {
125 		dlog_putchar(fill);
126 		len++;
127 	}
128 
129 	/* Now print the rest of the string. */
130 	print_raw_string(suffix);
131 }
132 
133 /**
134  * Prints a number to the debug log. The caller specifies the base, its minimum
135  * width and printf-style flags.
136  */
print_num(size_t v,size_t base,size_t width,int flags)137 static void print_num(size_t v, size_t base, size_t width, int flags)
138 {
139 	static const char *digits_lower = "0123456789abcdefx";
140 	static const char *digits_upper = "0123456789ABCDEFX";
141 	const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
142 	char buf[DLOG_MAX_STRING_LENGTH];
143 	char *ptr = &buf[sizeof(buf) - 1];
144 	char *num;
145 	*ptr = '\0';
146 	do {
147 		--ptr;
148 		*ptr = d[v % base];
149 		v /= base;
150 	} while (v);
151 
152 	/* Num stores where the actual number begins. */
153 	num = ptr;
154 
155 	/* Add prefix if requested. */
156 	if (flags & FLAG_ALT) {
157 		switch (base) {
158 		case 16:
159 			ptr -= 2;
160 			ptr[0] = '0';
161 			ptr[1] = d[16];
162 			break;
163 
164 		case 8:
165 			ptr--;
166 			*ptr = '0';
167 			break;
168 		}
169 	}
170 
171 	/* Add sign if requested. */
172 	if (flags & FLAG_NEG) {
173 		*--ptr = '-';
174 	} else if (flags & FLAG_PLUS) {
175 		*--ptr = '+';
176 	} else if (flags & FLAG_SPACE) {
177 		*--ptr = ' ';
178 	}
179 	if (flags & FLAG_ZERO) {
180 		print_string(ptr, num, width, flags, '0');
181 	} else {
182 		print_string(ptr, ptr, width, flags, ' ');
183 	}
184 }
185 
186 /**
187  * Parses the optional flags field of a printf-style format. It returns the spot
188  * on the string where a non-flag character was found.
189  */
parse_flags(const char * p,int * flags)190 static const char *parse_flags(const char *p, int *flags)
191 {
192 	for (;;) {
193 		switch (*p) {
194 		case ' ':
195 			*flags |= FLAG_SPACE;
196 			break;
197 
198 		case '0':
199 			*flags |= FLAG_ZERO;
200 			break;
201 
202 		case '-':
203 			*flags |= FLAG_MINUS;
204 			break;
205 
206 		case '+':
207 			*flags |= FLAG_PLUS;
208 
209 		case '#':
210 			*flags |= FLAG_ALT;
211 			break;
212 
213 		default:
214 			return p;
215 		}
216 		p++;
217 	}
218 }
219 
220 /**
221  * Send the contents of the given VM's log buffer to the log, preceded by the VM
222  * ID and followed by a newline.
223  */
dlog_flush_vm_buffer(ffa_vm_id_t id,char buffer[],size_t length)224 void dlog_flush_vm_buffer(ffa_vm_id_t id, char buffer[], size_t length)
225 {
226 	lock();
227 
228 	print_raw_string("VM ");
229 	print_num(id, 16, 0, 0);
230 	print_raw_string(": ");
231 
232 	for (size_t i = 0; i < length; ++i) {
233 		dlog_putchar(buffer[i]);
234 		buffer[i] = '\0';
235 	}
236 	dlog_putchar('\n');
237 
238 	unlock();
239 }
240 
241 /**
242  * Same as "dlog", except that arguments are passed as a va_list
243  */
vdlog(const char * fmt,va_list args)244 void vdlog(const char *fmt, va_list args)
245 {
246 	const char *p;
247 	size_t w;
248 	int flags;
249 	char buf[2];
250 
251 	lock();
252 
253 	for (p = fmt; *p; p++) {
254 		switch (*p) {
255 		default:
256 			dlog_putchar(*p);
257 			break;
258 
259 		case '%':
260 			/* Read optional flags. */
261 			flags = 0;
262 			p = parse_flags(p + 1, &flags) - 1;
263 
264 			/* Read the minimum width, if one is specified. */
265 			w = 0;
266 			while (p[1] >= '0' && p[1] <= '9') {
267 				w = (w * 10) + (p[1] - '0');
268 				p++;
269 			}
270 
271 			/* Read minimum width from arguments. */
272 			if (w == 0 && p[1] == '*') {
273 				int v = va_arg(args, int);
274 
275 				if (v >= 0) {
276 					w = v;
277 				} else {
278 					w = -v;
279 					flags |= FLAG_MINUS;
280 				}
281 				p++;
282 			}
283 
284 			/* Handle the format specifier. */
285 			switch (p[1]) {
286 			case 's': {
287 				char *str = va_arg(args, char *);
288 
289 				print_string(str, str, w, flags, ' ');
290 				p++;
291 			} break;
292 
293 			case 'd':
294 			case 'i': {
295 				int v = va_arg(args, int);
296 
297 				if (v < 0) {
298 					flags |= FLAG_NEG;
299 					v = -v;
300 				}
301 
302 				print_num((size_t)v, 10, w, flags);
303 				p++;
304 			} break;
305 
306 			case 'X':
307 				flags |= FLAG_UPPER;
308 				print_num(va_arg(args, size_t), 16, w, flags);
309 				p++;
310 				break;
311 
312 			case 'p':
313 				print_num(va_arg(args, size_t), 16,
314 					  sizeof(size_t) * 2, FLAG_ZERO);
315 				p++;
316 				break;
317 
318 			case 'x':
319 				print_num(va_arg(args, size_t), 16, w, flags);
320 				p++;
321 				break;
322 
323 			case 'u':
324 				print_num(va_arg(args, size_t), 10, w, flags);
325 				p++;
326 				break;
327 
328 			case 'o':
329 				print_num(va_arg(args, size_t), 8, w, flags);
330 				p++;
331 				break;
332 
333 			case 'c':
334 				buf[1] = 0;
335 				buf[0] = va_arg(args, int);
336 				print_string(buf, buf, w, flags, ' ');
337 				p++;
338 				break;
339 
340 			case '%':
341 				break;
342 
343 			default:
344 				dlog_putchar('%');
345 			}
346 
347 			break;
348 		}
349 	}
350 
351 	unlock();
352 }
353 
354 /**
355  * Prints the given format string to the debug log.
356  */
dlog(const char * fmt,...)357 void dlog(const char *fmt, ...)
358 {
359 	va_list args;
360 
361 	va_start(args, fmt);
362 	vdlog(fmt, args);
363 	va_end(args);
364 }
365