15084Sjohnlev /*
25084Sjohnlev * CDDL HEADER START
35084Sjohnlev *
45084Sjohnlev * The contents of this file are subject to the terms of the
55084Sjohnlev * Common Development and Distribution License (the "License").
65084Sjohnlev * You may not use this file except in compliance with the License.
75084Sjohnlev *
85084Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev * See the License for the specific language governing permissions
115084Sjohnlev * and limitations under the License.
125084Sjohnlev *
135084Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev *
195084Sjohnlev * CDDL HEADER END
205084Sjohnlev */
215084Sjohnlev
225084Sjohnlev /*
235084Sjohnlev * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
245084Sjohnlev * Use is subject to license terms.
255084Sjohnlev */
265084Sjohnlev
275084Sjohnlev /*
285084Sjohnlev * Client-facing interface for the Xenbus driver. In other words, the
295084Sjohnlev * interface between the Xenbus and the device-specific code, be it the
305084Sjohnlev * frontend or the backend of that driver.
315084Sjohnlev *
325084Sjohnlev * Copyright (C) 2005 XenSource Ltd
335084Sjohnlev *
345084Sjohnlev * This file may be distributed separately from the Linux kernel, or
355084Sjohnlev * incorporated into other software packages, subject to the following license:
365084Sjohnlev *
375084Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy
385084Sjohnlev * of this source file (the "Software"), to deal in the Software without
395084Sjohnlev * restriction, including without limitation the rights to use, copy, modify,
405084Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software,
415084Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to
425084Sjohnlev * the following conditions:
435084Sjohnlev *
445084Sjohnlev * The above copyright notice and this permission notice shall be included in
455084Sjohnlev * all copies or substantial portions of the Software.
465084Sjohnlev *
475084Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
485084Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
495084Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
505084Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
515084Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
525084Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
535084Sjohnlev * IN THE SOFTWARE.
545084Sjohnlev */
555084Sjohnlev
565084Sjohnlev #pragma ident "%Z%%M% %I% %E% SMI"
575084Sjohnlev
58*5741Smrj #ifdef XPV_HVM_DRIVER
59*5741Smrj #include <sys/xpv_support.h>
60*5741Smrj #include <sys/hypervisor.h>
61*5741Smrj #else
625084Sjohnlev #include <sys/hypervisor.h>
635084Sjohnlev #include <sys/xen_mmu.h>
645084Sjohnlev #include <sys/evtchn_impl.h>
65*5741Smrj #endif
665084Sjohnlev #include <sys/gnttab.h>
675084Sjohnlev #include <xen/sys/xenbus_impl.h>
685084Sjohnlev #include <sys/cmn_err.h>
695084Sjohnlev
705084Sjohnlev
715084Sjohnlev int
xenbus_watch_path(struct xenbus_device * dev,const char * path,struct xenbus_watch * watch,void (* callback)(struct xenbus_watch *,const char **,unsigned int))725084Sjohnlev xenbus_watch_path(struct xenbus_device *dev, const char *path,
735084Sjohnlev struct xenbus_watch *watch,
745084Sjohnlev void (*callback)(struct xenbus_watch *,
755084Sjohnlev const char **, unsigned int))
765084Sjohnlev {
775084Sjohnlev int err;
785084Sjohnlev
795084Sjohnlev watch->node = path;
805084Sjohnlev watch->callback = callback;
815084Sjohnlev
825084Sjohnlev err = register_xenbus_watch(watch);
835084Sjohnlev
845084Sjohnlev if (err) {
855084Sjohnlev watch->node = NULL;
865084Sjohnlev watch->callback = NULL;
875084Sjohnlev xenbus_dev_fatal(dev, err, "adding watch on %s", path);
885084Sjohnlev }
895084Sjohnlev
905084Sjohnlev return (err);
915084Sjohnlev }
925084Sjohnlev
935084Sjohnlev
945084Sjohnlev 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))955084Sjohnlev xenbus_watch_path2(struct xenbus_device *dev, const char *path,
965084Sjohnlev const char *path2, struct xenbus_watch *watch,
975084Sjohnlev void (*callback)(struct xenbus_watch *,
985084Sjohnlev const char **, unsigned int))
995084Sjohnlev {
1005084Sjohnlev int err;
1015084Sjohnlev char *state;
1025084Sjohnlev
1035084Sjohnlev state = kmem_alloc(strlen(path) + 1 + strlen(path2) + 1, KM_SLEEP);
1045084Sjohnlev (void) strcpy(state, path);
1055084Sjohnlev (void) strcat(state, "/");
1065084Sjohnlev (void) strcat(state, path2);
1075084Sjohnlev
1085084Sjohnlev err = xenbus_watch_path(dev, state, watch, callback);
1095084Sjohnlev if (err)
1105084Sjohnlev kmem_free(state, strlen(state) + 1);
1115084Sjohnlev return (err);
1125084Sjohnlev }
1135084Sjohnlev
1145084Sjohnlev /*
1155084Sjohnlev * Returns 0 on success, -1 if no change was made, or an errno on failure. We
1165084Sjohnlev * check whether the state is currently set to the given value, and if not,
1175084Sjohnlev * then the state is set. We don't want to unconditionally write the given
1185084Sjohnlev * state, because we don't want to fire watches unnecessarily. Furthermore, if
1195084Sjohnlev * the node has gone, we don't write to it, as the device will be tearing down,
1205084Sjohnlev * and we don't want to resurrect that directory.
1215084Sjohnlev *
1225084Sjohnlev * XXPV: not clear that this is still safe if two threads are racing to update
1235084Sjohnlev * the state?
1245084Sjohnlev */
1255084Sjohnlev int
xenbus_switch_state(struct xenbus_device * dev,xenbus_transaction_t xbt,XenbusState state)1265084Sjohnlev xenbus_switch_state(struct xenbus_device *dev, xenbus_transaction_t xbt,
1275084Sjohnlev XenbusState state)
1285084Sjohnlev {
1295084Sjohnlev int current_state;
1305084Sjohnlev int err;
1315084Sjohnlev
1325084Sjohnlev err = xenbus_scanf(xbt, dev->nodename, "state", "%d", ¤t_state);
1335084Sjohnlev
1345084Sjohnlev /* XXPV: is this really the right thing to do? */
1355084Sjohnlev if (err == ENOENT)
1365084Sjohnlev return (0);
1375084Sjohnlev if (err)
1385084Sjohnlev return (err);
1395084Sjohnlev
1405084Sjohnlev err = -1;
1415084Sjohnlev
1425084Sjohnlev if ((XenbusState)current_state != state) {
1435084Sjohnlev err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
1445084Sjohnlev if (err)
1455084Sjohnlev xenbus_dev_fatal(dev, err, "writing new state");
1465084Sjohnlev }
1475084Sjohnlev
1485084Sjohnlev return (err);
1495084Sjohnlev }
1505084Sjohnlev
1515084Sjohnlev
1525084Sjohnlev /*
1535084Sjohnlev * Return the path to the error node for the given device, or NULL on failure.
1545084Sjohnlev * If the value returned is non-NULL, then it is the caller's to kmem_free.
1555084Sjohnlev */
1565084Sjohnlev static char *
error_path(struct xenbus_device * dev)1575084Sjohnlev error_path(struct xenbus_device *dev)
1585084Sjohnlev {
1595084Sjohnlev char *path_buffer;
1605084Sjohnlev
1615084Sjohnlev path_buffer = kmem_alloc(strlen("error/") + strlen(dev->nodename) +
1625084Sjohnlev 1, KM_SLEEP);
1635084Sjohnlev
1645084Sjohnlev (void) strcpy(path_buffer, "error/");
1655084Sjohnlev (void) strcpy(path_buffer + strlen("error/"), dev->nodename);
1665084Sjohnlev
1675084Sjohnlev return (path_buffer);
1685084Sjohnlev }
1695084Sjohnlev
1705084Sjohnlev static void
common_dev_error(struct xenbus_device * dev,int err,const char * fmt,va_list ap)1715084Sjohnlev common_dev_error(struct xenbus_device *dev, int err, const char *fmt,
1725084Sjohnlev va_list ap)
1735084Sjohnlev {
1745084Sjohnlev int ret;
1755084Sjohnlev unsigned int len;
1765084Sjohnlev char *printf_buffer = NULL, *path_buffer = NULL;
1775084Sjohnlev
1785084Sjohnlev #define PRINTF_BUFFER_SIZE 4096
1795084Sjohnlev printf_buffer = kmem_alloc(PRINTF_BUFFER_SIZE, KM_SLEEP);
1805084Sjohnlev
1815084Sjohnlev (void) snprintf(printf_buffer, PRINTF_BUFFER_SIZE, "%d ", err);
1825084Sjohnlev len = strlen(printf_buffer);
1835084Sjohnlev ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
1845084Sjohnlev
1855084Sjohnlev ASSERT(len + ret <= PRINTF_BUFFER_SIZE-1);
1865084Sjohnlev dev->has_error = 1;
1875084Sjohnlev
1885084Sjohnlev path_buffer = error_path(dev);
1895084Sjohnlev
1905084Sjohnlev if (path_buffer == NULL) {
1915084Sjohnlev printf("xenbus: failed to write error node for %s (%s)\n",
1925084Sjohnlev dev->nodename, printf_buffer);
1935084Sjohnlev goto fail;
1945084Sjohnlev }
1955084Sjohnlev
1965084Sjohnlev if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
1975084Sjohnlev printf("xenbus: failed to write error node for %s (%s)\n",
1985084Sjohnlev dev->nodename, printf_buffer);
1995084Sjohnlev goto fail;
2005084Sjohnlev }
2015084Sjohnlev
2025084Sjohnlev fail:
2035084Sjohnlev if (printf_buffer)
2045084Sjohnlev kmem_free(printf_buffer, PRINTF_BUFFER_SIZE);
2055084Sjohnlev if (path_buffer)
2065084Sjohnlev kmem_free(path_buffer, strlen(path_buffer) + 1);
2075084Sjohnlev }
2085084Sjohnlev
2095084Sjohnlev
2105084Sjohnlev void
xenbus_dev_error(struct xenbus_device * dev,int err,const char * fmt,...)2115084Sjohnlev xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...)
2125084Sjohnlev {
2135084Sjohnlev va_list ap;
2145084Sjohnlev
2155084Sjohnlev va_start(ap, fmt);
2165084Sjohnlev common_dev_error(dev, err, fmt, ap);
2175084Sjohnlev va_end(ap);
2185084Sjohnlev }
2195084Sjohnlev
2205084Sjohnlev
2215084Sjohnlev void
xenbus_dev_fatal(struct xenbus_device * dev,int err,const char * fmt,...)2225084Sjohnlev xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...)
2235084Sjohnlev {
2245084Sjohnlev va_list ap;
2255084Sjohnlev
2265084Sjohnlev va_start(ap, fmt);
2275084Sjohnlev common_dev_error(dev, err, fmt, ap);
2285084Sjohnlev va_end(ap);
2295084Sjohnlev
2305084Sjohnlev (void) xenbus_switch_state(dev, XBT_NULL, XenbusStateClosing);
2315084Sjohnlev }
2325084Sjohnlev
2335084Sjohnlev /* Clear any error. */
2345084Sjohnlev void
xenbus_dev_ok(struct xenbus_device * dev)2355084Sjohnlev xenbus_dev_ok(struct xenbus_device *dev)
2365084Sjohnlev {
2375084Sjohnlev if (dev->has_error) {
2385084Sjohnlev if (xenbus_rm(NULL, dev->nodename, "error") != 0)
2395084Sjohnlev printf("xenbus: failed to clear error node for %s\n",
2405084Sjohnlev dev->nodename);
2415084Sjohnlev else
2425084Sjohnlev dev->has_error = 0;
2435084Sjohnlev }
2445084Sjohnlev }
2455084Sjohnlev
2465084Sjohnlev int
xenbus_grant_ring(struct xenbus_device * dev,unsigned long ring_mfn)2475084Sjohnlev xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
2485084Sjohnlev {
2495084Sjohnlev int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0);
2505084Sjohnlev if (err < 0)
2515084Sjohnlev xenbus_dev_fatal(dev, err, "granting access to ring page");
2525084Sjohnlev return (err);
2535084Sjohnlev }
2545084Sjohnlev
2555084Sjohnlev
2565084Sjohnlev int
xenbus_alloc_evtchn(struct xenbus_device * dev,int * port)2575084Sjohnlev xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
2585084Sjohnlev {
2595084Sjohnlev int err;
2605084Sjohnlev
2615084Sjohnlev err = xen_alloc_unbound_evtchn(dev->otherend_id, port);
2625084Sjohnlev if (err)
2635084Sjohnlev xenbus_dev_fatal(dev, err, "allocating event channel");
2645084Sjohnlev return (err);
2655084Sjohnlev }
2665084Sjohnlev
2675084Sjohnlev
2685084Sjohnlev XenbusState
xenbus_read_driver_state(const char * path)2695084Sjohnlev xenbus_read_driver_state(const char *path)
2705084Sjohnlev {
2715084Sjohnlev XenbusState result;
2725084Sjohnlev
2735084Sjohnlev int err = xenbus_gather(XBT_NULL, path, "state", "%d", &result, NULL);
2745084Sjohnlev if (err)
2755084Sjohnlev result = XenbusStateClosed;
2765084Sjohnlev
2775084Sjohnlev return (result);
2785084Sjohnlev }
2795084Sjohnlev
2805084Sjohnlev
2815084Sjohnlev /*
2825084Sjohnlev * Local variables:
2835084Sjohnlev * c-file-style: "solaris"
2845084Sjohnlev * indent-tabs-mode: t
2855084Sjohnlev * c-indent-level: 8
2865084Sjohnlev * c-basic-offset: 8
2875084Sjohnlev * tab-width: 8
2885084Sjohnlev * End:
2895084Sjohnlev */
290