1fa68c148Smikeb /*-
2fa68c148Smikeb * Copyright (c) 2009-2016 Microsoft Corp.
3fa68c148Smikeb * Copyright (c) 2012 NetApp Inc.
4fa68c148Smikeb * Copyright (c) 2012 Citrix Inc.
5fa68c148Smikeb * Copyright (c) 2016 Mike Belopuhov <mike@esdenera.com>
6fa68c148Smikeb * All rights reserved.
7fa68c148Smikeb *
8fa68c148Smikeb * Redistribution and use in source and binary forms, with or without
9fa68c148Smikeb * modification, are permitted provided that the following conditions
10fa68c148Smikeb * are met:
11fa68c148Smikeb * 1. Redistributions of source code must retain the above copyright
12fa68c148Smikeb * notice unmodified, this list of conditions, and the following
13fa68c148Smikeb * disclaimer.
14fa68c148Smikeb * 2. Redistributions in binary form must reproduce the above copyright
15fa68c148Smikeb * notice, this list of conditions and the following disclaimer in the
16fa68c148Smikeb * documentation and/or other materials provided with the distribution.
17fa68c148Smikeb *
18fa68c148Smikeb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19fa68c148Smikeb * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20fa68c148Smikeb * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21fa68c148Smikeb * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22fa68c148Smikeb * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23fa68c148Smikeb * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24fa68c148Smikeb * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25fa68c148Smikeb * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26fa68c148Smikeb * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27fa68c148Smikeb * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28fa68c148Smikeb */
29fa68c148Smikeb
30fa68c148Smikeb /*
31fa68c148Smikeb * The OpenBSD port was done under funding by Esdenera Networks GmbH.
32fa68c148Smikeb */
33fa68c148Smikeb
34fa68c148Smikeb #include <sys/param.h>
35fa68c148Smikeb
36fa68c148Smikeb /* Hyperv requires locked atomic operations */
37fa68c148Smikeb #ifndef MULTIPROCESSOR
38fa68c148Smikeb #define _HYPERVMPATOMICS
39fa68c148Smikeb #define MULTIPROCESSOR
40fa68c148Smikeb #endif
41fa68c148Smikeb #include <sys/atomic.h>
42fa68c148Smikeb #ifdef _HYPERVMPATOMICS
43fa68c148Smikeb #undef MULTIPROCESSOR
44fa68c148Smikeb #undef _HYPERVMPATOMICS
45fa68c148Smikeb #endif
46fa68c148Smikeb
47fa68c148Smikeb #include <sys/systm.h>
48fa68c148Smikeb #include <sys/malloc.h>
49fa68c148Smikeb #include <sys/kernel.h>
50fa68c148Smikeb #include <sys/device.h>
51171ec5abSmikeb #include <sys/pool.h>
52fa68c148Smikeb #include <sys/task.h>
5344bcf650Sjsg #include <sys/sensors.h>
54fa68c148Smikeb
55fa68c148Smikeb #include <machine/bus.h>
56fa68c148Smikeb
57ea58ab61Smikeb #include <net/if.h>
58ea58ab61Smikeb #include <net/if_dl.h>
59ea58ab61Smikeb #include <netinet/in.h>
60ea58ab61Smikeb #include <netinet/if_ether.h>
61ea58ab61Smikeb
62fa68c148Smikeb #include <dev/pv/pvvar.h>
63fa68c148Smikeb #include <dev/pv/hypervreg.h>
64fa68c148Smikeb #include <dev/pv/hypervvar.h>
65fa68c148Smikeb #include <dev/pv/hypervicreg.h>
66fa68c148Smikeb
67aa8f2e47Smikeb struct hv_ic_dev;
68aa8f2e47Smikeb
69171ec5abSmikeb #define NKVPPOOLS 4
70171ec5abSmikeb #define MAXPOOLENTS 1023
71171ec5abSmikeb
72171ec5abSmikeb struct kvp_entry {
73171ec5abSmikeb int kpe_index;
74171ec5abSmikeb uint32_t kpe_valtype;
75171ec5abSmikeb uint8_t kpe_key[HV_KVP_MAX_KEY_SIZE / 2];
76171ec5abSmikeb uint8_t kpe_val[HV_KVP_MAX_VAL_SIZE / 2];
77171ec5abSmikeb TAILQ_ENTRY(kvp_entry) kpe_entry;
78171ec5abSmikeb };
79171ec5abSmikeb TAILQ_HEAD(kvp_list, kvp_entry);
80171ec5abSmikeb
81171ec5abSmikeb struct kvp_pool {
82171ec5abSmikeb struct kvp_list kvp_entries;
835fbe4244Smikeb struct mutex kvp_lock;
84171ec5abSmikeb u_int kvp_index;
85171ec5abSmikeb };
86171ec5abSmikeb
87171ec5abSmikeb struct pool kvp_entry_pool;
88171ec5abSmikeb
89171ec5abSmikeb struct hv_kvp {
90171ec5abSmikeb struct kvp_pool kvp_pool[NKVPPOOLS];
91171ec5abSmikeb };
92171ec5abSmikeb
93bc1634ffSmikeb int hv_heartbeat_attach(struct hv_ic_dev *);
94fa68c148Smikeb void hv_heartbeat(void *);
95bc1634ffSmikeb int hv_kvp_attach(struct hv_ic_dev *);
96fa68c148Smikeb void hv_kvp(void *);
97fa68c148Smikeb int hv_kvop(void *, int, char *, char *, size_t);
98bc1634ffSmikeb int hv_shutdown_attach(struct hv_ic_dev *);
99fa68c148Smikeb void hv_shutdown(void *);
100bc1634ffSmikeb int hv_timesync_attach(struct hv_ic_dev *);
101fa68c148Smikeb void hv_timesync(void *);
102fa68c148Smikeb
103171ec5abSmikeb static struct hv_ic_dev {
104fa68c148Smikeb const char *dv_name;
105fa68c148Smikeb const struct hv_guid *dv_type;
106bc1634ffSmikeb int (*dv_attach)(struct hv_ic_dev *);
107fa68c148Smikeb void (*dv_handler)(void *);
108aa8f2e47Smikeb struct hv_channel *dv_ch;
109aa8f2e47Smikeb uint8_t *dv_buf;
110171ec5abSmikeb void *dv_priv;
111fa68c148Smikeb } hv_ic_devs[] = {
112fa68c148Smikeb {
113bc1634ffSmikeb "heartbeat",
114fa68c148Smikeb &hv_guid_heartbeat,
115bc1634ffSmikeb hv_heartbeat_attach,
116fa68c148Smikeb hv_heartbeat
117fa68c148Smikeb },
118fa68c148Smikeb {
119bc1634ffSmikeb "kvp",
120fa68c148Smikeb &hv_guid_kvp,
121fa68c148Smikeb hv_kvp_attach,
122fa68c148Smikeb hv_kvp
123fa68c148Smikeb },
124fa68c148Smikeb {
125bc1634ffSmikeb "shutdown",
126fa68c148Smikeb &hv_guid_shutdown,
127fa68c148Smikeb hv_shutdown_attach,
128fa68c148Smikeb hv_shutdown
129fa68c148Smikeb },
130fa68c148Smikeb {
131bc1634ffSmikeb "timesync",
132fa68c148Smikeb &hv_guid_timesync,
133fa68c148Smikeb hv_timesync_attach,
134fa68c148Smikeb hv_timesync
135fa68c148Smikeb }
136fa68c148Smikeb };
137fa68c148Smikeb
138171ec5abSmikeb static const struct {
139171ec5abSmikeb enum hv_kvp_pool poolidx;
140171ec5abSmikeb const char *poolname;
141171ec5abSmikeb size_t poolnamelen;
142171ec5abSmikeb } kvp_pools[] = {
143171ec5abSmikeb { HV_KVP_POOL_EXTERNAL, "External", sizeof("External") },
144171ec5abSmikeb { HV_KVP_POOL_GUEST, "Guest", sizeof("Guest") },
145171ec5abSmikeb { HV_KVP_POOL_AUTO, "Auto", sizeof("Auto") },
146171ec5abSmikeb { HV_KVP_POOL_AUTO_EXTERNAL, "Guest/Parameters",
147171ec5abSmikeb sizeof("Guest/Parameters") }
148171ec5abSmikeb };
149171ec5abSmikeb
150171ec5abSmikeb static const struct {
151171ec5abSmikeb int keyidx;
152171ec5abSmikeb const char *keyname;
153171ec5abSmikeb const char *value;
154171ec5abSmikeb } kvp_pool_auto[] = {
155171ec5abSmikeb { 0, "FullyQualifiedDomainName", hostname },
156171ec5abSmikeb { 1, "IntegrationServicesVersion", "6.6.6" },
157171ec5abSmikeb { 2, "NetworkAddressIPv4", "127.0.0.1" },
158171ec5abSmikeb { 3, "NetworkAddressIPv6", "::1" },
159171ec5abSmikeb { 4, "OSBuildNumber", osversion },
160171ec5abSmikeb { 5, "OSName", ostype },
161171ec5abSmikeb { 6, "OSMajorVersion", "6" }, /* free commit for mike */
162171ec5abSmikeb { 7, "OSMinorVersion", &osrelease[2] },
163171ec5abSmikeb { 8, "OSVersion", osrelease },
164ea58ab61Smikeb #ifdef __amd64__ /* As specified in SYSTEM_INFO.wProcessorArchitecture */
165ea58ab61Smikeb { 9, "ProcessorArchitecture", "9" }
166ea58ab61Smikeb #else
167ea58ab61Smikeb { 9, "ProcessorArchitecture", "0" }
168ea58ab61Smikeb #endif
169171ec5abSmikeb };
170171ec5abSmikeb
171fa68c148Smikeb void
hv_attach_icdevs(struct hv_softc * sc)172fa68c148Smikeb hv_attach_icdevs(struct hv_softc *sc)
173fa68c148Smikeb {
174aa8f2e47Smikeb struct hv_ic_dev *dv;
175fa68c148Smikeb struct hv_channel *ch;
176fa68c148Smikeb int i, header = 0;
177fa68c148Smikeb
178aa8f2e47Smikeb for (i = 0; i < nitems(hv_ic_devs); i++) {
179aa8f2e47Smikeb dv = &hv_ic_devs[i];
180aa8f2e47Smikeb
181fa68c148Smikeb TAILQ_FOREACH(ch, &sc->sc_channels, ch_entry) {
182fa68c148Smikeb if (ch->ch_state != HV_CHANSTATE_OFFERED)
183fa68c148Smikeb continue;
184fa68c148Smikeb if (ch->ch_flags & CHF_MONITOR)
185fa68c148Smikeb continue;
186aa8f2e47Smikeb if (memcmp(dv->dv_type, &ch->ch_type,
187aa8f2e47Smikeb sizeof(ch->ch_type)) == 0)
188aa8f2e47Smikeb break;
189aa8f2e47Smikeb }
190aa8f2e47Smikeb if (ch == NULL)
191fa68c148Smikeb continue;
192aa8f2e47Smikeb
193aa8f2e47Smikeb dv->dv_ch = ch;
194aa8f2e47Smikeb
195fa68c148Smikeb /*
196fa68c148Smikeb * These services are not performance critical and
197fa68c148Smikeb * do not need batched reading. Furthermore, some
198fa68c148Smikeb * services such as KVP can only handle one message
199fa68c148Smikeb * from the host at a time.
200fa68c148Smikeb */
201aa8f2e47Smikeb dv->dv_ch->ch_flags &= ~CHF_BATCHED;
202fa68c148Smikeb
203bc1634ffSmikeb if (dv->dv_attach && dv->dv_attach(dv) != 0)
204fa68c148Smikeb continue;
205fa68c148Smikeb
20600998797Smikeb if (hv_channel_open(ch, VMBUS_IC_BUFRINGSIZE, NULL, 0,
20700998797Smikeb dv->dv_handler, dv)) {
208fa68c148Smikeb printf("%s: failed to open channel for %s\n",
209aa8f2e47Smikeb sc->sc_dev.dv_xname, dv->dv_name);
210fa68c148Smikeb continue;
211fa68c148Smikeb }
212aa8f2e47Smikeb evcount_attach(&ch->ch_evcnt, dv->dv_name, &sc->sc_idtvec);
213fa68c148Smikeb
214fa68c148Smikeb if (!header) {
215aa8f2e47Smikeb printf("%s: %s", sc->sc_dev.dv_xname, dv->dv_name);
216fa68c148Smikeb header = 1;
217fa68c148Smikeb } else
218aa8f2e47Smikeb printf(", %s", dv->dv_name);
219fa68c148Smikeb }
220fa68c148Smikeb if (header)
221fa68c148Smikeb printf("\n");
222fa68c148Smikeb }
223fa68c148Smikeb
224fa68c148Smikeb static inline void
hv_ic_negotiate(struct vmbus_icmsg_hdr * hdr,uint32_t * rlen,uint32_t fwver,uint32_t msgver)2251b88968fSmikeb hv_ic_negotiate(struct vmbus_icmsg_hdr *hdr, uint32_t *rlen, uint32_t fwver,
2261b88968fSmikeb uint32_t msgver)
227fa68c148Smikeb {
228fa68c148Smikeb struct vmbus_icmsg_negotiate *msg;
2291b88968fSmikeb uint16_t propmin, propmaj, chosenmaj, chosenmin;
2301b88968fSmikeb int i;
231fa68c148Smikeb
232fa68c148Smikeb msg = (struct vmbus_icmsg_negotiate *)hdr;
2331b88968fSmikeb
2341b88968fSmikeb chosenmaj = chosenmin = 0;
2351b88968fSmikeb for (i = 0; i < msg->ic_fwver_cnt; i++) {
2361b88968fSmikeb propmaj = VMBUS_ICVER_MAJOR(msg->ic_ver[i]);
2371b88968fSmikeb propmin = VMBUS_ICVER_MINOR(msg->ic_ver[i]);
2381b88968fSmikeb if (propmaj > chosenmaj &&
2391b88968fSmikeb propmaj <= VMBUS_ICVER_MAJOR(fwver) &&
2401b88968fSmikeb propmin >= chosenmin &&
2411b88968fSmikeb propmin <= VMBUS_ICVER_MINOR(fwver)) {
2421b88968fSmikeb chosenmaj = propmaj;
2431b88968fSmikeb chosenmin = propmin;
244fa68c148Smikeb }
2451b88968fSmikeb }
2461b88968fSmikeb fwver = VMBUS_IC_VERSION(chosenmaj, chosenmin);
2471b88968fSmikeb
2481b88968fSmikeb chosenmaj = chosenmin = 0;
2491b88968fSmikeb for (; i < msg->ic_fwver_cnt + msg->ic_msgver_cnt; i++) {
2501b88968fSmikeb propmaj = VMBUS_ICVER_MAJOR(msg->ic_ver[i]);
2511b88968fSmikeb propmin = VMBUS_ICVER_MINOR(msg->ic_ver[i]);
2521b88968fSmikeb if (propmaj > chosenmaj &&
2531b88968fSmikeb propmaj <= VMBUS_ICVER_MAJOR(msgver) &&
2541b88968fSmikeb propmin >= chosenmin &&
2551b88968fSmikeb propmin <= VMBUS_ICVER_MINOR(msgver)) {
2561b88968fSmikeb chosenmaj = propmaj;
2571b88968fSmikeb chosenmin = propmin;
2581b88968fSmikeb }
2591b88968fSmikeb }
2601b88968fSmikeb msgver = VMBUS_IC_VERSION(chosenmaj, chosenmin);
2611b88968fSmikeb
262fa68c148Smikeb msg->ic_fwver_cnt = 1;
2631b88968fSmikeb msg->ic_ver[0] = fwver;
264fa68c148Smikeb msg->ic_msgver_cnt = 1;
2651b88968fSmikeb msg->ic_ver[1] = msgver;
2661b88968fSmikeb hdr->ic_dsize = sizeof(*msg) + 2 * sizeof(uint32_t) -
2671b88968fSmikeb sizeof(struct vmbus_icmsg_hdr);
2681b88968fSmikeb if (*rlen < sizeof(*msg) + 2 * sizeof(uint32_t))
2691b88968fSmikeb *rlen = sizeof(*msg) + 2 * sizeof(uint32_t);
270fa68c148Smikeb }
271fa68c148Smikeb
272bc1634ffSmikeb int
hv_heartbeat_attach(struct hv_ic_dev * dv)273bc1634ffSmikeb hv_heartbeat_attach(struct hv_ic_dev *dv)
274bc1634ffSmikeb {
275bc1634ffSmikeb struct hv_channel *ch = dv->dv_ch;
276bc1634ffSmikeb struct hv_softc *sc = ch->ch_sc;
277bc1634ffSmikeb
278bc1634ffSmikeb dv->dv_buf = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO |
279bc1634ffSmikeb (cold ? M_NOWAIT : M_WAITOK));
280bc1634ffSmikeb if (dv->dv_buf == NULL) {
281bc1634ffSmikeb printf("%s: failed to allocate receive buffer\n",
282bc1634ffSmikeb sc->sc_dev.dv_xname);
283bc1634ffSmikeb return (-1);
284bc1634ffSmikeb }
285bc1634ffSmikeb return (0);
286bc1634ffSmikeb }
287bc1634ffSmikeb
288fa68c148Smikeb void
hv_heartbeat(void * arg)289fa68c148Smikeb hv_heartbeat(void *arg)
290fa68c148Smikeb {
291aa8f2e47Smikeb struct hv_ic_dev *dv = arg;
292aa8f2e47Smikeb struct hv_channel *ch = dv->dv_ch;
293fa68c148Smikeb struct hv_softc *sc = ch->ch_sc;
294fa68c148Smikeb struct vmbus_icmsg_hdr *hdr;
295fa68c148Smikeb struct vmbus_icmsg_heartbeat *msg;
296fa68c148Smikeb uint64_t rid;
297fa68c148Smikeb uint32_t rlen;
298fa68c148Smikeb int rv;
299fa68c148Smikeb
30000998797Smikeb rv = hv_channel_recv(ch, dv->dv_buf, PAGE_SIZE, &rlen, &rid, 0);
301fa68c148Smikeb if (rv || rlen == 0) {
302fa68c148Smikeb if (rv != EAGAIN)
303fa68c148Smikeb DPRINTF("%s: heartbeat rv=%d rlen=%u\n",
304fa68c148Smikeb sc->sc_dev.dv_xname, rv, rlen);
305fa68c148Smikeb return;
306fa68c148Smikeb }
307fa68c148Smikeb if (rlen < sizeof(struct vmbus_icmsg_hdr)) {
308fa68c148Smikeb DPRINTF("%s: heartbeat short read rlen=%u\n",
309fa68c148Smikeb sc->sc_dev.dv_xname, rlen);
310fa68c148Smikeb return;
311fa68c148Smikeb }
312aa8f2e47Smikeb hdr = (struct vmbus_icmsg_hdr *)dv->dv_buf;
313fa68c148Smikeb switch (hdr->ic_type) {
314fa68c148Smikeb case VMBUS_ICMSG_TYPE_NEGOTIATE:
3151b88968fSmikeb hv_ic_negotiate(hdr, &rlen, VMBUS_IC_VERSION(3, 0),
3161b88968fSmikeb VMBUS_IC_VERSION(3, 0));
317fa68c148Smikeb break;
318fa68c148Smikeb case VMBUS_ICMSG_TYPE_HEARTBEAT:
319fa68c148Smikeb msg = (struct vmbus_icmsg_heartbeat *)hdr;
320fa68c148Smikeb msg->ic_seq += 1;
321fa68c148Smikeb break;
322fa68c148Smikeb default:
323fa68c148Smikeb printf("%s: unhandled heartbeat message type %u\n",
324fa68c148Smikeb sc->sc_dev.dv_xname, hdr->ic_type);
325fa68c148Smikeb return;
326fa68c148Smikeb }
327fa68c148Smikeb hdr->ic_flags = VMBUS_ICMSG_FLAG_TRANSACTION | VMBUS_ICMSG_FLAG_RESPONSE;
328aa8f2e47Smikeb hv_channel_send(ch, dv->dv_buf, rlen, rid, VMBUS_CHANPKT_TYPE_INBAND, 0);
329fa68c148Smikeb }
330fa68c148Smikeb
331fa68c148Smikeb static void
hv_shutdown_task(void * arg)332fa68c148Smikeb hv_shutdown_task(void *arg)
333fa68c148Smikeb {
3340f265ed3Sreyk struct hv_softc *sc = arg;
3350f265ed3Sreyk pvbus_shutdown(&sc->sc_dev);
336fa68c148Smikeb }
337fa68c148Smikeb
338bc1634ffSmikeb int
hv_shutdown_attach(struct hv_ic_dev * dv)339aa8f2e47Smikeb hv_shutdown_attach(struct hv_ic_dev *dv)
340fa68c148Smikeb {
341aa8f2e47Smikeb struct hv_channel *ch = dv->dv_ch;
342fa68c148Smikeb struct hv_softc *sc = ch->ch_sc;
343fa68c148Smikeb
344bc1634ffSmikeb dv->dv_buf = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO |
345bc1634ffSmikeb (cold ? M_NOWAIT : M_WAITOK));
346bc1634ffSmikeb if (dv->dv_buf == NULL) {
347bc1634ffSmikeb printf("%s: failed to allocate receive buffer\n",
348bc1634ffSmikeb sc->sc_dev.dv_xname);
349bc1634ffSmikeb return (-1);
350bc1634ffSmikeb }
351bc1634ffSmikeb
352fa68c148Smikeb task_set(&sc->sc_sdtask, hv_shutdown_task, sc);
353bc1634ffSmikeb
354bc1634ffSmikeb return (0);
355fa68c148Smikeb }
356fa68c148Smikeb
357fa68c148Smikeb void
hv_shutdown(void * arg)358fa68c148Smikeb hv_shutdown(void *arg)
359fa68c148Smikeb {
360aa8f2e47Smikeb struct hv_ic_dev *dv = arg;
361aa8f2e47Smikeb struct hv_channel *ch = dv->dv_ch;
362fa68c148Smikeb struct hv_softc *sc = ch->ch_sc;
363fa68c148Smikeb struct vmbus_icmsg_hdr *hdr;
364fa68c148Smikeb struct vmbus_icmsg_shutdown *msg;
365fa68c148Smikeb uint64_t rid;
366fa68c148Smikeb uint32_t rlen;
367fa68c148Smikeb int rv, shutdown = 0;
368fa68c148Smikeb
36900998797Smikeb rv = hv_channel_recv(ch, dv->dv_buf, PAGE_SIZE, &rlen, &rid, 0);
370fa68c148Smikeb if (rv || rlen == 0) {
371fa68c148Smikeb if (rv != EAGAIN)
372fa68c148Smikeb DPRINTF("%s: shutdown rv=%d rlen=%u\n",
373fa68c148Smikeb sc->sc_dev.dv_xname, rv, rlen);
374fa68c148Smikeb return;
375fa68c148Smikeb }
376fa68c148Smikeb if (rlen < sizeof(struct vmbus_icmsg_hdr)) {
377fa68c148Smikeb DPRINTF("%s: shutdown short read rlen=%u\n",
378fa68c148Smikeb sc->sc_dev.dv_xname, rlen);
379fa68c148Smikeb return;
380fa68c148Smikeb }
381aa8f2e47Smikeb hdr = (struct vmbus_icmsg_hdr *)dv->dv_buf;
382fa68c148Smikeb switch (hdr->ic_type) {
383fa68c148Smikeb case VMBUS_ICMSG_TYPE_NEGOTIATE:
3841b88968fSmikeb hv_ic_negotiate(hdr, &rlen, VMBUS_IC_VERSION(3, 0),
3851b88968fSmikeb VMBUS_IC_VERSION(3, 0));
386fa68c148Smikeb break;
387fa68c148Smikeb case VMBUS_ICMSG_TYPE_SHUTDOWN:
388fa68c148Smikeb msg = (struct vmbus_icmsg_shutdown *)hdr;
389fa68c148Smikeb if (msg->ic_haltflags == 0 || msg->ic_haltflags == 1) {
390fa68c148Smikeb shutdown = 1;
391fa68c148Smikeb hdr->ic_status = VMBUS_ICMSG_STATUS_OK;
392fa68c148Smikeb } else
393fa68c148Smikeb hdr->ic_status = VMBUS_ICMSG_STATUS_FAIL;
394fa68c148Smikeb break;
395fa68c148Smikeb default:
396fa68c148Smikeb printf("%s: unhandled shutdown message type %u\n",
397fa68c148Smikeb sc->sc_dev.dv_xname, hdr->ic_type);
398fa68c148Smikeb return;
399fa68c148Smikeb }
400fa68c148Smikeb
401fa68c148Smikeb hdr->ic_flags = VMBUS_ICMSG_FLAG_TRANSACTION | VMBUS_ICMSG_FLAG_RESPONSE;
402aa8f2e47Smikeb hv_channel_send(ch, dv->dv_buf, rlen, rid, VMBUS_CHANPKT_TYPE_INBAND, 0);
403fa68c148Smikeb
404fa68c148Smikeb if (shutdown)
405fa68c148Smikeb task_add(systq, &sc->sc_sdtask);
406fa68c148Smikeb }
407fa68c148Smikeb
408bc1634ffSmikeb int
hv_timesync_attach(struct hv_ic_dev * dv)409aa8f2e47Smikeb hv_timesync_attach(struct hv_ic_dev *dv)
410fa68c148Smikeb {
411aa8f2e47Smikeb struct hv_channel *ch = dv->dv_ch;
412fa68c148Smikeb struct hv_softc *sc = ch->ch_sc;
413fa68c148Smikeb
414bc1634ffSmikeb dv->dv_buf = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO |
415bc1634ffSmikeb (cold ? M_NOWAIT : M_WAITOK));
416bc1634ffSmikeb if (dv->dv_buf == NULL) {
417bc1634ffSmikeb printf("%s: failed to allocate receive buffer\n",
418bc1634ffSmikeb sc->sc_dev.dv_xname);
419bc1634ffSmikeb return (-1);
420bc1634ffSmikeb }
421bc1634ffSmikeb
422fa68c148Smikeb strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
423fa68c148Smikeb sizeof(sc->sc_sensordev.xname));
424fa68c148Smikeb
425fa68c148Smikeb sc->sc_sensor.type = SENSOR_TIMEDELTA;
426fa68c148Smikeb sc->sc_sensor.status = SENSOR_S_UNKNOWN;
427fa68c148Smikeb
428fa68c148Smikeb sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
429fa68c148Smikeb sensordev_install(&sc->sc_sensordev);
430bc1634ffSmikeb
431bc1634ffSmikeb return (0);
432fa68c148Smikeb }
433fa68c148Smikeb
434fa68c148Smikeb void
hv_timesync(void * arg)435fa68c148Smikeb hv_timesync(void *arg)
436fa68c148Smikeb {
437aa8f2e47Smikeb struct hv_ic_dev *dv = arg;
438aa8f2e47Smikeb struct hv_channel *ch = dv->dv_ch;
439fa68c148Smikeb struct hv_softc *sc = ch->ch_sc;
440fa68c148Smikeb struct vmbus_icmsg_hdr *hdr;
441fa68c148Smikeb struct vmbus_icmsg_timesync *msg;
442fa68c148Smikeb struct timespec guest, host, diff;
443fa68c148Smikeb uint64_t tns;
444fa68c148Smikeb uint64_t rid;
445fa68c148Smikeb uint32_t rlen;
446fa68c148Smikeb int rv;
447fa68c148Smikeb
44800998797Smikeb rv = hv_channel_recv(ch, dv->dv_buf, PAGE_SIZE, &rlen, &rid, 0);
449fa68c148Smikeb if (rv || rlen == 0) {
450fa68c148Smikeb if (rv != EAGAIN)
451fa68c148Smikeb DPRINTF("%s: timesync rv=%d rlen=%u\n",
452fa68c148Smikeb sc->sc_dev.dv_xname, rv, rlen);
453fa68c148Smikeb return;
454fa68c148Smikeb }
455fa68c148Smikeb if (rlen < sizeof(struct vmbus_icmsg_hdr)) {
456fa68c148Smikeb DPRINTF("%s: timesync short read rlen=%u\n",
457fa68c148Smikeb sc->sc_dev.dv_xname, rlen);
458fa68c148Smikeb return;
459fa68c148Smikeb }
460aa8f2e47Smikeb hdr = (struct vmbus_icmsg_hdr *)dv->dv_buf;
461fa68c148Smikeb switch (hdr->ic_type) {
462fa68c148Smikeb case VMBUS_ICMSG_TYPE_NEGOTIATE:
4631b88968fSmikeb hv_ic_negotiate(hdr, &rlen, VMBUS_IC_VERSION(3, 0),
4641b88968fSmikeb VMBUS_IC_VERSION(3, 0));
465fa68c148Smikeb break;
466fa68c148Smikeb case VMBUS_ICMSG_TYPE_TIMESYNC:
467fa68c148Smikeb msg = (struct vmbus_icmsg_timesync *)hdr;
46854c3cf97Sreyk if (msg->ic_tsflags == VMBUS_ICMSG_TS_FLAG_SAMPLE) {
469fa68c148Smikeb microtime(&sc->sc_sensor.tv);
470fa68c148Smikeb nanotime(&guest);
471fa68c148Smikeb tns = (msg->ic_hvtime - 116444736000000000LL) * 100;
472fa68c148Smikeb host.tv_sec = tns / 1000000000LL;
473fa68c148Smikeb host.tv_nsec = tns % 1000000000LL;
474fa68c148Smikeb timespecsub(&guest, &host, &diff);
475fa68c148Smikeb sc->sc_sensor.value = (int64_t)diff.tv_sec *
476fa68c148Smikeb 1000000000LL + diff.tv_nsec;
477fa68c148Smikeb sc->sc_sensor.status = SENSOR_S_OK;
478fa68c148Smikeb }
479fa68c148Smikeb break;
480fa68c148Smikeb default:
481fa68c148Smikeb printf("%s: unhandled timesync message type %u\n",
482fa68c148Smikeb sc->sc_dev.dv_xname, hdr->ic_type);
483fa68c148Smikeb return;
484fa68c148Smikeb }
485fa68c148Smikeb
486fa68c148Smikeb hdr->ic_flags = VMBUS_ICMSG_FLAG_TRANSACTION | VMBUS_ICMSG_FLAG_RESPONSE;
487aa8f2e47Smikeb hv_channel_send(ch, dv->dv_buf, rlen, rid, VMBUS_CHANPKT_TYPE_INBAND, 0);
488aa8f2e47Smikeb }
489aa8f2e47Smikeb
490171ec5abSmikeb static inline int
copyout_utf16le(void * dst,const void * src,size_t dlen,size_t slen)491171ec5abSmikeb copyout_utf16le(void *dst, const void *src, size_t dlen, size_t slen)
492171ec5abSmikeb {
493171ec5abSmikeb const uint8_t *sp = src;
494171ec5abSmikeb uint8_t *dp = dst;
495171ec5abSmikeb int i, j;
496171ec5abSmikeb
497171ec5abSmikeb KASSERT(dlen >= slen * 2);
498171ec5abSmikeb
499171ec5abSmikeb for (i = j = 0; i < slen; i++, j += 2) {
500171ec5abSmikeb dp[j] = sp[i];
501171ec5abSmikeb dp[j + 1] = '\0';
502171ec5abSmikeb }
503171ec5abSmikeb return (j);
504171ec5abSmikeb }
505171ec5abSmikeb
506171ec5abSmikeb static inline int
copyin_utf16le(void * dst,const void * src,size_t dlen,size_t slen)507171ec5abSmikeb copyin_utf16le(void *dst, const void *src, size_t dlen, size_t slen)
508171ec5abSmikeb {
509171ec5abSmikeb const uint8_t *sp = src;
510171ec5abSmikeb uint8_t *dp = dst;
511171ec5abSmikeb int i, j;
512171ec5abSmikeb
513171ec5abSmikeb KASSERT(dlen >= slen / 2);
514171ec5abSmikeb
515171ec5abSmikeb for (i = j = 0; i < slen; i += 2, j++)
516171ec5abSmikeb dp[j] = sp[i];
517171ec5abSmikeb return (j);
518171ec5abSmikeb }
519171ec5abSmikeb
520d3ffed7fSmikeb static inline int
keycmp_utf16le(const uint8_t * key,const uint8_t * ukey,size_t ukeylen)521d3ffed7fSmikeb keycmp_utf16le(const uint8_t *key, const uint8_t *ukey, size_t ukeylen)
522d3ffed7fSmikeb {
523d3ffed7fSmikeb int i, j;
524d3ffed7fSmikeb
525d3ffed7fSmikeb for (i = j = 0; i < ukeylen; i += 2, j++) {
526d3ffed7fSmikeb if (key[j] != ukey[i])
527d3ffed7fSmikeb return (key[j] > ukey[i] ?
528d3ffed7fSmikeb key[j] - ukey[i] :
529d3ffed7fSmikeb ukey[i] - key[j]);
530d3ffed7fSmikeb }
531d3ffed7fSmikeb return (0);
532d3ffed7fSmikeb }
533d3ffed7fSmikeb
534171ec5abSmikeb static void
kvp_pool_init(struct kvp_pool * kvpl)535171ec5abSmikeb kvp_pool_init(struct kvp_pool *kvpl)
536171ec5abSmikeb {
537171ec5abSmikeb TAILQ_INIT(&kvpl->kvp_entries);
5385fbe4244Smikeb mtx_init(&kvpl->kvp_lock, IPL_NET);
5395fbe4244Smikeb kvpl->kvp_index = 0;
540171ec5abSmikeb }
541171ec5abSmikeb
542171ec5abSmikeb static int
kvp_pool_insert(struct kvp_pool * kvpl,const char * key,const char * val,uint32_t vallen,uint32_t valtype)543171ec5abSmikeb kvp_pool_insert(struct kvp_pool *kvpl, const char *key, const char *val,
544171ec5abSmikeb uint32_t vallen, uint32_t valtype)
545171ec5abSmikeb {
546d3ffed7fSmikeb struct kvp_entry *kpe;
547171ec5abSmikeb int keylen = strlen(key);
548171ec5abSmikeb
549171ec5abSmikeb if (keylen > HV_KVP_MAX_KEY_SIZE / 2)
550171ec5abSmikeb return (ERANGE);
551171ec5abSmikeb
5525fbe4244Smikeb mtx_enter(&kvpl->kvp_lock);
5535fbe4244Smikeb
554171ec5abSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
5555fbe4244Smikeb if (strcmp(kpe->kpe_key, key) == 0) {
5565fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
557171ec5abSmikeb return (EEXIST);
558171ec5abSmikeb }
5595fbe4244Smikeb }
560171ec5abSmikeb
561d3ffed7fSmikeb kpe = pool_get(&kvp_entry_pool, PR_ZERO | PR_NOWAIT);
562755dbb03Sjsg if (kpe == NULL) {
563755dbb03Sjsg mtx_leave(&kvpl->kvp_lock);
564171ec5abSmikeb return (ENOMEM);
565755dbb03Sjsg }
566171ec5abSmikeb
567d3ffed7fSmikeb strlcpy(kpe->kpe_key, key, HV_KVP_MAX_KEY_SIZE / 2);
568171ec5abSmikeb
569d3ffed7fSmikeb if ((kpe->kpe_valtype = valtype) == HV_KVP_REG_SZ)
570d3ffed7fSmikeb strlcpy(kpe->kpe_val, val, HV_KVP_MAX_KEY_SIZE / 2);
571171ec5abSmikeb else
572d3ffed7fSmikeb memcpy(kpe->kpe_val, val, vallen);
573171ec5abSmikeb
574d3ffed7fSmikeb kpe->kpe_index = kvpl->kvp_index++ & MAXPOOLENTS;
575171ec5abSmikeb
576d3ffed7fSmikeb TAILQ_INSERT_TAIL(&kvpl->kvp_entries, kpe, kpe_entry);
577171ec5abSmikeb
5785fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
5795fbe4244Smikeb
580171ec5abSmikeb return (0);
581171ec5abSmikeb }
582171ec5abSmikeb
583171ec5abSmikeb static int
kvp_pool_update(struct kvp_pool * kvpl,const char * key,const char * val,uint32_t vallen,uint32_t valtype)584171ec5abSmikeb kvp_pool_update(struct kvp_pool *kvpl, const char *key, const char *val,
585171ec5abSmikeb uint32_t vallen, uint32_t valtype)
586171ec5abSmikeb {
587171ec5abSmikeb struct kvp_entry *kpe;
588171ec5abSmikeb int keylen = strlen(key);
589171ec5abSmikeb
590171ec5abSmikeb if (keylen > HV_KVP_MAX_KEY_SIZE / 2)
591171ec5abSmikeb return (ERANGE);
592171ec5abSmikeb
5935fbe4244Smikeb mtx_enter(&kvpl->kvp_lock);
5945fbe4244Smikeb
595171ec5abSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
596171ec5abSmikeb if (strcmp(kpe->kpe_key, key) == 0)
597171ec5abSmikeb break;
598171ec5abSmikeb }
5995fbe4244Smikeb if (kpe == NULL) {
6005fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
601171ec5abSmikeb return (ENOENT);
6025fbe4244Smikeb }
603171ec5abSmikeb
604171ec5abSmikeb if ((kpe->kpe_valtype = valtype) == HV_KVP_REG_SZ)
605171ec5abSmikeb strlcpy(kpe->kpe_val, val, HV_KVP_MAX_KEY_SIZE / 2);
606171ec5abSmikeb else
607171ec5abSmikeb memcpy(kpe->kpe_val, val, vallen);
608171ec5abSmikeb
6095fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
6105fbe4244Smikeb
611171ec5abSmikeb return (0);
612171ec5abSmikeb }
613171ec5abSmikeb
614171ec5abSmikeb static int
kvp_pool_import(struct kvp_pool * kvpl,const char * key,uint32_t keylen,const char * val,uint32_t vallen,uint32_t valtype)615171ec5abSmikeb kvp_pool_import(struct kvp_pool *kvpl, const char *key, uint32_t keylen,
616171ec5abSmikeb const char *val, uint32_t vallen, uint32_t valtype)
617171ec5abSmikeb {
618d3ffed7fSmikeb struct kvp_entry *kpe;
619171ec5abSmikeb
620171ec5abSmikeb if (keylen > HV_KVP_MAX_KEY_SIZE ||
621171ec5abSmikeb vallen > HV_KVP_MAX_VAL_SIZE)
622171ec5abSmikeb return (ERANGE);
623171ec5abSmikeb
6245fbe4244Smikeb mtx_enter(&kvpl->kvp_lock);
6255fbe4244Smikeb
626171ec5abSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
627d3ffed7fSmikeb if (keycmp_utf16le(kpe->kpe_key, key, keylen) == 0)
628d3ffed7fSmikeb break;
629171ec5abSmikeb }
630d3ffed7fSmikeb if (kpe == NULL) {
631d3ffed7fSmikeb kpe = pool_get(&kvp_entry_pool, PR_ZERO | PR_NOWAIT);
632755dbb03Sjsg if (kpe == NULL) {
633755dbb03Sjsg mtx_leave(&kvpl->kvp_lock);
634d3ffed7fSmikeb return (ENOMEM);
635755dbb03Sjsg }
636d3ffed7fSmikeb
637d3ffed7fSmikeb copyin_utf16le(kpe->kpe_key, key, HV_KVP_MAX_KEY_SIZE / 2,
638d3ffed7fSmikeb keylen);
639d3ffed7fSmikeb
640d3ffed7fSmikeb kpe->kpe_index = kvpl->kvp_index++ & MAXPOOLENTS;
641d3ffed7fSmikeb
642d3ffed7fSmikeb TAILQ_INSERT_TAIL(&kvpl->kvp_entries, kpe, kpe_entry);
643171ec5abSmikeb }
644171ec5abSmikeb
645d3ffed7fSmikeb copyin_utf16le(kpe->kpe_val, val, HV_KVP_MAX_VAL_SIZE / 2, vallen);
646d3ffed7fSmikeb kpe->kpe_valtype = valtype;
647171ec5abSmikeb
6485fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
649171ec5abSmikeb
650171ec5abSmikeb return (0);
651171ec5abSmikeb }
652171ec5abSmikeb
653171ec5abSmikeb static int
kvp_pool_export(struct kvp_pool * kvpl,uint32_t index,char * key,uint32_t * keylen,char * val,uint32_t * vallen,uint32_t * valtype)654171ec5abSmikeb kvp_pool_export(struct kvp_pool *kvpl, uint32_t index, char *key,
655171ec5abSmikeb uint32_t *keylen, char *val, uint32_t *vallen, uint32_t *valtype)
656171ec5abSmikeb {
657171ec5abSmikeb struct kvp_entry *kpe;
658171ec5abSmikeb
6595fbe4244Smikeb mtx_enter(&kvpl->kvp_lock);
6605fbe4244Smikeb
661171ec5abSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
662171ec5abSmikeb if (kpe->kpe_index == index)
663171ec5abSmikeb break;
664171ec5abSmikeb }
6655fbe4244Smikeb if (kpe == NULL) {
6665fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
667171ec5abSmikeb return (ENOENT);
6685fbe4244Smikeb }
669171ec5abSmikeb
670171ec5abSmikeb *keylen = copyout_utf16le(key, kpe->kpe_key, HV_KVP_MAX_KEY_SIZE,
671171ec5abSmikeb strlen(kpe->kpe_key) + 1);
672171ec5abSmikeb *vallen = copyout_utf16le(val, kpe->kpe_val, HV_KVP_MAX_VAL_SIZE,
673171ec5abSmikeb strlen(kpe->kpe_val) + 1);
674171ec5abSmikeb *valtype = kpe->kpe_valtype;
675171ec5abSmikeb
6765fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
6775fbe4244Smikeb
678171ec5abSmikeb return (0);
679171ec5abSmikeb }
680171ec5abSmikeb
681171ec5abSmikeb static int
kvp_pool_remove(struct kvp_pool * kvpl,const char * key,uint32_t keylen)682d3ffed7fSmikeb kvp_pool_remove(struct kvp_pool *kvpl, const char *key, uint32_t keylen)
683d3ffed7fSmikeb {
684d3ffed7fSmikeb struct kvp_entry *kpe;
685d3ffed7fSmikeb
686d3ffed7fSmikeb mtx_enter(&kvpl->kvp_lock);
687d3ffed7fSmikeb
688d3ffed7fSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
689d3ffed7fSmikeb if (keycmp_utf16le(kpe->kpe_key, key, keylen) == 0)
690d3ffed7fSmikeb break;
691d3ffed7fSmikeb }
692d3ffed7fSmikeb if (kpe == NULL) {
693d3ffed7fSmikeb mtx_leave(&kvpl->kvp_lock);
694d3ffed7fSmikeb return (ENOENT);
695d3ffed7fSmikeb }
696d3ffed7fSmikeb
697d3ffed7fSmikeb TAILQ_REMOVE(&kvpl->kvp_entries, kpe, kpe_entry);
698d3ffed7fSmikeb
699d3ffed7fSmikeb mtx_leave(&kvpl->kvp_lock);
700d3ffed7fSmikeb
701d3ffed7fSmikeb pool_put(&kvp_entry_pool, kpe);
702d3ffed7fSmikeb
703d3ffed7fSmikeb return (0);
704d3ffed7fSmikeb }
705d3ffed7fSmikeb
706d3ffed7fSmikeb static int
kvp_pool_extract(struct kvp_pool * kvpl,const char * key,char * val,uint32_t vallen)707171ec5abSmikeb kvp_pool_extract(struct kvp_pool *kvpl, const char *key, char *val,
708171ec5abSmikeb uint32_t vallen)
709171ec5abSmikeb {
710171ec5abSmikeb struct kvp_entry *kpe;
711171ec5abSmikeb
712171ec5abSmikeb if (vallen < HV_KVP_MAX_VAL_SIZE / 2)
713171ec5abSmikeb return (ERANGE);
714171ec5abSmikeb
7155fbe4244Smikeb mtx_enter(&kvpl->kvp_lock);
7165fbe4244Smikeb
717171ec5abSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
718171ec5abSmikeb if (strcmp(kpe->kpe_key, key) == 0)
719171ec5abSmikeb break;
720171ec5abSmikeb }
7215fbe4244Smikeb if (kpe == NULL) {
7225fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
723171ec5abSmikeb return (ENOENT);
7245fbe4244Smikeb }
725171ec5abSmikeb
726171ec5abSmikeb switch (kpe->kpe_valtype) {
727171ec5abSmikeb case HV_KVP_REG_SZ:
728171ec5abSmikeb strlcpy(val, kpe->kpe_val, HV_KVP_MAX_VAL_SIZE / 2);
729171ec5abSmikeb break;
730171ec5abSmikeb case HV_KVP_REG_U32:
731171ec5abSmikeb snprintf(val, HV_KVP_MAX_VAL_SIZE / 2, "%u",
732171ec5abSmikeb *(uint32_t *)kpe->kpe_val);
733171ec5abSmikeb break;
734171ec5abSmikeb case HV_KVP_REG_U64:
735171ec5abSmikeb snprintf(val, HV_KVP_MAX_VAL_SIZE / 2, "%llu",
736171ec5abSmikeb *(uint64_t *)kpe->kpe_val);
737171ec5abSmikeb break;
738171ec5abSmikeb }
739171ec5abSmikeb
7405fbe4244Smikeb mtx_leave(&kvpl->kvp_lock);
7415fbe4244Smikeb
742171ec5abSmikeb return (0);
743171ec5abSmikeb }
744171ec5abSmikeb
745171ec5abSmikeb static int
kvp_pool_keys(struct kvp_pool * kvpl,int next,char * key,size_t * keylen)746171ec5abSmikeb kvp_pool_keys(struct kvp_pool *kvpl, int next, char *key, size_t *keylen)
747171ec5abSmikeb {
748171ec5abSmikeb struct kvp_entry *kpe;
749171ec5abSmikeb int iter = 0;
750171ec5abSmikeb
7511ab70bb7Sasou mtx_enter(&kvpl->kvp_lock);
7521ab70bb7Sasou
753171ec5abSmikeb TAILQ_FOREACH(kpe, &kvpl->kvp_entries, kpe_entry) {
754171ec5abSmikeb if (iter++ < next)
755171ec5abSmikeb continue;
756171ec5abSmikeb *keylen = strlen(kpe->kpe_key) + 1;
757171ec5abSmikeb strlcpy(key, kpe->kpe_key, *keylen);
7581ab70bb7Sasou
7591ab70bb7Sasou mtx_leave(&kvpl->kvp_lock);
7601ab70bb7Sasou
761171ec5abSmikeb return (0);
762171ec5abSmikeb }
763171ec5abSmikeb
7641ab70bb7Sasou mtx_leave(&kvpl->kvp_lock);
7651ab70bb7Sasou
766171ec5abSmikeb return (-1);
767171ec5abSmikeb }
768171ec5abSmikeb
769bc1634ffSmikeb int
hv_kvp_attach(struct hv_ic_dev * dv)770aa8f2e47Smikeb hv_kvp_attach(struct hv_ic_dev *dv)
771aa8f2e47Smikeb {
772aa8f2e47Smikeb struct hv_channel *ch = dv->dv_ch;
773aa8f2e47Smikeb struct hv_softc *sc = ch->ch_sc;
774171ec5abSmikeb struct hv_kvp *kvp;
775171ec5abSmikeb int i;
776171ec5abSmikeb
777171ec5abSmikeb dv->dv_buf = malloc(2 * PAGE_SIZE, M_DEVBUF, M_ZERO |
778171ec5abSmikeb (cold ? M_NOWAIT : M_WAITOK));
779171ec5abSmikeb if (dv->dv_buf == NULL) {
780171ec5abSmikeb printf("%s: failed to allocate receive buffer\n",
781171ec5abSmikeb sc->sc_dev.dv_xname);
782171ec5abSmikeb return (-1);
783171ec5abSmikeb }
784171ec5abSmikeb
785171ec5abSmikeb dv->dv_priv = malloc(sizeof(struct hv_kvp), M_DEVBUF, M_ZERO |
786171ec5abSmikeb (cold ? M_NOWAIT : M_WAITOK));
787171ec5abSmikeb if (dv->dv_priv == NULL) {
788171ec5abSmikeb free(dv->dv_buf, M_DEVBUF, 2 * PAGE_SIZE);
789171ec5abSmikeb printf("%s: failed to allocate KVP private data\n",
790171ec5abSmikeb sc->sc_dev.dv_xname);
791171ec5abSmikeb return (-1);
792171ec5abSmikeb }
793171ec5abSmikeb kvp = dv->dv_priv;
794171ec5abSmikeb
795171ec5abSmikeb pool_init(&kvp_entry_pool, sizeof(struct kvp_entry), 0, IPL_NET, 0,
796171ec5abSmikeb "hvkvpl", NULL);
797171ec5abSmikeb
798171ec5abSmikeb for (i = 0; i < NKVPPOOLS; i++)
799171ec5abSmikeb kvp_pool_init(&kvp->kvp_pool[i]);
800171ec5abSmikeb
801d3ffed7fSmikeb /* Initialize 'Auto' pool */
802171ec5abSmikeb for (i = 0; i < nitems(kvp_pool_auto); i++) {
803171ec5abSmikeb if (kvp_pool_insert(&kvp->kvp_pool[HV_KVP_POOL_AUTO],
804171ec5abSmikeb kvp_pool_auto[i].keyname, kvp_pool_auto[i].value,
805171ec5abSmikeb strlen(kvp_pool_auto[i].value), HV_KVP_REG_SZ))
806d3ffed7fSmikeb DPRINTF("%s: failed to insert into 'Auto' pool\n",
807d3ffed7fSmikeb sc->sc_dev.dv_xname);
808171ec5abSmikeb }
809aa8f2e47Smikeb
810aa8f2e47Smikeb sc->sc_pvbus->hv_kvop = hv_kvop;
811171ec5abSmikeb sc->sc_pvbus->hv_arg = dv;
812bc1634ffSmikeb
813bc1634ffSmikeb return (0);
814aa8f2e47Smikeb }
815aa8f2e47Smikeb
816ea58ab61Smikeb static int
nibble(int ch)817ea58ab61Smikeb nibble(int ch)
818ea58ab61Smikeb {
819ea58ab61Smikeb if (ch >= '0' && ch <= '9')
820ea58ab61Smikeb return (ch - '0');
821ea58ab61Smikeb if (ch >= 'A' && ch <= 'F')
822ea58ab61Smikeb return (10 + ch - 'A');
823ea58ab61Smikeb if (ch >= 'a' && ch <= 'f')
824ea58ab61Smikeb return (10 + ch - 'a');
825ea58ab61Smikeb return (-1);
826ea58ab61Smikeb }
827ea58ab61Smikeb
828ea58ab61Smikeb static int
kvp_get_ip_info(struct hv_kvp * kvp,const uint8_t * mac,uint8_t * family,uint8_t * addr,uint8_t * netmask,size_t addrlen)829ea58ab61Smikeb kvp_get_ip_info(struct hv_kvp *kvp, const uint8_t *mac, uint8_t *family,
830ea58ab61Smikeb uint8_t *addr, uint8_t *netmask, size_t addrlen)
831ea58ab61Smikeb {
832ea58ab61Smikeb struct ifnet *ifp;
833ea58ab61Smikeb struct ifaddr *ifa, *ifa6, *ifa6ll;
834ea58ab61Smikeb struct sockaddr_in *sin;
835ea58ab61Smikeb struct sockaddr_in6 *sin6, sa6;
836ea58ab61Smikeb uint8_t enaddr[ETHER_ADDR_LEN];
837ea58ab61Smikeb uint8_t ipaddr[INET6_ADDRSTRLEN];
838*8dedfb50Smvs int i, j, lo, hi, s, af;
839ea58ab61Smikeb
840ea58ab61Smikeb /* Convert from the UTF-16LE string format to binary */
841ea58ab61Smikeb for (i = 0, j = 0; j < ETHER_ADDR_LEN; i += 6) {
842ea58ab61Smikeb if ((hi = nibble(mac[i])) == -1 ||
843ea58ab61Smikeb (lo = nibble(mac[i+2])) == -1)
844ea58ab61Smikeb return (-1);
845ea58ab61Smikeb enaddr[j++] = hi << 4 | lo;
846ea58ab61Smikeb }
847ea58ab61Smikeb
848ea58ab61Smikeb switch (*family) {
849ea58ab61Smikeb case ADDR_FAMILY_NONE:
850ea58ab61Smikeb af = AF_UNSPEC;
851ea58ab61Smikeb break;
852ea58ab61Smikeb case ADDR_FAMILY_IPV4:
853ea58ab61Smikeb af = AF_INET;
854ea58ab61Smikeb break;
855ea58ab61Smikeb case ADDR_FAMILY_IPV6:
856ea58ab61Smikeb af = AF_INET6;
857ea58ab61Smikeb break;
85881280da7Smikeb default:
85981280da7Smikeb return (-1);
860ea58ab61Smikeb }
861ea58ab61Smikeb
862*8dedfb50Smvs KERNEL_LOCK();
863*8dedfb50Smvs s = splnet();
864ea58ab61Smikeb
865ccb45f8eSkn TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
866ea58ab61Smikeb if (!memcmp(LLADDR(ifp->if_sadl), enaddr, ETHER_ADDR_LEN))
867ea58ab61Smikeb break;
868ea58ab61Smikeb }
869ea58ab61Smikeb if (ifp == NULL) {
870*8dedfb50Smvs splx(s);
871*8dedfb50Smvs KERNEL_UNLOCK();
872ea58ab61Smikeb return (-1);
873ea58ab61Smikeb }
874ea58ab61Smikeb
875ea58ab61Smikeb ifa6 = ifa6ll = NULL;
876ea58ab61Smikeb
877ea58ab61Smikeb /* Try to find a best matching address, preferring IPv4 */
878ea58ab61Smikeb TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
879ea58ab61Smikeb /*
880ea58ab61Smikeb * First IPv4 address is always a best match unless
881b3af768dSjsg * we were asked for an IPv6 address.
882ea58ab61Smikeb */
883ea58ab61Smikeb if ((af == AF_INET || af == AF_UNSPEC) &&
884ea58ab61Smikeb (ifa->ifa_addr->sa_family == AF_INET)) {
885ea58ab61Smikeb af = AF_INET;
886ea58ab61Smikeb goto found;
887ea58ab61Smikeb }
888ea58ab61Smikeb if ((af == AF_INET6 || af == AF_UNSPEC) &&
889ea58ab61Smikeb (ifa->ifa_addr->sa_family == AF_INET6)) {
890ea58ab61Smikeb if (!IN6_IS_ADDR_LINKLOCAL(
891ea58ab61Smikeb &satosin6(ifa->ifa_addr)->sin6_addr)) {
892ea58ab61Smikeb /* Done if we're looking for an IPv6 address */
893ea58ab61Smikeb if (af == AF_INET6)
894ea58ab61Smikeb goto found;
895ea58ab61Smikeb /* Stick to the first one */
896ea58ab61Smikeb if (ifa6 == NULL)
897ea58ab61Smikeb ifa6 = ifa;
898ea58ab61Smikeb } else /* Pick the last one */
899ea58ab61Smikeb ifa6ll = ifa;
900ea58ab61Smikeb }
901ea58ab61Smikeb }
902ea58ab61Smikeb /* If we haven't found any IPv4 or IPv6 direct matches... */
903ea58ab61Smikeb if (ifa == NULL) {
904ea58ab61Smikeb /* ... try the last global IPv6 address... */
905ea58ab61Smikeb if (ifa6 != NULL)
906ea58ab61Smikeb ifa = ifa6;
907ea58ab61Smikeb /* ... or the last link-local... */
908ea58ab61Smikeb else if (ifa6ll != NULL)
909ea58ab61Smikeb ifa = ifa6ll;
910ea58ab61Smikeb else {
911*8dedfb50Smvs splx(s);
912*8dedfb50Smvs KERNEL_UNLOCK();
913ea58ab61Smikeb return (-1);
914ea58ab61Smikeb }
915ea58ab61Smikeb }
916ea58ab61Smikeb found:
917ea58ab61Smikeb switch (af) {
918ea58ab61Smikeb case AF_INET:
919ea58ab61Smikeb sin = satosin(ifa->ifa_addr);
920ea58ab61Smikeb inet_ntop(AF_INET, &sin->sin_addr, ipaddr, sizeof(ipaddr));
921ea58ab61Smikeb copyout_utf16le(addr, ipaddr, addrlen, INET_ADDRSTRLEN);
922ea58ab61Smikeb
923ea58ab61Smikeb sin = satosin(ifa->ifa_netmask);
924ea58ab61Smikeb inet_ntop(AF_INET, &sin->sin_addr, ipaddr, sizeof(ipaddr));
925ea58ab61Smikeb copyout_utf16le(netmask, ipaddr, addrlen, INET_ADDRSTRLEN);
926ea58ab61Smikeb
927ea58ab61Smikeb *family = ADDR_FAMILY_IPV4;
928ea58ab61Smikeb break;
929ea58ab61Smikeb case AF_UNSPEC:
930ea58ab61Smikeb case AF_INET6:
931ea58ab61Smikeb sin6 = satosin6(ifa->ifa_addr);
932ea58ab61Smikeb if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
933ea58ab61Smikeb sa6 = *satosin6(ifa->ifa_addr);
934ea58ab61Smikeb sa6.sin6_addr.s6_addr16[1] = 0;
935ea58ab61Smikeb sin6 = &sa6;
936ea58ab61Smikeb }
937ea58ab61Smikeb inet_ntop(AF_INET6, &sin6->sin6_addr, ipaddr, sizeof(ipaddr));
938ea58ab61Smikeb copyout_utf16le(addr, ipaddr, addrlen, INET6_ADDRSTRLEN);
939ea58ab61Smikeb
940ea58ab61Smikeb sin6 = satosin6(ifa->ifa_netmask);
941ea58ab61Smikeb inet_ntop(AF_INET6, &sin6->sin6_addr, ipaddr, sizeof(ipaddr));
942ea58ab61Smikeb copyout_utf16le(netmask, ipaddr, addrlen, INET6_ADDRSTRLEN);
943ea58ab61Smikeb
944ea58ab61Smikeb *family = ADDR_FAMILY_IPV6;
945ea58ab61Smikeb break;
946ea58ab61Smikeb }
947ea58ab61Smikeb
948*8dedfb50Smvs splx(s);
949*8dedfb50Smvs KERNEL_UNLOCK();
950ea58ab61Smikeb
951ea58ab61Smikeb return (0);
952ea58ab61Smikeb }
953171ec5abSmikeb
954171ec5abSmikeb static void
hv_kvp_process(struct hv_kvp * kvp,struct vmbus_icmsg_kvp * msg)955171ec5abSmikeb hv_kvp_process(struct hv_kvp *kvp, struct vmbus_icmsg_kvp *msg)
956171ec5abSmikeb {
957171ec5abSmikeb union hv_kvp_hdr *kvh = &msg->ic_kvh;
958171ec5abSmikeb union hv_kvp_msg *kvm = &msg->ic_kvm;
959171ec5abSmikeb
960171ec5abSmikeb switch (kvh->kvh_op) {
961171ec5abSmikeb case HV_KVP_OP_SET:
962171ec5abSmikeb if (kvh->kvh_pool == HV_KVP_POOL_AUTO_EXTERNAL &&
963171ec5abSmikeb kvp_pool_import(&kvp->kvp_pool[HV_KVP_POOL_AUTO_EXTERNAL],
964171ec5abSmikeb kvm->kvm_val.kvm_key, kvm->kvm_val.kvm_keylen,
965171ec5abSmikeb kvm->kvm_val.kvm_val, kvm->kvm_val.kvm_vallen,
966171ec5abSmikeb kvm->kvm_val.kvm_valtype)) {
967d3ffed7fSmikeb DPRINTF("%s: failed to import into 'Guest/Parameters'"
968d3ffed7fSmikeb " pool\n", __func__);
969171ec5abSmikeb kvh->kvh_err = HV_KVP_S_CONT;
970171ec5abSmikeb } else if (kvh->kvh_pool == HV_KVP_POOL_EXTERNAL &&
971171ec5abSmikeb kvp_pool_import(&kvp->kvp_pool[HV_KVP_POOL_EXTERNAL],
972171ec5abSmikeb kvm->kvm_val.kvm_key, kvm->kvm_val.kvm_keylen,
973171ec5abSmikeb kvm->kvm_val.kvm_val, kvm->kvm_val.kvm_vallen,
974171ec5abSmikeb kvm->kvm_val.kvm_valtype)) {
975d3ffed7fSmikeb DPRINTF("%s: failed to import into 'External' pool\n",
976d3ffed7fSmikeb __func__);
977171ec5abSmikeb kvh->kvh_err = HV_KVP_S_CONT;
978171ec5abSmikeb } else if (kvh->kvh_pool != HV_KVP_POOL_AUTO_EXTERNAL &&
979171ec5abSmikeb kvh->kvh_pool != HV_KVP_POOL_EXTERNAL) {
980171ec5abSmikeb kvh->kvh_err = HV_KVP_S_CONT;
981171ec5abSmikeb } else
982171ec5abSmikeb kvh->kvh_err = HV_KVP_S_OK;
983171ec5abSmikeb break;
984d3ffed7fSmikeb case HV_KVP_OP_DELETE:
985d3ffed7fSmikeb if (kvh->kvh_pool != HV_KVP_POOL_EXTERNAL ||
986d3ffed7fSmikeb kvp_pool_remove(&kvp->kvp_pool[HV_KVP_POOL_EXTERNAL],
987d3ffed7fSmikeb kvm->kvm_del.kvm_key, kvm->kvm_del.kvm_keylen)) {
988d3ffed7fSmikeb DPRINTF("%s: failed to remove from 'External' pool\n",
989d3ffed7fSmikeb __func__);
990d3ffed7fSmikeb kvh->kvh_err = HV_KVP_S_CONT;
991d3ffed7fSmikeb } else
992d3ffed7fSmikeb kvh->kvh_err = HV_KVP_S_OK;
993d3ffed7fSmikeb break;
994171ec5abSmikeb case HV_KVP_OP_ENUMERATE:
995171ec5abSmikeb if (kvh->kvh_pool == HV_KVP_POOL_AUTO &&
996171ec5abSmikeb kvp_pool_export(&kvp->kvp_pool[HV_KVP_POOL_AUTO],
997171ec5abSmikeb kvm->kvm_enum.kvm_index, kvm->kvm_enum.kvm_key,
998171ec5abSmikeb &kvm->kvm_enum.kvm_keylen, kvm->kvm_enum.kvm_val,
999171ec5abSmikeb &kvm->kvm_enum.kvm_vallen, &kvm->kvm_enum.kvm_valtype))
1000171ec5abSmikeb kvh->kvh_err = HV_KVP_S_CONT;
1001171ec5abSmikeb else if (kvh->kvh_pool == HV_KVP_POOL_GUEST &&
1002171ec5abSmikeb kvp_pool_export(&kvp->kvp_pool[HV_KVP_POOL_GUEST],
1003171ec5abSmikeb kvm->kvm_enum.kvm_index, kvm->kvm_enum.kvm_key,
1004171ec5abSmikeb &kvm->kvm_enum.kvm_keylen, kvm->kvm_enum.kvm_val,
1005171ec5abSmikeb &kvm->kvm_enum.kvm_vallen, &kvm->kvm_enum.kvm_valtype))
1006171ec5abSmikeb kvh->kvh_err = HV_KVP_S_CONT;
1007171ec5abSmikeb else
1008171ec5abSmikeb kvh->kvh_err = HV_KVP_S_OK;
1009171ec5abSmikeb break;
1010ea58ab61Smikeb case HV_KVP_OP_GET_IP_INFO:
1011ea58ab61Smikeb if (VMBUS_ICVER_MAJOR(msg->ic_hdr.ic_msgver) <= 4) {
1012ea58ab61Smikeb struct vmbus_icmsg_kvp_addr *amsg;
1013ea58ab61Smikeb struct hv_kvp_msg_addr *kva;
1014ea58ab61Smikeb
1015ea58ab61Smikeb amsg = (struct vmbus_icmsg_kvp_addr *)msg;
1016ea58ab61Smikeb kva = &amsg->ic_kvm;
1017ea58ab61Smikeb
1018ea58ab61Smikeb if (kvp_get_ip_info(kvp, kva->kvm_mac,
1019ea58ab61Smikeb &kva->kvm_family, kva->kvm_addr,
1020ea58ab61Smikeb kva->kvm_netmask, sizeof(kva->kvm_addr)))
1021ea58ab61Smikeb kvh->kvh_err = HV_KVP_S_CONT;
1022ea58ab61Smikeb else
1023ea58ab61Smikeb kvh->kvh_err = HV_KVP_S_OK;
1024ea58ab61Smikeb } else {
1025ea58ab61Smikeb DPRINTF("KVP GET_IP_INFO fw %u.%u msg %u.%u dsize=%u\n",
1026ea58ab61Smikeb VMBUS_ICVER_MAJOR(msg->ic_hdr.ic_fwver),
1027ea58ab61Smikeb VMBUS_ICVER_MINOR(msg->ic_hdr.ic_fwver),
1028ea58ab61Smikeb VMBUS_ICVER_MAJOR(msg->ic_hdr.ic_msgver),
1029ea58ab61Smikeb VMBUS_ICVER_MINOR(msg->ic_hdr.ic_msgver),
1030ea58ab61Smikeb msg->ic_hdr.ic_dsize);
1031ea58ab61Smikeb kvh->kvh_err = HV_KVP_S_CONT;
1032ea58ab61Smikeb }
1033ea58ab61Smikeb break;
1034171ec5abSmikeb default:
1035171ec5abSmikeb DPRINTF("KVP message op %u pool %u\n", kvh->kvh_op,
1036171ec5abSmikeb kvh->kvh_pool);
1037171ec5abSmikeb kvh->kvh_err = HV_KVP_S_CONT;
1038171ec5abSmikeb }
1039171ec5abSmikeb }
1040171ec5abSmikeb
1041aa8f2e47Smikeb void
hv_kvp(void * arg)1042aa8f2e47Smikeb hv_kvp(void *arg)
1043aa8f2e47Smikeb {
1044171ec5abSmikeb struct hv_ic_dev *dv = arg;
1045171ec5abSmikeb struct hv_channel *ch = dv->dv_ch;
1046171ec5abSmikeb struct hv_softc *sc = ch->ch_sc;
1047171ec5abSmikeb struct hv_kvp *kvp = dv->dv_priv;
1048171ec5abSmikeb struct vmbus_icmsg_hdr *hdr;
1049171ec5abSmikeb uint64_t rid;
1050171ec5abSmikeb uint32_t fwver, msgver, rlen;
1051171ec5abSmikeb int rv;
1052171ec5abSmikeb
1053171ec5abSmikeb for (;;) {
1054171ec5abSmikeb rv = hv_channel_recv(ch, dv->dv_buf, 2 * PAGE_SIZE,
1055171ec5abSmikeb &rlen, &rid, 0);
1056171ec5abSmikeb if (rv || rlen == 0) {
1057171ec5abSmikeb if (rv != EAGAIN)
1058171ec5abSmikeb DPRINTF("%s: kvp rv=%d rlen=%u\n",
1059171ec5abSmikeb sc->sc_dev.dv_xname, rv, rlen);
1060171ec5abSmikeb return;
1061171ec5abSmikeb }
1062171ec5abSmikeb if (rlen < sizeof(struct vmbus_icmsg_hdr)) {
1063171ec5abSmikeb DPRINTF("%s: kvp short read rlen=%u\n",
1064171ec5abSmikeb sc->sc_dev.dv_xname, rlen);
1065171ec5abSmikeb return;
1066171ec5abSmikeb }
1067171ec5abSmikeb hdr = (struct vmbus_icmsg_hdr *)dv->dv_buf;
1068171ec5abSmikeb switch (hdr->ic_type) {
1069171ec5abSmikeb case VMBUS_ICMSG_TYPE_NEGOTIATE:
1070171ec5abSmikeb switch (sc->sc_proto) {
1071171ec5abSmikeb case VMBUS_VERSION_WS2008:
1072171ec5abSmikeb fwver = VMBUS_IC_VERSION(1, 0);
1073171ec5abSmikeb msgver = VMBUS_IC_VERSION(1, 0);
1074171ec5abSmikeb break;
1075171ec5abSmikeb case VMBUS_VERSION_WIN7:
1076171ec5abSmikeb fwver = VMBUS_IC_VERSION(3, 0);
1077171ec5abSmikeb msgver = VMBUS_IC_VERSION(3, 0);
1078171ec5abSmikeb break;
1079171ec5abSmikeb default:
1080171ec5abSmikeb fwver = VMBUS_IC_VERSION(3, 0);
1081ea58ab61Smikeb msgver = VMBUS_IC_VERSION(4, 0);
1082171ec5abSmikeb }
1083171ec5abSmikeb hv_ic_negotiate(hdr, &rlen, fwver, msgver);
1084171ec5abSmikeb break;
1085171ec5abSmikeb case VMBUS_ICMSG_TYPE_KVP:
1086171ec5abSmikeb if (hdr->ic_dsize >= sizeof(union hv_kvp_hdr))
1087171ec5abSmikeb hv_kvp_process(kvp,
1088171ec5abSmikeb (struct vmbus_icmsg_kvp *)hdr);
1089171ec5abSmikeb else
1090171ec5abSmikeb printf("%s: message too short: %u\n",
1091171ec5abSmikeb sc->sc_dev.dv_xname, hdr->ic_dsize);
1092171ec5abSmikeb break;
1093171ec5abSmikeb default:
1094171ec5abSmikeb printf("%s: unhandled kvp message type %u\n",
1095171ec5abSmikeb sc->sc_dev.dv_xname, hdr->ic_type);
1096171ec5abSmikeb continue;
1097171ec5abSmikeb }
1098171ec5abSmikeb hdr->ic_flags = VMBUS_ICMSG_FLAG_TRANSACTION |
1099171ec5abSmikeb VMBUS_ICMSG_FLAG_RESPONSE;
1100171ec5abSmikeb hv_channel_send(ch, dv->dv_buf, rlen, rid,
1101171ec5abSmikeb VMBUS_CHANPKT_TYPE_INBAND, 0);
1102171ec5abSmikeb }
1103171ec5abSmikeb }
1104171ec5abSmikeb
1105171ec5abSmikeb static int
kvp_poolname(char ** key)1106171ec5abSmikeb kvp_poolname(char **key)
1107171ec5abSmikeb {
1108171ec5abSmikeb char *p;
1109171ec5abSmikeb int i, rv = -1;
1110171ec5abSmikeb
1111171ec5abSmikeb if ((p = strrchr(*key, '/')) == NULL)
1112171ec5abSmikeb return (rv);
1113171ec5abSmikeb *p = '\0';
1114171ec5abSmikeb for (i = 0; i < nitems(kvp_pools); i++) {
1115171ec5abSmikeb if (strncasecmp(*key, kvp_pools[i].poolname,
1116171ec5abSmikeb kvp_pools[i].poolnamelen) == 0) {
1117171ec5abSmikeb rv = kvp_pools[i].poolidx;
1118171ec5abSmikeb break;
1119171ec5abSmikeb }
1120171ec5abSmikeb }
1121171ec5abSmikeb if (rv >= 0)
1122171ec5abSmikeb *key = ++p;
1123171ec5abSmikeb return (rv);
1124aa8f2e47Smikeb }
1125aa8f2e47Smikeb
1126aa8f2e47Smikeb int
hv_kvop(void * arg,int op,char * key,char * val,size_t vallen)1127171ec5abSmikeb hv_kvop(void *arg, int op, char *key, char *val, size_t vallen)
1128aa8f2e47Smikeb {
1129171ec5abSmikeb struct hv_ic_dev *dv = arg;
1130171ec5abSmikeb struct hv_kvp *kvp = dv->dv_priv;
1131171ec5abSmikeb struct kvp_pool *kvpl;
1132171ec5abSmikeb int next, pool, error = 0;
1133171ec5abSmikeb char *vp = val;
1134171ec5abSmikeb size_t keylen;
1135171ec5abSmikeb
1136171ec5abSmikeb pool = kvp_poolname(&key);
1137171ec5abSmikeb if (pool == -1)
1138171ec5abSmikeb return (EINVAL);
1139171ec5abSmikeb
1140171ec5abSmikeb kvpl = &kvp->kvp_pool[pool];
1141171ec5abSmikeb if (strlen(key) == 0) {
1142171ec5abSmikeb for (next = 0; next < MAXPOOLENTS; next++) {
114398dbb30eSasou if (val + vallen < vp + HV_KVP_MAX_KEY_SIZE / 2)
114498dbb30eSasou return (ERANGE);
114598dbb30eSasou if (kvp_pool_keys(kvpl, next, vp, &keylen))
1146171ec5abSmikeb goto out;
1147171ec5abSmikeb if (strlcat(val, "\n", vallen) >= vallen)
114898dbb30eSasou return (ERANGE);
1149171ec5abSmikeb vp += keylen;
1150aa8f2e47Smikeb }
1151171ec5abSmikeb out:
1152171ec5abSmikeb if (vp > val)
1153171ec5abSmikeb *(vp - 1) = '\0';
1154171ec5abSmikeb return (0);
1155171ec5abSmikeb }
1156171ec5abSmikeb
1157171ec5abSmikeb if (op == PVBUS_KVWRITE) {
1158171ec5abSmikeb if (pool == HV_KVP_POOL_AUTO)
1159171ec5abSmikeb error = kvp_pool_update(kvpl, key, val, vallen,
1160171ec5abSmikeb HV_KVP_REG_SZ);
1161171ec5abSmikeb else if (pool == HV_KVP_POOL_GUEST)
1162171ec5abSmikeb error = kvp_pool_insert(kvpl, key, val, vallen,
1163171ec5abSmikeb HV_KVP_REG_SZ);
1164171ec5abSmikeb else
1165171ec5abSmikeb error = EINVAL;
1166171ec5abSmikeb } else
1167171ec5abSmikeb error = kvp_pool_extract(kvpl, key, val, vallen);
1168171ec5abSmikeb
1169171ec5abSmikeb return (error);
1170fa68c148Smikeb }
1171