Merge branch '2021-08-02-numeric-input-cleanups'
[platform/kernel/u-boot.git] / lib / hexdump.c
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)
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
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
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  */
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
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 */