1843e1988Sjohnlev /*
2843e1988Sjohnlev * CDDL HEADER START
3843e1988Sjohnlev *
4843e1988Sjohnlev * The contents of this file are subject to the terms of the
5843e1988Sjohnlev * Common Development and Distribution License (the "License").
6843e1988Sjohnlev * You may not use this file except in compliance with the License.
7843e1988Sjohnlev *
8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev * See the License for the specific language governing permissions
11843e1988Sjohnlev * and limitations under the License.
12843e1988Sjohnlev *
13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev *
19843e1988Sjohnlev * CDDL HEADER END
20843e1988Sjohnlev */
21843e1988Sjohnlev
22843e1988Sjohnlev /*
23b26a64aeSjohnlev * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24843e1988Sjohnlev * Use is subject to license terms.
25843e1988Sjohnlev */
26843e1988Sjohnlev
27843e1988Sjohnlev /*
28843e1988Sjohnlev * xenbus_dev.c
29843e1988Sjohnlev *
30843e1988Sjohnlev * Driver giving user-space access to the kernel's xenbus connection
31843e1988Sjohnlev * to xenstore.
32843e1988Sjohnlev *
33843e1988Sjohnlev * Copyright (c) 2005, Christian Limpach
34843e1988Sjohnlev * Copyright (c) 2005, Rusty Russell, IBM Corporation
35843e1988Sjohnlev *
36843e1988Sjohnlev * This file may be distributed separately from the Linux kernel, or
37843e1988Sjohnlev * incorporated into other software packages, subject to the following license:
38843e1988Sjohnlev *
39843e1988Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy
40843e1988Sjohnlev * of this source file (the "Software"), to deal in the Software without
41843e1988Sjohnlev * restriction, including without limitation the rights to use, copy, modify,
42843e1988Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software,
43843e1988Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to
44843e1988Sjohnlev * the following conditions:
45843e1988Sjohnlev *
46843e1988Sjohnlev * The above copyright notice and this permission notice shall be included in
47843e1988Sjohnlev * all copies or substantial portions of the Software.
48843e1988Sjohnlev *
49843e1988Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50843e1988Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51843e1988Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52843e1988Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53843e1988Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
54843e1988Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
55843e1988Sjohnlev * IN THE SOFTWARE.
56843e1988Sjohnlev */
57843e1988Sjohnlev
58843e1988Sjohnlev
59843e1988Sjohnlev #include <sys/types.h>
60843e1988Sjohnlev #include <sys/sysmacros.h>
61843e1988Sjohnlev #include <sys/conf.h>
62843e1988Sjohnlev #include <sys/stat.h>
63843e1988Sjohnlev #include <sys/modctl.h>
64843e1988Sjohnlev #include <sys/uio.h>
65843e1988Sjohnlev #include <sys/list.h>
66843e1988Sjohnlev #include <sys/file.h>
67843e1988Sjohnlev #include <sys/errno.h>
68843e1988Sjohnlev #include <sys/open.h>
69843e1988Sjohnlev #include <sys/cred.h>
70843e1988Sjohnlev #include <sys/condvar.h>
71843e1988Sjohnlev #include <sys/ddi.h>
72843e1988Sjohnlev #include <sys/sunddi.h>
73b26a64aeSjohnlev #include <sys/policy.h>
74b26a64aeSjohnlev
75551bc2a6Smrj #ifdef XPV_HVM_DRIVER
76551bc2a6Smrj #include <public/io/xenbus.h>
77551bc2a6Smrj #include <public/io/xs_wire.h>
78551bc2a6Smrj #include <sys/xpv_support.h>
79551bc2a6Smrj #endif
80843e1988Sjohnlev #include <sys/hypervisor.h>
81551bc2a6Smrj #include <xen/sys/xenbus.h>
82843e1988Sjohnlev #include <xen/sys/xenbus_comms.h>
83843e1988Sjohnlev #include <xen/sys/xenbus_impl.h>
84843e1988Sjohnlev #include <xen/public/io/xs_wire.h>
85843e1988Sjohnlev
86843e1988Sjohnlev #ifdef DEBUG
87843e1988Sjohnlev #define XENBUSDRV_DBPRINT(fmt) { if (xenbusdrv_debug) cmn_err fmt; }
88843e1988Sjohnlev #else
89843e1988Sjohnlev #define XENBUSDRV_DBPRINT(fmt)
90843e1988Sjohnlev #endif /* ifdef DEBUG */
91843e1988Sjohnlev
92843e1988Sjohnlev /* Some handy macros */
93843e1988Sjohnlev #define XENBUSDRV_MASK_READ_IDX(idx) ((idx) & (PAGESIZE - 1))
94843e1988Sjohnlev #define XENBUSDRV_MINOR2INST(minor) ((int)(minor))
95843e1988Sjohnlev #define XENBUSDRV_NCLONES 256
96843e1988Sjohnlev #define XENBUSDRV_INST2SOFTS(instance) \
97843e1988Sjohnlev ((xenbus_dev_t *)ddi_get_soft_state(xenbusdrv_statep, (instance)))
98843e1988Sjohnlev
99843e1988Sjohnlev static int xenbusdrv_debug = 0;
100843e1988Sjohnlev static int xenbusdrv_clone_tab[XENBUSDRV_NCLONES];
101843e1988Sjohnlev static dev_info_t *xenbusdrv_dip;
102843e1988Sjohnlev static kmutex_t xenbusdrv_clone_tab_mutex;
103843e1988Sjohnlev
104843e1988Sjohnlev struct xenbus_dev_transaction {
105843e1988Sjohnlev list_t list;
106843e1988Sjohnlev xenbus_transaction_t handle;
107843e1988Sjohnlev };
108843e1988Sjohnlev
109843e1988Sjohnlev /* Soft state data structure for xenbus driver */
110843e1988Sjohnlev struct xenbus_dev_data {
111843e1988Sjohnlev dev_info_t *dip;
112843e1988Sjohnlev
113843e1988Sjohnlev /* In-progress transaction. */
114843e1988Sjohnlev list_t transactions;
115843e1988Sjohnlev
116843e1988Sjohnlev /* Partial request. */
117843e1988Sjohnlev unsigned int len;
118843e1988Sjohnlev union {
119843e1988Sjohnlev struct xsd_sockmsg msg;
120843e1988Sjohnlev char buffer[MMU_PAGESIZE];
121843e1988Sjohnlev } u;
122843e1988Sjohnlev
123843e1988Sjohnlev /* Response queue. */
124843e1988Sjohnlev char read_buffer[MMU_PAGESIZE];
125843e1988Sjohnlev unsigned int read_cons, read_prod;
126843e1988Sjohnlev kcondvar_t read_cv;
127843e1988Sjohnlev kmutex_t read_mutex;
128843e1988Sjohnlev int xenstore_inst;
129843e1988Sjohnlev };
130843e1988Sjohnlev typedef struct xenbus_dev_data xenbus_dev_t;
131843e1988Sjohnlev static void *xenbusdrv_statep;
132843e1988Sjohnlev
133843e1988Sjohnlev static int xenbusdrv_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
134843e1988Sjohnlev static int xenbusdrv_attach(dev_info_t *, ddi_attach_cmd_t);
135843e1988Sjohnlev static int xenbusdrv_detach(dev_info_t *, ddi_detach_cmd_t);
136843e1988Sjohnlev static int xenbusdrv_open(dev_t *, int, int, cred_t *);
137843e1988Sjohnlev static int xenbusdrv_close(dev_t, int, int, cred_t *);
138843e1988Sjohnlev static int xenbusdrv_read(dev_t, struct uio *, cred_t *);
139843e1988Sjohnlev static int xenbusdrv_write(dev_t, struct uio *, cred_t *);
140843e1988Sjohnlev static int xenbusdrv_devmap(dev_t, devmap_cookie_t, offset_t, size_t, size_t *,
141843e1988Sjohnlev uint_t);
142b26a64aeSjohnlev static int xenbusdrv_segmap(dev_t, off_t, ddi_as_handle_t, caddr_t *, off_t,
143b26a64aeSjohnlev uint_t, uint_t, uint_t, cred_t *);
144843e1988Sjohnlev static int xenbusdrv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
145843e1988Sjohnlev static int xenbusdrv_queue_reply(xenbus_dev_t *, const struct xsd_sockmsg *,
146843e1988Sjohnlev const char *);
147843e1988Sjohnlev
148843e1988Sjohnlev /* Solaris driver framework */
149843e1988Sjohnlev
150843e1988Sjohnlev static struct cb_ops xenbusdrv_cb_ops = {
151843e1988Sjohnlev xenbusdrv_open, /* cb_open */
152843e1988Sjohnlev xenbusdrv_close, /* cb_close */
153843e1988Sjohnlev nodev, /* cb_strategy */
154843e1988Sjohnlev nodev, /* cb_print */
155843e1988Sjohnlev nodev, /* cb_dump */
156843e1988Sjohnlev xenbusdrv_read, /* cb_read */
157843e1988Sjohnlev xenbusdrv_write, /* cb_write */
158843e1988Sjohnlev xenbusdrv_ioctl, /* cb_ioctl */
159843e1988Sjohnlev xenbusdrv_devmap, /* cb_devmap */
160843e1988Sjohnlev NULL, /* cb_mmap */
161b26a64aeSjohnlev xenbusdrv_segmap, /* cb_segmap */
162843e1988Sjohnlev nochpoll, /* cb_chpoll */
163843e1988Sjohnlev ddi_prop_op, /* cb_prop_op */
164843e1988Sjohnlev 0, /* cb_stream */
165843e1988Sjohnlev D_DEVMAP | D_NEW | D_MP, /* cb_flag */
166843e1988Sjohnlev CB_REV
167843e1988Sjohnlev };
168843e1988Sjohnlev
169843e1988Sjohnlev static struct dev_ops xenbusdrv_dev_ops = {
170843e1988Sjohnlev DEVO_REV, /* devo_rev */
171843e1988Sjohnlev 0, /* devo_refcnt */
172843e1988Sjohnlev xenbusdrv_info, /* devo_getinfo */
173843e1988Sjohnlev nulldev, /* devo_identify */
174843e1988Sjohnlev nulldev, /* devo_probe */
175843e1988Sjohnlev xenbusdrv_attach, /* devo_attach */
176843e1988Sjohnlev xenbusdrv_detach, /* devo_detach */
177843e1988Sjohnlev nodev, /* devo_reset */
178843e1988Sjohnlev &xenbusdrv_cb_ops, /* devo_cb_ops */
179843e1988Sjohnlev NULL, /* devo_bus_ops */
180*19397407SSherry Moore NULL, /* devo_power */
181*19397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */
182843e1988Sjohnlev };
183843e1988Sjohnlev
184843e1988Sjohnlev static struct modldrv modldrv = {
185843e1988Sjohnlev &mod_driverops, /* Type of module. This one is a driver */
186*19397407SSherry Moore "virtual bus driver", /* Name of the module. */
187843e1988Sjohnlev &xenbusdrv_dev_ops /* driver ops */
188843e1988Sjohnlev };
189843e1988Sjohnlev
190843e1988Sjohnlev static struct modlinkage modlinkage = {
191843e1988Sjohnlev MODREV_1,
192843e1988Sjohnlev &modldrv,
193843e1988Sjohnlev NULL
194843e1988Sjohnlev };
195843e1988Sjohnlev
196843e1988Sjohnlev int
_init(void)197843e1988Sjohnlev _init(void)
198843e1988Sjohnlev {
199843e1988Sjohnlev int e;
200843e1988Sjohnlev
201843e1988Sjohnlev e = ddi_soft_state_init(&xenbusdrv_statep, sizeof (xenbus_dev_t), 1);
202843e1988Sjohnlev if (e)
203843e1988Sjohnlev return (e);
204843e1988Sjohnlev
205843e1988Sjohnlev e = mod_install(&modlinkage);
206843e1988Sjohnlev if (e)
207843e1988Sjohnlev ddi_soft_state_fini(&xenbusdrv_statep);
208843e1988Sjohnlev
209843e1988Sjohnlev return (e);
210843e1988Sjohnlev }
211843e1988Sjohnlev
212843e1988Sjohnlev int
_fini(void)213843e1988Sjohnlev _fini(void)
214843e1988Sjohnlev {
215843e1988Sjohnlev int e;
216843e1988Sjohnlev
217843e1988Sjohnlev e = mod_remove(&modlinkage);
218843e1988Sjohnlev if (e)
219843e1988Sjohnlev return (e);
220843e1988Sjohnlev
221843e1988Sjohnlev ddi_soft_state_fini(&xenbusdrv_statep);
222843e1988Sjohnlev
223843e1988Sjohnlev return (0);
224843e1988Sjohnlev }
225843e1988Sjohnlev
226843e1988Sjohnlev int
_info(struct modinfo * modinfop)227843e1988Sjohnlev _info(struct modinfo *modinfop)
228843e1988Sjohnlev {
229843e1988Sjohnlev return (mod_info(&modlinkage, modinfop));
230843e1988Sjohnlev }
231843e1988Sjohnlev
232843e1988Sjohnlev /* ARGSUSED */
233843e1988Sjohnlev static int
xenbusdrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)234843e1988Sjohnlev xenbusdrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
235843e1988Sjohnlev {
236843e1988Sjohnlev dev_t dev = (dev_t)arg;
237843e1988Sjohnlev minor_t minor = getminor(dev);
238843e1988Sjohnlev int retval;
239843e1988Sjohnlev
240843e1988Sjohnlev switch (cmd) {
241843e1988Sjohnlev case DDI_INFO_DEVT2DEVINFO:
242843e1988Sjohnlev if (minor != 0 || xenbusdrv_dip == NULL) {
243843e1988Sjohnlev *result = (void *)NULL;
244843e1988Sjohnlev retval = DDI_FAILURE;
245843e1988Sjohnlev } else {
246843e1988Sjohnlev *result = (void *)xenbusdrv_dip;
247843e1988Sjohnlev retval = DDI_SUCCESS;
248843e1988Sjohnlev }
249843e1988Sjohnlev break;
250843e1988Sjohnlev case DDI_INFO_DEVT2INSTANCE:
251843e1988Sjohnlev *result = (void *)0;
252843e1988Sjohnlev retval = DDI_SUCCESS;
253843e1988Sjohnlev break;
254843e1988Sjohnlev default:
255843e1988Sjohnlev retval = DDI_FAILURE;
256843e1988Sjohnlev }
257843e1988Sjohnlev return (retval);
258843e1988Sjohnlev }
259843e1988Sjohnlev
260843e1988Sjohnlev static int
xenbusdrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)261843e1988Sjohnlev xenbusdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
262843e1988Sjohnlev {
263843e1988Sjohnlev int error;
264843e1988Sjohnlev int unit = ddi_get_instance(dip);
265843e1988Sjohnlev
266843e1988Sjohnlev
267843e1988Sjohnlev switch (cmd) {
268843e1988Sjohnlev case DDI_ATTACH:
269843e1988Sjohnlev break;
270843e1988Sjohnlev case DDI_RESUME:
271843e1988Sjohnlev return (DDI_SUCCESS);
272843e1988Sjohnlev default:
273843e1988Sjohnlev cmn_err(CE_WARN, "xenbus_attach: unknown cmd 0x%x\n", cmd);
274843e1988Sjohnlev return (DDI_FAILURE);
275843e1988Sjohnlev }
276843e1988Sjohnlev
277843e1988Sjohnlev /* DDI_ATTACH */
278843e1988Sjohnlev
279843e1988Sjohnlev /*
280843e1988Sjohnlev * only one instance - but we clone using the open routine
281843e1988Sjohnlev */
282843e1988Sjohnlev if (ddi_get_instance(dip) > 0)
283843e1988Sjohnlev return (DDI_FAILURE);
284843e1988Sjohnlev
285843e1988Sjohnlev mutex_init(&xenbusdrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
286843e1988Sjohnlev NULL);
287843e1988Sjohnlev
288843e1988Sjohnlev error = ddi_create_minor_node(dip, "xenbus", S_IFCHR, unit,
289843e1988Sjohnlev DDI_PSEUDO, NULL);
290843e1988Sjohnlev if (error != DDI_SUCCESS)
291843e1988Sjohnlev goto fail;
292843e1988Sjohnlev
293843e1988Sjohnlev /*
294843e1988Sjohnlev * save dip for getinfo
295843e1988Sjohnlev */
296843e1988Sjohnlev xenbusdrv_dip = dip;
297843e1988Sjohnlev ddi_report_dev(dip);
298843e1988Sjohnlev
299551bc2a6Smrj #ifndef XPV_HVM_DRIVER
300843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info))
301843e1988Sjohnlev xs_dom0_init();
302551bc2a6Smrj #endif
303843e1988Sjohnlev
304843e1988Sjohnlev return (DDI_SUCCESS);
305843e1988Sjohnlev
306843e1988Sjohnlev fail:
307843e1988Sjohnlev (void) xenbusdrv_detach(dip, DDI_DETACH);
308843e1988Sjohnlev return (error);
309843e1988Sjohnlev }
310843e1988Sjohnlev
311843e1988Sjohnlev static int
xenbusdrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)312843e1988Sjohnlev xenbusdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
313843e1988Sjohnlev {
314843e1988Sjohnlev /*
315843e1988Sjohnlev * again, only one instance
316843e1988Sjohnlev */
317843e1988Sjohnlev if (ddi_get_instance(dip) > 0)
318843e1988Sjohnlev return (DDI_FAILURE);
319843e1988Sjohnlev
320843e1988Sjohnlev switch (cmd) {
321843e1988Sjohnlev case DDI_DETACH:
322843e1988Sjohnlev ddi_remove_minor_node(dip, NULL);
323843e1988Sjohnlev mutex_destroy(&xenbusdrv_clone_tab_mutex);
324843e1988Sjohnlev xenbusdrv_dip = NULL;
325843e1988Sjohnlev return (DDI_SUCCESS);
326843e1988Sjohnlev case DDI_SUSPEND:
327843e1988Sjohnlev return (DDI_SUCCESS);
328843e1988Sjohnlev default:
329843e1988Sjohnlev cmn_err(CE_WARN, "xenbus_detach: unknown cmd 0x%x\n", cmd);
330843e1988Sjohnlev return (DDI_FAILURE);
331843e1988Sjohnlev }
332843e1988Sjohnlev }
333843e1988Sjohnlev
334843e1988Sjohnlev /* ARGSUSED */
335843e1988Sjohnlev static int
xenbusdrv_open(dev_t * devp,int flag,int otyp,cred_t * cr)336b26a64aeSjohnlev xenbusdrv_open(dev_t *devp, int flag, int otyp, cred_t *cr)
337843e1988Sjohnlev {
338843e1988Sjohnlev xenbus_dev_t *xbs;
339843e1988Sjohnlev minor_t minor = getminor(*devp);
340843e1988Sjohnlev
341843e1988Sjohnlev if (otyp == OTYP_BLK)
342843e1988Sjohnlev return (ENXIO);
343843e1988Sjohnlev
344843e1988Sjohnlev /*
345843e1988Sjohnlev * only allow open on minor = 0 - the clone device
346843e1988Sjohnlev */
347843e1988Sjohnlev if (minor != 0)
348843e1988Sjohnlev return (ENXIO);
349843e1988Sjohnlev
350843e1988Sjohnlev /*
351843e1988Sjohnlev * find a free slot and grab it
352843e1988Sjohnlev */
353843e1988Sjohnlev mutex_enter(&xenbusdrv_clone_tab_mutex);
354843e1988Sjohnlev for (minor = 1; minor < XENBUSDRV_NCLONES; minor++) {
355843e1988Sjohnlev if (xenbusdrv_clone_tab[minor] == 0) {
356843e1988Sjohnlev xenbusdrv_clone_tab[minor] = 1;
357843e1988Sjohnlev break;
358843e1988Sjohnlev }
359843e1988Sjohnlev }
360843e1988Sjohnlev mutex_exit(&xenbusdrv_clone_tab_mutex);
361843e1988Sjohnlev if (minor == XENBUSDRV_NCLONES)
362843e1988Sjohnlev return (EAGAIN);
363843e1988Sjohnlev
364843e1988Sjohnlev /* Allocate softstate structure */
365843e1988Sjohnlev if (ddi_soft_state_zalloc(xenbusdrv_statep,
366843e1988Sjohnlev XENBUSDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
367843e1988Sjohnlev mutex_enter(&xenbusdrv_clone_tab_mutex);
368843e1988Sjohnlev xenbusdrv_clone_tab[minor] = 0;
369843e1988Sjohnlev mutex_exit(&xenbusdrv_clone_tab_mutex);
370843e1988Sjohnlev return (EAGAIN);
371843e1988Sjohnlev }
372843e1988Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
373843e1988Sjohnlev
374843e1988Sjohnlev /* ... and init it */
375843e1988Sjohnlev xbs->dip = xenbusdrv_dip;
376843e1988Sjohnlev mutex_init(&xbs->read_mutex, NULL, MUTEX_DRIVER, NULL);
377843e1988Sjohnlev cv_init(&xbs->read_cv, NULL, CV_DEFAULT, NULL);
378843e1988Sjohnlev list_create(&xbs->transactions, sizeof (struct xenbus_dev_transaction),
379843e1988Sjohnlev offsetof(struct xenbus_dev_transaction, list));
380843e1988Sjohnlev
381843e1988Sjohnlev /* clone driver */
382843e1988Sjohnlev *devp = makedevice(getmajor(*devp), minor);
383843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv open succeeded, minor=%d",
384843e1988Sjohnlev minor));
385843e1988Sjohnlev
386843e1988Sjohnlev return (0);
387843e1988Sjohnlev }
388843e1988Sjohnlev
389843e1988Sjohnlev /* ARGSUSED */
390843e1988Sjohnlev static int
xenbusdrv_close(dev_t dev,int flag,int otyp,struct cred * cr)391b26a64aeSjohnlev xenbusdrv_close(dev_t dev, int flag, int otyp, struct cred *cr)
392843e1988Sjohnlev {
393843e1988Sjohnlev xenbus_dev_t *xbs;
394843e1988Sjohnlev minor_t minor = getminor(dev);
395843e1988Sjohnlev struct xenbus_dev_transaction *trans;
396843e1988Sjohnlev
397843e1988Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
398843e1988Sjohnlev if (xbs == NULL)
399843e1988Sjohnlev return (ENXIO);
400843e1988Sjohnlev
401843e1988Sjohnlev #ifdef notyet
402843e1988Sjohnlev /*
403843e1988Sjohnlev * XXPV - would like to be able to notify xenstore down here, but
404843e1988Sjohnlev * as the daemon is currently written, it doesn't leave the device
405843e1988Sjohnlev * open after initial setup, so we have no way of knowing if it has
406843e1988Sjohnlev * gone away.
407843e1988Sjohnlev */
408843e1988Sjohnlev if (xbs->xenstore_inst)
409843e1988Sjohnlev xs_notify_xenstore_down();
410843e1988Sjohnlev #endif
411843e1988Sjohnlev /* free pending transaction */
412843e1988Sjohnlev while (trans = (struct xenbus_dev_transaction *)
413843e1988Sjohnlev list_head(&xbs->transactions)) {
414843e1988Sjohnlev (void) xenbus_transaction_end(trans->handle, 1);
415843e1988Sjohnlev list_remove(&xbs->transactions, (void *)trans);
416843e1988Sjohnlev kmem_free(trans, sizeof (*trans));
417843e1988Sjohnlev }
418843e1988Sjohnlev
419843e1988Sjohnlev mutex_destroy(&xbs->read_mutex);
420843e1988Sjohnlev cv_destroy(&xbs->read_cv);
421843e1988Sjohnlev ddi_soft_state_free(xenbusdrv_statep, XENBUSDRV_MINOR2INST(minor));
422843e1988Sjohnlev
423843e1988Sjohnlev /*
424843e1988Sjohnlev * free clone tab slot
425843e1988Sjohnlev */
426843e1988Sjohnlev mutex_enter(&xenbusdrv_clone_tab_mutex);
427843e1988Sjohnlev xenbusdrv_clone_tab[minor] = 0;
428843e1988Sjohnlev mutex_exit(&xenbusdrv_clone_tab_mutex);
429843e1988Sjohnlev
430843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv close succeeded, minor=%d",
431843e1988Sjohnlev minor));
432843e1988Sjohnlev
433843e1988Sjohnlev return (0);
434843e1988Sjohnlev }
435843e1988Sjohnlev
436843e1988Sjohnlev /* ARGSUSED */
437843e1988Sjohnlev static int
xenbusdrv_read(dev_t dev,struct uio * uiop,cred_t * cr)438b26a64aeSjohnlev xenbusdrv_read(dev_t dev, struct uio *uiop, cred_t *cr)
439843e1988Sjohnlev {
440843e1988Sjohnlev xenbus_dev_t *xbs;
441843e1988Sjohnlev size_t len;
442843e1988Sjohnlev int res, ret;
443843e1988Sjohnlev int idx;
444843e1988Sjohnlev
445843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_read called"));
446843e1988Sjohnlev
447b26a64aeSjohnlev if (secpolicy_xvm_control(cr))
448b26a64aeSjohnlev return (EPERM);
449b26a64aeSjohnlev
450843e1988Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
451843e1988Sjohnlev
452843e1988Sjohnlev mutex_enter(&xbs->read_mutex);
453843e1988Sjohnlev
454843e1988Sjohnlev /* check if we have something to read */
455843e1988Sjohnlev while (xbs->read_prod == xbs->read_cons) {
456843e1988Sjohnlev if (cv_wait_sig(&xbs->read_cv, &xbs->read_mutex) == 0) {
457843e1988Sjohnlev mutex_exit(&xbs->read_mutex);
458843e1988Sjohnlev return (EINTR);
459843e1988Sjohnlev }
460843e1988Sjohnlev }
461843e1988Sjohnlev
462843e1988Sjohnlev idx = XENBUSDRV_MASK_READ_IDX(xbs->read_cons);
463843e1988Sjohnlev res = uiop->uio_resid;
464843e1988Sjohnlev
465843e1988Sjohnlev len = xbs->read_prod - xbs->read_cons;
466843e1988Sjohnlev
467843e1988Sjohnlev if (len > (sizeof (xbs->read_buffer) - idx))
468843e1988Sjohnlev len = sizeof (xbs->read_buffer) - idx;
469843e1988Sjohnlev if (len > res)
470843e1988Sjohnlev len = res;
471843e1988Sjohnlev
472843e1988Sjohnlev ret = uiomove(xbs->read_buffer + idx, len, UIO_READ, uiop);
473843e1988Sjohnlev xbs->read_cons += res - uiop->uio_resid;
474843e1988Sjohnlev mutex_exit(&xbs->read_mutex);
475843e1988Sjohnlev
476843e1988Sjohnlev return (ret);
477843e1988Sjohnlev }
478843e1988Sjohnlev
479843e1988Sjohnlev /*
480843e1988Sjohnlev * prepare data for xenbusdrv_read()
481843e1988Sjohnlev */
482843e1988Sjohnlev static int
xenbusdrv_queue_reply(xenbus_dev_t * xbs,const struct xsd_sockmsg * msg,const char * reply)483843e1988Sjohnlev xenbusdrv_queue_reply(xenbus_dev_t *xbs, const struct xsd_sockmsg *msg,
484843e1988Sjohnlev const char *reply)
485843e1988Sjohnlev {
486843e1988Sjohnlev int i;
487843e1988Sjohnlev int remaining;
488843e1988Sjohnlev
489843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply called"));
490843e1988Sjohnlev
491843e1988Sjohnlev mutex_enter(&xbs->read_mutex);
492843e1988Sjohnlev
493843e1988Sjohnlev remaining = sizeof (xbs->read_buffer) -
494843e1988Sjohnlev (xbs->read_prod - xbs->read_cons);
495843e1988Sjohnlev
496843e1988Sjohnlev if (sizeof (*msg) + msg->len > remaining) {
497843e1988Sjohnlev mutex_exit(&xbs->read_mutex);
498843e1988Sjohnlev return (EOVERFLOW);
499843e1988Sjohnlev }
500843e1988Sjohnlev
501843e1988Sjohnlev for (i = 0; i < sizeof (*msg); i++, xbs->read_prod++) {
502843e1988Sjohnlev xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
503843e1988Sjohnlev ((char *)msg)[i];
504843e1988Sjohnlev }
505843e1988Sjohnlev
506843e1988Sjohnlev for (i = 0; i < msg->len; i++, xbs->read_prod++) {
507843e1988Sjohnlev xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
508843e1988Sjohnlev reply[i];
509843e1988Sjohnlev }
510843e1988Sjohnlev
511843e1988Sjohnlev cv_broadcast(&xbs->read_cv);
512843e1988Sjohnlev
513843e1988Sjohnlev mutex_exit(&xbs->read_mutex);
514843e1988Sjohnlev
515843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply exited"));
516843e1988Sjohnlev
517843e1988Sjohnlev return (0);
518843e1988Sjohnlev }
519843e1988Sjohnlev
520843e1988Sjohnlev /* ARGSUSED */
521843e1988Sjohnlev static int
xenbusdrv_write(dev_t dev,struct uio * uiop,cred_t * cr)522b26a64aeSjohnlev xenbusdrv_write(dev_t dev, struct uio *uiop, cred_t *cr)
523843e1988Sjohnlev {
524843e1988Sjohnlev xenbus_dev_t *xbs;
525843e1988Sjohnlev struct xenbus_dev_transaction *trans;
526843e1988Sjohnlev void *reply;
527843e1988Sjohnlev size_t len;
528843e1988Sjohnlev int rc = 0;
529843e1988Sjohnlev
530843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_write called"));
531843e1988Sjohnlev
532b26a64aeSjohnlev if (secpolicy_xvm_control(cr))
533b26a64aeSjohnlev return (EPERM);
534b26a64aeSjohnlev
535843e1988Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
536843e1988Sjohnlev len = uiop->uio_resid;
537843e1988Sjohnlev
538843e1988Sjohnlev if ((len + xbs->len) > sizeof (xbs->u.buffer)) {
539843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_WARN, "Request is too big"));
540843e1988Sjohnlev rc = EINVAL;
541843e1988Sjohnlev goto out;
542843e1988Sjohnlev }
543843e1988Sjohnlev
544843e1988Sjohnlev if (uiomove(xbs->u.buffer + xbs->len, len, UIO_WRITE, uiop) != 0) {
545843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_WARN, "Uiomove failed"));
546843e1988Sjohnlev rc = EFAULT;
547843e1988Sjohnlev goto out;
548843e1988Sjohnlev }
549843e1988Sjohnlev
550843e1988Sjohnlev xbs->len += len;
551843e1988Sjohnlev
552843e1988Sjohnlev if (xbs->len < (sizeof (xbs->u.msg)) ||
553843e1988Sjohnlev xbs->len < (sizeof (xbs->u.msg) + xbs->u.msg.len)) {
554843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_NOTE, "Partial request"));
555843e1988Sjohnlev return (0);
556843e1988Sjohnlev }
557843e1988Sjohnlev
558843e1988Sjohnlev switch (xbs->u.msg.type) {
559843e1988Sjohnlev case XS_TRANSACTION_START:
560843e1988Sjohnlev case XS_TRANSACTION_END:
561843e1988Sjohnlev case XS_DIRECTORY:
562843e1988Sjohnlev case XS_READ:
563843e1988Sjohnlev case XS_GET_PERMS:
564843e1988Sjohnlev case XS_RELEASE:
565843e1988Sjohnlev case XS_GET_DOMAIN_PATH:
566843e1988Sjohnlev case XS_WRITE:
567843e1988Sjohnlev case XS_MKDIR:
568843e1988Sjohnlev case XS_RM:
569843e1988Sjohnlev case XS_SET_PERMS:
570843e1988Sjohnlev /* send the request to xenstore and get feedback */
571843e1988Sjohnlev rc = xenbus_dev_request_and_reply(&xbs->u.msg, &reply);
572843e1988Sjohnlev if (rc) {
573843e1988Sjohnlev XENBUSDRV_DBPRINT((CE_WARN,
574843e1988Sjohnlev "xenbus_dev_request_and_reply failed"));
575843e1988Sjohnlev goto out;
576843e1988Sjohnlev }
577843e1988Sjohnlev
578843e1988Sjohnlev /* handle transaction start/end */
579843e1988Sjohnlev if (xbs->u.msg.type == XS_TRANSACTION_START) {
580843e1988Sjohnlev trans = kmem_alloc(sizeof (*trans), KM_SLEEP);
581843e1988Sjohnlev (void) ddi_strtoul((char *)reply, NULL, 0,
582843e1988Sjohnlev (unsigned long *)&trans->handle);
583843e1988Sjohnlev list_insert_tail(&xbs->transactions, (void *)trans);
584843e1988Sjohnlev } else if (xbs->u.msg.type == XS_TRANSACTION_END) {
585843e1988Sjohnlev /* try to find out the ending transaction */
586843e1988Sjohnlev for (trans = (struct xenbus_dev_transaction *)
587843e1988Sjohnlev list_head(&xbs->transactions); trans;
588843e1988Sjohnlev trans = (struct xenbus_dev_transaction *)
589843e1988Sjohnlev list_next(&xbs->transactions, (void *)trans))
590843e1988Sjohnlev if (trans->handle ==
591843e1988Sjohnlev (xenbus_transaction_t)
592843e1988Sjohnlev xbs->u.msg.tx_id)
593843e1988Sjohnlev break;
594843e1988Sjohnlev ASSERT(trans);
595843e1988Sjohnlev /* free it, if we find it */
596843e1988Sjohnlev list_remove(&xbs->transactions, (void *)trans);
597843e1988Sjohnlev kmem_free(trans, sizeof (*trans));
598843e1988Sjohnlev }
599843e1988Sjohnlev
600843e1988Sjohnlev /* prepare data for xenbusdrv_read() to get */
601843e1988Sjohnlev rc = xenbusdrv_queue_reply(xbs, &xbs->u.msg, reply);
602843e1988Sjohnlev
603843e1988Sjohnlev kmem_free(reply, xbs->u.msg.len + 1);
604843e1988Sjohnlev break;
605843e1988Sjohnlev default:
606843e1988Sjohnlev rc = EINVAL;
607843e1988Sjohnlev }
608843e1988Sjohnlev
609843e1988Sjohnlev out:
610843e1988Sjohnlev xbs->len = 0;
611843e1988Sjohnlev return (rc);
612843e1988Sjohnlev }
613843e1988Sjohnlev
614843e1988Sjohnlev /*ARGSUSED*/
615843e1988Sjohnlev static int
xenbusdrv_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)616843e1988Sjohnlev xenbusdrv_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
617843e1988Sjohnlev size_t *maplen, uint_t model)
618843e1988Sjohnlev {
619843e1988Sjohnlev xenbus_dev_t *xbs;
620843e1988Sjohnlev int err;
621843e1988Sjohnlev
622843e1988Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
623843e1988Sjohnlev
624843e1988Sjohnlev if (off != 0 || len != PAGESIZE)
625843e1988Sjohnlev return (-1);
626843e1988Sjohnlev
627843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info))
628843e1988Sjohnlev return (-1);
629843e1988Sjohnlev
630843e1988Sjohnlev err = devmap_umem_setup(dhp, xbs->dip, NULL, xb_xenstore_cookie(),
631843e1988Sjohnlev 0, PAGESIZE, PROT_READ | PROT_WRITE | PROT_USER, 0, NULL);
632843e1988Sjohnlev
633843e1988Sjohnlev if (err)
634843e1988Sjohnlev return (err);
635843e1988Sjohnlev
636843e1988Sjohnlev *maplen = PAGESIZE;
637843e1988Sjohnlev
638843e1988Sjohnlev return (0);
639843e1988Sjohnlev }
640843e1988Sjohnlev
641b26a64aeSjohnlev 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)642b26a64aeSjohnlev xenbusdrv_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp,
643b26a64aeSjohnlev off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr)
644b26a64aeSjohnlev {
645b26a64aeSjohnlev
646b26a64aeSjohnlev if (secpolicy_xvm_control(cr))
647b26a64aeSjohnlev return (EPERM);
648b26a64aeSjohnlev
649b26a64aeSjohnlev return (ddi_devmap_segmap(dev, off, as, addrp, len, prot,
650b26a64aeSjohnlev maxprot, flags, cr));
651b26a64aeSjohnlev }
652b26a64aeSjohnlev
653843e1988Sjohnlev /*ARGSUSED*/
654843e1988Sjohnlev static int
xenbusdrv_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rvalp)655b26a64aeSjohnlev xenbusdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr,
656843e1988Sjohnlev int *rvalp)
657843e1988Sjohnlev {
658843e1988Sjohnlev xenbus_dev_t *xbs;
659843e1988Sjohnlev
660b26a64aeSjohnlev if (secpolicy_xvm_control(cr))
661b26a64aeSjohnlev return (EPERM);
662b26a64aeSjohnlev
663843e1988Sjohnlev xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
664843e1988Sjohnlev switch (cmd) {
665843e1988Sjohnlev case IOCTL_XENBUS_XENSTORE_EVTCHN:
666843e1988Sjohnlev *rvalp = xen_info->store_evtchn;
667843e1988Sjohnlev break;
668843e1988Sjohnlev case IOCTL_XENBUS_NOTIFY_UP:
669843e1988Sjohnlev xs_notify_xenstore_up();
670843e1988Sjohnlev xbs->xenstore_inst = 1;
671843e1988Sjohnlev break;
672843e1988Sjohnlev default:
673843e1988Sjohnlev return (EINVAL);
674843e1988Sjohnlev }
675843e1988Sjohnlev
676843e1988Sjohnlev return (0);
677843e1988Sjohnlev }
678