1/* $NetBSD: xenbus_dev.c,v 1.8 2009/03/16 06:17:39 cegger 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.8 2009/03/16 06:17:39 cegger 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 58static int xenbus_dev_read(void *); 59static int xenbus_dev_write(void *); 60static int xenbus_dev_open(void *); 61static int xenbus_dev_close(void *); 62static int xsd_port_read(void *); 63 64struct 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) 71static 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) 79static const struct kernfs_fileop xsd_port_fileops[] = { 80 { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xsd_port_read }, 81}; 82 83void 84xenbus_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 104struct 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 122static int 123xenbus_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 158end: 159 splx(s); 160 return err; 161} 162 163static void 164queue_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 177static int 178xenbus_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 261static int 262xenbus_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 288static int 289xenbus_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 316static int 317xsd_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