freebsd-dev/usr.sbin/ofwdump/ofwdump.c
Brandon Bergren 26869ad14c [PowerPC] Allow traversal of oversize OF properties.
In standards such as LoPAPR, property names in excess of the usual 31
characters exist.

This breaks property traversal.

While in IEEE 1275-1994, nextprop is defined explicitly to work with a
32-byte region of memory, using a larger buffer should be fine. There is
actually no way to pass a buffer length to the nextprop call in the OF
client interface, so SLOF actually just blindly overflows the buffer.

So we have to defensively make the buffer larger, to avoid memory
corruption when reading out long properties on live OF systems.

Note also that on real-mode OF, things are pretty tight because we are
allocating against a static bounce buffer in low memory, so we can't just
use a huge buffer to work around this without it being wasteful of our
limited amount of 32-bit physical memory.

This allows a patched ofwdump to operate properly on SLOF (i.e. pseries)
systems, as well as any other PowerPC systems with overlength properties.

Reviewed by:	jhibbits
MFC after:	2 weeks
Sponsored by:	Tag1 Consulting, Inc.
Differential Revision:	https://reviews.freebsd.org/D26669
2020-11-13 16:49:41 +00:00

251 lines
6.1 KiB
C

/*-
* Copyright (c) 2002 by Thomas Moestl <tmm@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/ofw/openfirm.h>
#include <dev/ofw/openfirmio.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <vis.h>
#include "ofw_util.h"
/* Constants controlling the layout of the output. */
#define LVLINDENT 2
#define NAMEINDENT 2
#define DUMPINDENT 4
#define CHARSPERLINE 60
#define BYTESPERLINE (CHARSPERLINE / 3)
static void usage(void);
static void ofw_indent(int);
static void ofw_dump_properties(int, phandle_t, int, int, int);
static void ofw_dump_property(int fd, phandle_t n, int level,
const char *prop, int raw, int str);
static void ofw_dump(int, const char *, int, int, const char *, int, int);
static void
usage(void)
{
fprintf(stderr,
"usage: ofwdump -a [-p | -P property] [-R | -S]\n"
" ofwdump [-p | -P property] [-r] [-R | -S] [--] nodes\n");
exit(EX_USAGE);
}
int
main(int argc, char *argv[])
{
int opt, i, fd;
int aflag, pflag, rflag, Rflag, Sflag;
char *Parg;
aflag = pflag = rflag = Rflag = Sflag = 0;
Parg = NULL;
while ((opt = getopt(argc, argv, "-aprP:RS")) != -1) {
if (opt == '-')
break;
switch (opt) {
case 'a':
aflag = 1;
rflag = 1;
break;
case 'p':
if (Parg != NULL)
usage();
pflag = 1;
break;
case 'r':
rflag = 1;
break;
case 'P':
if (pflag)
usage();
pflag = 1;
Parg = optarg;
break;
case 'R':
if (Sflag)
usage();
Rflag = 1;
break;
case 'S':
if (Rflag)
usage();
Sflag = 1;
break;
case '?':
default:
usage();
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
fd = ofw_open(O_RDONLY);
if (aflag) {
if (argc != 0)
usage();
ofw_dump(fd, NULL, rflag, pflag, Parg, Rflag, Sflag);
} else {
/*
* For the sake of scripts, usage() is not called here if
* argc == 0.
*/
for (i = 0; i < argc; i++)
ofw_dump(fd, argv[i], rflag, pflag, Parg, Rflag, Sflag);
}
ofw_close(fd);
return (EX_OK);
}
static void
ofw_indent(int level)
{
int i;
for (i = 0; i < level; i++)
putchar(' ');
}
static void
ofw_dump_properties(int fd, phandle_t n, int level, int raw, int str)
{
int nlen;
char prop[OFIOCSUGGPROPNAMELEN];
for (nlen = ofw_firstprop(fd, n, prop, sizeof(prop)); nlen != 0;
nlen = ofw_nextprop(fd, n, prop, prop, sizeof(prop)))
ofw_dump_property(fd, n, level, prop, raw, str);
}
static void
ofw_dump_property(int fd, phandle_t n, int level, const char *prop, int raw,
int str)
{
static void *pbuf = NULL;
static char *visbuf = NULL;
static char printbuf[CHARSPERLINE + 1];
static int pblen = 0, vblen = 0;
int len, i, j, max, vlen;
len = ofw_getprop_alloc(fd, n, prop, &pbuf, &pblen, 1);
if (len < 0)
return;
if (raw)
write(STDOUT_FILENO, pbuf, len);
else if (str)
printf("%.*s\n", len, (char *)pbuf);
else {
ofw_indent(level * LVLINDENT + NAMEINDENT);
printf("%s:\n", prop);
/* Print in hex. */
for (i = 0; i < len; i += BYTESPERLINE) {
max = len - i;
max = max > BYTESPERLINE ? BYTESPERLINE : max;
ofw_indent(level * LVLINDENT + DUMPINDENT);
for (j = 0; j < max; j++)
printf("%02x ",
((unsigned char *)pbuf)[i + j]);
printf("\n");
}
/*
* strvis() and print if it looks like it is
* zero-terminated.
*/
if (((char *)pbuf)[len - 1] == '\0' &&
strlen(pbuf) == (unsigned)len - 1) {
if (vblen < (len - 1) * 4 + 1) {
if (visbuf != NULL)
free(visbuf);
vblen = (OFIOCMAXVALUE + len) * 4 + 1;
if ((visbuf = malloc(vblen)) == NULL)
err(EX_OSERR,
"malloc() failed");
}
vlen = strvis(visbuf, pbuf, VIS_TAB | VIS_NL);
for (i = 0; i < vlen; i += CHARSPERLINE) {
ofw_indent(level * LVLINDENT +
DUMPINDENT);
strlcpy(printbuf, &visbuf[i],
sizeof(printbuf));
printf("'%s'\n", printbuf);
}
}
}
}
static void
ofw_dump_node(int fd, phandle_t n, int level, int rec, int prop,
const char *pmatch, int raw, int str)
{
static void *nbuf = NULL;
static int nblen = 0;
int plen;
phandle_t c;
if (!(raw || str)) {
ofw_indent(level * LVLINDENT);
printf("Node %#lx", (unsigned long)n);
plen = ofw_getprop_alloc(fd, n, "name", &nbuf, &nblen, 1);
if (plen > 0)
printf(": %.*s\n", (int)plen, (char *)nbuf);
else
putchar('\n');
}
if (prop) {
if (pmatch)
ofw_dump_property(fd, n, level, pmatch, raw, str);
else
ofw_dump_properties(fd, n, level, raw, str);
}
if (rec) {
for (c = ofw_child(fd, n); c != 0; c = ofw_peer(fd, c)) {
ofw_dump_node(fd, c, level + 1, rec, prop, pmatch,
raw, str);
}
}
}
static void
ofw_dump(int fd, const char *start, int rec, int prop, const char *pmatch,
int raw, int str)
{
phandle_t n;
n = start == NULL ? ofw_root(fd) : ofw_finddevice(fd, start);
ofw_dump_node(fd, n, 0, rec, prop, pmatch, raw, str);
}