xref: /netbsd-src/sys/arch/xen/xenbus/xenbus_client.c (revision b95803dc6bc74fded327c6183611d21456253da8)
1*b95803dcSjdolecek /* $NetBSD: xenbus_client.c,v 1.17 2020/04/07 15:16:52 jdolecek Exp $ */
2ed3fe49dSbouyer /******************************************************************************
3ed3fe49dSbouyer  * Client-facing interface for the Xenbus driver.  In other words, the
4ed3fe49dSbouyer  * interface between the Xenbus and the device-specific code, be it the
5ed3fe49dSbouyer  * frontend or the backend of that driver.
6ed3fe49dSbouyer  *
7ed3fe49dSbouyer  * Copyright (C) 2005 XenSource Ltd
8ed3fe49dSbouyer  *
9ed3fe49dSbouyer  * This file may be distributed separately from the Linux kernel, or
10ed3fe49dSbouyer  * incorporated into other software packages, subject to the following license:
11ed3fe49dSbouyer  *
12ed3fe49dSbouyer  * Permission is hereby granted, free of charge, to any person obtaining a copy
13ed3fe49dSbouyer  * of this source file (the "Software"), to deal in the Software without
14ed3fe49dSbouyer  * restriction, including without limitation the rights to use, copy, modify,
15ed3fe49dSbouyer  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
16ed3fe49dSbouyer  * and to permit persons to whom the Software is furnished to do so, subject to
17ed3fe49dSbouyer  * the following conditions:
18ed3fe49dSbouyer  *
19ed3fe49dSbouyer  * The above copyright notice and this permission notice shall be included in
20ed3fe49dSbouyer  * all copies or substantial portions of the Software.
21ed3fe49dSbouyer  *
22ed3fe49dSbouyer  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23ed3fe49dSbouyer  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24ed3fe49dSbouyer  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25ed3fe49dSbouyer  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26ed3fe49dSbouyer  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27ed3fe49dSbouyer  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28ed3fe49dSbouyer  * IN THE SOFTWARE.
29ed3fe49dSbouyer  */
30ed3fe49dSbouyer 
3116046408Sbouyer #include <sys/cdefs.h>
32*b95803dcSjdolecek __KERNEL_RCSID(0, "$NetBSD: xenbus_client.c,v 1.17 2020/04/07 15:16:52 jdolecek Exp $");
33ed3fe49dSbouyer 
34ed3fe49dSbouyer #if 0
35ed3fe49dSbouyer #define DPRINTK(fmt, args...) \
369b2b412cSperry     printk("xenbus_client (%s:%d) " fmt ".\n", __func__, __LINE__, ##args)
37ed3fe49dSbouyer #else
38ed3fe49dSbouyer #define DPRINTK(fmt, args...) ((void)0)
39ed3fe49dSbouyer #endif
40ed3fe49dSbouyer 
4116046408Sbouyer #include <sys/types.h>
4216046408Sbouyer #include <sys/null.h>
4316046408Sbouyer #include <sys/errno.h>
44418f6d13Sjdolecek #include <sys/kmem.h>
4516046408Sbouyer #include <sys/systm.h>
46ed3fe49dSbouyer 
474e541343Sbouyer #include <xen/xen.h>
484e541343Sbouyer #include <xen/hypervisor.h>
494e541343Sbouyer #include <xen/evtchn.h>
504e541343Sbouyer #include <xen/xenbus.h>
514e541343Sbouyer #include <xen/granttables.h>
52ed3fe49dSbouyer 
5316046408Sbouyer int
xenbus_watch_path2(struct xenbus_device * dev,const char * path,const char * path2,struct xenbus_watch * watch,void (* callback)(struct xenbus_watch *,const char **,unsigned int))5416046408Sbouyer xenbus_watch_path2(struct xenbus_device *dev, const char *path,
55ed3fe49dSbouyer 		       const char *path2, struct xenbus_watch *watch,
56ed3fe49dSbouyer 		       void (*callback)(struct xenbus_watch *,
57ed3fe49dSbouyer 					const char **, unsigned int))
58ed3fe49dSbouyer {
59ed3fe49dSbouyer 	int err;
60aceceafcSbouyer 	char *state;
61aceceafcSbouyer 
62aceceafcSbouyer 	DPRINTK("xenbus_watch_path2 path %s path2 %s\n", path, path2);
63418f6d13Sjdolecek 
64418f6d13Sjdolecek 	watch->node_sz = strlen(path) + 1 + strlen(path2) + 1;
65418f6d13Sjdolecek 	state = kmem_alloc(watch->node_sz, KM_SLEEP);
66ed3fe49dSbouyer 	strcpy(state, path);
67ed3fe49dSbouyer 	strcat(state, "/");
68ed3fe49dSbouyer 	strcat(state, path2);
69ed3fe49dSbouyer 
70418f6d13Sjdolecek 	watch->node = state;
71418f6d13Sjdolecek 	watch->xbw_callback = callback;
72418f6d13Sjdolecek 
73418f6d13Sjdolecek 	err = register_xenbus_watch(watch);
74ed3fe49dSbouyer 
75ed3fe49dSbouyer 	if (err) {
76418f6d13Sjdolecek 		watch->node = NULL;
77418f6d13Sjdolecek 		watch->node_sz = 0;
78418f6d13Sjdolecek 		watch->xbw_callback = NULL;
79418f6d13Sjdolecek 		xenbus_dev_fatal(dev, err, "adding watch on %s", state);
80418f6d13Sjdolecek 		kmem_free(state, watch->node_sz);
81ed3fe49dSbouyer 	}
82ed3fe49dSbouyer 	return err;
83ed3fe49dSbouyer }
84ed3fe49dSbouyer 
85418f6d13Sjdolecek void
xenbus_unwatch_path(struct xenbus_watch * watch)86418f6d13Sjdolecek xenbus_unwatch_path(struct xenbus_watch *watch)
87418f6d13Sjdolecek {
88418f6d13Sjdolecek 	if (watch->node != NULL) {
89418f6d13Sjdolecek 		unregister_xenbus_watch(watch);
90418f6d13Sjdolecek 		kmem_free(watch->node, watch->node_sz);
91418f6d13Sjdolecek 		watch->node = NULL;
92418f6d13Sjdolecek 	}
93418f6d13Sjdolecek }
94ed3fe49dSbouyer 
9516046408Sbouyer int
xenbus_switch_state(struct xenbus_device * dev,struct xenbus_transaction * xbt,XenbusState state)9616046408Sbouyer xenbus_switch_state(struct xenbus_device *dev,
97ed3fe49dSbouyer 			struct xenbus_transaction *xbt,
98ed3fe49dSbouyer 			XenbusState state)
99ed3fe49dSbouyer {
100ed3fe49dSbouyer 	/* We check whether the state is currently set to the given value, and
101ed3fe49dSbouyer 	   if not, then the state is set.  We don't want to unconditionally
102ed3fe49dSbouyer 	   write the given state, because we don't want to fire watches
103ed3fe49dSbouyer 	   unnecessarily.  Furthermore, if the node has gone, we don't write
104ed3fe49dSbouyer 	   to it, as the device will be tearing down, and we don't want to
105ed3fe49dSbouyer 	   resurrect that directory.
106ed3fe49dSbouyer 	 */
107ed3fe49dSbouyer 
10814e4157fSbouyer 	u_long current_state;
109ed3fe49dSbouyer 
1102613e19eSbouyer 	int err = xenbus_read_ul(xbt, dev->xbusd_path, "state",
1112613e19eSbouyer 	    &current_state, 10);
11214e4157fSbouyer 	if (err)
11314e4157fSbouyer 		return 0;
11414e4157fSbouyer 
11514e4157fSbouyer 	if ((XenbusState)current_state == state)
116ed3fe49dSbouyer 		return 0;
117ed3fe49dSbouyer 
11816046408Sbouyer 	err = xenbus_printf(xbt, dev->xbusd_path, "state", "%d", state);
119ed3fe49dSbouyer 	if (err) {
120ed3fe49dSbouyer 		xenbus_dev_fatal(dev, err, "writing new state");
121ed3fe49dSbouyer 		return err;
122ed3fe49dSbouyer 	}
123ed3fe49dSbouyer 	return 0;
124ed3fe49dSbouyer }
125ed3fe49dSbouyer 
126ed3fe49dSbouyer /**
127ed3fe49dSbouyer  * Return the path to the error node for the given device, or NULL on failure.
128ed3fe49dSbouyer  * If the value returned is non-NULL, then it is the caller's to kfree.
129ed3fe49dSbouyer  */
13016046408Sbouyer static char *
error_path(struct xenbus_device * dev,size_t * len)131*b95803dcSjdolecek error_path(struct xenbus_device *dev, size_t *len)
132ed3fe49dSbouyer {
133*b95803dcSjdolecek 	*len = strlen("error/") + strlen(dev->xbusd_path) + 1;
134*b95803dcSjdolecek 	char *path_buffer = kmem_alloc(*len, KM_NOSLEEP);
135*b95803dcSjdolecek 	if (path_buffer == NULL)
136ed3fe49dSbouyer 		return NULL;
137ed3fe49dSbouyer 
138ed3fe49dSbouyer 	strcpy(path_buffer, "error/");
13916046408Sbouyer 	strcpy(path_buffer + strlen("error/"), dev->xbusd_path);
140ed3fe49dSbouyer 
141ed3fe49dSbouyer 	return path_buffer;
142ed3fe49dSbouyer }
143ed3fe49dSbouyer 
144ed3fe49dSbouyer 
14516046408Sbouyer static void
_dev_error(struct xenbus_device * dev,int err,const char * fmt,va_list ap)14616046408Sbouyer _dev_error(struct xenbus_device *dev, int err, const char *fmt,
147ed3fe49dSbouyer 		va_list ap)
148ed3fe49dSbouyer {
149bb4d9fb0Sbouyer 	int ret __diagused;
150ed3fe49dSbouyer 	unsigned int len;
151ed3fe49dSbouyer 	char *printf_buffer = NULL, *path_buffer = NULL;
152*b95803dcSjdolecek 	size_t path_buffer_sz = 0;
153ed3fe49dSbouyer 
154ed3fe49dSbouyer #define PRINTF_BUFFER_SIZE 4096
155*b95803dcSjdolecek 	printf_buffer = kmem_alloc(PRINTF_BUFFER_SIZE, KM_NOSLEEP);
156ed3fe49dSbouyer 	if (printf_buffer == NULL)
157ed3fe49dSbouyer 		goto fail;
158ed3fe49dSbouyer 
15918c8862fScegger 	len = snprintf(printf_buffer, PRINTF_BUFFER_SIZE, "%i ", -err);
160e93b33c9Schristos 	KASSERT(len < PRINTF_BUFFER_SIZE);
161ed3fe49dSbouyer 	ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
16216046408Sbouyer 	KASSERT(len + ret < PRINTF_BUFFER_SIZE);
16316046408Sbouyer 	dev->xbusd_has_error = 1;
164ed3fe49dSbouyer 
165*b95803dcSjdolecek 	path_buffer = error_path(dev, &path_buffer_sz);
166ed3fe49dSbouyer 	if (path_buffer == NULL) {
167ed3fe49dSbouyer 		printk("xenbus: failed to write error node for %s (%s)\n",
16816046408Sbouyer 		       dev->xbusd_path, printf_buffer);
169ed3fe49dSbouyer 		goto fail;
170ed3fe49dSbouyer 	}
171ed3fe49dSbouyer 
172ed3fe49dSbouyer 	if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
173ed3fe49dSbouyer 		printk("xenbus: failed to write error node for %s (%s)\n",
17416046408Sbouyer 		       dev->xbusd_path, printf_buffer);
175ed3fe49dSbouyer 		goto fail;
176ed3fe49dSbouyer 	}
177ed3fe49dSbouyer 
178ed3fe49dSbouyer fail:
179ed3fe49dSbouyer 	if (printf_buffer)
180*b95803dcSjdolecek 		kmem_free(printf_buffer, PRINTF_BUFFER_SIZE);
181ed3fe49dSbouyer 	if (path_buffer)
182*b95803dcSjdolecek 		kmem_free(path_buffer, path_buffer_sz);
183ed3fe49dSbouyer }
184ed3fe49dSbouyer 
185ed3fe49dSbouyer 
18616046408Sbouyer void
xenbus_dev_error(struct xenbus_device * dev,int err,const char * fmt,...)18716046408Sbouyer xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,
188ed3fe49dSbouyer 		      ...)
189ed3fe49dSbouyer {
190ed3fe49dSbouyer 	va_list ap;
191ed3fe49dSbouyer 
192ed3fe49dSbouyer 	va_start(ap, fmt);
193ed3fe49dSbouyer 	_dev_error(dev, err, fmt, ap);
194ed3fe49dSbouyer 	va_end(ap);
195ed3fe49dSbouyer }
196ed3fe49dSbouyer 
197ed3fe49dSbouyer 
19816046408Sbouyer void
xenbus_dev_fatal(struct xenbus_device * dev,int err,const char * fmt,...)19916046408Sbouyer xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt,
200ed3fe49dSbouyer 		      ...)
201ed3fe49dSbouyer {
202ed3fe49dSbouyer 	va_list ap;
203ed3fe49dSbouyer 
204ed3fe49dSbouyer 	va_start(ap, fmt);
205ed3fe49dSbouyer 	_dev_error(dev, err, fmt, ap);
206ed3fe49dSbouyer 	va_end(ap);
207ed3fe49dSbouyer 
208ed3fe49dSbouyer 	xenbus_switch_state(dev, NULL, XenbusStateClosing);
209ed3fe49dSbouyer }
210ed3fe49dSbouyer 
211ed3fe49dSbouyer 
21216046408Sbouyer int
xenbus_grant_ring(struct xenbus_device * dev,paddr_t ring_pa,grant_ref_t * entryp)21316046408Sbouyer xenbus_grant_ring(struct xenbus_device *dev, paddr_t ring_pa,
21416046408Sbouyer     grant_ref_t *entryp)
215ed3fe49dSbouyer {
21616046408Sbouyer 	int err = xengnt_grant_access(dev->xbusd_otherend_id, ring_pa,
21716046408Sbouyer 	    0, entryp);
21816046408Sbouyer 	if (err != 0)
219ed3fe49dSbouyer 		xenbus_dev_fatal(dev, err, "granting access to ring page");
220ed3fe49dSbouyer 	return err;
221ed3fe49dSbouyer }
222ed3fe49dSbouyer 
223ed3fe49dSbouyer 
22416046408Sbouyer int
xenbus_alloc_evtchn(struct xenbus_device * dev,int * port)22516046408Sbouyer xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
226ed3fe49dSbouyer {
227ed3fe49dSbouyer 	evtchn_op_t op = {
228ed3fe49dSbouyer 		.cmd = EVTCHNOP_alloc_unbound,
2290f256b9dStron 		.u.alloc_unbound = {
2300f256b9dStron 			.dom = DOMID_SELF,
2310f256b9dStron 			.remote_dom = dev->xbusd_otherend_id,
2320f256b9dStron 			.port = 0
2330f256b9dStron 		}
2340f256b9dStron 	};
235ed3fe49dSbouyer 
236ed3fe49dSbouyer 	int err = HYPERVISOR_event_channel_op(&op);
237ed3fe49dSbouyer 	if (err)
238ed3fe49dSbouyer 		xenbus_dev_fatal(dev, err, "allocating event channel");
239ed3fe49dSbouyer 	else
240ed3fe49dSbouyer 		*port = op.u.alloc_unbound.port;
241ed3fe49dSbouyer 	return err;
242ed3fe49dSbouyer }
243ed3fe49dSbouyer 
244ed3fe49dSbouyer 
24516046408Sbouyer XenbusState
xenbus_read_driver_state(const char * path)24616046408Sbouyer xenbus_read_driver_state(const char *path)
247ed3fe49dSbouyer {
24814e4157fSbouyer 	u_long result;
249ed3fe49dSbouyer 
2502613e19eSbouyer 	int err = xenbus_read_ul(NULL, path, "state", &result, 10);
251ed3fe49dSbouyer 	if (err)
252ed3fe49dSbouyer 		result = XenbusStateClosed;
253ed3fe49dSbouyer 
254ed3fe49dSbouyer 	return result;
255ed3fe49dSbouyer }
256ed3fe49dSbouyer 
257ed3fe49dSbouyer 
258ed3fe49dSbouyer /*
259ed3fe49dSbouyer  * Local variables:
260ed3fe49dSbouyer  *  c-file-style: "linux"
261ed3fe49dSbouyer  *  indent-tabs-mode: t
262ed3fe49dSbouyer  *  c-indent-level: 8
263ed3fe49dSbouyer  *  c-basic-offset: 8
264ed3fe49dSbouyer  *  tab-width: 8
265ed3fe49dSbouyer  * End:
266ed3fe49dSbouyer  */
267