diff --git a/tools/tools/bhyve/Makefile b/tools/tools/bhyve/Makefile new file mode 100644 index 000000000000..3d9ea4167bab --- /dev/null +++ b/tools/tools/bhyve/Makefile @@ -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 diff --git a/tools/tools/bhyve/fwctl_fetch.c b/tools/tools/bhyve/fwctl_fetch.c new file mode 100644 index 000000000000..f3139cf4f010 --- /dev/null +++ b/tools/tools/bhyve/fwctl_fetch.c @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2023 John Baldwin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}