1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2000-2002
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 */
6
7#include <compiler.h>
8#include <console.h>
9#include <display_options.h>
10#include <div64.h>
11#include <version_string.h>
12#include <linux/ctype.h>
13#include <linux/kernel.h>
14#include <asm/io.h>
15#include <vsprintf.h>
16
17char *display_options_get_banner_priv(bool newlines, const char *build_tag,
18				      char *buf, int size)
19{
20	int len;
21
22	len = snprintf(buf, size, "%s%s", newlines ? "\n\n" : "",
23		       version_string);
24	if (build_tag && len < size)
25		len += snprintf(buf + len, size - len, ", Build: %s",
26				build_tag);
27	if (len > size - 3)
28		len = size - 3;
29	if (len < 0)
30		len = 0;
31	snprintf(buf + len, size - len, "\n\n");
32
33	return buf;
34}
35
36#ifndef BUILD_TAG
37#define BUILD_TAG NULL
38#endif
39
40char *display_options_get_banner(bool newlines, char *buf, int size)
41{
42	return display_options_get_banner_priv(newlines, BUILD_TAG, buf, size);
43}
44
45int display_options(void)
46{
47	char buf[DISPLAY_OPTIONS_BANNER_LENGTH];
48
49	display_options_get_banner(true, buf, sizeof(buf));
50	printf("%s", buf);
51
52	return 0;
53}
54
55void print_freq(uint64_t freq, const char *s)
56{
57	unsigned long m = 0;
58	uint32_t f;
59	static const char names[] = {'G', 'M', 'k'};
60	unsigned long d = 1e9;
61	char c = 0;
62	unsigned int i;
63
64	for (i = 0; i < ARRAY_SIZE(names); i++, d /= 1000) {
65		if (freq >= d) {
66			c = names[i];
67			break;
68		}
69	}
70
71	if (!c) {
72		printf("%llu Hz%s", freq, s);
73		return;
74	}
75
76	f = do_div(freq, d);
77
78	/* If there's a remainder, show the first few digits */
79	if (f) {
80		m = f;
81		while (m > 1000)
82			m /= 10;
83		while (m && !(m % 10))
84			m /= 10;
85		if (m >= 100)
86			m = (m / 10) + (m % 100 >= 50);
87	}
88
89	printf("%lu", (unsigned long) freq);
90	if (m)
91		printf(".%ld", m);
92	printf(" %cHz%s", c, s);
93}
94
95void print_size(uint64_t size, const char *s)
96{
97	unsigned long m = 0, n;
98	uint64_t f;
99	static const char names[] = {'E', 'P', 'T', 'G', 'M', 'K'};
100	unsigned long d = 10 * ARRAY_SIZE(names);
101	char c = 0;
102	unsigned int i;
103
104	for (i = 0; i < ARRAY_SIZE(names); i++, d -= 10) {
105		if (size >> d) {
106			c = names[i];
107			break;
108		}
109	}
110
111	if (!c) {
112		/*
113		 * SPL tiny-printf is not capable for printing uint64_t.
114		 * We have just checked that the size is small enought to fit
115		 * unsigned int safely.
116		 */
117		printf("%u Bytes%s", (unsigned int)size, s);
118		return;
119	}
120
121	n = size >> d;
122	f = size & ((1ULL << d) - 1);
123
124	/* If there's a remainder, deal with it */
125	if (f) {
126		m = (10ULL * f + (1ULL << (d - 1))) >> d;
127
128		if (m >= 10) {
129			m -= 10;
130			n += 1;
131
132			if (n == 1024 && i > 0) {
133				n = 1;
134				m = 0;
135				c = names[i - 1];
136			}
137		}
138	}
139
140	printf ("%lu", n);
141	if (m) {
142		printf (".%ld", m);
143	}
144	printf (" %ciB%s", c, s);
145}
146
147#define MAX_LINE_LENGTH_BYTES		64
148#define DEFAULT_LINE_LENGTH_BYTES	16
149
150int hexdump_line(ulong addr, const void *data, uint width, uint count,
151		 uint linelen, char *out, int size)
152{
153	/* linebuf as a union causes proper alignment */
154	union linebuf {
155		uint64_t uq[MAX_LINE_LENGTH_BYTES/sizeof(uint64_t) + 1];
156		uint32_t ui[MAX_LINE_LENGTH_BYTES/sizeof(uint32_t) + 1];
157		uint16_t us[MAX_LINE_LENGTH_BYTES/sizeof(uint16_t) + 1];
158		uint8_t  uc[MAX_LINE_LENGTH_BYTES/sizeof(uint8_t) + 1];
159	} lb;
160	uint thislinelen;
161	int i;
162	ulong x;
163
164	if (linelen * width > MAX_LINE_LENGTH_BYTES)
165		linelen = MAX_LINE_LENGTH_BYTES / width;
166	if (linelen < 1)
167		linelen = DEFAULT_LINE_LENGTH_BYTES / width;
168
169	/*
170	 * Check the size here so that we don't need to use snprintf(). This
171	 * helps to reduce code size
172	 */
173	if (size < HEXDUMP_MAX_BUF_LENGTH(linelen * width))
174		return -ENOSPC;
175
176	thislinelen = linelen;
177	out += sprintf(out, "%08lx:", addr);
178
179	/* check for overflow condition */
180	if (count < thislinelen)
181		thislinelen = count;
182
183	/* Copy from memory into linebuf and print hex values */
184	for (i = 0; i < thislinelen; i++) {
185		if (width == 4)
186			x = lb.ui[i] = *(volatile uint32_t *)data;
187		else if (MEM_SUPPORT_64BIT_DATA && width == 8)
188			x = lb.uq[i] = *(volatile ulong *)data;
189		else if (width == 2)
190			x = lb.us[i] = *(volatile uint16_t *)data;
191		else
192			x = lb.uc[i] = *(volatile uint8_t *)data;
193		if (CONFIG_IS_ENABLED(USE_TINY_PRINTF))
194			out += sprintf(out, " %x", (uint)x);
195		else
196			out += sprintf(out, " %0*lx", width * 2, x);
197		data += width;
198	}
199
200	/* fill line with whitespace for nice ASCII print */
201	for (i = 0; i < (linelen - thislinelen) * (width * 2 + 1); i++)
202		*out++ = ' ';
203
204	/* Print data in ASCII characters */
205	for (i = 0; i < thislinelen * width; i++) {
206		if (!isprint(lb.uc[i]) || lb.uc[i] >= 0x80)
207			lb.uc[i] = '.';
208	}
209	lb.uc[i] = '\0';
210	out += sprintf(out, "  %s", lb.uc);
211
212	return thislinelen;
213}
214
215int print_buffer(ulong addr, const void *data, uint width, uint count,
216		 uint linelen)
217{
218	if (linelen*width > MAX_LINE_LENGTH_BYTES)
219		linelen = MAX_LINE_LENGTH_BYTES / width;
220	if (linelen < 1)
221		linelen = DEFAULT_LINE_LENGTH_BYTES / width;
222
223	while (count) {
224		uint thislinelen;
225		char buf[HEXDUMP_MAX_BUF_LENGTH(width * linelen)];
226
227		thislinelen = hexdump_line(addr, data, width, count, linelen,
228					   buf, sizeof(buf));
229		assert(thislinelen >= 0);
230		puts(buf);
231		putc('\n');
232
233		/* update references */
234		data += thislinelen * width;
235		addr += thislinelen * width;
236		count -= thislinelen;
237
238		if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc())
239			return -EINTR;
240	}
241
242	return 0;
243}
244