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