|
@@ -0,0 +1,245 @@
|
|
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 <linux/ctype.h>
|
|
14
|
+#include <linux/compat.h>
|
|
15
|
+#include <linux/log2.h>
|
|
16
|
+#include <asm/unaligned.h>
|
|
17
|
+
|
|
18
|
+const char hex_asc[] = "0123456789abcdef";
|
|
19
|
+const char hex_asc_upper[] = "0123456789ABCDEF";
|
|
20
|
+
|
|
21
|
+#ifdef CONFIG_HEXDUMP
|
|
22
|
+/**
|
|
23
|
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
|
|
24
|
+ * @buf: data blob to dump
|
|
25
|
+ * @len: number of bytes in the @buf
|
|
26
|
+ * @rowsize: number of bytes to print per line; must be 16 or 32
|
|
27
|
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
|
|
28
|
+ * @linebuf: where to put the converted data
|
|
29
|
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
|
|
30
|
+ * @ascii: include ASCII after the hex output
|
|
31
|
+ *
|
|
32
|
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
|
|
33
|
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
|
|
34
|
+ *
|
|
35
|
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
|
|
36
|
+ * to a hex + ASCII dump at the supplied memory location.
|
|
37
|
+ * The converted output is always NUL-terminated.
|
|
38
|
+ *
|
|
39
|
+ * E.g.:
|
|
40
|
+ * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
|
|
41
|
+ * linebuf, sizeof(linebuf), true);
|
|
42
|
+ *
|
|
43
|
+ * example output buffer:
|
|
44
|
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
|
|
45
|
+ *
|
|
46
|
+ * Return:
|
|
47
|
+ * The amount of bytes placed in the buffer without terminating NUL. If the
|
|
48
|
+ * output was truncated, then the return value is the number of bytes
|
|
49
|
+ * (excluding the terminating NUL) which would have been written to the final
|
|
50
|
+ * string if enough space had been available.
|
|
51
|
+ */
|
|
52
|
+int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
|
|
53
|
+ char *linebuf, size_t linebuflen, bool ascii)
|
|
54
|
+{
|
|
55
|
+ const u8 *ptr = buf;
|
|
56
|
+ int ngroups;
|
|
57
|
+ u8 ch;
|
|
58
|
+ int j, lx = 0;
|
|
59
|
+ int ascii_column;
|
|
60
|
+ int ret;
|
|
61
|
+
|
|
62
|
+ if (rowsize != 16 && rowsize != 32)
|
|
63
|
+ rowsize = 16;
|
|
64
|
+
|
|
65
|
+ if (len > rowsize) /* limit to one line at a time */
|
|
66
|
+ len = rowsize;
|
|
67
|
+ if (!is_power_of_2(groupsize) || groupsize > 8)
|
|
68
|
+ groupsize = 1;
|
|
69
|
+ if ((len % groupsize) != 0) /* no mixed size output */
|
|
70
|
+ groupsize = 1;
|
|
71
|
+
|
|
72
|
+ ngroups = len / groupsize;
|
|
73
|
+ ascii_column = rowsize * 2 + rowsize / groupsize + 1;
|
|
74
|
+
|
|
75
|
+ if (!linebuflen)
|
|
76
|
+ goto overflow1;
|
|
77
|
+
|
|
78
|
+ if (!len)
|
|
79
|
+ goto nil;
|
|
80
|
+
|
|
81
|
+ if (groupsize == 8) {
|
|
82
|
+ const u64 *ptr8 = buf;
|
|
83
|
+
|
|
84
|
+ for (j = 0; j < ngroups; j++) {
|
|
85
|
+ ret = snprintf(linebuf + lx, linebuflen - lx,
|
|
86
|
+ "%s%16.16llx", j ? " " : "",
|
|
87
|
+ get_unaligned(ptr8 + j));
|
|
88
|
+ if (ret >= linebuflen - lx)
|
|
89
|
+ goto overflow1;
|
|
90
|
+ lx += ret;
|
|
91
|
+ }
|
|
92
|
+ } else if (groupsize == 4) {
|
|
93
|
+ const u32 *ptr4 = buf;
|
|
94
|
+
|
|
95
|
+ for (j = 0; j < ngroups; j++) {
|
|
96
|
+ ret = snprintf(linebuf + lx, linebuflen - lx,
|
|
97
|
+ "%s%8.8x", j ? " " : "",
|
|
98
|
+ get_unaligned(ptr4 + j));
|
|
99
|
+ if (ret >= linebuflen - lx)
|
|
100
|
+ goto overflow1;
|
|
101
|
+ lx += ret;
|
|
102
|
+ }
|
|
103
|
+ } else if (groupsize == 2) {
|
|
104
|
+ const u16 *ptr2 = buf;
|
|
105
|
+
|
|
106
|
+ for (j = 0; j < ngroups; j++) {
|
|
107
|
+ ret = snprintf(linebuf + lx, linebuflen - lx,
|
|
108
|
+ "%s%4.4x", j ? " " : "",
|
|
109
|
+ get_unaligned(ptr2 + j));
|
|
110
|
+ if (ret >= linebuflen - lx)
|
|
111
|
+ goto overflow1;
|
|
112
|
+ lx += ret;
|
|
113
|
+ }
|
|
114
|
+ } else {
|
|
115
|
+ for (j = 0; j < len; j++) {
|
|
116
|
+ if (linebuflen < lx + 2)
|
|
117
|
+ goto overflow2;
|
|
118
|
+ ch = ptr[j];
|
|
119
|
+ linebuf[lx++] = hex_asc_hi(ch);
|
|
120
|
+ if (linebuflen < lx + 2)
|
|
121
|
+ goto overflow2;
|
|
122
|
+ linebuf[lx++] = hex_asc_lo(ch);
|
|
123
|
+ if (linebuflen < lx + 2)
|
|
124
|
+ goto overflow2;
|
|
125
|
+ linebuf[lx++] = ' ';
|
|
126
|
+ }
|
|
127
|
+ if (j)
|
|
128
|
+ lx--;
|
|
129
|
+ }
|
|
130
|
+ if (!ascii)
|
|
131
|
+ goto nil;
|
|
132
|
+
|
|
133
|
+ while (lx < ascii_column) {
|
|
134
|
+ if (linebuflen < lx + 2)
|
|
135
|
+ goto overflow2;
|
|
136
|
+ linebuf[lx++] = ' ';
|
|
137
|
+ }
|
|
138
|
+ for (j = 0; j < len; j++) {
|
|
139
|
+ if (linebuflen < lx + 2)
|
|
140
|
+ goto overflow2;
|
|
141
|
+ ch = ptr[j];
|
|
142
|
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
|
|
143
|
+ }
|
|
144
|
+nil:
|
|
145
|
+ linebuf[lx] = '\0';
|
|
146
|
+ return lx;
|
|
147
|
+overflow2:
|
|
148
|
+ linebuf[lx++] = '\0';
|
|
149
|
+overflow1:
|
|
150
|
+ return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
|
|
151
|
+}
|
|
152
|
+
|
|
153
|
+/**
|
|
154
|
+ * print_hex_dump - print a text hex dump to syslog for a binary blob of data
|
|
155
|
+ * @prefix_str: string to prefix each line with;
|
|
156
|
+ * caller supplies trailing spaces for alignment if desired
|
|
157
|
+ * @prefix_type: controls whether prefix of an offset, address, or none
|
|
158
|
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
|
|
159
|
+ * @rowsize: number of bytes to print per line; must be 16 or 32
|
|
160
|
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
|
|
161
|
+ * @buf: data blob to dump
|
|
162
|
+ * @len: number of bytes in the @buf
|
|
163
|
+ * @ascii: include ASCII after the hex output
|
|
164
|
+ *
|
|
165
|
+ * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
|
|
166
|
+ * to the stdio, with an optional leading prefix.
|
|
167
|
+ *
|
|
168
|
+ * print_hex_dump() works on one "line" of output at a time, i.e.,
|
|
169
|
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
|
|
170
|
+ * print_hex_dump() iterates over the entire input @buf, breaking it into
|
|
171
|
+ * "line size" chunks to format and print.
|
|
172
|
+ *
|
|
173
|
+ * E.g.:
|
|
174
|
+ * print_hex_dump("raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, frame->data,
|
|
175
|
+ * frame->len, true);
|
|
176
|
+ *
|
|
177
|
+ * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
|
|
178
|
+ * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
|
|
179
|
+ * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
|
|
180
|
+ * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.
|
|
181
|
+ */
|
|
182
|
+void print_hex_dump(const char *prefix_str, int prefix_type, int rowsize,
|
|
183
|
+ int groupsize, const void *buf, size_t len, bool ascii)
|
|
184
|
+{
|
|
185
|
+ const u8 *ptr = buf;
|
|
186
|
+ int i, linelen, remaining = len;
|
|
187
|
+ char linebuf[32 * 3 + 2 + 32 + 1];
|
|
188
|
+
|
|
189
|
+ if (rowsize != 16 && rowsize != 32)
|
|
190
|
+ rowsize = 16;
|
|
191
|
+
|
|
192
|
+ for (i = 0; i < len; i += rowsize) {
|
|
193
|
+ linelen = min(remaining, rowsize);
|
|
194
|
+ remaining -= rowsize;
|
|
195
|
+
|
|
196
|
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
|
|
197
|
+ linebuf, sizeof(linebuf), ascii);
|
|
198
|
+
|
|
199
|
+ switch (prefix_type) {
|
|
200
|
+ case DUMP_PREFIX_ADDRESS:
|
|
201
|
+ printf("%s%p: %s\n", prefix_str, ptr + i, linebuf);
|
|
202
|
+ break;
|
|
203
|
+ case DUMP_PREFIX_OFFSET:
|
|
204
|
+ printf("%s%.8x: %s\n", prefix_str, i, linebuf);
|
|
205
|
+ break;
|
|
206
|
+ default:
|
|
207
|
+ printf("%s%s\n", prefix_str, linebuf);
|
|
208
|
+ break;
|
|
209
|
+ }
|
|
210
|
+ }
|
|
211
|
+}
|
|
212
|
+
|
|
213
|
+/**
|
|
214
|
+ * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params
|
|
215
|
+ * @prefix_str: string to prefix each line with;
|
|
216
|
+ * caller supplies trailing spaces for alignment if desired
|
|
217
|
+ * @prefix_type: controls whether prefix of an offset, address, or none
|
|
218
|
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
|
|
219
|
+ * @buf: data blob to dump
|
|
220
|
+ * @len: number of bytes in the @buf
|
|
221
|
+ *
|
|
222
|
+ * Calls print_hex_dump(), rowsize of 16, groupsize of 1,
|
|
223
|
+ * and ASCII output included.
|
|
224
|
+ */
|
|
225
|
+void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
|
|
226
|
+ const void *buf, size_t len)
|
|
227
|
+{
|
|
228
|
+ print_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true);
|
|
229
|
+}
|
|
230
|
+#else
|
|
231
|
+/*
|
|
232
|
+ * Some code in U-Boot copy-pasted from Linux kernel uses both
|
|
233
|
+ * functions below so to keep stuff compilable we keep these stubs here.
|
|
234
|
+ */
|
|
235
|
+void print_hex_dump(const char *prefix_str, int prefix_type,
|
|
236
|
+ int rowsize, int groupsize, const void *buf,
|
|
237
|
+ size_t len, bool ascii)
|
|
238
|
+{
|
|
239
|
+}
|
|
240
|
+
|
|
241
|
+void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
|
|
242
|
+ const void *buf, size_t len)
|
|
243
|
+{
|
|
244
|
+}
|
|
245
|
+#endif /* CONFIG_HEXDUMP */
|