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 /*
236784Sjohnlev * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
245084Sjohnlev * Use is subject to license terms.
255084Sjohnlev */
265084Sjohnlev
275084Sjohnlev /*
285084Sjohnlev * xenbus_dev.c
295084Sjohnlev *
305084Sjohnlev * Driver giving user-space access to the kernel's xenbus connection
315084Sjohnlev * to xenstore.
325084Sjohnlev *
335084Sjohnlev * Copyright (c) 2005, Christian Limpach
345084Sjohnlev * Copyright (c) 2005, Rusty Russell, IBM Corporation
355084Sjohnlev *
365084Sjohnlev * This file may be distributed separately from the Linux kernel, or
375084Sjohnlev * incorporated into other software packages, subject to the following license:
385084Sjohnlev *
395084Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy
405084Sjohnlev * of this source file (the "Software"), to deal in the Software without
415084Sjohnlev * restriction, including without limitation the rights to use, copy, modify,
425084Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software,
435084Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to
445084Sjohnlev * the following conditions:
455084Sjohnlev *
465084Sjohnlev * The above copyright notice and this permission notice shall be included in
475084Sjohnlev * all copies or substantial portions of the Software.
485084Sjohnlev *
495084Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
505084Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
515084Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
525084Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
535084Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
545084Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
555084Sjohnlev * IN THE SOFTWARE.
565084Sjohnlev */
575084Sjohnlev
585084Sjohnlev
595084Sjohnlev #include <sys/types.h>
605084Sjohnlev #include <sys/sysmacros.h>
615084Sjohnlev #include <sys/conf.h>
625084Sjohnlev #include <sys/stat.h>
635084Sjohnlev #include <sys/modctl.h>
645084Sjohnlev #include <sys/uio.h>
655084Sjohnlev #include <sys/list.h>
665084Sjohnlev #include <sys/file.h>
675084Sjohnlev #include <sys/errno.h>
685084Sjohnlev #include <sys/open.h>
695084Sjohnlev #include <sys/cred.h>
705084Sjohnlev #include <sys/condvar.h>
715084Sjohnlev #include <sys/ddi.h>
725084Sjohnlev #include <sys/sunddi.h>
736784Sjohnlev #include <sys/policy.h>
746784Sjohnlev
755741Smrj #ifdef XPV_HVM_DRIVER
765741Smrj #include <public/io/xenbus.h>
775741Smrj #include <public/io/xs_wire.h>
785741Smrj #include <sys/xpv_support.h>
795741Smrj #endif
805084Sjohnlev #include <sys/hypervisor.h>
815741Smrj #include <xen/sys/xenbus.h>
825084Sjohnlev #include <xen/sys/xenbus_comms.h>
835084Sjohnlev #include <xen/sys/xenbus_impl.h>
845084Sjohnlev #include <xen/public/io/xs_wire.h>
855084Sjohnlev
865084Sjohnlev #ifdef DEBUG
875084Sjohnlev #define XENBUSDRV_DBPRINT(fmt) { if (xenbusdrv_debug) cmn_err fmt; }
885084Sjohnlev #else
895084Sjohnlev #define XENBUSDRV_DBPRINT(fmt)
905084Sjohnlev #endif /* ifdef DEBUG */
915084Sjohnlev
925084Sjohnlev /* Some handy macros */
935084Sjohnlev #define XENBUSDRV_MASK_READ_IDX(idx) ((idx) & (PAGESIZE - 1))
945084Sjohnlev #define XENBUSDRV_MINOR2INST(minor) ((int)(minor))
955084Sjohnlev #define XENBUSDRV_NCLONES 256
965084Sjohnlev #define XENBUSDRV_INST2SOFTS(instance) \
975084Sjohnlev ((xenbus_dev_t *)ddi_get_soft_state(xenbusdrv_statep, (instance)))
985084Sjohnlev
995084Sjohnlev static int xenbusdrv_debug = 0;
1005084Sjohnlev static int xenbusdrv_clone_tab[XENBUSDRV_NCLONES];
1015084Sjohnlev static dev_info_t *xenbusdrv_dip;
1025084Sjohnlev static kmutex_t xenbusdrv_clone_tab_mutex;
1035084Sjohnlev
1045084Sjohnlev struct xenbus_dev_transaction {
1055084Sjohnlev list_t list;
1065084Sjohnlev xenbus_transaction_t handle;
1075084Sjohnlev };
1085084Sjohnlev
1095084Sjohnlev /* Soft state data structure for xenbus driver */
1105084Sjohnlev struct xenbus_dev_data {
1115084Sjohnlev dev_info_t *dip;
1125084Sjohnlev
1135084Sjohnlev /* In-progress transaction. */
1145084Sjohnlev list_t transactions;
1155084Sjohnlev
1165084Sjohnlev /* Partial request. */
1175084Sjohnlev unsigned int len;
1185084Sjohnlev union {
1195084Sjohnlev struct xsd_sockmsg msg;
1205084Sjohnlev char buffer[MMU_PAGESIZE];
1215084Sjohnlev } u;
1225084Sjohnlev
1235084Sjohnlev /* Response queue. */
1245084Sjohnlev char read_buffer[MMU_PAGESIZE];
1255084Sjohnlev unsigned int read_cons, read_prod;
1265084Sjohnlev kcondvar_t read_cv;
1275084Sjohnlev kmutex_t read_mutex;
1285084Sjohnlev int xenstore_inst;
1295084Sjohnlev };
1305084Sjohnlev typedef struct xenbus_dev_data xenbus_dev_t;
1315084Sjohnlev static void *xenbusdrv_statep;
1325084Sjohnlev
1335084Sjohnlev static int xenbusdrv_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1345084Sjohnlev static int xenbusdrv_attach(dev_info_t *, ddi_attach_cmd_t);
1355084Sjohnlev static int xenbusdrv_detach(dev_info_t *, ddi_detach_cmd_t);
1365084Sjohnlev static int xenbusdrv_open(dev_t *, int, int, cred_t *);
1375084Sjohnlev static int xenbusdrv_close(dev_t, int, int, cred_t *);
1385084Sjohnlev static int xenbusdrv_read(dev_t, struct uio *, cred_t *);
1395084Sjohnlev static int xenbusdrv_write(dev_t, struct uio *, cred_t *);
1405084Sjohnlev static int xenbusdrv_devmap(dev_t, devmap_cookie_t, offset_t, size_t, size_t *,
1415084Sjohnlev uint_t);
1426784Sjohnlev static int xenbusdrv_segmap(dev_t, off_t, ddi_as_handle_t, caddr_t *, off_t,
1436784Sjohnlev uint_t, uint_t, uint_t, cred_t *);
1445084Sjohnlev static int xenbusdrv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1455084Sjohnlev static int xenbusdrv_queue_reply(xenbus_dev_t *, const struct xsd_sockmsg *,
1465084Sjohnlev const char *);
1475084Sjohnlev
1485084Sjohnlev /* Solaris driver framework */
1495084Sjohnlev
1505084Sjohnlev static struct cb_ops xenbusdrv_cb_ops = {
1515084Sjohnlev xenbusdrv_open, /* cb_open */
1525084Sjohnlev xenbusdrv_close, /* cb_close */
1535084Sjohnlev nodev, /* cb_strategy */
1545084Sjohnlev nodev, /* cb_print */
1555084Sjohnlev nodev, /* cb_dump */
1565084Sjohnlev xenbusdrv_read, /* cb_read */
1575084Sjohnlev xenbusdrv_write, /* cb_write */
1585084Sjohnlev xenbusdrv_ioctl, /* cb_ioctl */
1595084Sjohnlev xenbusdrv_devmap, /* cb_devmap */
1605084Sjohnlev NULL, /* cb_mmap */
1616784Sjohnlev xenbusdrv_segmap, /* cb_segmap */
1625084Sjohnlev nochpoll, /* cb_chpoll */
1635084Sjohnlev ddi_prop_op, /* cb_prop_op */
1645084Sjohnlev 0, /* cb_stream */
1655084Sjohnlev D_DEVMAP | D_NEW | D_MP, /* cb_flag */
1665084Sjohnlev CB_REV
1675084Sjohnlev };
1685084Sjohnlev
1695084Sjohnlev static struct dev_ops xenbusdrv_dev_ops = {
1705084Sjohnlev DEVO_REV, /* devo_rev */
1715084Sjohnlev 0, /* devo_refcnt */
1725084Sjohnlev xenbusdrv_info, /* devo_getinfo */
1735084Sjohnlev nulldev, /* devo_identify */
1745084Sjohnlev nulldev, /* devo_probe */
1755084Sjohnlev xenbusdrv_attach, /* devo_attach */
1765084Sjohnlev xenbusdrv_detach, /* devo_detach */
1775084Sjohnlev nodev, /* devo_reset */
1785084Sjohnlev &xenbusdrv_cb_ops, /* devo_cb_ops */
1795084Sjohnlev NULL, /* devo_bus_ops */
180*7656SSherry.Moore@Sun.COM NULL, /* devo_power */
181*7656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* devo_quiesce */
1825084Sjohnlev };
1835084Sjohnlev
1845084Sjohnlev static struct modldrv modldrv = {
1855084Sjohnlev &mod_driverops, /* Type of module. This one is a driver */
186*7656SSherry.Moore@Sun.COM "virtual bus driver", /* Name of the module. */
1875084Sjohnlev &xenbusdrv_dev_ops /* driver ops */
1885084Sjohnlev };
1895084Sjohnlev
1905084Sjohnlev static struct modlinkage modlinkage = {
1915084Sjohnlev MODREV_1,
1925084Sjohnlev &modldrv,
1935084Sjohnlev NULL
1945084Sjohnlev };
1955084Sjohnlev
1965084Sjohnlev int
_init(void)1975084Sjohnlev _init(void)
1985084Sjohnlev {
1995084Sjohnlev int e;
2005084Sjohnlev
2015084Sjohnlev e = ddi_soft_state_init(&xenbusdrv_statep, sizeof (xenbus_dev_t), 1);
2025084Sjohnlev if (e)
2035084Sjohnlev return (e);
2045084Sjohnlev
2055084Sjohnlev e = mod_install(&modlinkage);
2065084Sjohnlev if (e)
2075084Sjohnlev ddi_soft_state_fini(&xenbusdrv_statep);
2085084Sjohnlev
2095084Sjohnlev return (e);
2105084Sjohnlev }
2115084Sjohnlev
2125084Sjohnlev int
_fini(void)2135084Sjohnlev _fini(void)
2145084Sjohnlev {
2155084Sjohnlev int e;
2165084Sjohnlev
2175084Sjohnlev e = mod_remove(&modlinkage);
2185084Sjohnlev if (e)
2195084Sjohnlev return (e);
2205084Sjohnlev
2215084Sjohnlev ddi_soft_state_fini(&xenbusdrv_statep);
2225084Sjohnlev
2235084Sjohnlev return (0);
2245084Sjohnlev }
2255084Sjohnlev
2265084Sjohnlev int
_info(struct modinfo * modinfop)2275084Sjohnlev _info(struct modinfo *modinfop)
2285084Sjohnlev {
2295084Sjohnlev return (mod_info(&modlinkage, modinfop));
2305084Sjohnlev }
2315084Sjohnlev
2325084Sjohnlev /* ARGSUSED */
2335084Sjohnlev static int
xenbusdrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)2345084Sjohnlev xenbusdrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
2355084Sjohnlev {
2365084Sjohnlev dev_t dev = (dev_t)arg;
2375084Sjohnlev minor_t minor = getminor(dev);
2385084Sjohnlev int retval;
2395084Sjohnlev
2405084Sjohnlev switch (cmd) {
2415084Sjohnlev case DDI_INFO_DEVT2DEVINFO:
2425084Sjohnlev if (minor != 0 || xenbusdrv_dip == NULL) {
2435084Sjohnlev *result = (void *)NULL;
2445084Sjohnlev retval = DDI_FAILURE;
2455084Sjohnlev } else {
2465084Sjohnlev *result = (void *)xenbusdrv_dip;
2475084Sjohnlev retval = DDI_SUCCESS;
2485084Sjohnlev }
2495084Sjohnlev break;
2505084Sjohnlev case DDI_INFO_DEVT2INSTANCE:
2515084Sjohnlev *result = (void *)0;
2525084Sjohnlev retval = DDI_SUCCESS;
2535084Sjohnlev break;
2545084Sjohnlev default:
2555084Sjohnlev retval = DDI_FAILURE;
2565084Sjohnlev }
2575084Sjohnlev return (retval);
2585084Sjohnlev }
2595084Sjohnlev
2605084Sjohnlev static int
xenbusdrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2615084Sjohnlev xenbusdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2625084Sjohnlev {
2635084Sjohnlev int error;
2645084Sjohnlev int unit = ddi_get_instance(dip);
2655084Sjohnlev
2665084Sjohnlev
2675084Sjohnlev switch (cmd) {
2685084Sjohnlev case DDI_ATTACH:
2695084Sjohnlev break;
2705084Sjohnlev case DDI_RESUME:
2715084Sjohnlev return (DDI_SUCCESS);
2725084Sjohnlev default:
2735084Sjohnlev cmn_err(CE_WARN, "xenbus_attach: unknown cmd 0x%x\n", cmd);
2745084Sjohnlev return (DDI_FAILURE);
2755084Sjohnlev }
2765084Sjohnlev
2775084Sjohnlev /* DDI_ATTACH */
2785084Sjohnlev
2795084Sjohnlev /*
2805084Sjohnlev * only one instance - but we clone using the open routine
2815084Sjohnlev */
2825084Sjohnlev if (ddi_get_instance(dip) > 0)
2835084Sjohnlev return (DDI_FAILURE);
2845084Sjohnlev
2855084Sjohnlev mutex_init(&xenbusdrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
2865084Sjohnlev NULL);
2875084Sjohnlev
2885084Sjohnlev error = ddi_create_minor_node(dip, "xenbus", S_IFCHR, unit,
2895084Sjohnlev DDI_PSEUDO, NULL);
2905084Sjohnlev if (error != DDI_SUCCESS)
2915084Sjohnlev goto fail;
2925084Sjohnlev
2935084Sjohnlev /*
2945084Sjohnlev * save dip for getinfo
2955084Sjohnlev */
2965084Sjohnlev xenbusdrv_dip = dip;
2975084Sjohnlev ddi_report_dev(dip);
2985084Sjohnlev
2995741Smrj #ifndef XPV_HVM_DRIVER
3005084Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info))
3015084Sjohnlev xs_dom0_init();
3025741Smrj #endif
3035084Sjohnlev
3045084Sjohnlev return (DDI_SUCCESS);
3055084Sjohnlev
3065084Sjohnlev fail:
3075084Sjohnlev (void) xenbusdrv_detach(dip, DDI_DETACH);
3085084Sjohnlev return (error);
3095084Sjohnlev }
3105084Sjohnlev
3115084Sjohnlev static int
xenbusdrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3125084Sjohnlev xenbusdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3135084Sjohnlev {
3145084Sjohnlev /*
3155084Sjohnlev * again, only one instance
3165084Sjohnlev */
3175084Sjohnlev if (ddi_get_instance(dip) > 0)
3185084Sjohnlev return (DDI_FAILURE);
3195084Sjohnlev
3205084Sjohnlev switch (cmd) {
3215084Sjohnlev case DDI_DETACH:
3225084Sjohnlev ddi_remove_minor_node(dip, NULL);
3235084Sjohnlev mutex_destroy(&xenbusdrv_clone_tab_mutex);
3245084Sjohnlev xenbusdrv_dip = NULL;
3255084Sjohnlev return (DDI_SUCCESS);
3265084Sjohnlev case DDI_SUSPEND:
3275084Sjohnlev return (DDI_SUCCESS);
3285084Sjohnlev default:
3295084Sjohnlev cmn_err(CE_WARN, "xenbus_detach: unknown cmd 0x%x\n", cmd);
3305084Sjohnlev return (DDI_FAILURE);
3315084Sjohnlev }
3325084Sjohnlev }
3335084Sjohnlev
3345084Sjohnlev /* ARGSUSED */
3355084Sjohnlev static int
xenbusdrv_open(dev_t * devp,int flag,int otyp,cred_t * cr)3366784Sjohnlev xenbusdrv_open(dev_t *devp, int flag, int otyp, cred_t *cr)
3375084Sjohnlev {
3385084Sjohnlev xenbus_dev_t *xbs;
3395084Sjohnlev minor_t minor = getminor(*devp);
3405084Sjohnlev
3415084Sjohnlev if (otyp == OTYP_BLK)
3425084Sjohnlev return (ENXIO);
3435084Sjohnlev
3445084Sjohnlev /*
3455084Sjohnlev * only allow open on minor = 0 - the clone device
3465084Sjohnlev */
3475084Sjohnlev if (minor != 0)
3485084Sjohnlev return (ENXIO);
3495084Sjohnlev
3505084Sjohnlev /*
3515084Sjohnlev * find a free slot and grab it
3525084Sjohnlev */
3535084Sjohnlev mutex_enter(&xenbusdrv_clone_tab_mutex);
3545084Sjohnlev for (minor = 1; minor < XENBUSDRV_NCLONES; minor++) {
3555084Sjohnlev if (xenbusdrv_clone_tab[minor] == 0) {
3565084Sjohnlev xenbusdrv_clone_tab[minor] = 1;
3575084Sjohnlev break;
3585084Sjohnlev }
3595084Sjohnlev }
3605084Sjohnlev mutex_exit(&xenbusdrv_clone_tab_mutex);
3615084Sjohnlev if (minor == XENBUSDRV_NCLONES)
3625084Sjohnlev return (EAGAIN);
3635084Sjohnlev
3645084Sjohnlev /* Allocate softstate structure */
3655084Sjohnlev if (ddi_soft_state_zalloc(xenbusdrv_statep,
3665084Sjohnlev XENBUSDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
3675084Sjohnlev mutex_enter(&xenbusdrv_clone_tab_mutex);
3685084Sjohnlev xenbusdrv_clone_tab[minor] = 0;
3695084Sjohnlev mutex_exit(&xenbusdrv_clone_tab_mutex);
3705084Sjohnlev return (EAGAIN);
3715084Sjohnlev }
3725084Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
3735084Sjohnlev
3745084Sjohnlev /* ... and init it */
3755084Sjohnlev xbs->dip = xenbusdrv_dip;
3765084Sjohnlev mutex_init(&xbs->read_mutex, NULL, MUTEX_DRIVER, NULL);
3775084Sjohnlev cv_init(&xbs->read_cv, NULL, CV_DEFAULT, NULL);
3785084Sjohnlev list_create(&xbs->transactions, sizeof (struct xenbus_dev_transaction),
3795084Sjohnlev offsetof(struct xenbus_dev_transaction, list));
3805084Sjohnlev
3815084Sjohnlev /* clone driver */
3825084Sjohnlev *devp = makedevice(getmajor(*devp), minor);
3835084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv open succeeded, minor=%d",
3845084Sjohnlev minor));
3855084Sjohnlev
3865084Sjohnlev return (0);
3875084Sjohnlev }
3885084Sjohnlev
3895084Sjohnlev /* ARGSUSED */
3905084Sjohnlev static int
xenbusdrv_close(dev_t dev,int flag,int otyp,struct cred * cr)3916784Sjohnlev xenbusdrv_close(dev_t dev, int flag, int otyp, struct cred *cr)
3925084Sjohnlev {
3935084Sjohnlev xenbus_dev_t *xbs;
3945084Sjohnlev minor_t minor = getminor(dev);
3955084Sjohnlev struct xenbus_dev_transaction *trans;
3965084Sjohnlev
3975084Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
3985084Sjohnlev if (xbs == NULL)
3995084Sjohnlev return (ENXIO);
4005084Sjohnlev
4015084Sjohnlev #ifdef notyet
4025084Sjohnlev /*
4035084Sjohnlev * XXPV - would like to be able to notify xenstore down here, but
4045084Sjohnlev * as the daemon is currently written, it doesn't leave the device
4055084Sjohnlev * open after initial setup, so we have no way of knowing if it has
4065084Sjohnlev * gone away.
4075084Sjohnlev */
4085084Sjohnlev if (xbs->xenstore_inst)
4095084Sjohnlev xs_notify_xenstore_down();
4105084Sjohnlev #endif
4115084Sjohnlev /* free pending transaction */
4125084Sjohnlev while (trans = (struct xenbus_dev_transaction *)
4135084Sjohnlev list_head(&xbs->transactions)) {
4145084Sjohnlev (void) xenbus_transaction_end(trans->handle, 1);
4155084Sjohnlev list_remove(&xbs->transactions, (void *)trans);
4165084Sjohnlev kmem_free(trans, sizeof (*trans));
4175084Sjohnlev }
4185084Sjohnlev
4195084Sjohnlev mutex_destroy(&xbs->read_mutex);
4205084Sjohnlev cv_destroy(&xbs->read_cv);
4215084Sjohnlev ddi_soft_state_free(xenbusdrv_statep, XENBUSDRV_MINOR2INST(minor));
4225084Sjohnlev
4235084Sjohnlev /*
4245084Sjohnlev * free clone tab slot
4255084Sjohnlev */
4265084Sjohnlev mutex_enter(&xenbusdrv_clone_tab_mutex);
4275084Sjohnlev xenbusdrv_clone_tab[minor] = 0;
4285084Sjohnlev mutex_exit(&xenbusdrv_clone_tab_mutex);
4295084Sjohnlev
4305084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv close succeeded, minor=%d",
4315084Sjohnlev minor));
4325084Sjohnlev
4335084Sjohnlev return (0);
4345084Sjohnlev }
4355084Sjohnlev
4365084Sjohnlev /* ARGSUSED */
4375084Sjohnlev static int
xenbusdrv_read(dev_t dev,struct uio * uiop,cred_t * cr)4386784Sjohnlev xenbusdrv_read(dev_t dev, struct uio *uiop, cred_t *cr)
4395084Sjohnlev {
4405084Sjohnlev xenbus_dev_t *xbs;
4415084Sjohnlev size_t len;
4425084Sjohnlev int res, ret;
4435084Sjohnlev int idx;
4445084Sjohnlev
4455084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_read called"));
4465084Sjohnlev
4476784Sjohnlev if (secpolicy_xvm_control(cr))
4486784Sjohnlev return (EPERM);
4496784Sjohnlev
4505084Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
4515084Sjohnlev
4525084Sjohnlev mutex_enter(&xbs->read_mutex);
4535084Sjohnlev
4545084Sjohnlev /* check if we have something to read */
4555084Sjohnlev while (xbs->read_prod == xbs->read_cons) {
4565084Sjohnlev if (cv_wait_sig(&xbs->read_cv, &xbs->read_mutex) == 0) {
4575084Sjohnlev mutex_exit(&xbs->read_mutex);
4585084Sjohnlev return (EINTR);
4595084Sjohnlev }
4605084Sjohnlev }
4615084Sjohnlev
4625084Sjohnlev idx = XENBUSDRV_MASK_READ_IDX(xbs->read_cons);
4635084Sjohnlev res = uiop->uio_resid;
4645084Sjohnlev
4655084Sjohnlev len = xbs->read_prod - xbs->read_cons;
4665084Sjohnlev
4675084Sjohnlev if (len > (sizeof (xbs->read_buffer) - idx))
4685084Sjohnlev len = sizeof (xbs->read_buffer) - idx;
4695084Sjohnlev if (len > res)
4705084Sjohnlev len = res;
4715084Sjohnlev
4725084Sjohnlev ret = uiomove(xbs->read_buffer + idx, len, UIO_READ, uiop);
4735084Sjohnlev xbs->read_cons += res - uiop->uio_resid;
4745084Sjohnlev mutex_exit(&xbs->read_mutex);
4755084Sjohnlev
4765084Sjohnlev return (ret);
4775084Sjohnlev }
4785084Sjohnlev
4795084Sjohnlev /*
4805084Sjohnlev * prepare data for xenbusdrv_read()
4815084Sjohnlev */
4825084Sjohnlev static int
xenbusdrv_queue_reply(xenbus_dev_t * xbs,const struct xsd_sockmsg * msg,const char * reply)4835084Sjohnlev xenbusdrv_queue_reply(xenbus_dev_t *xbs, const struct xsd_sockmsg *msg,
4845084Sjohnlev const char *reply)
4855084Sjohnlev {
4865084Sjohnlev int i;
4875084Sjohnlev int remaining;
4885084Sjohnlev
4895084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply called"));
4905084Sjohnlev
4915084Sjohnlev mutex_enter(&xbs->read_mutex);
4925084Sjohnlev
4935084Sjohnlev remaining = sizeof (xbs->read_buffer) -
4945084Sjohnlev (xbs->read_prod - xbs->read_cons);
4955084Sjohnlev
4965084Sjohnlev if (sizeof (*msg) + msg->len > remaining) {
4975084Sjohnlev mutex_exit(&xbs->read_mutex);
4985084Sjohnlev return (EOVERFLOW);
4995084Sjohnlev }
5005084Sjohnlev
5015084Sjohnlev for (i = 0; i < sizeof (*msg); i++, xbs->read_prod++) {
5025084Sjohnlev xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
5035084Sjohnlev ((char *)msg)[i];
5045084Sjohnlev }
5055084Sjohnlev
5065084Sjohnlev for (i = 0; i < msg->len; i++, xbs->read_prod++) {
5075084Sjohnlev xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
5085084Sjohnlev reply[i];
5095084Sjohnlev }
5105084Sjohnlev
5115084Sjohnlev cv_broadcast(&xbs->read_cv);
5125084Sjohnlev
5135084Sjohnlev mutex_exit(&xbs->read_mutex);
5145084Sjohnlev
5155084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply exited"));
5165084Sjohnlev
5175084Sjohnlev return (0);
5185084Sjohnlev }
5195084Sjohnlev
5205084Sjohnlev /* ARGSUSED */
5215084Sjohnlev static int
xenbusdrv_write(dev_t dev,struct uio * uiop,cred_t * cr)5226784Sjohnlev xenbusdrv_write(dev_t dev, struct uio *uiop, cred_t *cr)
5235084Sjohnlev {
5245084Sjohnlev xenbus_dev_t *xbs;
5255084Sjohnlev struct xenbus_dev_transaction *trans;
5265084Sjohnlev void *reply;
5275084Sjohnlev size_t len;
5285084Sjohnlev int rc = 0;
5295084Sjohnlev
5305084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_write called"));
5315084Sjohnlev
5326784Sjohnlev if (secpolicy_xvm_control(cr))
5336784Sjohnlev return (EPERM);
5346784Sjohnlev
5355084Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
5365084Sjohnlev len = uiop->uio_resid;
5375084Sjohnlev
5385084Sjohnlev if ((len + xbs->len) > sizeof (xbs->u.buffer)) {
5395084Sjohnlev XENBUSDRV_DBPRINT((CE_WARN, "Request is too big"));
5405084Sjohnlev rc = EINVAL;
5415084Sjohnlev goto out;
5425084Sjohnlev }
5435084Sjohnlev
5445084Sjohnlev if (uiomove(xbs->u.buffer + xbs->len, len, UIO_WRITE, uiop) != 0) {
5455084Sjohnlev XENBUSDRV_DBPRINT((CE_WARN, "Uiomove failed"));
5465084Sjohnlev rc = EFAULT;
5475084Sjohnlev goto out;
5485084Sjohnlev }
5495084Sjohnlev
5505084Sjohnlev xbs->len += len;
5515084Sjohnlev
5525084Sjohnlev if (xbs->len < (sizeof (xbs->u.msg)) ||
5535084Sjohnlev xbs->len < (sizeof (xbs->u.msg) + xbs->u.msg.len)) {
5545084Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "Partial request"));
5555084Sjohnlev return (0);
5565084Sjohnlev }
5575084Sjohnlev
5585084Sjohnlev switch (xbs->u.msg.type) {
5595084Sjohnlev case XS_TRANSACTION_START:
5605084Sjohnlev case XS_TRANSACTION_END:
5615084Sjohnlev case XS_DIRECTORY:
5625084Sjohnlev case XS_READ:
5635084Sjohnlev case XS_GET_PERMS:
5645084Sjohnlev case XS_RELEASE:
5655084Sjohnlev case XS_GET_DOMAIN_PATH:
5665084Sjohnlev case XS_WRITE:
5675084Sjohnlev case XS_MKDIR:
5685084Sjohnlev case XS_RM:
5695084Sjohnlev case XS_SET_PERMS:
5705084Sjohnlev /* send the request to xenstore and get feedback */
5715084Sjohnlev rc = xenbus_dev_request_and_reply(&xbs->u.msg, &reply);
5725084Sjohnlev if (rc) {
5735084Sjohnlev XENBUSDRV_DBPRINT((CE_WARN,
5745084Sjohnlev "xenbus_dev_request_and_reply failed"));
5755084Sjohnlev goto out;
5765084Sjohnlev }
5775084Sjohnlev
5785084Sjohnlev /* handle transaction start/end */
5795084Sjohnlev if (xbs->u.msg.type == XS_TRANSACTION_START) {
5805084Sjohnlev trans = kmem_alloc(sizeof (*trans), KM_SLEEP);
5815084Sjohnlev (void) ddi_strtoul((char *)reply, NULL, 0,
5825084Sjohnlev (unsigned long *)&trans->handle);
5835084Sjohnlev list_insert_tail(&xbs->transactions, (void *)trans);
5845084Sjohnlev } else if (xbs->u.msg.type == XS_TRANSACTION_END) {
5855084Sjohnlev /* try to find out the ending transaction */
5865084Sjohnlev for (trans = (struct xenbus_dev_transaction *)
5875084Sjohnlev list_head(&xbs->transactions); trans;
5885084Sjohnlev trans = (struct xenbus_dev_transaction *)
5895084Sjohnlev list_next(&xbs->transactions, (void *)trans))
5905084Sjohnlev if (trans->handle ==
5915084Sjohnlev (xenbus_transaction_t)
5925084Sjohnlev xbs->u.msg.tx_id)
5935084Sjohnlev break;
5945084Sjohnlev ASSERT(trans);
5955084Sjohnlev /* free it, if we find it */
5965084Sjohnlev list_remove(&xbs->transactions, (void *)trans);
5975084Sjohnlev kmem_free(trans, sizeof (*trans));
5985084Sjohnlev }
5995084Sjohnlev
6005084Sjohnlev /* prepare data for xenbusdrv_read() to get */
6015084Sjohnlev rc = xenbusdrv_queue_reply(xbs, &xbs->u.msg, reply);
6025084Sjohnlev
6035084Sjohnlev kmem_free(reply, xbs->u.msg.len + 1);
6045084Sjohnlev break;
6055084Sjohnlev default:
6065084Sjohnlev rc = EINVAL;
6075084Sjohnlev }
6085084Sjohnlev
6095084Sjohnlev out:
6105084Sjohnlev xbs->len = 0;
6115084Sjohnlev return (rc);
6125084Sjohnlev }
6135084Sjohnlev
6145084Sjohnlev /*ARGSUSED*/
6155084Sjohnlev static int
xenbusdrv_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)6165084Sjohnlev xenbusdrv_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
6175084Sjohnlev size_t *maplen, uint_t model)
6185084Sjohnlev {
6195084Sjohnlev xenbus_dev_t *xbs;
6205084Sjohnlev int err;
6215084Sjohnlev
6225084Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
6235084Sjohnlev
6245084Sjohnlev if (off != 0 || len != PAGESIZE)
6255084Sjohnlev return (-1);
6265084Sjohnlev
6275084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info))
6285084Sjohnlev return (-1);
6295084Sjohnlev
6305084Sjohnlev err = devmap_umem_setup(dhp, xbs->dip, NULL, xb_xenstore_cookie(),
6315084Sjohnlev 0, PAGESIZE, PROT_READ | PROT_WRITE | PROT_USER, 0, NULL);
6325084Sjohnlev
6335084Sjohnlev if (err)
6345084Sjohnlev return (err);
6355084Sjohnlev
6365084Sjohnlev *maplen = PAGESIZE;
6375084Sjohnlev
6385084Sjohnlev return (0);
6395084Sjohnlev }
6405084Sjohnlev
6416784Sjohnlev static int
xenbusdrv_segmap(dev_t dev,off_t off,ddi_as_handle_t as,caddr_t * addrp,off_t len,uint_t prot,uint_t maxprot,uint_t flags,cred_t * cr)6426784Sjohnlev xenbusdrv_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp,
6436784Sjohnlev off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr)
6446784Sjohnlev {
6456784Sjohnlev
6466784Sjohnlev if (secpolicy_xvm_control(cr))
6476784Sjohnlev return (EPERM);
6486784Sjohnlev
6496784Sjohnlev return (ddi_devmap_segmap(dev, off, as, addrp, len, prot,
6506784Sjohnlev maxprot, flags, cr));
6516784Sjohnlev }
6526784Sjohnlev
6535084Sjohnlev /*ARGSUSED*/
6545084Sjohnlev static int
xenbusdrv_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rvalp)6556784Sjohnlev xenbusdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr,
6565084Sjohnlev int *rvalp)
6575084Sjohnlev {
6585084Sjohnlev xenbus_dev_t *xbs;
6595084Sjohnlev
6606784Sjohnlev if (secpolicy_xvm_control(cr))
6616784Sjohnlev return (EPERM);
6626784Sjohnlev
6635084Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
6645084Sjohnlev switch (cmd) {
6655084Sjohnlev case IOCTL_XENBUS_XENSTORE_EVTCHN:
6665084Sjohnlev *rvalp = xen_info->store_evtchn;
6675084Sjohnlev break;
6685084Sjohnlev case IOCTL_XENBUS_NOTIFY_UP:
6695084Sjohnlev xs_notify_xenstore_up();
6705084Sjohnlev xbs->xenstore_inst = 1;
6715084Sjohnlev break;
6725084Sjohnlev default:
6735084Sjohnlev return (EINVAL);
6745084Sjohnlev }
6755084Sjohnlev
6765084Sjohnlev return (0);
6775084Sjohnlev }
678