1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * lib/hexdump.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation. See README and COPYING for
8  * more details.
9  */
10 
11 #include <common.h>
12 #include <hexdump.h>
13 #include <mapmem.h>
14 #include <linux/ctype.h>
15 #include <linux/compat.h>
16 #include <linux/log2.h>
17 #include <asm/unaligned.h>
18 
19 #define MAX_LINE_LENGTH_BYTES	64
20 
21 const char hex_asc[] = "0123456789abcdef";
22 const char hex_asc_upper[] = "0123456789ABCDEF";
23 
24 #if CONFIG_IS_ENABLED(HEXDUMP)
hex_dump_to_buffer(const void * buf,size_t len,int rowsize,int groupsize,char * linebuf,size_t linebuflen,bool ascii)25 int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
26 		       char *linebuf, size_t linebuflen, bool ascii)
27 {
28 	const u8 *ptr = buf;
29 	int ngroups;
30 	u8 ch;
31 	int j, lx = 0;
32 	int ascii_column;
33 	int ret;
34 
35 	if (!rowsize)
36 		rowsize = 16;
37 	else
38 		rowsize = min(rowsize, MAX_LINE_LENGTH_BYTES);
39 
40 	if (len > rowsize)		/* limit to one line at a time */
41 		len = rowsize;
42 	if (!is_power_of_2(groupsize) || groupsize > 8)
43 		groupsize = 1;
44 	if ((len % groupsize) != 0)	/* no mixed size output */
45 		groupsize = 1;
46 
47 	ngroups = len / groupsize;
48 	ascii_column = rowsize * 2 + rowsize / groupsize + 1;
49 
50 	if (!linebuflen)
51 		goto overflow1;
52 
53 	if (!len)
54 		goto nil;
55 
56 	if (groupsize == 8) {
57 		const u64 *ptr8 = buf;
58 
59 		for (j = 0; j < ngroups; j++) {
60 			ret = snprintf(linebuf + lx, linebuflen - lx,
61 				       "%s%16.16llx", j ? " " : "",
62 				       get_unaligned(ptr8 + j));
63 			if (ret >= linebuflen - lx)
64 				goto overflow1;
65 			lx += ret;
66 		}
67 	} else if (groupsize == 4) {
68 		const u32 *ptr4 = buf;
69 
70 		for (j = 0; j < ngroups; j++) {
71 			ret = snprintf(linebuf + lx, linebuflen - lx,
72 				       "%s%8.8x", j ? " " : "",
73 				       get_unaligned(ptr4 + j));
74 			if (ret >= linebuflen - lx)
75 				goto overflow1;
76 			lx += ret;
77 		}
78 	} else if (groupsize == 2) {
79 		const u16 *ptr2 = buf;
80 
81 		for (j = 0; j < ngroups; j++) {
82 			ret = snprintf(linebuf + lx, linebuflen - lx,
83 				       "%s%4.4x", j ? " " : "",
84 				       get_unaligned(ptr2 + j));
85 			if (ret >= linebuflen - lx)
86 				goto overflow1;
87 			lx += ret;
88 		}
89 	} else {
90 		for (j = 0; j < len; j++) {
91 			if (linebuflen < lx + 2)
92 				goto overflow2;
93 			ch = ptr[j];
94 			linebuf[lx++] = hex_asc_hi(ch);
95 			if (linebuflen < lx + 2)
96 				goto overflow2;
97 			linebuf[lx++] = hex_asc_lo(ch);
98 			if (linebuflen < lx + 2)
99 				goto overflow2;
100 			linebuf[lx++] = ' ';
101 		}
102 		if (j)
103 			lx--;
104 	}
105 	if (!ascii)
106 		goto nil;
107 
108 	while (lx < ascii_column) {
109 		if (linebuflen < lx + 2)
110 			goto overflow2;
111 		linebuf[lx++] = ' ';
112 	}
113 	for (j = 0; j < len; j++) {
114 		if (linebuflen < lx + 2)
115 			goto overflow2;
116 		ch = ptr[j];
117 		linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
118 	}
119 nil:
120 	linebuf[lx] = '\0';
121 	return lx;
122 overflow2:
123 	linebuf[lx++] = '\0';
124 overflow1:
125 	return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
126 }
127 
print_hex_dump(const char * prefix_str,int prefix_type,int rowsize,int groupsize,const void * buf,size_t len,bool ascii)128 int print_hex_dump(const char *prefix_str, int prefix_type, int rowsize,
129 		   int groupsize, const void *buf, size_t len, bool ascii)
130 {
131 	const u8 *ptr = buf;
132 	int i, linelen, remaining = len;
133 	char linebuf[MAX_LINE_LENGTH_BYTES * 3 + 2 + MAX_LINE_LENGTH_BYTES + 1];
134 
135 	if (!rowsize)
136 		rowsize = 16;
137 	else
138 		rowsize = min(rowsize, MAX_LINE_LENGTH_BYTES);
139 
140 	for (i = 0; i < len; i += rowsize) {
141 		linelen = min(remaining, rowsize);
142 		remaining -= rowsize;
143 
144 		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
145 				   linebuf, sizeof(linebuf), ascii);
146 
147 		switch (prefix_type) {
148 		case DUMP_PREFIX_ADDRESS:
149 			printf("%s%0*lx: %s\n", prefix_str,
150 			       IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8,
151 			       (ulong)map_to_sysmem(ptr) + i, linebuf);
152 			break;
153 		case DUMP_PREFIX_OFFSET:
154 			printf("%s%.8x: %s\n", prefix_str, i, linebuf);
155 			break;
156 		default:
157 			printf("%s%s\n", prefix_str, linebuf);
158 			break;
159 		}
160 		if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc())
161 			return -EINTR;
162 	}
163 
164 	return 0;
165 }
166 
print_hex_dump_bytes(const char * prefix_str,int prefix_type,const void * buf,size_t len)167 void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
168 			  const void *buf, size_t len)
169 {
170 	print_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true);
171 }
172 #else
173 /*
174  * Some code in U-Boot copy-pasted from Linux kernel uses both
175  * functions below so to keep stuff compilable we keep these stubs here.
176  */
print_hex_dump(const char * prefix_str,int prefix_type,int rowsize,int groupsize,const void * buf,size_t len,bool ascii)177 int print_hex_dump(const char *prefix_str, int prefix_type, int rowsize,
178 		   int groupsize, const void *buf, size_t len, bool ascii)
179 {
180 	return -ENOSYS;
181 }
182 
print_hex_dump_bytes(const char * prefix_str,int prefix_type,const void * buf,size_t len)183 void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
184 			  const void *buf, size_t len)
185 {
186 }
187 #endif /* CONFIG_HEXDUMP */
188