Home | History | Annotate | Line # | Download | only in xenbus
xenbus_dev.c revision 1.10
      1 /* $NetBSD: xenbus_dev.c,v 1.10 2016/07/07 06:55:40 msaitoh Exp $ */
      2 /*
      3  * xenbus_dev.c
      4  *
      5  * Driver giving user-space access to the kernel's xenbus connection
      6  * to xenstore.
      7  *
      8  * Copyright (c) 2005, Christian Limpach
      9  * Copyright (c) 2005, Rusty Russell, IBM Corporation
     10  *
     11  * This file may be distributed separately from the Linux kernel, or
     12  * incorporated into other software packages, subject to the following license:
     13  *
     14  * Permission is hereby granted, free of charge, to any person obtaining a copy
     15  * of this source file (the "Software"), to deal in the Software without
     16  * restriction, including without limitation the rights to use, copy, modify,
     17  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
     18  * and to permit persons to whom the Software is furnished to do so, subject to
     19  * the following conditions:
     20  *
     21  * The above copyright notice and this permission notice shall be included in
     22  * all copies or substantial portions of the Software.
     23  *
     24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     30  * IN THE SOFTWARE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 __KERNEL_RCSID(0, "$NetBSD: xenbus_dev.c,v 1.10 2016/07/07 06:55:40 msaitoh Exp $");
     35 
     36 #include "opt_xen.h"
     37 
     38 #include <sys/types.h>
     39 #include <sys/null.h>
     40 #include <sys/errno.h>
     41 #include <sys/malloc.h>
     42 #include <sys/param.h>
     43 #include <sys/proc.h>
     44 #include <sys/systm.h>
     45 #include <sys/dirent.h>
     46 #include <sys/stat.h>
     47 #include <sys/tree.h>
     48 #include <sys/vnode.h>
     49 #include <miscfs/specfs/specdev.h>
     50 #include <miscfs/kernfs/kernfs.h>
     51 
     52 #include <xen/kernfs_machdep.h>
     53 
     54 #include <xen/hypervisor.h>
     55 #include <xen/xenbus.h>
     56 #include "xenbus_comms.h"
     57 
     58 static int xenbus_dev_read(void *);
     59 static int xenbus_dev_write(void *);
     60 static int xenbus_dev_open(void *);
     61 static int xenbus_dev_close(void *);
     62 static int xsd_port_read(void *);
     63 
     64 struct xenbus_dev_transaction {
     65 	SLIST_ENTRY(xenbus_dev_transaction) trans_next;
     66 	struct xenbus_transaction *handle;
     67 };
     68 
     69 #define DIR_MODE	 (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
     70 #define PRIVCMD_MODE    (S_IRUSR | S_IWUSR)
     71 static const struct kernfs_fileop xenbus_fileops[] = {
     72   { .kf_fileop = KERNFS_FILEOP_OPEN, .kf_vop = xenbus_dev_open },
     73   { .kf_fileop = KERNFS_FILEOP_CLOSE, .kf_vop = xenbus_dev_close },
     74   { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xenbus_dev_read },
     75   { .kf_fileop = KERNFS_FILEOP_WRITE, .kf_vop = xenbus_dev_write },
     76 };
     77 
     78 #define XSD_MODE    (S_IRUSR)
     79 static const struct kernfs_fileop xsd_port_fileops[] = {
     80     { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xsd_port_read },
     81 };
     82 
     83 void
     84 xenbus_kernfs_init(void)
     85 {
     86 	kernfs_entry_t *dkt;
     87 	kfstype kfst;
     88 
     89 	kfst = KERNFS_ALLOCTYPE(xenbus_fileops);
     90 	KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK);
     91 	KERNFS_INITENTRY(dkt, DT_REG, "xenbus", NULL, kfst, VREG,
     92 	    PRIVCMD_MODE);
     93 	kernfs_addentry(kernxen_pkt, dkt);
     94 
     95 	if (xendomain_is_dom0()) {
     96 		kfst = KERNFS_ALLOCTYPE(xsd_port_fileops);
     97 		KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK);
     98 		KERNFS_INITENTRY(dkt, DT_REG, "xsd_port", NULL,
     99 		    kfst, VREG, XSD_MODE);
    100 		kernfs_addentry(kernxen_pkt, dkt);
    101 	}
    102 }
    103 
    104 struct xenbus_dev_data {
    105 #define BUFFER_SIZE (PAGE_SIZE)
    106 #define MASK_READ_IDX(idx) ((idx)&(BUFFER_SIZE-1))
    107 	/* In-progress transaction. */
    108 	SLIST_HEAD(, xenbus_dev_transaction) transactions;
    109 
    110 	/* Partial request. */
    111 	unsigned int len;
    112 	union {
    113 		struct xsd_sockmsg msg;
    114 		char buffer[BUFFER_SIZE];
    115 	} u;
    116 
    117 	/* Response queue. */
    118 	char read_buffer[BUFFER_SIZE];
    119 	unsigned int read_cons, read_prod;
    120 };
    121 
    122 static int
    123 xenbus_dev_read(void *v)
    124 {
    125 	struct vop_read_args /* {
    126 		struct vnode *a_vp;
    127 		struct uio *a_uio;
    128 		int  a_ioflag;
    129 		struct ucred *a_cred;
    130 	} */ *ap = v;
    131 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
    132 	struct uio *uio = ap->a_uio;
    133 	struct xenbus_dev_data *u = kfs->kfs_v;
    134 	int err;
    135 	off_t offset;
    136 	int s = spltty();
    137 
    138 	while (u->read_prod == u->read_cons) {
    139 		err = tsleep(u, PRIBIO | PCATCH, "xbrd", 0);
    140 		if (err)
    141 			goto end;
    142 	}
    143 	offset = uio->uio_offset;
    144 
    145 	if (u->read_cons > u->read_prod) {
    146 		err = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)],
    147 		    0U - u->read_cons, uio);
    148 		if (err)
    149 			goto end;
    150 		u->read_cons += (uio->uio_offset - offset);
    151 		offset = uio->uio_offset;
    152 	}
    153 	err = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)],
    154 	    u->read_prod - u->read_cons, uio);
    155 	if (err == 0)
    156 		u->read_cons += (uio->uio_offset - offset);
    157 
    158 end:
    159 	splx(s);
    160 	return err;
    161 }
    162 
    163 static void
    164 queue_reply(struct xenbus_dev_data *u,
    165 			char *data, unsigned int len)
    166 {
    167 	int i;
    168 
    169 	for (i = 0; i < len; i++, u->read_prod++)
    170 		u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i];
    171 
    172 	KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer));
    173 
    174 	wakeup(&u);
    175 }
    176 
    177 static int
    178 xenbus_dev_write(void *v)
    179 {
    180 	struct vop_write_args /* {
    181 		struct vnode *a_vp;
    182 		struct uio *a_uio;
    183 		int  a_ioflag;
    184 		struct ucred *a_cred;
    185 	} */ *ap = v;
    186 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
    187 	struct uio *uio = ap->a_uio;
    188 
    189 	struct xenbus_dev_data *u = kfs->kfs_v;
    190 	struct xenbus_dev_transaction *trans;
    191 	void *reply;
    192 	int err;
    193 	size_t size;
    194 
    195 	if (uio->uio_offset < 0)
    196 		return EINVAL;
    197 	size = uio->uio_resid;
    198 
    199 	if ((size + u->len) > sizeof(u->u.buffer))
    200 		return EINVAL;
    201 
    202 	err = uiomove(u->u.buffer + u->len, sizeof(u->u.buffer) -  u->len, uio);
    203 	if (err)
    204 		return err;
    205 
    206 	u->len += size;
    207 	if (u->len < (sizeof(u->u.msg) + u->u.msg.len))
    208 		return 0;
    209 
    210 	switch (u->u.msg.type) {
    211 	case XS_TRANSACTION_START:
    212 	case XS_TRANSACTION_END:
    213 	case XS_DIRECTORY:
    214 	case XS_READ:
    215 	case XS_GET_PERMS:
    216 	case XS_RELEASE:
    217 	case XS_GET_DOMAIN_PATH:
    218 	case XS_WRITE:
    219 	case XS_MKDIR:
    220 	case XS_RM:
    221 	case XS_SET_PERMS:
    222 		err = xenbus_dev_request_and_reply(&u->u.msg, &reply);
    223 		if (err == 0) {
    224 			if (u->u.msg.type == XS_TRANSACTION_START) {
    225 				trans = malloc(sizeof(*trans), M_DEVBUF,
    226 				    M_WAITOK);
    227 				trans->handle = (struct xenbus_transaction *)
    228 					strtoul(reply, NULL, 0);
    229 				SLIST_INSERT_HEAD(&u->transactions,
    230 				    trans, trans_next);
    231 			} else if (u->u.msg.type == XS_TRANSACTION_END) {
    232 				SLIST_FOREACH(trans, &u->transactions,
    233 						    trans_next) {
    234 					if ((unsigned long)trans->handle ==
    235 					    (unsigned long)u->u.msg.tx_id)
    236 						break;
    237 				}
    238 				KASSERT(trans != NULL);
    239 				SLIST_REMOVE(&u->transactions, trans,
    240 				    xenbus_dev_transaction, trans_next);
    241 				free(trans, M_DEVBUF);
    242 			}
    243 			queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg));
    244 			queue_reply(u, (char *)reply, u->u.msg.len);
    245 			free(reply, M_DEVBUF);
    246 		}
    247 		break;
    248 
    249 	default:
    250 		err = EINVAL;
    251 		break;
    252 	}
    253 
    254 	if (err == 0) {
    255 		u->len = 0;
    256 	}
    257 
    258 	return err;
    259 }
    260 
    261 static int
    262 xenbus_dev_open(void *v)
    263 {
    264 	struct vop_open_args /* {
    265 		struct vnode *a_vp;
    266 		int a_mode;
    267 		struct ucred *a_cred;
    268 	} */ *ap = v;
    269 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
    270 
    271 	struct xenbus_dev_data *u;
    272 
    273 	if (xen_start_info.store_evtchn == 0)
    274 		return ENOENT;
    275 
    276 	u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK);
    277 	if (u == NULL)
    278 		return ENOMEM;
    279 
    280 	memset(u, 0, sizeof(*u));
    281 	SLIST_INIT(&u->transactions);
    282 
    283 	kfs->kfs_v = u;
    284 
    285 	return 0;
    286 }
    287 
    288 static int
    289 xenbus_dev_close(void *v)
    290 {
    291 	struct vop_close_args /* {
    292 		struct vnode *a_vp;
    293 		int a_fflag;
    294 		struct ucred *a_cred;
    295 	} */ *ap = v;
    296 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
    297 
    298 	struct xenbus_dev_data *u = kfs->kfs_v;
    299 	struct xenbus_dev_transaction *trans;
    300 
    301 	while (!SLIST_EMPTY(&u->transactions)) {
    302 		trans = SLIST_FIRST(&u->transactions);
    303 		xenbus_transaction_end(trans->handle, 1);
    304 		SLIST_REMOVE_HEAD(&u->transactions, trans_next);
    305 		free(trans, M_DEVBUF);
    306 	}
    307 
    308 	free(u, M_DEVBUF);
    309 	kfs->kfs_v = NULL;
    310 
    311 	return 0;
    312 }
    313 
    314 #define LD_STRLEN 21 /* a 64bit integer needs 20 digits in base10 */
    315 
    316 static int
    317 xsd_port_read(void *v)
    318 {
    319 	struct vop_read_args /* {
    320 		struct vnode *a_vp;
    321 		struct uio *a_uio;
    322 		int  a_ioflag;
    323 		struct ucred *a_cred;
    324 	} */ *ap = v;
    325 	struct uio *uio = ap->a_uio;
    326 	int off, error;
    327 	size_t len;
    328 	char strbuf[LD_STRLEN], *bf;
    329 
    330 	off = (int)uio->uio_offset;
    331 	if (off < 0)
    332 		return EINVAL;
    333 
    334 	len  = snprintf(strbuf, sizeof(strbuf), "%ld\n",
    335 	    (long)xen_start_info.store_evtchn);
    336 	if (off >= len) {
    337 		bf = strbuf;
    338 		len = 0;
    339 	} else {
    340 		bf = &strbuf[off];
    341 		len -= off;
    342 	}
    343 	error = uiomove(bf, len, uio);
    344 	return error;
    345 }
    346 
    347 /*
    348  * Local variables:
    349  *  c-file-style: "linux"
    350  *  indent-tabs-mode: t
    351  *  c-indent-level: 8
    352  *  c-basic-offset: 8
    353  *  tab-width: 8
    354  * End:
    355  */
    356