mirror of
				https://github.com/riscv-software-src/opensbi
				synced 2025-11-04 05:50:22 +00:00 
			
		
		
		
	Avoid getting messages from multiple harts mingled into garbage text with a spinlock serializing calls to sbi_puts() and sbi_printf(). Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
		
			
				
	
	
		
			377 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2018 Western Digital Corporation or its affiliates.
 | 
						|
 *
 | 
						|
 * Authors:
 | 
						|
 *   Anup Patel <anup.patel@wdc.com>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: BSD-2-Clause
 | 
						|
 */
 | 
						|
 | 
						|
#include <sbi/sbi_platform.h>
 | 
						|
#include <sbi/sbi_console.h>
 | 
						|
#include <sbi/riscv_locks.h>
 | 
						|
 | 
						|
static struct sbi_platform *console_plat = NULL;
 | 
						|
static spinlock_t console_out_lock = SPIN_LOCK_INITIALIZER;
 | 
						|
 | 
						|
bool sbi_isprintable(char c)
 | 
						|
{
 | 
						|
	if (((31 < c) && (c < 127)) ||
 | 
						|
	   (c == '\f') ||
 | 
						|
	   (c == '\r') ||
 | 
						|
	   (c == '\n') ||
 | 
						|
	   (c == '\t')) {
 | 
						|
		return TRUE;
 | 
						|
	}
 | 
						|
	return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
char sbi_getc(void)
 | 
						|
{
 | 
						|
	return sbi_platform_console_getc(console_plat);
 | 
						|
}
 | 
						|
 | 
						|
void sbi_putc(char ch)
 | 
						|
{
 | 
						|
	if (ch == '\n')
 | 
						|
		sbi_platform_console_putc(console_plat, '\r');
 | 
						|
	sbi_platform_console_putc(console_plat, ch);
 | 
						|
}
 | 
						|
 | 
						|
void sbi_puts(const char *str)
 | 
						|
{
 | 
						|
	spin_lock(&console_out_lock);
 | 
						|
	while (*str) {
 | 
						|
		sbi_putc(*str);
 | 
						|
		str++;
 | 
						|
	}
 | 
						|
	spin_unlock(&console_out_lock);
 | 
						|
}
 | 
						|
 | 
						|
void sbi_gets(char *s, int maxwidth, char endchar)
 | 
						|
{
 | 
						|
	char ch, *retval = s;
 | 
						|
 | 
						|
	while ((ch = sbi_getc()) != endchar && maxwidth > 1) {
 | 
						|
		*retval = ch;
 | 
						|
		retval++;
 | 
						|
		maxwidth--;
 | 
						|
	}
 | 
						|
	*retval = '\0';
 | 
						|
}
 | 
						|
 | 
						|
#define PAD_RIGHT	1
 | 
						|
#define PAD_ZERO	2
 | 
						|
#define PAD_ALTERNATE	4
 | 
						|
#define PRINT_BUF_LEN	64
 | 
						|
 | 
						|
#define va_start(v,l)		__builtin_va_start((v),l)
 | 
						|
#define va_end			__builtin_va_end
 | 
						|
#define va_arg			__builtin_va_arg
 | 
						|
typedef __builtin_va_list	va_list;
 | 
						|
 | 
						|
static void printc(char **out, u32 *out_len, char ch)
 | 
						|
{
 | 
						|
	if (out) {
 | 
						|
		if (*out) {
 | 
						|
			if (out_len && (0 < *out_len)) {
 | 
						|
				**out = ch;
 | 
						|
				++(*out);
 | 
						|
				(*out_len)--;
 | 
						|
			} else {
 | 
						|
				**out = ch;
 | 
						|
				++(*out);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		sbi_putc(ch);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int prints(char **out, u32 *out_len, const char *string, int width, int flags)
 | 
						|
{
 | 
						|
	int pc = 0;
 | 
						|
	char padchar = ' ';
 | 
						|
 | 
						|
	if (width > 0) {
 | 
						|
		int len = 0;
 | 
						|
		const char *ptr;
 | 
						|
		for (ptr = string; *ptr; ++ptr)
 | 
						|
			++len;
 | 
						|
		if (len >= width)
 | 
						|
			width = 0;
 | 
						|
		else
 | 
						|
			width -= len;
 | 
						|
		if (flags & PAD_ZERO)
 | 
						|
			padchar = '0';
 | 
						|
	}
 | 
						|
	if (!(flags & PAD_RIGHT)) {
 | 
						|
		for (; width > 0; --width) {
 | 
						|
			printc(out, out_len, padchar);
 | 
						|
			++pc;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for (; *string; ++string) {
 | 
						|
		printc(out, out_len, *string);
 | 
						|
		++pc;
 | 
						|
	}
 | 
						|
	for (; width > 0; --width) {
 | 
						|
		printc(out, out_len, padchar);
 | 
						|
		++pc;
 | 
						|
	}
 | 
						|
 | 
						|
	return pc;
 | 
						|
}
 | 
						|
 | 
						|
static int printi(char **out, u32 *out_len, long long i, int b, int sg,
 | 
						|
		  int width, int flags, int letbase)
 | 
						|
{
 | 
						|
	char print_buf[PRINT_BUF_LEN];
 | 
						|
	char *s;
 | 
						|
	int neg = 0, pc = 0;
 | 
						|
	u64 t;
 | 
						|
	unsigned long long u = i;
 | 
						|
 | 
						|
	if (sg && b == 10 && i < 0) {
 | 
						|
		neg = 1;
 | 
						|
		u = -i;
 | 
						|
	}
 | 
						|
 | 
						|
	s = print_buf + PRINT_BUF_LEN - 1;
 | 
						|
	*s = '\0';
 | 
						|
 | 
						|
	if (!u) {
 | 
						|
		*--s = '0';
 | 
						|
	} else {
 | 
						|
		while (u) {
 | 
						|
			t = u % b;
 | 
						|
			u = u / b;
 | 
						|
			if (t >= 10)
 | 
						|
				t += letbase - '0' - 10;
 | 
						|
			*--s = t + '0';
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (flags & PAD_ALTERNATE) {
 | 
						|
		if ((b == 16) && (letbase == 'A')) {
 | 
						|
			*--s = 'X';
 | 
						|
		} else if ((b == 16) && (letbase == 'a')) {
 | 
						|
			*--s = 'x';
 | 
						|
		}
 | 
						|
		*--s = '0';
 | 
						|
	}
 | 
						|
 | 
						|
	if (neg) {
 | 
						|
		if (width && (flags & PAD_ZERO)) {
 | 
						|
			printc(out, out_len, '-');
 | 
						|
			++pc;
 | 
						|
			--width;
 | 
						|
		} else {
 | 
						|
			*--s = '-';
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return pc + prints(out, out_len, s, width, flags);
 | 
						|
}
 | 
						|
 | 
						|
static int print(char **out, u32 *out_len, const char *format, va_list args)
 | 
						|
{
 | 
						|
	int width, flags, acnt = 0;
 | 
						|
	int pc = 0;
 | 
						|
	char scr[2];
 | 
						|
	unsigned long long tmp;
 | 
						|
 | 
						|
	for (; *format != 0; ++format) {
 | 
						|
		if (*format == '%') {
 | 
						|
			++format;
 | 
						|
			width = flags = 0;
 | 
						|
			if (*format == '\0')
 | 
						|
				break;
 | 
						|
			if (*format == '%')
 | 
						|
				goto out;
 | 
						|
			/* Get flags */
 | 
						|
			if (*format == '-') {
 | 
						|
				++format;
 | 
						|
				flags = PAD_RIGHT;
 | 
						|
			}
 | 
						|
			if (*format == '#') {
 | 
						|
				++format;
 | 
						|
				flags |= PAD_ALTERNATE;
 | 
						|
			}
 | 
						|
			while (*format == '0') {
 | 
						|
				++format;
 | 
						|
				flags |= PAD_ZERO;
 | 
						|
			}
 | 
						|
			/* Get width */
 | 
						|
			for (; *format >= '0' && *format <= '9'; ++format) {
 | 
						|
				width *= 10;
 | 
						|
				width += *format - '0';
 | 
						|
			}
 | 
						|
			if (*format == 's') {
 | 
						|
				char *s = va_arg(args, char *);
 | 
						|
				acnt += sizeof(char *);
 | 
						|
				pc += prints(out, out_len,
 | 
						|
					     s ? s : "(null)", width, flags);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if ((*format == 'd') || (*format == 'i')) {
 | 
						|
				pc += printi(out, out_len,
 | 
						|
					va_arg(args, int),
 | 
						|
					10, 1, width, flags, '0');
 | 
						|
				acnt += sizeof(int);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (*format == 'x') {
 | 
						|
				pc += printi(out, out_len,
 | 
						|
					va_arg(args, unsigned int),
 | 
						|
					16, 0, width, flags, 'a');
 | 
						|
				acnt += sizeof(unsigned int);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (*format == 'X') {
 | 
						|
				pc += printi(out, out_len,
 | 
						|
					va_arg(args, unsigned int),
 | 
						|
					16, 0, width, flags, 'A');
 | 
						|
				acnt += sizeof(unsigned int);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (*format == 'u') {
 | 
						|
				pc += printi(out, out_len,
 | 
						|
					va_arg(args, unsigned int),
 | 
						|
					10, 0, width, flags, 'a');
 | 
						|
				acnt += sizeof(unsigned int);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (*format == 'p') {
 | 
						|
				pc += printi(out, out_len,
 | 
						|
					va_arg(args, unsigned long),
 | 
						|
					16, 0, width, flags, 'a');
 | 
						|
				acnt += sizeof(unsigned long);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (*format == 'P') {
 | 
						|
				pc += printi(out, out_len,
 | 
						|
					va_arg(args, unsigned long),
 | 
						|
					16, 0, width, flags, 'A');
 | 
						|
				acnt += sizeof(unsigned long);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (*format == 'l' && *(format + 1) == 'l') {
 | 
						|
				while (acnt & (sizeof(unsigned long long)-1)) {
 | 
						|
					va_arg(args, int);
 | 
						|
					acnt += sizeof(int);
 | 
						|
				}
 | 
						|
				if (sizeof(unsigned long long) ==
 | 
						|
						sizeof(unsigned long)) {
 | 
						|
					tmp = va_arg(args, unsigned long long);
 | 
						|
					acnt += sizeof(unsigned long long);
 | 
						|
				} else {
 | 
						|
					((unsigned long *)&tmp)[0] =
 | 
						|
						va_arg(args, unsigned long);
 | 
						|
					((unsigned long *)&tmp)[1] =
 | 
						|
						va_arg(args, unsigned long);
 | 
						|
					acnt += 2*sizeof(unsigned long);
 | 
						|
				}
 | 
						|
				if (*(format + 2) == 'u') {
 | 
						|
					format += 2;
 | 
						|
					pc += printi(out, out_len, tmp,
 | 
						|
						10, 0, width, flags, 'a');
 | 
						|
				} else if (*(format + 2) == 'x') {
 | 
						|
					format += 2;
 | 
						|
					pc += printi(out, out_len, tmp,
 | 
						|
						16, 0, width, flags, 'a');
 | 
						|
				} else if (*(format + 2) == 'X') {
 | 
						|
					format += 2;
 | 
						|
					pc += printi(out, out_len, tmp,
 | 
						|
						16, 0, width, flags, 'A');
 | 
						|
				} else {
 | 
						|
					format += 1;
 | 
						|
					pc += printi(out, out_len, tmp,
 | 
						|
						10, 1, width, flags, '0');
 | 
						|
				}
 | 
						|
				continue;
 | 
						|
			} else if (*format == 'l') {
 | 
						|
				if (*(format + 1) == 'u') {
 | 
						|
					format += 1;
 | 
						|
					pc += printi(out, out_len,
 | 
						|
						va_arg(args, unsigned long),
 | 
						|
						10, 0, width, flags, 'a');
 | 
						|
				} else if (*(format + 1) == 'x') {
 | 
						|
					format += 1;
 | 
						|
					pc += printi(out, out_len,
 | 
						|
						va_arg(args, unsigned long),
 | 
						|
						16, 0, width, flags, 'a');
 | 
						|
					acnt += sizeof(unsigned long);
 | 
						|
				} else if (*(format + 1) == 'X') {
 | 
						|
					format += 1;
 | 
						|
					pc += printi(out, out_len,
 | 
						|
						va_arg(args, unsigned long),
 | 
						|
						16, 0, width, flags, 'A');
 | 
						|
					acnt += sizeof(unsigned long);
 | 
						|
				} else {
 | 
						|
					pc += printi(out, out_len,
 | 
						|
						va_arg(args, long),
 | 
						|
						10, 1, width, flags, '0');
 | 
						|
					acnt += sizeof(long);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (*format == 'c') {
 | 
						|
				/* char are converted to int then pushed on the stack */
 | 
						|
				scr[0] = va_arg(args, int);
 | 
						|
				scr[1] = '\0';
 | 
						|
				pc += prints(out, out_len, scr, width, flags);
 | 
						|
				acnt += sizeof(int);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
out:
 | 
						|
			printc(out, out_len, *format);
 | 
						|
			++pc;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (out)
 | 
						|
		**out = '\0';
 | 
						|
	return pc;
 | 
						|
}
 | 
						|
 | 
						|
int sbi_sprintf(char *out, const char *format, ...)
 | 
						|
{
 | 
						|
	va_list args;
 | 
						|
	int retval;
 | 
						|
	va_start(args, format);
 | 
						|
	retval = print(&out, NULL, format, args);
 | 
						|
	va_end(args);
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
int sbi_snprintf(char *out, u32 out_sz, const char *format, ...)
 | 
						|
{
 | 
						|
	va_list args;
 | 
						|
	int retval;
 | 
						|
	va_start(args, format);
 | 
						|
	retval = print(&out, &out_sz, format, args);
 | 
						|
	va_end(args);
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
int sbi_printf(const char *format, ...)
 | 
						|
{
 | 
						|
	va_list args;
 | 
						|
	int retval;
 | 
						|
 | 
						|
	spin_lock(&console_out_lock);
 | 
						|
	va_start(args, format);
 | 
						|
	retval = print(NULL, NULL, format, args);
 | 
						|
	va_end(args);
 | 
						|
	spin_unlock(&console_out_lock);
 | 
						|
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
int sbi_console_init(struct sbi_scratch *scratch)
 | 
						|
{
 | 
						|
	console_plat = sbi_platform_ptr(scratch);
 | 
						|
 | 
						|
	return sbi_platform_console_init(console_plat);
 | 
						|
}
 |