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 <hexdump.h> 12#include <mapmem.h> 13#include <vsprintf.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 21const char hex_asc[] = "0123456789abcdef"; 22const char hex_asc_upper[] = "0123456789ABCDEF"; 23 24#if CONFIG_IS_ENABLED(HEXDUMP) 25int 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 } 119nil: 120 linebuf[lx] = '\0'; 121 return lx; 122overflow2: 123 linebuf[lx++] = '\0'; 124overflow1: 125 return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1; 126} 127 128int 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 167void 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 */ 177int 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 183void 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