| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * lib/hexdump.c | 
 | 3 |  * | 
 | 4 |  * This program is free software; you can redistribute it and/or modify | 
 | 5 |  * it under the terms of the GNU General Public License version 2 as | 
 | 6 |  * published by the Free Software Foundation. See README and COPYING for | 
 | 7 |  * more details. | 
 | 8 |  */ | 
 | 9 |  | 
 | 10 | #include <linux/types.h> | 
 | 11 | #include <linux/ctype.h> | 
 | 12 | #include <linux/kernel.h> | 
 | 13 | #include <linux/module.h> | 
 | 14 |  | 
| Harvey Harrison | 3fc9577 | 2008-05-14 16:05:49 -0700 | [diff] [blame] | 15 | const char hex_asc[] = "0123456789abcdef"; | 
 | 16 | EXPORT_SYMBOL(hex_asc); | 
 | 17 |  | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 18 | /** | 
 | 19 |  * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory | 
 | 20 |  * @buf: data blob to dump | 
 | 21 |  * @len: number of bytes in the @buf | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 22 |  * @rowsize: number of bytes to print per line; must be 16 or 32 | 
 | 23 |  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 24 |  * @linebuf: where to put the converted data | 
 | 25 |  * @linebuflen: total size of @linebuf, including space for terminating NUL | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 26 |  * @ascii: include ASCII after the hex output | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 27 |  * | 
 | 28 |  * hex_dump_to_buffer() works on one "line" of output at a time, i.e., | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 29 |  * 16 or 32 bytes of input data converted to hex + ASCII output. | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 30 |  * | 
 | 31 |  * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data | 
 | 32 |  * to a hex + ASCII dump at the supplied memory location. | 
 | 33 |  * The converted output is always NUL-terminated. | 
 | 34 |  * | 
 | 35 |  * E.g.: | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 36 |  *   hex_dump_to_buffer(frame->data, frame->len, 16, 1, | 
 | 37 |  *			linebuf, sizeof(linebuf), 1); | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 38 |  * | 
 | 39 |  * example output buffer: | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 40 |  * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 41 |  */ | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 42 | void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, | 
 | 43 | 			int groupsize, char *linebuf, size_t linebuflen, | 
 | 44 | 			bool ascii) | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 45 | { | 
 | 46 | 	const u8 *ptr = buf; | 
 | 47 | 	u8 ch; | 
 | 48 | 	int j, lx = 0; | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 49 | 	int ascii_column; | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 50 |  | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 51 | 	if (rowsize != 16 && rowsize != 32) | 
 | 52 | 		rowsize = 16; | 
 | 53 |  | 
 | 54 | 	if (!len) | 
 | 55 | 		goto nil; | 
 | 56 | 	if (len > rowsize)		/* limit to one line at a time */ | 
 | 57 | 		len = rowsize; | 
 | 58 | 	if ((len % groupsize) != 0)	/* no mixed size output */ | 
 | 59 | 		groupsize = 1; | 
 | 60 |  | 
 | 61 | 	switch (groupsize) { | 
 | 62 | 	case 8: { | 
 | 63 | 		const u64 *ptr8 = buf; | 
 | 64 | 		int ngroups = len / groupsize; | 
 | 65 |  | 
 | 66 | 		for (j = 0; j < ngroups; j++) | 
 | 67 | 			lx += scnprintf(linebuf + lx, linebuflen - lx, | 
| Li Zefan | c67ae69 | 2009-06-16 15:33:45 -0700 | [diff] [blame] | 68 | 				"%s%16.16llx", j ? " " : "", | 
 | 69 | 				(unsigned long long)*(ptr8 + j)); | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 70 | 		ascii_column = 17 * ngroups + 2; | 
 | 71 | 		break; | 
 | 72 | 	} | 
 | 73 |  | 
 | 74 | 	case 4: { | 
 | 75 | 		const u32 *ptr4 = buf; | 
 | 76 | 		int ngroups = len / groupsize; | 
 | 77 |  | 
 | 78 | 		for (j = 0; j < ngroups; j++) | 
 | 79 | 			lx += scnprintf(linebuf + lx, linebuflen - lx, | 
| Li Zefan | c67ae69 | 2009-06-16 15:33:45 -0700 | [diff] [blame] | 80 | 				"%s%8.8x", j ? " " : "", *(ptr4 + j)); | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 81 | 		ascii_column = 9 * ngroups + 2; | 
 | 82 | 		break; | 
 | 83 | 	} | 
 | 84 |  | 
 | 85 | 	case 2: { | 
 | 86 | 		const u16 *ptr2 = buf; | 
 | 87 | 		int ngroups = len / groupsize; | 
 | 88 |  | 
 | 89 | 		for (j = 0; j < ngroups; j++) | 
 | 90 | 			lx += scnprintf(linebuf + lx, linebuflen - lx, | 
| Li Zefan | c67ae69 | 2009-06-16 15:33:45 -0700 | [diff] [blame] | 91 | 				"%s%4.4x", j ? " " : "", *(ptr2 + j)); | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 92 | 		ascii_column = 5 * ngroups + 2; | 
 | 93 | 		break; | 
 | 94 | 	} | 
 | 95 |  | 
 | 96 | 	default: | 
| Li Zefan | c67ae69 | 2009-06-16 15:33:45 -0700 | [diff] [blame] | 97 | 		for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 98 | 			ch = ptr[j]; | 
| Harvey Harrison | 3fc9577 | 2008-05-14 16:05:49 -0700 | [diff] [blame] | 99 | 			linebuf[lx++] = hex_asc_hi(ch); | 
 | 100 | 			linebuf[lx++] = hex_asc_lo(ch); | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 101 | 			linebuf[lx++] = ' '; | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 102 | 		} | 
| Li Zefan | c67ae69 | 2009-06-16 15:33:45 -0700 | [diff] [blame] | 103 | 		if (j) | 
 | 104 | 			lx--; | 
 | 105 |  | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 106 | 		ascii_column = 3 * rowsize + 2; | 
 | 107 | 		break; | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 108 | 	} | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 109 | 	if (!ascii) | 
 | 110 | 		goto nil; | 
 | 111 |  | 
 | 112 | 	while (lx < (linebuflen - 1) && lx < (ascii_column - 1)) | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 113 | 		linebuf[lx++] = ' '; | 
| Li Zefan | c67ae69 | 2009-06-16 15:33:45 -0700 | [diff] [blame] | 114 | 	for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) | 
| Randy Dunlap | d0eec99 | 2007-11-28 16:21:46 -0800 | [diff] [blame] | 115 | 		linebuf[lx++] = (isascii(ptr[j]) && isprint(ptr[j])) ? ptr[j] | 
 | 116 | 				: '.'; | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 117 | nil: | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 118 | 	linebuf[lx++] = '\0'; | 
 | 119 | } | 
 | 120 | EXPORT_SYMBOL(hex_dump_to_buffer); | 
 | 121 |  | 
 | 122 | /** | 
 | 123 |  * print_hex_dump - print a text hex dump to syslog for a binary blob of data | 
 | 124 |  * @level: kernel log level (e.g. KERN_DEBUG) | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 125 |  * @prefix_str: string to prefix each line with; | 
 | 126 |  *  caller supplies trailing spaces for alignment if desired | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 127 |  * @prefix_type: controls whether prefix of an offset, address, or none | 
 | 128 |  *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 129 |  * @rowsize: number of bytes to print per line; must be 16 or 32 | 
 | 130 |  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 131 |  * @buf: data blob to dump | 
 | 132 |  * @len: number of bytes in the @buf | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 133 |  * @ascii: include ASCII after the hex output | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 134 |  * | 
 | 135 |  * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump | 
 | 136 |  * to the kernel log at the specified kernel log level, with an optional | 
 | 137 |  * leading prefix. | 
 | 138 |  * | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 139 |  * print_hex_dump() works on one "line" of output at a time, i.e., | 
 | 140 |  * 16 or 32 bytes of input data converted to hex + ASCII output. | 
 | 141 |  * print_hex_dump() iterates over the entire input @buf, breaking it into | 
 | 142 |  * "line size" chunks to format and print. | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 143 |  * | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 144 |  * E.g.: | 
 | 145 |  *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, | 
 | 146 |  *		16, 1, frame->data, frame->len, 1); | 
 | 147 |  * | 
 | 148 |  * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode: | 
 | 149 |  * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO | 
 | 150 |  * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode: | 
 | 151 |  * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~. | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 152 |  */ | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 153 | void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, | 
 | 154 | 			int rowsize, int groupsize, | 
| Artem Bityutskiy | 6a0ed91 | 2007-08-07 23:43:14 +0300 | [diff] [blame] | 155 | 			const void *buf, size_t len, bool ascii) | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 156 | { | 
| Artem Bityutskiy | 6a0ed91 | 2007-08-07 23:43:14 +0300 | [diff] [blame] | 157 | 	const u8 *ptr = buf; | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 158 | 	int i, linelen, remaining = len; | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 159 | 	unsigned char linebuf[200]; | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 160 |  | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 161 | 	if (rowsize != 16 && rowsize != 32) | 
 | 162 | 		rowsize = 16; | 
 | 163 |  | 
 | 164 | 	for (i = 0; i < len; i += rowsize) { | 
 | 165 | 		linelen = min(remaining, rowsize); | 
 | 166 | 		remaining -= rowsize; | 
 | 167 | 		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, | 
 | 168 | 				linebuf, sizeof(linebuf), ascii); | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 169 |  | 
 | 170 | 		switch (prefix_type) { | 
 | 171 | 		case DUMP_PREFIX_ADDRESS: | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 172 | 			printk("%s%s%*p: %s\n", level, prefix_str, | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 173 | 				(int)(2 * sizeof(void *)), ptr + i, linebuf); | 
 | 174 | 			break; | 
 | 175 | 		case DUMP_PREFIX_OFFSET: | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 176 | 			printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 177 | 			break; | 
 | 178 | 		default: | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 179 | 			printk("%s%s%s\n", level, prefix_str, linebuf); | 
| Randy Dunlap | 99eaf3c | 2007-05-10 22:22:39 -0700 | [diff] [blame] | 180 | 			break; | 
 | 181 | 		} | 
 | 182 | 	} | 
 | 183 | } | 
 | 184 | EXPORT_SYMBOL(print_hex_dump); | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 185 |  | 
 | 186 | /** | 
 | 187 |  * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params | 
 | 188 |  * @prefix_str: string to prefix each line with; | 
 | 189 |  *  caller supplies trailing spaces for alignment if desired | 
 | 190 |  * @prefix_type: controls whether prefix of an offset, address, or none | 
 | 191 |  *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) | 
 | 192 |  * @buf: data blob to dump | 
 | 193 |  * @len: number of bytes in the @buf | 
 | 194 |  * | 
 | 195 |  * Calls print_hex_dump(), with log level of KERN_DEBUG, | 
 | 196 |  * rowsize of 16, groupsize of 1, and ASCII output included. | 
 | 197 |  */ | 
 | 198 | void print_hex_dump_bytes(const char *prefix_str, int prefix_type, | 
| Alan Stern | eb9a9a5 | 2007-08-10 13:01:07 -0700 | [diff] [blame] | 199 | 			const void *buf, size_t len) | 
| Randy Dunlap | c790923 | 2007-06-08 13:47:04 -0700 | [diff] [blame] | 200 | { | 
 | 201 | 	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1, | 
 | 202 | 			buf, len, 1); | 
 | 203 | } | 
 | 204 | EXPORT_SYMBOL(print_hex_dump_bytes); |