hexdump.c 6.51 KB
Newer Older
Randy Dunlap's avatar
Randy Dunlap committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * 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>
#include <linux/module.h>

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);

Randy Dunlap's avatar
Randy Dunlap committed
36 37 38 39
/**
 * 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
40 41
 * @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
42 43
 * @linebuf: where to put the converted data
 * @linebuflen: total size of @linebuf, including space for terminating NUL
Randy Dunlap's avatar
Randy Dunlap committed
44
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
45 46
 *
 * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
Randy Dunlap's avatar
Randy Dunlap committed
47
 * 16 or 32 bytes of input data converted to hex + ASCII output.
Randy Dunlap's avatar
Randy Dunlap committed
48 49 50 51 52 53
 *
 * 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
54
 *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
55
 *			linebuf, sizeof(linebuf), true);
Randy Dunlap's avatar
Randy Dunlap committed
56 57
 *
 * example output buffer:
Randy Dunlap's avatar
Randy Dunlap committed
58
 * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
Randy Dunlap's avatar
Randy Dunlap committed
59
 */
Randy Dunlap's avatar
Randy Dunlap committed
60 61 62
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
63 64 65 66
{
	const u8 *ptr = buf;
	u8 ch;
	int j, lx = 0;
Randy Dunlap's avatar
Randy Dunlap committed
67
	int ascii_column;
Randy Dunlap's avatar
Randy Dunlap committed
68

Randy Dunlap's avatar
Randy Dunlap committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	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,
86 87
					"%s%16.16llx", j ? " " : "",
					(unsigned long long)*(ptr8 + j));
Randy Dunlap's avatar
Randy Dunlap committed
88 89 90 91 92 93 94 95 96 97
		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,
98
					"%s%8.8x", j ? " " : "", *(ptr4 + j));
Randy Dunlap's avatar
Randy Dunlap committed
99 100 101 102 103 104 105 106 107 108
		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,
109
					"%s%4.4x", j ? " " : "", *(ptr2 + j));
Randy Dunlap's avatar
Randy Dunlap committed
110 111 112 113 114
		ascii_column = 5 * ngroups + 2;
		break;
	}

	default:
Li Zefan's avatar
Li Zefan committed
115
		for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) {
Randy Dunlap's avatar
Randy Dunlap committed
116
			ch = ptr[j];
117 118
			linebuf[lx++] = hex_asc_hi(ch);
			linebuf[lx++] = hex_asc_lo(ch);
Randy Dunlap's avatar
Randy Dunlap committed
119
			linebuf[lx++] = ' ';
Randy Dunlap's avatar
Randy Dunlap committed
120
		}
Li Zefan's avatar
Li Zefan committed
121 122 123
		if (j)
			lx--;

Randy Dunlap's avatar
Randy Dunlap committed
124 125
		ascii_column = 3 * rowsize + 2;
		break;
Randy Dunlap's avatar
Randy Dunlap committed
126
	}
Randy Dunlap's avatar
Randy Dunlap committed
127 128 129 130
	if (!ascii)
		goto nil;

	while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
Randy Dunlap's avatar
Randy Dunlap committed
131
		linebuf[lx++] = ' ';
132 133 134 135
	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
136
nil:
Randy Dunlap's avatar
Randy Dunlap committed
137 138 139 140 141 142 143
	linebuf[lx++] = '\0';
}
EXPORT_SYMBOL(hex_dump_to_buffer);

/**
 * 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
144 145
 * @prefix_str: string to prefix each line with;
 *  caller supplies trailing spaces for alignment if desired
Randy Dunlap's avatar
Randy Dunlap committed
146 147
 * @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
148 149
 * @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
150 151
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
152
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
153 154 155 156 157
 *
 * 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
158 159 160 161 162
 * 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
163
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
164
 *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
165
 *		    16, 1, frame->data, frame->len, true);
Randy Dunlap's avatar
Randy Dunlap committed
166
 *
Randy Dunlap's avatar
Randy Dunlap committed
167 168 169 170
 * 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
171
 */
Randy Dunlap's avatar
Randy Dunlap committed
172
void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
173 174
		    int rowsize, int groupsize,
		    const void *buf, size_t len, bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
175
{
Artem Bityutskiy's avatar
Artem Bityutskiy committed
176
	const u8 *ptr = buf;
Randy Dunlap's avatar
Randy Dunlap committed
177
	int i, linelen, remaining = len;
178
	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
Randy Dunlap's avatar
Randy Dunlap committed
179

Randy Dunlap's avatar
Randy Dunlap committed
180 181 182 183 184 185
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

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

Randy Dunlap's avatar
Randy Dunlap committed
187
		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
188
				   linebuf, sizeof(linebuf), ascii);
Randy Dunlap's avatar
Randy Dunlap committed
189 190 191

		switch (prefix_type) {
		case DUMP_PREFIX_ADDRESS:
192 193
			printk("%s%s%p: %s\n",
			       level, prefix_str, ptr + i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
194 195
			break;
		case DUMP_PREFIX_OFFSET:
Randy Dunlap's avatar
Randy Dunlap committed
196
			printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
197 198
			break;
		default:
Randy Dunlap's avatar
Randy Dunlap committed
199
			printk("%s%s%s\n", level, prefix_str, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
200 201 202 203 204
			break;
		}
	}
}
EXPORT_SYMBOL(print_hex_dump);
Randy Dunlap's avatar
Randy Dunlap committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218

/**
 * 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,
219
			  const void *buf, size_t len)
Randy Dunlap's avatar
Randy Dunlap committed
220 221
{
	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
222
		       buf, len, true);
Randy Dunlap's avatar
Randy Dunlap committed
223 224
}
EXPORT_SYMBOL(print_hex_dump_bytes);