xref: /illumos-gate/usr/src/common/hexdump/hexdump.c (revision 80b758da23abee3ef0e550f533aada9ce3b1b01f)
143379a28SAndy Fiddaman /*
243379a28SAndy Fiddaman  * This file and its contents are supplied under the terms of the
343379a28SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
443379a28SAndy Fiddaman  * You may only use this file in accordance with the terms of version
543379a28SAndy Fiddaman  * 1.0 of the CDDL.
643379a28SAndy Fiddaman  *
743379a28SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
843379a28SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
943379a28SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
1043379a28SAndy Fiddaman  */
1143379a28SAndy Fiddaman 
1243379a28SAndy Fiddaman /*
1343379a28SAndy Fiddaman  * Copyright 2024 Oxide Computer Company
1443379a28SAndy Fiddaman  */
1543379a28SAndy Fiddaman 
1643379a28SAndy Fiddaman /*
1743379a28SAndy Fiddaman  * A generic hexdump implementation suitable for use in the kernel or userland.
1843379a28SAndy Fiddaman  * The output style is influenced by mdb's ::dump.
1943379a28SAndy Fiddaman  */
2043379a28SAndy Fiddaman 
2143379a28SAndy Fiddaman #include <sys/ilstr.h>
2243379a28SAndy Fiddaman #include <sys/kmem.h>
2343379a28SAndy Fiddaman #include <sys/stdbool.h>
2443379a28SAndy Fiddaman #include <sys/sysmacros.h>
2543379a28SAndy Fiddaman #include <sys/hexdump.h>
2643379a28SAndy Fiddaman 
2743379a28SAndy Fiddaman #ifdef _KERNEL
2843379a28SAndy Fiddaman #include <sys/sunddi.h>
2943379a28SAndy Fiddaman #include <sys/errno.h>
3043379a28SAndy Fiddaman #else
3143379a28SAndy Fiddaman #include <stdint.h>
3243379a28SAndy Fiddaman #include <stdio.h>
3343379a28SAndy Fiddaman #include <strings.h>
3443379a28SAndy Fiddaman #include <errno.h>
3543379a28SAndy Fiddaman #endif
3643379a28SAndy Fiddaman 
3743379a28SAndy Fiddaman #define	HD_DEFGROUPING		1
3843379a28SAndy Fiddaman #define	HD_DEFWIDTH		16
3943379a28SAndy Fiddaman 
4043379a28SAndy Fiddaman /*
4143379a28SAndy Fiddaman  * We use a simple comparison to determine whether a character is printable in
4243379a28SAndy Fiddaman  * the ASCII table. This is unaffected by locale settings and provides
4343379a28SAndy Fiddaman  * consistent results regardless of the environment.
4443379a28SAndy Fiddaman  */
4543379a28SAndy Fiddaman #define	HEXDUMP_PRINTABLE(c) ((c) >= ' ' && (c) <= '~')
4643379a28SAndy Fiddaman 
4743379a28SAndy Fiddaman typedef struct {
4843379a28SAndy Fiddaman 	uint64_t	hdp_flags;
4943379a28SAndy Fiddaman 	uint8_t		hdp_grouping;	/* display bytes in groups of.. */
5043379a28SAndy Fiddaman 	uint8_t		hdp_width;	/* bytes per row */
5143379a28SAndy Fiddaman 	uint8_t		hdp_indent;	/* indent size */
5243379a28SAndy Fiddaman 
5343379a28SAndy Fiddaman 	const uint8_t	*hdp_data;	/* Data to dump */
5443379a28SAndy Fiddaman 	uint64_t	hdp_len;	/* Length of data */
5543379a28SAndy Fiddaman 
5643379a28SAndy Fiddaman 	uint8_t		hdp_addrwidth;	/* Width of address field */
5743379a28SAndy Fiddaman 	uint64_t	hdp_bufaddr;	/* The supplied buffer address */
5843379a28SAndy Fiddaman 	uint64_t	hdp_baseaddr;	/* The base address for aligned print */
5943379a28SAndy Fiddaman 	uint64_t	hdp_adj;	/* The adjustment (buf - base) */
6043379a28SAndy Fiddaman 
6143379a28SAndy Fiddaman 	ilstr_t		hdp_buf;	/* Working buffer */
6243379a28SAndy Fiddaman 	ilstr_t		hdp_pbuf;	/* Previous line */
6343379a28SAndy Fiddaman 	uint64_t	hdp_offset;	/* Current offset */
6443379a28SAndy Fiddaman 	uint8_t		hdp_marker;	/* Marker offset */
6543379a28SAndy Fiddaman } hexdump_param_t;
6643379a28SAndy Fiddaman 
6743379a28SAndy Fiddaman #ifdef _KERNEL
6843379a28SAndy Fiddaman /*
6943379a28SAndy Fiddaman  * In the kernel we do not have flsll, and ddi_fls is not available until
7043379a28SAndy Fiddaman  * genunix is loaded. Since we wish to be usable before that point, provide a
7143379a28SAndy Fiddaman  * simple alternative.
7243379a28SAndy Fiddaman  */
7343379a28SAndy Fiddaman static int
flsll(unsigned long x)7443379a28SAndy Fiddaman flsll(unsigned long x)
7543379a28SAndy Fiddaman {
7643379a28SAndy Fiddaman 	int pos = 0;
7743379a28SAndy Fiddaman 
7843379a28SAndy Fiddaman 	while (x != 0) {
7943379a28SAndy Fiddaman 		pos++;
8043379a28SAndy Fiddaman 		x >>= 1;
8143379a28SAndy Fiddaman 	}
8243379a28SAndy Fiddaman 
8343379a28SAndy Fiddaman 	return (pos);
8443379a28SAndy Fiddaman }
8543379a28SAndy Fiddaman #endif /* _KERNEL */
8643379a28SAndy Fiddaman 
8743379a28SAndy Fiddaman void
hexdump_init(hexdump_t * h)8843379a28SAndy Fiddaman hexdump_init(hexdump_t *h)
8943379a28SAndy Fiddaman {
9043379a28SAndy Fiddaman 	bzero(h, sizeof (*h));
9143379a28SAndy Fiddaman 	h->h_grouping = HD_DEFGROUPING;
9243379a28SAndy Fiddaman 	h->h_width = HD_DEFWIDTH;
9343379a28SAndy Fiddaman }
9443379a28SAndy Fiddaman 
9543379a28SAndy Fiddaman void
hexdump_fini(hexdump_t * h __unused)9643379a28SAndy Fiddaman hexdump_fini(hexdump_t *h __unused)
9743379a28SAndy Fiddaman {
9843379a28SAndy Fiddaman }
9943379a28SAndy Fiddaman 
10043379a28SAndy Fiddaman void
hexdump_set_width(hexdump_t * h,uint8_t width)10143379a28SAndy Fiddaman hexdump_set_width(hexdump_t *h, uint8_t width)
10243379a28SAndy Fiddaman {
10343379a28SAndy Fiddaman 	if (width == 0)
10443379a28SAndy Fiddaman 		h->h_width = HD_DEFWIDTH;
10543379a28SAndy Fiddaman 	else
10643379a28SAndy Fiddaman 		h->h_width = width;
10743379a28SAndy Fiddaman }
10843379a28SAndy Fiddaman 
10943379a28SAndy Fiddaman void
hexdump_set_grouping(hexdump_t * h,uint8_t grouping)11043379a28SAndy Fiddaman hexdump_set_grouping(hexdump_t *h, uint8_t grouping)
11143379a28SAndy Fiddaman {
11243379a28SAndy Fiddaman 	if (grouping == 0)
11343379a28SAndy Fiddaman 		h->h_grouping = 1;
11443379a28SAndy Fiddaman 	else
11543379a28SAndy Fiddaman 		h->h_grouping = grouping;
11643379a28SAndy Fiddaman }
11743379a28SAndy Fiddaman 
11843379a28SAndy Fiddaman void
hexdump_set_indent(hexdump_t * h,uint8_t indent)11943379a28SAndy Fiddaman hexdump_set_indent(hexdump_t *h, uint8_t indent)
12043379a28SAndy Fiddaman {
12143379a28SAndy Fiddaman 	h->h_indent = indent;
12243379a28SAndy Fiddaman }
12343379a28SAndy Fiddaman 
12443379a28SAndy Fiddaman void
hexdump_set_addr(hexdump_t * h,uint64_t addr)12543379a28SAndy Fiddaman hexdump_set_addr(hexdump_t *h, uint64_t addr)
12643379a28SAndy Fiddaman {
12743379a28SAndy Fiddaman 	h->h_addr = addr;
12843379a28SAndy Fiddaman }
12943379a28SAndy Fiddaman 
13043379a28SAndy Fiddaman void
hexdump_set_addrwidth(hexdump_t * h,uint8_t width)131*80b758daSAndy Fiddaman hexdump_set_addrwidth(hexdump_t *h, uint8_t width)
132*80b758daSAndy Fiddaman {
133*80b758daSAndy Fiddaman 	h->h_addrwidth = width;
134*80b758daSAndy Fiddaman }
135*80b758daSAndy Fiddaman 
136*80b758daSAndy Fiddaman void
hexdump_set_marker(hexdump_t * h,uint8_t marker)13743379a28SAndy Fiddaman hexdump_set_marker(hexdump_t *h, uint8_t marker)
13843379a28SAndy Fiddaman {
13943379a28SAndy Fiddaman 	h->h_marker = marker;
14043379a28SAndy Fiddaman }
14143379a28SAndy Fiddaman 
14243379a28SAndy Fiddaman void
hexdump_set_buf(hexdump_t * h,uint8_t * buf,size_t buflen)14343379a28SAndy Fiddaman hexdump_set_buf(hexdump_t *h, uint8_t *buf, size_t buflen)
14443379a28SAndy Fiddaman {
14543379a28SAndy Fiddaman 	h->h_buf = buf;
14643379a28SAndy Fiddaman 	h->h_buflen = buflen;
14743379a28SAndy Fiddaman }
14843379a28SAndy Fiddaman 
14943379a28SAndy Fiddaman static void
hexdump_space(hexdump_param_t * hdp,uint_t idx)15043379a28SAndy Fiddaman hexdump_space(hexdump_param_t *hdp, uint_t idx)
15143379a28SAndy Fiddaman {
15243379a28SAndy Fiddaman 	if (idx != 0 && idx % hdp->hdp_grouping == 0) {
15343379a28SAndy Fiddaman 		ilstr_append_char(&hdp->hdp_buf, ' ');
15443379a28SAndy Fiddaman 		/*
15543379a28SAndy Fiddaman 		 * If we are putting each byte in its own group, add an extra
15643379a28SAndy Fiddaman 		 * space every 8 bytes to improve readability.
15743379a28SAndy Fiddaman 		 */
15843379a28SAndy Fiddaman 		if (hdp->hdp_grouping == 1 && idx % 8 == 0)
15943379a28SAndy Fiddaman 			ilstr_append_char(&hdp->hdp_buf, ' ');
16043379a28SAndy Fiddaman 	}
16143379a28SAndy Fiddaman 	if (idx != 0 && hdp->hdp_flags & HDF_DOUBLESPACE)
16243379a28SAndy Fiddaman 		ilstr_append_char(&hdp->hdp_buf, ' ');
16343379a28SAndy Fiddaman }
16443379a28SAndy Fiddaman 
16543379a28SAndy Fiddaman static void
hexdump_header(hexdump_param_t * hdp)16643379a28SAndy Fiddaman hexdump_header(hexdump_param_t *hdp)
16743379a28SAndy Fiddaman {
16843379a28SAndy Fiddaman 	int markerpos = -1;
16943379a28SAndy Fiddaman 
17043379a28SAndy Fiddaman 	if (hdp->hdp_indent != 0)
17143379a28SAndy Fiddaman 		ilstr_aprintf(&hdp->hdp_buf, "%*s", hdp->hdp_indent, "");
17243379a28SAndy Fiddaman 
17343379a28SAndy Fiddaman 	if (hdp->hdp_flags & HDF_ADDRESS)
17443379a28SAndy Fiddaman 		ilstr_aprintf(&hdp->hdp_buf, "%*s   ", hdp->hdp_addrwidth, "");
17543379a28SAndy Fiddaman 
17643379a28SAndy Fiddaman 	for (uint_t i = 0; i < hdp->hdp_width; i++) {
17743379a28SAndy Fiddaman 		hexdump_space(hdp, i);
17843379a28SAndy Fiddaman 
17943379a28SAndy Fiddaman 		if ((hdp->hdp_marker > 0 && i == hdp->hdp_marker) ||
18043379a28SAndy Fiddaman 		    ((hdp->hdp_flags & HDF_ALIGN) &&
18143379a28SAndy Fiddaman 		    hdp->hdp_baseaddr + i == hdp->hdp_bufaddr)) {
18243379a28SAndy Fiddaman 			ilstr_append_str(&hdp->hdp_buf, "\\/");
18343379a28SAndy Fiddaman 			markerpos = i;
18443379a28SAndy Fiddaman 		} else {
18543379a28SAndy Fiddaman 			ilstr_aprintf(&hdp->hdp_buf, "%2x",
18643379a28SAndy Fiddaman 			    (i + hdp->hdp_offset) & 0xf);
18743379a28SAndy Fiddaman 		}
18843379a28SAndy Fiddaman 	}
18943379a28SAndy Fiddaman 
19043379a28SAndy Fiddaman 	if (hdp->hdp_flags & HDF_ASCII) {
19143379a28SAndy Fiddaman 		ilstr_append_str(&hdp->hdp_buf, "   ");
19243379a28SAndy Fiddaman 		for (uint_t i = 0; i < hdp->hdp_width; i++) {
19343379a28SAndy Fiddaman 			if (markerpos != -1 && markerpos == i) {
19443379a28SAndy Fiddaman 				ilstr_append_char(&hdp->hdp_buf, 'v');
19543379a28SAndy Fiddaman 			} else {
19643379a28SAndy Fiddaman 				ilstr_aprintf(&hdp->hdp_buf, "%x",
19743379a28SAndy Fiddaman 				    (i + hdp->hdp_offset) & 0xf);
19843379a28SAndy Fiddaman 			}
19943379a28SAndy Fiddaman 		}
20043379a28SAndy Fiddaman 	}
20143379a28SAndy Fiddaman }
20243379a28SAndy Fiddaman 
20343379a28SAndy Fiddaman static void
hexdump_data(hexdump_param_t * hdp)20443379a28SAndy Fiddaman hexdump_data(hexdump_param_t *hdp)
20543379a28SAndy Fiddaman {
20643379a28SAndy Fiddaman 	uint64_t addr = hdp->hdp_baseaddr + hdp->hdp_offset;
20743379a28SAndy Fiddaman 
20843379a28SAndy Fiddaman 	if (hdp->hdp_indent != 0)
20943379a28SAndy Fiddaman 		ilstr_aprintf(&hdp->hdp_buf, "%*s", hdp->hdp_indent, "");
21043379a28SAndy Fiddaman 
21143379a28SAndy Fiddaman 	if (hdp->hdp_flags & HDF_ADDRESS) {
21243379a28SAndy Fiddaman 		ilstr_aprintf(&hdp->hdp_buf, "%0*llx:  ",
21343379a28SAndy Fiddaman 		    hdp->hdp_addrwidth, addr);
21443379a28SAndy Fiddaman 	}
21543379a28SAndy Fiddaman 
21643379a28SAndy Fiddaman 	for (uint_t i = 0; i < hdp->hdp_width; i++) {
21743379a28SAndy Fiddaman 		if (hdp->hdp_offset + i >= hdp->hdp_len) {
21843379a28SAndy Fiddaman 			/*
21943379a28SAndy Fiddaman 			 * If we are not going to an ascii table, we don't need
22043379a28SAndy Fiddaman 			 * to pad this out with spaces to the right.
22143379a28SAndy Fiddaman 			 */
22243379a28SAndy Fiddaman 			if (!(hdp->hdp_flags & HDF_ASCII))
22343379a28SAndy Fiddaman 				break;
22443379a28SAndy Fiddaman 		}
22543379a28SAndy Fiddaman 		hexdump_space(hdp, i);
22643379a28SAndy Fiddaman 		if (addr + i < hdp->hdp_bufaddr ||
22743379a28SAndy Fiddaman 		    hdp->hdp_offset + i >= hdp->hdp_len) {
22843379a28SAndy Fiddaman 			ilstr_append_str(&hdp->hdp_buf, "  ");
22943379a28SAndy Fiddaman 		} else {
23043379a28SAndy Fiddaman 			ilstr_aprintf(&hdp->hdp_buf, "%02x",
23143379a28SAndy Fiddaman 			    hdp->hdp_data[hdp->hdp_offset + i - hdp->hdp_adj]);
23243379a28SAndy Fiddaman 		}
23343379a28SAndy Fiddaman 	}
23443379a28SAndy Fiddaman 
23543379a28SAndy Fiddaman 	if (hdp->hdp_flags & HDF_ASCII) {
23643379a28SAndy Fiddaman 		ilstr_append_str(&hdp->hdp_buf, " | ");
23743379a28SAndy Fiddaman 		for (uint_t i = 0; i < hdp->hdp_width; i++) {
23843379a28SAndy Fiddaman 			if (hdp->hdp_offset + i >= hdp->hdp_len)
23943379a28SAndy Fiddaman 				break;
24043379a28SAndy Fiddaman 			if (addr + i < hdp->hdp_bufaddr) {
24143379a28SAndy Fiddaman 				ilstr_append_char(&hdp->hdp_buf, ' ');
24243379a28SAndy Fiddaman 			} else {
24343379a28SAndy Fiddaman 				char c = hdp->hdp_data[hdp->hdp_offset + i
24443379a28SAndy Fiddaman 				    - hdp->hdp_adj];
24543379a28SAndy Fiddaman 
24643379a28SAndy Fiddaman 				ilstr_append_char(&hdp->hdp_buf,
24743379a28SAndy Fiddaman 				    HEXDUMP_PRINTABLE(c) ? c : '.');
24843379a28SAndy Fiddaman 			}
24943379a28SAndy Fiddaman 		}
25043379a28SAndy Fiddaman 	}
25143379a28SAndy Fiddaman }
25243379a28SAndy Fiddaman 
25343379a28SAndy Fiddaman static int
hexdump_output(hexdump_param_t * hdp,bool hdr,hexdump_cb_f cb,void * cbarg)25443379a28SAndy Fiddaman hexdump_output(hexdump_param_t *hdp, bool hdr, hexdump_cb_f cb, void *cbarg)
25543379a28SAndy Fiddaman {
25643379a28SAndy Fiddaman 	int ret;
25743379a28SAndy Fiddaman 
25843379a28SAndy Fiddaman 	/*
25943379a28SAndy Fiddaman 	 * The callback is invoked with an address of UINT64_MAX for the header
26043379a28SAndy Fiddaman 	 * row.
26143379a28SAndy Fiddaman 	 */
26243379a28SAndy Fiddaman 	ret = cb(cbarg, hdr ? UINT64_MAX : hdp->hdp_bufaddr + hdp->hdp_offset,
26343379a28SAndy Fiddaman 	    ilstr_cstr(&hdp->hdp_buf), ilstr_len(&hdp->hdp_buf));
26443379a28SAndy Fiddaman 	ilstr_reset(&hdp->hdp_buf);
26543379a28SAndy Fiddaman 
26643379a28SAndy Fiddaman 	return (ret);
26743379a28SAndy Fiddaman }
26843379a28SAndy Fiddaman 
26943379a28SAndy Fiddaman static bool
hexdump_squishable(hexdump_param_t * hdp)27043379a28SAndy Fiddaman hexdump_squishable(hexdump_param_t *hdp)
27143379a28SAndy Fiddaman {
27243379a28SAndy Fiddaman 	const char *str = ilstr_cstr(&hdp->hdp_buf);
27343379a28SAndy Fiddaman 	const char *pstr = ilstr_cstr(&hdp->hdp_pbuf);
27443379a28SAndy Fiddaman 
27543379a28SAndy Fiddaman 	if (hdp->hdp_flags & HDF_ADDRESS) {
27643379a28SAndy Fiddaman 		size_t strl = ilstr_len(&hdp->hdp_buf);
27743379a28SAndy Fiddaman 		size_t pstrl = ilstr_len(&hdp->hdp_pbuf);
27843379a28SAndy Fiddaman 
27943379a28SAndy Fiddaman 		if (strl <= hdp->hdp_addrwidth || pstrl <= hdp->hdp_addrwidth)
28043379a28SAndy Fiddaman 			return (false);
28143379a28SAndy Fiddaman 
28243379a28SAndy Fiddaman 		str += hdp->hdp_addrwidth;
28343379a28SAndy Fiddaman 		pstr += hdp->hdp_addrwidth;
28443379a28SAndy Fiddaman 	}
28543379a28SAndy Fiddaman 
28643379a28SAndy Fiddaman 	return (strcmp(str, pstr) == 0);
28743379a28SAndy Fiddaman }
28843379a28SAndy Fiddaman 
28943379a28SAndy Fiddaman int
hexdumph(hexdump_t * h,const uint8_t * data,size_t len,hexdump_flag_t flags,hexdump_cb_f cb,void * cbarg)29043379a28SAndy Fiddaman hexdumph(hexdump_t *h, const uint8_t *data, size_t len, hexdump_flag_t flags,
29143379a28SAndy Fiddaman     hexdump_cb_f cb, void *cbarg)
29243379a28SAndy Fiddaman {
29343379a28SAndy Fiddaman 	hexdump_param_t hdp = { 0 };
29443379a28SAndy Fiddaman 	int ret = 0;
29543379a28SAndy Fiddaman 
29643379a28SAndy Fiddaman 	if (data == NULL)
29743379a28SAndy Fiddaman 		return (0);
29843379a28SAndy Fiddaman 
29943379a28SAndy Fiddaman 	/*
30043379a28SAndy Fiddaman 	 * We can use a pre-allocated buffer for the constructed lines if the
30143379a28SAndy Fiddaman 	 * caller has provided one. One use of this is to invoke this routine
30243379a28SAndy Fiddaman 	 * early in boot before kmem is ready.
30343379a28SAndy Fiddaman 	 */
30443379a28SAndy Fiddaman #ifdef _KERNEL
30543379a28SAndy Fiddaman 	if (kmem_ready == 0 && (h == NULL || h->h_buf == NULL)) {
30643379a28SAndy Fiddaman 		panic("hexdump before kmem is ready requires pre-allocated "
30743379a28SAndy Fiddaman 		    "buffer");
30843379a28SAndy Fiddaman 	}
30943379a28SAndy Fiddaman #endif
31043379a28SAndy Fiddaman 	if (h != NULL && h->h_buf != NULL) {
31143379a28SAndy Fiddaman 		ilstr_init_prealloc(&hdp.hdp_buf, (char *)h->h_buf,
31243379a28SAndy Fiddaman 		    h->h_buflen);
31343379a28SAndy Fiddaman 		/* We do not support HDF_DEDUP with a pre-allocated buffer */
31443379a28SAndy Fiddaman 		flags &= ~HDF_DEDUP;
31543379a28SAndy Fiddaman 	} else {
31643379a28SAndy Fiddaman 		/*
31743379a28SAndy Fiddaman 		 * The KM_SLEEP flag is ignored outside kernel context. If a
31843379a28SAndy Fiddaman 		 * memory allocation fails in userland that will result in the
31943379a28SAndy Fiddaman 		 * caller ultimately receiving -1 with errno set to ENOMEM.
32043379a28SAndy Fiddaman 		 */
32143379a28SAndy Fiddaman 		ilstr_init(&hdp.hdp_buf, KM_SLEEP);
32243379a28SAndy Fiddaman 		if (flags & HDF_DEDUP)
32343379a28SAndy Fiddaman 			ilstr_init(&hdp.hdp_pbuf, KM_SLEEP);
32443379a28SAndy Fiddaman 	}
32543379a28SAndy Fiddaman 
32643379a28SAndy Fiddaman 	hdp.hdp_flags = flags;
32743379a28SAndy Fiddaman 	hdp.hdp_grouping = HD_DEFGROUPING;
32843379a28SAndy Fiddaman 	hdp.hdp_width = HD_DEFWIDTH;
32943379a28SAndy Fiddaman 	hdp.hdp_data = data;
33043379a28SAndy Fiddaman 	hdp.hdp_len = len;
33143379a28SAndy Fiddaman 
33243379a28SAndy Fiddaman 	hdp.hdp_bufaddr = hdp.hdp_baseaddr = 0;
33343379a28SAndy Fiddaman 	hdp.hdp_offset = hdp.hdp_marker = hdp.hdp_adj = 0;
33443379a28SAndy Fiddaman 
33543379a28SAndy Fiddaman 	if (h != NULL) {
33643379a28SAndy Fiddaman 		hdp.hdp_bufaddr = hdp.hdp_baseaddr = h->h_addr;
33743379a28SAndy Fiddaman 		hdp.hdp_width = h->h_width;
33843379a28SAndy Fiddaman 		hdp.hdp_grouping = h->h_grouping;
33943379a28SAndy Fiddaman 		hdp.hdp_indent = h->h_indent;
34043379a28SAndy Fiddaman 		hdp.hdp_marker = h->h_marker;
34143379a28SAndy Fiddaman 	}
34243379a28SAndy Fiddaman 
34343379a28SAndy Fiddaman 	if (hdp.hdp_width == 0)
34443379a28SAndy Fiddaman 		hdp.hdp_width = HD_DEFWIDTH;
34543379a28SAndy Fiddaman 	if (hdp.hdp_grouping == 0)
34643379a28SAndy Fiddaman 		hdp.hdp_grouping = HD_DEFGROUPING;
34743379a28SAndy Fiddaman 	if (hdp.hdp_marker > HD_DEFWIDTH)
34843379a28SAndy Fiddaman 		hdp.hdp_marker = 0;
34943379a28SAndy Fiddaman 
35043379a28SAndy Fiddaman 	/*
35143379a28SAndy Fiddaman 	 * If the grouping isn't a power of two, or the display width is not
35243379a28SAndy Fiddaman 	 * evenly divisible by the grouping we ignore the specified grouping
35343379a28SAndy Fiddaman 	 * and default to 4.
35443379a28SAndy Fiddaman 	 */
35543379a28SAndy Fiddaman 	if (!ISP2(hdp.hdp_grouping) || hdp.hdp_width % hdp.hdp_grouping != 0)
35643379a28SAndy Fiddaman 		hdp.hdp_grouping = 4;
35743379a28SAndy Fiddaman 
35843379a28SAndy Fiddaman 	/*
35943379a28SAndy Fiddaman 	 * Determine how much space is required for the address field.
36043379a28SAndy Fiddaman 	 * We need one character for every four bits of the final address.
36143379a28SAndy Fiddaman 	 */
36243379a28SAndy Fiddaman 	hdp.hdp_addrwidth = (flsll(hdp.hdp_baseaddr + len) + 3) / 4;
363*80b758daSAndy Fiddaman 	if (h != NULL && h->h_addrwidth > hdp.hdp_addrwidth)
364*80b758daSAndy Fiddaman 		hdp.hdp_addrwidth = h->h_addrwidth;
36543379a28SAndy Fiddaman 
36643379a28SAndy Fiddaman 	/*
36743379a28SAndy Fiddaman 	 * If alignment was requested and the address is not already aligned,
36843379a28SAndy Fiddaman 	 * adjust the starting address down to the nearest boundary. Missing
36943379a28SAndy Fiddaman 	 * data will be displayed as spaces.
37043379a28SAndy Fiddaman 	 */
37143379a28SAndy Fiddaman 	if (flags & HDF_ALIGN) {
37243379a28SAndy Fiddaman 		hdp.hdp_baseaddr = P2ALIGN(hdp.hdp_baseaddr, hdp.hdp_width);
37343379a28SAndy Fiddaman 		hdp.hdp_adj = hdp.hdp_bufaddr - hdp.hdp_baseaddr;
37443379a28SAndy Fiddaman 		hdp.hdp_len += hdp.hdp_adj;
37543379a28SAndy Fiddaman 	}
37643379a28SAndy Fiddaman 
37743379a28SAndy Fiddaman 	if (flags & HDF_HEADER) {
37843379a28SAndy Fiddaman 		hexdump_header(&hdp);
37943379a28SAndy Fiddaman 		if ((ret = hexdump_output(&hdp, true, cb, cbarg)) != 0)
38043379a28SAndy Fiddaman 			goto out;
38143379a28SAndy Fiddaman 	}
38243379a28SAndy Fiddaman 
38343379a28SAndy Fiddaman 	bool squishing = false;
38443379a28SAndy Fiddaman 
38543379a28SAndy Fiddaman 	while (hdp.hdp_offset < hdp.hdp_len) {
38643379a28SAndy Fiddaman 		hexdump_data(&hdp);
38743379a28SAndy Fiddaman 		if (flags & HDF_DEDUP) {
38843379a28SAndy Fiddaman 			if (hexdump_squishable(&hdp)) {
38943379a28SAndy Fiddaman 				ilstr_reset(&hdp.hdp_buf);
39043379a28SAndy Fiddaman 				if (squishing) {
39143379a28SAndy Fiddaman 					hdp.hdp_offset += hdp.hdp_width;
39243379a28SAndy Fiddaman 					continue;
39343379a28SAndy Fiddaman 				}
39443379a28SAndy Fiddaman 				ilstr_append_str(&hdp.hdp_buf, "*");
39543379a28SAndy Fiddaman 				squishing = true;
39643379a28SAndy Fiddaman 			} else {
39743379a28SAndy Fiddaman 				ilstr_reset(&hdp.hdp_pbuf);
39843379a28SAndy Fiddaman 				ilstr_append_str(&hdp.hdp_pbuf,
39943379a28SAndy Fiddaman 				    ilstr_cstr(&hdp.hdp_buf));
40043379a28SAndy Fiddaman 				squishing = false;
40143379a28SAndy Fiddaman 			}
40243379a28SAndy Fiddaman 		}
40343379a28SAndy Fiddaman 		if ((ret = hexdump_output(&hdp, false, cb, cbarg)) != 0)
40443379a28SAndy Fiddaman 			break;
40543379a28SAndy Fiddaman 		hdp.hdp_offset += hdp.hdp_width;
40643379a28SAndy Fiddaman 	}
40743379a28SAndy Fiddaman 
40843379a28SAndy Fiddaman out:
40943379a28SAndy Fiddaman 	/*
41043379a28SAndy Fiddaman 	 * If ret is not zero then it is a value returned by a callback and we
41143379a28SAndy Fiddaman 	 * return that to the caller as-is. Otherwise we translate any errors
41243379a28SAndy Fiddaman 	 * from ilstr.
41343379a28SAndy Fiddaman 	 */
41443379a28SAndy Fiddaman 	if (ret == 0) {
41543379a28SAndy Fiddaman 		ilstr_errno_t ilerr = ilstr_errno(&hdp.hdp_buf);
41643379a28SAndy Fiddaman 
41743379a28SAndy Fiddaman 		switch (ilerr) {
41843379a28SAndy Fiddaman 		case ILSTR_ERROR_OK:
41943379a28SAndy Fiddaman 			break;
42043379a28SAndy Fiddaman 		case ILSTR_ERROR_NOMEM:
42143379a28SAndy Fiddaman 			ret = ENOMEM;
42243379a28SAndy Fiddaman 			break;
42343379a28SAndy Fiddaman 		case ILSTR_ERROR_OVERFLOW:
42443379a28SAndy Fiddaman 			ret = EOVERFLOW;
42543379a28SAndy Fiddaman 			break;
42643379a28SAndy Fiddaman 		case ILSTR_ERROR_PRINTF:
42743379a28SAndy Fiddaman 		default:
42843379a28SAndy Fiddaman 			/*
42943379a28SAndy Fiddaman 			 * We don't expect to end up here but we should return
43043379a28SAndy Fiddaman 			 * an error if we somehow do.
43143379a28SAndy Fiddaman 			 */
43243379a28SAndy Fiddaman 			ret = EIO;
43343379a28SAndy Fiddaman 			break;
43443379a28SAndy Fiddaman 		}
43543379a28SAndy Fiddaman #ifndef _KERNEL
43643379a28SAndy Fiddaman 		if (ret != 0) {
43743379a28SAndy Fiddaman 			errno = ret;
43843379a28SAndy Fiddaman 			ret = -1;
43943379a28SAndy Fiddaman 		}
44043379a28SAndy Fiddaman #endif
44143379a28SAndy Fiddaman 	}
44243379a28SAndy Fiddaman 
44343379a28SAndy Fiddaman 	ilstr_fini(&hdp.hdp_buf);
44443379a28SAndy Fiddaman 	if (flags & HDF_DEDUP)
44543379a28SAndy Fiddaman 		ilstr_fini(&hdp.hdp_pbuf);
44643379a28SAndy Fiddaman 
44743379a28SAndy Fiddaman 	return (ret);
44843379a28SAndy Fiddaman }
44943379a28SAndy Fiddaman 
45043379a28SAndy Fiddaman int
hexdump(const uint8_t * data,size_t len,hexdump_flag_t flags,hexdump_cb_f cb,void * cbarg)45143379a28SAndy Fiddaman hexdump(const uint8_t *data, size_t len, hexdump_flag_t flags, hexdump_cb_f cb,
45243379a28SAndy Fiddaman     void *cbarg)
45343379a28SAndy Fiddaman {
45443379a28SAndy Fiddaman 	return (hexdumph(NULL, data, len, flags, cb, cbarg));
45543379a28SAndy Fiddaman }
45643379a28SAndy Fiddaman 
45743379a28SAndy Fiddaman #ifndef _KERNEL
45843379a28SAndy Fiddaman static int
hexdump_file_cb(void * arg,uint64_t addr __unused,const char * str,size_t len __unused)45943379a28SAndy Fiddaman hexdump_file_cb(void *arg, uint64_t addr __unused, const char *str,
46043379a28SAndy Fiddaman     size_t len __unused)
46143379a28SAndy Fiddaman {
46243379a28SAndy Fiddaman 	FILE *fp = (FILE *)arg;
46343379a28SAndy Fiddaman 
46443379a28SAndy Fiddaman 	if (fprintf(fp, "%s\n", str) < 0)
46543379a28SAndy Fiddaman 		return (-1);
46643379a28SAndy Fiddaman 	return (0);
46743379a28SAndy Fiddaman }
46843379a28SAndy Fiddaman 
46943379a28SAndy Fiddaman int
hexdump_fileh(hexdump_t * h,const uint8_t * data,size_t len,hexdump_flag_t flags,FILE * fp)47043379a28SAndy Fiddaman hexdump_fileh(hexdump_t *h, const uint8_t *data, size_t len,
47143379a28SAndy Fiddaman     hexdump_flag_t flags, FILE *fp)
47243379a28SAndy Fiddaman {
47343379a28SAndy Fiddaman 	return (hexdumph(h, data, len, flags, hexdump_file_cb, fp));
47443379a28SAndy Fiddaman }
47543379a28SAndy Fiddaman 
47643379a28SAndy Fiddaman int
hexdump_file(const uint8_t * data,size_t len,hexdump_flag_t flags,FILE * fp)47743379a28SAndy Fiddaman hexdump_file(const uint8_t *data, size_t len, hexdump_flag_t flags, FILE *fp)
47843379a28SAndy Fiddaman {
47943379a28SAndy Fiddaman 	return (hexdumph(NULL, data, len, flags, hexdump_file_cb, fp));
48043379a28SAndy Fiddaman }
48143379a28SAndy Fiddaman #endif /* _KERNEL */
482