fwctl_fetch: A small test utility for the fwctl bhyve device.

This can be run inside a bhyve guest to query the value of fwctl
nodes.  Note that fwctl in bhyve only supports a single hw.ncpu node.

Reviewed by:	markj
Differential Revision:	https://reviews.freebsd.org/D40803
This commit is contained in:
John Baldwin 2023-07-07 13:02:13 -07:00
parent 9ac841e922
commit d8bfccb220
2 changed files with 145 additions and 0 deletions

View file

@ -0,0 +1,8 @@
PROGS= fwctl_fetch
MAN=
BINDIR?= /usr/local/bin
# fwctl_fetch: fetch the value of fwctl nodes from a guest
LIBADD.fwctl_fetch+= util
.include <bsd.progs.mk>

View file

@ -0,0 +1,137 @@
/*-
* Copyright (c) 2023 John Baldwin <jhb@FreeBSD.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <sys/param.h>
#include <err.h>
#include <fcntl.h>
#include <libutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <machine/cpufunc.h>
#define OP_GET 3
#define OP_GET_LEN 4
/* I/O ports */
#define FWCTL_OUT 0x510
#define FWCTL_IN 0x511
static void
reset_fwctl(void)
{
char buf[4];
outw(FWCTL_OUT, 0);
for (u_int i = 0; i < 4; i++)
buf[i] = inb(FWCTL_IN);
if (memcmp(buf, "BHYV", 4) != 0)
errx(1, "Signature mismatch: %.4s", buf);
}
static void
send_node_name(const char *name)
{
uint32_t value;
size_t len;
len = strlen(name) + 1;
while (len > 4) {
memcpy(&value, name, 4);
outl(FWCTL_OUT, value);
name += 4;
len -= 4;
}
if (len > 0) {
value = 0;
memcpy(&value, name, len);
outl(FWCTL_OUT, value);
}
}
static void
fwctl_op(uint32_t op, uint32_t id, const char *name, void *buf, size_t len)
{
char *cp;
uint32_t value, rsplen;
/* Length */
outl(FWCTL_OUT, 12 + strlen(name) + 1);
/* Operation */
outl(FWCTL_OUT, op);
/* Transaction ID */
outl(FWCTL_OUT, id);
send_node_name(name);
/* Length */
rsplen = inl(FWCTL_IN);
/* If there is an error, the response will have no payload. */
if (rsplen < 4 * sizeof(value))
errx(1, "Invalid response length (%u): %u", id, rsplen);
/* Operation */
value = inl(FWCTL_IN);
if (value != op)
errx(1, "Invalid response type (%u): %u", id, value);
/* Transaction ID */
value = inl(FWCTL_IN);
if (value != id)
errx(1, "Invalid response ID (%u): %u", id, value);
/* Error */
value = inl(FWCTL_IN);
if (value != 0)
errx(1, "Error from op %u (%u): %u", op, id, value);
/* If there wasn't an error, require payload length to match */
if (rsplen != 4 * sizeof(value) + len)
errx(1, "Response payload length mismatch (%u): %zu vs %zu", id,
rsplen - 4 * sizeof(value), len);
cp = buf;
while (len > 0) {
value = inl(FWCTL_IN);
memcpy(cp, &value, 4);
cp += 4;
len -= 4;
}
}
int
main(int ac, char **av)
{
char *p;
size_t len, buflen, len2;
if (ac != 2)
errx(1, "Need node name");
if (open("/dev/io", O_RDWR) == -1)
err(1, "Failed to open /dev/io");
reset_fwctl();
fwctl_op(OP_GET_LEN, 1, av[1], &len, sizeof(len));
if (len == 0)
errx(1, "Node has length of 0");
/* Buffer includes embedded length followed by value. */
buflen = sizeof(size_t) + roundup2(len, 4);
p = malloc(buflen);
fwctl_op(OP_GET, 2, av[1], p, buflen);
memcpy(&len2, p, sizeof(len2));
if (len2 != len)
errx(1, "Length mismatch: %zu vs %zu", len, len2);
hexdump(p + sizeof(len2), len, NULL, 0);
return (0);
}