1/*-
2 * Copyright (c) 2023 John Baldwin <jhb@FreeBSD.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7/*
8 * Fetch the value of fwctl nodes from a guest.
9 *
10 * Usage: fwctl_fetch <node>
11 */
12
13#include <sys/param.h>
14#include <err.h>
15#include <fcntl.h>
16#include <libutil.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <machine/cpufunc.h>
21
22#define	OP_GET		3
23#define	OP_GET_LEN	4
24
25/* I/O ports */
26#define	FWCTL_OUT	0x510
27#define	FWCTL_IN	0x511
28
29static void
30reset_fwctl(void)
31{
32	char buf[4];
33
34	outw(FWCTL_OUT, 0);
35	for (u_int i = 0; i < 4; i++)
36		buf[i] = inb(FWCTL_IN);
37	if (memcmp(buf, "BHYV", 4) != 0)
38		errx(1, "Signature mismatch: %.4s", buf);
39}
40
41static void
42send_node_name(const char *name)
43{
44	uint32_t value;
45	size_t len;
46
47	len = strlen(name) + 1;
48	while (len > 4) {
49		memcpy(&value, name, 4);
50		outl(FWCTL_OUT, value);
51		name += 4;
52		len -= 4;
53	}
54
55	if (len > 0) {
56		value = 0;
57		memcpy(&value, name, len);
58		outl(FWCTL_OUT, value);
59	}
60}
61
62static void
63fwctl_op(uint32_t op, uint32_t id, const char *name, void *buf, size_t len)
64{
65	char *cp;
66	uint32_t value, rsplen;
67
68	/* Length */
69	outl(FWCTL_OUT, 12 + strlen(name) + 1);
70
71	/* Operation */
72	outl(FWCTL_OUT, op);
73
74	/* Transaction ID */
75	outl(FWCTL_OUT, id);
76
77	send_node_name(name);
78
79	/* Length */
80	rsplen = inl(FWCTL_IN);
81
82	/* If there is an error, the response will have no payload. */
83	if (rsplen < 4 * sizeof(value))
84		errx(1, "Invalid response length (%u): %u", id, rsplen);
85
86	/* Operation */
87	value = inl(FWCTL_IN);
88	if (value != op)
89		errx(1, "Invalid response type (%u): %u", id, value);
90
91	/* Transaction ID */
92	value = inl(FWCTL_IN);
93	if (value != id)
94		errx(1, "Invalid response ID (%u): %u", id, value);
95
96	/* Error */
97	value = inl(FWCTL_IN);
98	if (value != 0)
99		errx(1, "Error from op %u (%u): %u", op, id, value);
100
101	/* If there wasn't an error, require payload length to match */
102	if (rsplen != 4 * sizeof(value) + len)
103		errx(1, "Response payload length mismatch (%u): %zu vs %zu", id,
104		    rsplen - 4 * sizeof(value), len);
105
106	cp = buf;
107	while (len > 0) {
108		value = inl(FWCTL_IN);
109		memcpy(cp, &value, 4);
110		cp += 4;
111		len -= 4;
112	}
113}
114
115int
116main(int ac, char **av)
117{
118	char *p;
119	size_t len, buflen, len2;
120
121	if (ac != 2)
122		errx(1, "Need node name");
123
124	if (open("/dev/io", O_RDWR) == -1)
125		err(1, "Failed to open /dev/io");
126
127	reset_fwctl();
128
129	fwctl_op(OP_GET_LEN, 1, av[1], &len, sizeof(len));
130	if (len == 0)
131		errx(1, "Node has length of 0");
132
133	/* Buffer includes embedded length followed by value. */
134	buflen = sizeof(size_t) + roundup2(len, 4);
135	p = malloc(buflen);
136	fwctl_op(OP_GET, 2, av[1], p, buflen);
137	memcpy(&len2, p, sizeof(len2));
138	if (len2 != len)
139		errx(1, "Length mismatch: %zu vs %zu", len, len2);
140	hexdump(p + sizeof(len2), len, NULL, 0);
141
142	return (0);
143}
144