1 /* $NetBSD: xenbus_dev.c,v 1.2 2006/03/06 20:21:35 bouyer 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.2 2006/03/06 20:21:35 bouyer 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 #include <machine/kernfs_machdep.h> 52 53 #include <machine/hypervisor.h> 54 #include <machine/xenbus.h> 55 #include "xenbus_comms.h" 56 57 static int xenbus_dev_read(void *); 58 static int xenbus_dev_write(void *); 59 static int xenbus_dev_open(void *); 60 static int xenbus_dev_close(void *); 61 62 struct xenbus_dev_transaction { 63 SLIST_ENTRY(xenbus_dev_transaction) trans_next; 64 struct xenbus_transaction *handle; 65 }; 66 67 #define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 68 #define PRIVCMD_MODE (S_IRUSR | S_IWUSR) 69 static const struct kernfs_fileop xenbus_fileops[] = { 70 { .kf_fileop = KERNFS_FILEOP_OPEN, .kf_vop = xenbus_dev_open }, 71 { .kf_fileop = KERNFS_FILEOP_CLOSE, .kf_vop = xenbus_dev_close }, 72 { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xenbus_dev_read }, 73 { .kf_fileop = KERNFS_FILEOP_WRITE, .kf_vop = xenbus_dev_write }, 74 }; 75 76 77 void 78 xenbus_kernfs_init() 79 { 80 kernfs_entry_t *dkt; 81 kfstype kfst; 82 83 kfst = KERNFS_ALLOCTYPE(xenbus_fileops); 84 KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK); 85 KERNFS_INITENTRY(dkt, DT_REG, "xenbus", NULL, kfst, VREG, 86 PRIVCMD_MODE); 87 kernfs_addentry(kernxen_pkt, dkt); 88 } 89 90 struct xenbus_dev_data { 91 #define BUFFER_SIZE (PAGE_SIZE) 92 #define MASK_READ_IDX(idx) ((idx)&(BUFFER_SIZE-1)) 93 /* In-progress transaction. */ 94 SLIST_HEAD(, xenbus_dev_transaction) transactions; 95 96 /* Partial request. */ 97 unsigned int len; 98 union { 99 struct xsd_sockmsg msg; 100 char buffer[BUFFER_SIZE]; 101 } u; 102 103 /* Response queue. */ 104 char read_buffer[BUFFER_SIZE]; 105 unsigned int read_cons, read_prod; 106 }; 107 108 static int 109 xenbus_dev_read(void *v) 110 { 111 struct vop_read_args /* { 112 struct vnode *a_vp; 113 struct uio *a_uio; 114 int a_ioflag; 115 struct ucred *a_cred; 116 } */ *ap = v; 117 struct kernfs_node *kfs = VTOKERN(ap->a_vp); 118 struct uio *uio = ap->a_uio; 119 struct xenbus_dev_data *u = kfs->kfs_v; 120 int err; 121 off_t offset; 122 int s = spltty(); 123 124 while (u->read_prod == u->read_cons) { 125 err = tsleep(u, PRIBIO | PCATCH, "xbrd", 0); 126 if (err) 127 goto end; 128 } 129 if (uio->uio_offset != 0) { 130 err = EINVAL; 131 goto end; 132 } 133 offset = 0; 134 135 if (u->read_cons > u->read_prod) { 136 err = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)], 137 0U - u->read_cons, uio); 138 if (err) 139 goto end; 140 u->read_cons += uio->uio_offset; 141 offset = uio->uio_offset; 142 } 143 err = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)], 144 u->read_prod - u->read_cons, uio); 145 if (err == 0) 146 u->read_cons += uio->uio_offset - offset; 147 148 end: 149 splx(s); 150 return err; 151 } 152 153 static void 154 queue_reply(struct xenbus_dev_data *u, 155 char *data, unsigned int len) 156 { 157 int i; 158 159 for (i = 0; i < len; i++, u->read_prod++) 160 u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; 161 162 KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer)); 163 164 wakeup(&u); 165 } 166 167 static int 168 xenbus_dev_write(void *v) 169 { 170 struct vop_write_args /* { 171 struct vnode *a_vp; 172 struct uio *a_uio; 173 int a_ioflag; 174 struct ucred *a_cred; 175 } */ *ap = v; 176 struct kernfs_node *kfs = VTOKERN(ap->a_vp); 177 struct uio *uio = ap->a_uio; 178 179 struct xenbus_dev_data *u = kfs->kfs_v; 180 struct xenbus_dev_transaction *trans; 181 void *reply; 182 int err; 183 size_t size; 184 185 if (uio->uio_offset < 0) 186 return EINVAL; 187 size = uio->uio_resid; 188 189 if ((size + u->len) > sizeof(u->u.buffer)) 190 return EINVAL; 191 192 err = uiomove(u->u.buffer + u->len, sizeof(u->u.buffer) - u->len, uio); 193 if (err) 194 return err; 195 196 u->len += size; 197 if (u->len < (sizeof(u->u.msg) + u->u.msg.len)) 198 return 0; 199 200 switch (u->u.msg.type) { 201 case XS_TRANSACTION_START: 202 case XS_TRANSACTION_END: 203 case XS_DIRECTORY: 204 case XS_READ: 205 case XS_GET_PERMS: 206 case XS_RELEASE: 207 case XS_GET_DOMAIN_PATH: 208 case XS_WRITE: 209 case XS_MKDIR: 210 case XS_RM: 211 case XS_SET_PERMS: 212 err = xenbus_dev_request_and_reply(&u->u.msg, &reply); 213 if (err == 0) { 214 if (u->u.msg.type == XS_TRANSACTION_START) { 215 trans = malloc(sizeof(*trans), M_DEVBUF, 216 M_WAITOK); 217 trans->handle = (struct xenbus_transaction *) 218 strtoul(reply, NULL, 0); 219 SLIST_INSERT_HEAD(&u->transactions, 220 trans, trans_next); 221 } else if (u->u.msg.type == XS_TRANSACTION_END) { 222 SLIST_FOREACH(trans, &u->transactions, 223 trans_next) { 224 if ((unsigned long)trans->handle == 225 (unsigned long)u->u.msg.tx_id) 226 break; 227 } 228 KASSERT(trans != NULL); 229 SLIST_REMOVE(&u->transactions, trans, 230 xenbus_dev_transaction, trans_next); 231 free(trans, M_DEVBUF); 232 } 233 queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg)); 234 queue_reply(u, (char *)reply, u->u.msg.len); 235 free(reply, M_DEVBUF); 236 } 237 break; 238 239 default: 240 err = EINVAL; 241 break; 242 } 243 244 if (err == 0) { 245 u->len = 0; 246 } 247 248 return err; 249 } 250 251 static int 252 xenbus_dev_open(void *v) 253 { 254 struct vop_open_args /* { 255 struct vnode *a_vp; 256 int a_mode; 257 struct ucred *a_cred; 258 struct lwp *a_l; 259 } */ *ap = v; 260 struct kernfs_node *kfs = VTOKERN(ap->a_vp); 261 262 struct xenbus_dev_data *u; 263 264 if (xen_start_info.store_evtchn == 0) 265 return ENOENT; 266 267 u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK); 268 if (u == NULL) 269 return ENOMEM; 270 271 memset(u, 0, sizeof(*u)); 272 SLIST_INIT(&u->transactions); 273 274 kfs->kfs_v = u; 275 276 return 0; 277 } 278 279 static int 280 xenbus_dev_close(void *v) 281 { 282 struct vop_close_args /* { 283 struct vnode *a_vp; 284 int a_fflag; 285 struct ucred *a_cred; 286 struct lwp *a_l; 287 } */ *ap = v; 288 struct kernfs_node *kfs = VTOKERN(ap->a_vp); 289 290 struct xenbus_dev_data *u = kfs->kfs_v; 291 struct xenbus_dev_transaction *trans; 292 293 while (!SLIST_EMPTY(&u->transactions)) { 294 trans = SLIST_FIRST(&u->transactions); 295 xenbus_transaction_end(trans->handle, 1); 296 SLIST_REMOVE_HEAD(&u->transactions, trans_next); 297 free(trans, M_DEVBUF); 298 } 299 300 free(u, M_DEVBUF); 301 kfs->kfs_v = NULL; 302 303 return 0; 304 } 305 306 /* 307 * Local variables: 308 * c-file-style: "linux" 309 * indent-tabs-mode: t 310 * c-indent-level: 8 311 * c-basic-offset: 8 312 * tab-width: 8 313 * End: 314 */ 315