hexdump.c 6.99 KB
Newer Older
Randy Dunlap's avatar
Randy Dunlap committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * lib/hexdump.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. See README and COPYING for
 * more details.
 */

#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
13
#include <linux/export.h>
Randy Dunlap's avatar
Randy Dunlap committed
14

15 16 17
const char hex_asc[] = "0123456789abcdef";
EXPORT_SYMBOL(hex_asc);

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/**
 * hex_to_bin - convert a hex digit to its real value
 * @ch: ascii character represents hex digit
 *
 * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
 * input.
 */
int hex_to_bin(char ch)
{
	if ((ch >= '0') && (ch <= '9'))
		return ch - '0';
	ch = tolower(ch);
	if ((ch >= 'a') && (ch <= 'f'))
		return ch - 'a' + 10;
	return -1;
}
EXPORT_SYMBOL(hex_to_bin);

36 37 38 39 40
/**
 * hex2bin - convert an ascii hexadecimal string to its binary representation
 * @dst: binary result
 * @src: ascii hexadecimal string
 * @count: result length
41 42
 *
 * Return 0 on success, -1 in case of bad input.
43
 */
44
int hex2bin(u8 *dst, const char *src, size_t count)
45 46
{
	while (count--) {
47 48 49 50 51 52 53
		int hi = hex_to_bin(*src++);
		int lo = hex_to_bin(*src++);

		if ((hi < 0) || (lo < 0))
			return -1;

		*dst++ = (hi << 4) | lo;
54
	}
55
	return 0;
56 57 58
}
EXPORT_SYMBOL(hex2bin);

Randy Dunlap's avatar
Randy Dunlap committed
59 60 61 62
/**
 * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
63 64
 * @rowsize: number of bytes to print per line; must be 16 or 32
 * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
Randy Dunlap's avatar
Randy Dunlap committed
65 66
 * @linebuf: where to put the converted data
 * @linebuflen: total size of @linebuf, including space for terminating NUL
Randy Dunlap's avatar
Randy Dunlap committed
67
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
68 69
 *
 * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
Randy Dunlap's avatar
Randy Dunlap committed
70
 * 16 or 32 bytes of input data converted to hex + ASCII output.
Randy Dunlap's avatar
Randy Dunlap committed
71 72 73 74 75 76
 *
 * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
 * to a hex + ASCII dump at the supplied memory location.
 * The converted output is always NUL-terminated.
 *
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
77
 *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
78
 *			linebuf, sizeof(linebuf), true);
Randy Dunlap's avatar
Randy Dunlap committed
79 80
 *
 * example output buffer:
Randy Dunlap's avatar
Randy Dunlap committed
81
 * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
Randy Dunlap's avatar
Randy Dunlap committed
82
 */
Randy Dunlap's avatar
Randy Dunlap committed
83 84 85
void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
			int groupsize, char *linebuf, size_t linebuflen,
			bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
86 87 88 89
{
	const u8 *ptr = buf;
	u8 ch;
	int j, lx = 0;
Randy Dunlap's avatar
Randy Dunlap committed
90
	int ascii_column;
Randy Dunlap's avatar
Randy Dunlap committed
91

Randy Dunlap's avatar
Randy Dunlap committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	if (!len)
		goto nil;
	if (len > rowsize)		/* limit to one line at a time */
		len = rowsize;
	if ((len % groupsize) != 0)	/* no mixed size output */
		groupsize = 1;

	switch (groupsize) {
	case 8: {
		const u64 *ptr8 = buf;
		int ngroups = len / groupsize;

		for (j = 0; j < ngroups; j++)
			lx += scnprintf(linebuf + lx, linebuflen - lx,
109 110
					"%s%16.16llx", j ? " " : "",
					(unsigned long long)*(ptr8 + j));
Randy Dunlap's avatar
Randy Dunlap committed
111 112 113 114 115 116 117 118 119 120
		ascii_column = 17 * ngroups + 2;
		break;
	}

	case 4: {
		const u32 *ptr4 = buf;
		int ngroups = len / groupsize;

		for (j = 0; j < ngroups; j++)
			lx += scnprintf(linebuf + lx, linebuflen - lx,
121
					"%s%8.8x", j ? " " : "", *(ptr4 + j));
Randy Dunlap's avatar
Randy Dunlap committed
122 123 124 125 126 127 128 129 130 131
		ascii_column = 9 * ngroups + 2;
		break;
	}

	case 2: {
		const u16 *ptr2 = buf;
		int ngroups = len / groupsize;

		for (j = 0; j < ngroups; j++)
			lx += scnprintf(linebuf + lx, linebuflen - lx,
132
					"%s%4.4x", j ? " " : "", *(ptr2 + j));
Randy Dunlap's avatar
Randy Dunlap committed
133 134 135 136 137
		ascii_column = 5 * ngroups + 2;
		break;
	}

	default:
Li Zefan's avatar
Li Zefan committed
138
		for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) {
Randy Dunlap's avatar
Randy Dunlap committed
139
			ch = ptr[j];
140 141
			linebuf[lx++] = hex_asc_hi(ch);
			linebuf[lx++] = hex_asc_lo(ch);
Randy Dunlap's avatar
Randy Dunlap committed
142
			linebuf[lx++] = ' ';
Randy Dunlap's avatar
Randy Dunlap committed
143
		}
Li Zefan's avatar
Li Zefan committed
144 145 146
		if (j)
			lx--;

Randy Dunlap's avatar
Randy Dunlap committed
147 148
		ascii_column = 3 * rowsize + 2;
		break;
Randy Dunlap's avatar
Randy Dunlap committed
149
	}
Randy Dunlap's avatar
Randy Dunlap committed
150 151 152 153
	if (!ascii)
		goto nil;

	while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
Randy Dunlap's avatar
Randy Dunlap committed
154
		linebuf[lx++] = ' ';
155 156 157 158
	for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) {
		ch = ptr[j];
		linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
	}
Randy Dunlap's avatar
Randy Dunlap committed
159
nil:
Randy Dunlap's avatar
Randy Dunlap committed
160 161 162 163
	linebuf[lx++] = '\0';
}
EXPORT_SYMBOL(hex_dump_to_buffer);

164
#ifdef CONFIG_PRINTK
Randy Dunlap's avatar
Randy Dunlap committed
165 166 167
/**
 * print_hex_dump - print a text hex dump to syslog for a binary blob of data
 * @level: kernel log level (e.g. KERN_DEBUG)
Randy Dunlap's avatar
Randy Dunlap committed
168 169
 * @prefix_str: string to prefix each line with;
 *  caller supplies trailing spaces for alignment if desired
Randy Dunlap's avatar
Randy Dunlap committed
170 171
 * @prefix_type: controls whether prefix of an offset, address, or none
 *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
Randy Dunlap's avatar
Randy Dunlap committed
172 173
 * @rowsize: number of bytes to print per line; must be 16 or 32
 * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
Randy Dunlap's avatar
Randy Dunlap committed
174 175
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
176
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
177 178 179 180 181
 *
 * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
 * to the kernel log at the specified kernel log level, with an optional
 * leading prefix.
 *
Randy Dunlap's avatar
Randy Dunlap committed
182 183 184 185 186
 * print_hex_dump() works on one "line" of output at a time, i.e.,
 * 16 or 32 bytes of input data converted to hex + ASCII output.
 * print_hex_dump() iterates over the entire input @buf, breaking it into
 * "line size" chunks to format and print.
 *
Randy Dunlap's avatar
Randy Dunlap committed
187
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
188
 *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
189
 *		    16, 1, frame->data, frame->len, true);
Randy Dunlap's avatar
Randy Dunlap committed
190
 *
Randy Dunlap's avatar
Randy Dunlap committed
191 192 193 194
 * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
 * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
 * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
 * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.
Randy Dunlap's avatar
Randy Dunlap committed
195
 */
Randy Dunlap's avatar
Randy Dunlap committed
196
void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
197 198
		    int rowsize, int groupsize,
		    const void *buf, size_t len, bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
199
{
Artem Bityutskiy's avatar
Artem Bityutskiy committed
200
	const u8 *ptr = buf;
Randy Dunlap's avatar
Randy Dunlap committed
201
	int i, linelen, remaining = len;
202
	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
Randy Dunlap's avatar
Randy Dunlap committed
203

Randy Dunlap's avatar
Randy Dunlap committed
204 205 206 207 208 209
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	for (i = 0; i < len; i += rowsize) {
		linelen = min(remaining, rowsize);
		remaining -= rowsize;
210

Randy Dunlap's avatar
Randy Dunlap committed
211
		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
212
				   linebuf, sizeof(linebuf), ascii);
Randy Dunlap's avatar
Randy Dunlap committed
213 214 215

		switch (prefix_type) {
		case DUMP_PREFIX_ADDRESS:
216 217
			printk("%s%s%p: %s\n",
			       level, prefix_str, ptr + i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
218 219
			break;
		case DUMP_PREFIX_OFFSET:
Randy Dunlap's avatar
Randy Dunlap committed
220
			printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
221 222
			break;
		default:
Randy Dunlap's avatar
Randy Dunlap committed
223
			printk("%s%s%s\n", level, prefix_str, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
224 225 226 227 228
			break;
		}
	}
}
EXPORT_SYMBOL(print_hex_dump);
Randy Dunlap's avatar
Randy Dunlap committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242

/**
 * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params
 * @prefix_str: string to prefix each line with;
 *  caller supplies trailing spaces for alignment if desired
 * @prefix_type: controls whether prefix of an offset, address, or none
 *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
 *
 * Calls print_hex_dump(), with log level of KERN_DEBUG,
 * rowsize of 16, groupsize of 1, and ASCII output included.
 */
void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
243
			  const void *buf, size_t len)
Randy Dunlap's avatar
Randy Dunlap committed
244 245
{
	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
246
		       buf, len, true);
Randy Dunlap's avatar
Randy Dunlap committed
247 248
}
EXPORT_SYMBOL(print_hex_dump_bytes);
249
#endif