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 /* 23349b53ddSStuart Maybee * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24843e1988Sjohnlev * Use is subject to license terms. 25843e1988Sjohnlev */ 26*cd21e7c5SGarrett D'Amore /* 27*cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 28*cd21e7c5SGarrett D'Amore */ 29843e1988Sjohnlev 30843e1988Sjohnlev /* 31843e1988Sjohnlev * Host to hypervisor virtual devices nexus driver 32843e1988Sjohnlev * 33843e1988Sjohnlev * TODO: 34843e1988Sjohnlev * - Add watchpoints on vbd/vif and enumerate/offline on watch callback 35843e1988Sjohnlev * - Add DR IOCTLs 36843e1988Sjohnlev * - Filter/restrict property lookups into xenstore 37843e1988Sjohnlev */ 38843e1988Sjohnlev 39843e1988Sjohnlev #include <sys/conf.h> 40843e1988Sjohnlev #include <sys/kmem.h> 41843e1988Sjohnlev #include <sys/debug.h> 42843e1988Sjohnlev #include <sys/modctl.h> 43843e1988Sjohnlev #include <sys/autoconf.h> 44843e1988Sjohnlev #include <sys/ddi_impldefs.h> 45843e1988Sjohnlev #include <sys/ddi_subrdefs.h> 46843e1988Sjohnlev #include <sys/ddi.h> 47843e1988Sjohnlev #include <sys/sunddi.h> 48843e1988Sjohnlev #include <sys/sunndi.h> 49843e1988Sjohnlev #include <sys/avintr.h> 50843e1988Sjohnlev #include <sys/psm.h> 51843e1988Sjohnlev #include <sys/spl.h> 52843e1988Sjohnlev #include <sys/promif.h> 53843e1988Sjohnlev #include <sys/list.h> 54843e1988Sjohnlev #include <sys/bootconf.h> 55843e1988Sjohnlev #include <sys/bootsvcs.h> 56843e1988Sjohnlev #include <util/sscanf.h> 57551bc2a6Smrj #include <sys/mach_intr.h> 58551bc2a6Smrj #include <sys/bootinfo.h> 59551bc2a6Smrj #ifdef XPV_HVM_DRIVER 60551bc2a6Smrj #include <sys/xpv_support.h> 61551bc2a6Smrj #include <sys/hypervisor.h> 62551bc2a6Smrj #include <sys/archsystm.h> 63551bc2a6Smrj #include <sys/cpu.h> 64551bc2a6Smrj #include <public/xen.h> 65551bc2a6Smrj #include <public/event_channel.h> 66551bc2a6Smrj #include <public/io/xenbus.h> 67551bc2a6Smrj #else 68551bc2a6Smrj #include <sys/hypervisor.h> 69551bc2a6Smrj #include <sys/evtchn_impl.h> 70551bc2a6Smrj #include <sys/xen_mmu.h> 71551bc2a6Smrj #endif 72843e1988Sjohnlev #include <xen/sys/xenbus_impl.h> 73843e1988Sjohnlev #include <xen/sys/xendev.h> 74843e1988Sjohnlev 75843e1988Sjohnlev /* 76843e1988Sjohnlev * DDI dev_ops entrypoints 77843e1988Sjohnlev */ 78843e1988Sjohnlev static int xpvd_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 79843e1988Sjohnlev static int xpvd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 80843e1988Sjohnlev static int xpvd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 81843e1988Sjohnlev 82843e1988Sjohnlev 83843e1988Sjohnlev /* 84843e1988Sjohnlev * NDI bus_ops entrypoints 85843e1988Sjohnlev */ 86843e1988Sjohnlev static int xpvd_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 87843e1988Sjohnlev void *); 88843e1988Sjohnlev static int xpvd_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, 89843e1988Sjohnlev ddi_intr_handle_impl_t *, void *); 90843e1988Sjohnlev static int xpvd_prop_op(dev_t, dev_info_t *, dev_info_t *, ddi_prop_op_t, 91843e1988Sjohnlev int, char *, caddr_t, int *); 92843e1988Sjohnlev static int xpvd_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, 93843e1988Sjohnlev void *, dev_info_t **); 94843e1988Sjohnlev static int xpvd_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t, 95843e1988Sjohnlev void *); 96843e1988Sjohnlev static int xpvd_get_eventcookie(dev_info_t *, dev_info_t *, 97843e1988Sjohnlev char *, ddi_eventcookie_t *); 98843e1988Sjohnlev static int xpvd_add_eventcall(dev_info_t *, dev_info_t *, 99843e1988Sjohnlev ddi_eventcookie_t, void (*)(dev_info_t *, 100843e1988Sjohnlev ddi_eventcookie_t, void *, void *), 101843e1988Sjohnlev void *, ddi_callback_id_t *); 102843e1988Sjohnlev static int xpvd_remove_eventcall(dev_info_t *, ddi_callback_id_t); 103843e1988Sjohnlev static int xpvd_post_event(dev_info_t *, dev_info_t *, 104843e1988Sjohnlev ddi_eventcookie_t, void *); 105843e1988Sjohnlev 106843e1988Sjohnlev /* 107843e1988Sjohnlev * misc functions 108843e1988Sjohnlev */ 109843e1988Sjohnlev static int xpvd_enable_intr(dev_info_t *, ddi_intr_handle_impl_t *, int); 110843e1988Sjohnlev static void xpvd_disable_intr(dev_info_t *, ddi_intr_handle_impl_t *, int); 111843e1988Sjohnlev static int xpvd_removechild(dev_info_t *); 112843e1988Sjohnlev static int xpvd_initchild(dev_info_t *); 113843e1988Sjohnlev static int xpvd_name_child(dev_info_t *, char *, int); 114843e1988Sjohnlev static boolean_t i_xpvd_parse_devname(char *, xendev_devclass_t *, 115843e1988Sjohnlev domid_t *, int *); 116843e1988Sjohnlev 117843e1988Sjohnlev 118843e1988Sjohnlev /* Extern declarations */ 119843e1988Sjohnlev extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 120843e1988Sjohnlev psm_intr_op_t, int *); 121843e1988Sjohnlev 122843e1988Sjohnlev struct bus_ops xpvd_bus_ops = { 123843e1988Sjohnlev BUSO_REV, 124843e1988Sjohnlev i_ddi_bus_map, 125843e1988Sjohnlev NULL, 126843e1988Sjohnlev NULL, 127843e1988Sjohnlev NULL, 128843e1988Sjohnlev i_ddi_map_fault, 129*cd21e7c5SGarrett D'Amore NULL, 130843e1988Sjohnlev ddi_dma_allochdl, 131843e1988Sjohnlev ddi_dma_freehdl, 132843e1988Sjohnlev ddi_dma_bindhdl, 133843e1988Sjohnlev ddi_dma_unbindhdl, 134843e1988Sjohnlev ddi_dma_flush, 135843e1988Sjohnlev ddi_dma_win, 136843e1988Sjohnlev ddi_dma_mctl, 137843e1988Sjohnlev xpvd_ctlops, 138843e1988Sjohnlev xpvd_prop_op, 139843e1988Sjohnlev xpvd_get_eventcookie, 140843e1988Sjohnlev xpvd_add_eventcall, 141843e1988Sjohnlev xpvd_remove_eventcall, 142843e1988Sjohnlev xpvd_post_event, 143843e1988Sjohnlev 0, /* (*bus_intr_ctl)(); */ 144843e1988Sjohnlev xpvd_bus_config, 145843e1988Sjohnlev xpvd_bus_unconfig, 146843e1988Sjohnlev NULL, /* (*bus_fm_init)(); */ 147843e1988Sjohnlev NULL, /* (*bus_fm_fini)(); */ 148843e1988Sjohnlev NULL, /* (*bus_fm_access_enter)(); */ 149843e1988Sjohnlev NULL, /* (*bus_fm_access_exit)(); */ 150843e1988Sjohnlev NULL, /* (*bus_power)(); */ 151843e1988Sjohnlev xpvd_intr_ops /* (*bus_intr_op)(); */ 152843e1988Sjohnlev }; 153843e1988Sjohnlev 154843e1988Sjohnlev struct dev_ops xpvd_ops = { 155843e1988Sjohnlev DEVO_REV, /* devo_rev */ 156843e1988Sjohnlev 0, /* refcnt */ 157843e1988Sjohnlev xpvd_info, /* info */ 158843e1988Sjohnlev nulldev, /* identify */ 159843e1988Sjohnlev nulldev, /* probe */ 160843e1988Sjohnlev xpvd_attach, /* attach */ 161843e1988Sjohnlev xpvd_detach, /* detach */ 162843e1988Sjohnlev nulldev, /* reset */ 163843e1988Sjohnlev (struct cb_ops *)0, /* driver operations */ 16419397407SSherry Moore &xpvd_bus_ops, /* bus operations */ 16519397407SSherry Moore NULL, /* power */ 16619397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 167843e1988Sjohnlev }; 168843e1988Sjohnlev 169843e1988Sjohnlev 170843e1988Sjohnlev dev_info_t *xpvd_dip; 171843e1988Sjohnlev 172843e1988Sjohnlev #define CF_DBG 0x1 173843e1988Sjohnlev #define ALL_DBG 0xff 174843e1988Sjohnlev 175843e1988Sjohnlev static ndi_event_definition_t xpvd_ndi_event_defs[] = { 176843e1988Sjohnlev { 0, XS_OE_STATE, EPL_KERNEL, NDI_EVENT_POST_TO_TGT }, 177843e1988Sjohnlev { 1, XS_HP_STATE, EPL_KERNEL, NDI_EVENT_POST_TO_TGT }, 178843e1988Sjohnlev }; 179843e1988Sjohnlev 180843e1988Sjohnlev #define XENDEV_N_NDI_EVENTS \ 181843e1988Sjohnlev (sizeof (xpvd_ndi_event_defs) / sizeof (xpvd_ndi_event_defs[0])) 182843e1988Sjohnlev 183843e1988Sjohnlev static ndi_event_set_t xpvd_ndi_events = { 184843e1988Sjohnlev NDI_EVENTS_REV1, XENDEV_N_NDI_EVENTS, xpvd_ndi_event_defs 185843e1988Sjohnlev }; 186843e1988Sjohnlev 187843e1988Sjohnlev static ndi_event_hdl_t xpvd_ndi_event_handle; 188843e1988Sjohnlev 189843e1988Sjohnlev /* 190843e1988Sjohnlev * Hypervisor interrupt capabilities 191843e1988Sjohnlev */ 192843e1988Sjohnlev #define XENDEV_INTR_CAPABILITIES \ 193843e1988Sjohnlev (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_PENDING) 194843e1988Sjohnlev 195843e1988Sjohnlev /* 196843e1988Sjohnlev * Module linkage information for the kernel. 197843e1988Sjohnlev */ 198843e1988Sjohnlev 199843e1988Sjohnlev static struct modldrv modldrv = { 200843e1988Sjohnlev &mod_driverops, /* Type of module */ 2018793b36bSNick Todd "virtual device nexus driver", 202843e1988Sjohnlev &xpvd_ops, /* driver ops */ 203843e1988Sjohnlev }; 204843e1988Sjohnlev 205843e1988Sjohnlev static struct modlinkage modlinkage = { 206843e1988Sjohnlev MODREV_1, 207843e1988Sjohnlev (void *)&modldrv, 208843e1988Sjohnlev NULL 209843e1988Sjohnlev }; 210843e1988Sjohnlev 211843e1988Sjohnlev int 212843e1988Sjohnlev _init(void) 213843e1988Sjohnlev { 214843e1988Sjohnlev return (mod_install(&modlinkage)); 215843e1988Sjohnlev } 216843e1988Sjohnlev 217843e1988Sjohnlev int 218843e1988Sjohnlev _fini(void) 219843e1988Sjohnlev { 220843e1988Sjohnlev return (mod_remove(&modlinkage)); 221843e1988Sjohnlev } 222843e1988Sjohnlev 223843e1988Sjohnlev int 224843e1988Sjohnlev _info(struct modinfo *modinfop) 225843e1988Sjohnlev { 226843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 227843e1988Sjohnlev } 228843e1988Sjohnlev 229843e1988Sjohnlev /* ARGSUSED */ 230843e1988Sjohnlev static int 231843e1988Sjohnlev xpvd_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 232843e1988Sjohnlev { 233843e1988Sjohnlev switch (cmd) { 234843e1988Sjohnlev default: 235843e1988Sjohnlev return (DDI_FAILURE); 236843e1988Sjohnlev 237843e1988Sjohnlev case DDI_INFO_DEVT2INSTANCE: 238843e1988Sjohnlev *result = (void *)0; 239843e1988Sjohnlev return (DDI_SUCCESS); 240843e1988Sjohnlev 241843e1988Sjohnlev case DDI_INFO_DEVT2DEVINFO: 242843e1988Sjohnlev *result = (void *)xpvd_dip; 243843e1988Sjohnlev return (DDI_SUCCESS); 244843e1988Sjohnlev } 245843e1988Sjohnlev } 246843e1988Sjohnlev 247843e1988Sjohnlev /*ARGSUSED*/ 248843e1988Sjohnlev static int 249843e1988Sjohnlev xpvd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 250843e1988Sjohnlev { 251843e1988Sjohnlev extern void xvdi_watch_devices(int); 252551bc2a6Smrj #ifdef XPV_HVM_DRIVER 253349b53ddSStuart Maybee extern dev_info_t *xpv_dip; 254349b53ddSStuart Maybee 255349b53ddSStuart Maybee if (xpv_dip == NULL) { 256551bc2a6Smrj if (ddi_hold_installed_driver(ddi_name_to_major("xpv")) == 257551bc2a6Smrj NULL) { 258551bc2a6Smrj cmn_err(CE_WARN, "Couldn't initialize xpv framework"); 259551bc2a6Smrj return (DDI_FAILURE); 260551bc2a6Smrj } 261551bc2a6Smrj } 26206bbe1e0Sedp #endif /* XPV_HVM_DRIVER */ 263843e1988Sjohnlev 264843e1988Sjohnlev if (ndi_event_alloc_hdl(devi, 0, &xpvd_ndi_event_handle, 265843e1988Sjohnlev NDI_SLEEP) != NDI_SUCCESS) { 266843e1988Sjohnlev xpvd_dip = NULL; 267843e1988Sjohnlev return (DDI_FAILURE); 268843e1988Sjohnlev } 269843e1988Sjohnlev if (ndi_event_bind_set(xpvd_ndi_event_handle, &xpvd_ndi_events, 270843e1988Sjohnlev NDI_SLEEP) != NDI_SUCCESS) { 271843e1988Sjohnlev (void) ndi_event_free_hdl(xpvd_ndi_event_handle); 272843e1988Sjohnlev xpvd_dip = NULL; 273843e1988Sjohnlev return (DDI_FAILURE); 274843e1988Sjohnlev } 275843e1988Sjohnlev 27606bbe1e0Sedp #ifdef XPV_HVM_DRIVER 27706bbe1e0Sedp (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi, DDI_NO_AUTODETACH, 1); 2786eb35ee7Srab 2796eb35ee7Srab /* 2806eb35ee7Srab * Report our version to dom0. 2816eb35ee7Srab */ 282349b53ddSStuart Maybee if (xenbus_printf(XBT_NULL, "guest/xpvd", "version", "%d", 2836eb35ee7Srab HVMPV_XPVD_VERS)) 2846eb35ee7Srab cmn_err(CE_WARN, "xpvd: couldn't write version\n"); 28506bbe1e0Sedp #endif /* XPV_HVM_DRIVER */ 28606bbe1e0Sedp 287843e1988Sjohnlev /* watch both frontend and backend for new devices */ 288843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) 289843e1988Sjohnlev (void) xs_register_xenbus_callback(xvdi_watch_devices); 290843e1988Sjohnlev else 291843e1988Sjohnlev xvdi_watch_devices(XENSTORE_UP); 292843e1988Sjohnlev 293551bc2a6Smrj xpvd_dip = devi; 294843e1988Sjohnlev ddi_report_dev(devi); 295843e1988Sjohnlev 296843e1988Sjohnlev return (DDI_SUCCESS); 297843e1988Sjohnlev } 298843e1988Sjohnlev 299843e1988Sjohnlev /*ARGSUSED*/ 300843e1988Sjohnlev static int 301843e1988Sjohnlev xpvd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 302843e1988Sjohnlev { 303843e1988Sjohnlev return (DDI_FAILURE); 304843e1988Sjohnlev } 305843e1988Sjohnlev 306843e1988Sjohnlev /* 307843e1988Sjohnlev * xpvd_prop_op() 308843e1988Sjohnlev * 309843e1988Sjohnlev * Query xenstore for the value of properties if DDI_PROP_NOTPROM 310843e1988Sjohnlev * is not set. Xenstore property values are represented as ascii strings. 311843e1988Sjohnlev */ 312843e1988Sjohnlev static int 313843e1988Sjohnlev xpvd_prop_op(dev_t dev, dev_info_t *dip, dev_info_t *ch_dip, 314843e1988Sjohnlev ddi_prop_op_t prop_op, int mod_flags, char *name, caddr_t valuep, 315843e1988Sjohnlev int *lengthp) 316843e1988Sjohnlev { 317843e1988Sjohnlev caddr_t buff; 318843e1988Sjohnlev struct xendev_ppd *pdp; 319843e1988Sjohnlev void *prop_str; 320843e1988Sjohnlev size_t prop_len; 321843e1988Sjohnlev unsigned int len; 322843e1988Sjohnlev int rv; 323843e1988Sjohnlev 324843e1988Sjohnlev pdp = (struct xendev_ppd *)ddi_get_parent_data(ch_dip); 325843e1988Sjohnlev 326843e1988Sjohnlev if ((pdp == NULL) || !(mod_flags & (DDI_PROP_CANSLEEP)) || 327843e1988Sjohnlev (mod_flags & DDI_PROP_NOTPROM) || (pdp->xd_xsdev.nodename == NULL)) 328843e1988Sjohnlev goto toss_off; 329843e1988Sjohnlev /* 330843e1988Sjohnlev * First try reading the property off the the frontend. if that 331843e1988Sjohnlev * fails, try and read it from the backend node. If that 332843e1988Sjohnlev * also fails, pass the request on the DDI framework 333843e1988Sjohnlev */ 334843e1988Sjohnlev prop_str = NULL; 335843e1988Sjohnlev if ((xenbus_read(XBT_NULL, pdp->xd_xsdev.nodename, name, &prop_str, 336843e1988Sjohnlev &len) == 0) && (prop_str != NULL) && (strlen(prop_str) != 0)) 337843e1988Sjohnlev goto got_xs_prop; 338843e1988Sjohnlev 339843e1988Sjohnlev prop_str = NULL; 340843e1988Sjohnlev if ((pdp->xd_xsdev.otherend != NULL) && 341843e1988Sjohnlev (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, name, &prop_str, 342843e1988Sjohnlev &len) == 0) && (prop_str != NULL) && (strlen(prop_str) != 0)) 343843e1988Sjohnlev goto got_xs_prop; 344843e1988Sjohnlev 345843e1988Sjohnlev toss_off: 346843e1988Sjohnlev return (ddi_bus_prop_op(dev, dip, ch_dip, prop_op, 347843e1988Sjohnlev mod_flags | DDI_PROP_NOTPROM, name, valuep, lengthp)); 348843e1988Sjohnlev 349843e1988Sjohnlev got_xs_prop: 350843e1988Sjohnlev prop_len = strlen(prop_str) + 1; 351843e1988Sjohnlev rv = DDI_PROP_SUCCESS; 352843e1988Sjohnlev 353843e1988Sjohnlev switch (prop_op) { 354843e1988Sjohnlev case PROP_LEN: 355843e1988Sjohnlev *lengthp = prop_len; 356843e1988Sjohnlev break; 357843e1988Sjohnlev 358843e1988Sjohnlev case PROP_LEN_AND_VAL_ALLOC: 359843e1988Sjohnlev buff = kmem_alloc((size_t)prop_len, KM_SLEEP); 360843e1988Sjohnlev *(caddr_t *)valuep = (caddr_t)buff; 361843e1988Sjohnlev break; 362843e1988Sjohnlev case PROP_LEN_AND_VAL_BUF: 363843e1988Sjohnlev buff = (caddr_t)valuep; 364843e1988Sjohnlev if (*lengthp < prop_len) 365843e1988Sjohnlev rv = DDI_PROP_BUF_TOO_SMALL; 366843e1988Sjohnlev break; 367843e1988Sjohnlev default: 368843e1988Sjohnlev rv = DDI_PROP_INVAL_ARG; 369843e1988Sjohnlev break; 370843e1988Sjohnlev } 371843e1988Sjohnlev 372843e1988Sjohnlev if ((rv == DDI_PROP_SUCCESS) && (prop_len > 0)) { 373843e1988Sjohnlev bcopy(prop_str, buff, prop_len); 374843e1988Sjohnlev *lengthp = prop_len; 375843e1988Sjohnlev } 376843e1988Sjohnlev kmem_free(prop_str, len); 377843e1988Sjohnlev return (rv); 378843e1988Sjohnlev } 379843e1988Sjohnlev 380843e1988Sjohnlev 381843e1988Sjohnlev /* 382843e1988Sjohnlev * return address of the device's interrupt spec structure. 383843e1988Sjohnlev */ 384843e1988Sjohnlev /*ARGSUSED*/ 385843e1988Sjohnlev struct intrspec * 386843e1988Sjohnlev xpvd_get_ispec(dev_info_t *rdip, uint_t inumber) 387843e1988Sjohnlev { 388843e1988Sjohnlev struct xendev_ppd *pdp; 389843e1988Sjohnlev 390843e1988Sjohnlev ASSERT(inumber == 0); 391843e1988Sjohnlev 392843e1988Sjohnlev if ((pdp = ddi_get_parent_data(rdip)) == NULL) 393843e1988Sjohnlev return (NULL); 394843e1988Sjohnlev 395843e1988Sjohnlev return (&pdp->xd_ispec); 396843e1988Sjohnlev } 397843e1988Sjohnlev 398843e1988Sjohnlev /* 399843e1988Sjohnlev * return (and determine) the interrupt priority of the device. 400843e1988Sjohnlev */ 401843e1988Sjohnlev /*ARGSUSED*/ 402843e1988Sjohnlev static int 403843e1988Sjohnlev xpvd_get_priority(dev_info_t *dip, int inum, int *pri) 404843e1988Sjohnlev { 405843e1988Sjohnlev struct xendev_ppd *pdp; 406843e1988Sjohnlev struct intrspec *ispec; 407843e1988Sjohnlev int *intpriorities; 408843e1988Sjohnlev uint_t num_intpriorities; 409843e1988Sjohnlev 410843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_get_priority: dip = 0x%p\n", 411843e1988Sjohnlev (void *)dip)); 412843e1988Sjohnlev 413843e1988Sjohnlev ASSERT(inum == 0); 414843e1988Sjohnlev 415843e1988Sjohnlev if ((pdp = ddi_get_parent_data(dip)) == NULL) 416843e1988Sjohnlev return (DDI_FAILURE); 417843e1988Sjohnlev 418843e1988Sjohnlev ispec = &pdp->xd_ispec; 419843e1988Sjohnlev 420843e1988Sjohnlev /* 421843e1988Sjohnlev * Set the default priority based on the device class. The 422843e1988Sjohnlev * "interrupt-priorities" property can be used to override 423843e1988Sjohnlev * the default. 424843e1988Sjohnlev */ 425843e1988Sjohnlev if (ispec->intrspec_pri == 0) { 426843e1988Sjohnlev ispec->intrspec_pri = xendev_devclass_ipl(pdp->xd_devclass); 427843e1988Sjohnlev if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 428843e1988Sjohnlev DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 429843e1988Sjohnlev "interrupt-priorities", &intpriorities, 430843e1988Sjohnlev &num_intpriorities) == DDI_PROP_SUCCESS) { 431843e1988Sjohnlev ispec->intrspec_pri = intpriorities[0]; 432843e1988Sjohnlev ddi_prop_free(intpriorities); 433843e1988Sjohnlev } 434843e1988Sjohnlev } 435843e1988Sjohnlev *pri = ispec->intrspec_pri; 436843e1988Sjohnlev return (DDI_SUCCESS); 437843e1988Sjohnlev } 438843e1988Sjohnlev 439843e1988Sjohnlev 440843e1988Sjohnlev /* 441843e1988Sjohnlev * xpvd_intr_ops: bus_intr_op() function for interrupt support 442843e1988Sjohnlev */ 443843e1988Sjohnlev /* ARGSUSED */ 444843e1988Sjohnlev static int 445843e1988Sjohnlev xpvd_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 446843e1988Sjohnlev ddi_intr_handle_impl_t *hdlp, void *result) 447843e1988Sjohnlev { 448843e1988Sjohnlev int priority = 0; 449843e1988Sjohnlev struct intrspec *ispec; 450843e1988Sjohnlev struct xendev_ppd *pdp; 451843e1988Sjohnlev 452843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, 453843e1988Sjohnlev "xpvd_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 454843e1988Sjohnlev (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 455843e1988Sjohnlev 456843e1988Sjohnlev /* Process the request */ 457843e1988Sjohnlev switch (intr_op) { 458843e1988Sjohnlev case DDI_INTROP_SUPPORTED_TYPES: 459843e1988Sjohnlev /* Fixed supported by default */ 460843e1988Sjohnlev *(int *)result = DDI_INTR_TYPE_FIXED; 461843e1988Sjohnlev break; 462843e1988Sjohnlev 463843e1988Sjohnlev case DDI_INTROP_NINTRS: 464843e1988Sjohnlev *(int *)result = 1; 465843e1988Sjohnlev break; 466843e1988Sjohnlev 467843e1988Sjohnlev case DDI_INTROP_ALLOC: 468843e1988Sjohnlev /* 469843e1988Sjohnlev * FIXED interrupts: just return available interrupts 470843e1988Sjohnlev */ 471843e1988Sjohnlev if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 472843e1988Sjohnlev /* 473843e1988Sjohnlev * event channels are edge-triggered, maskable, 474843e1988Sjohnlev * and support int pending. 475843e1988Sjohnlev */ 476843e1988Sjohnlev hdlp->ih_cap |= XENDEV_INTR_CAPABILITIES; 477843e1988Sjohnlev *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 478843e1988Sjohnlev } else { 479843e1988Sjohnlev return (DDI_FAILURE); 480843e1988Sjohnlev } 481843e1988Sjohnlev break; 482843e1988Sjohnlev 483843e1988Sjohnlev case DDI_INTROP_FREE: 484843e1988Sjohnlev ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum); 485843e1988Sjohnlev if (ispec == NULL) 486843e1988Sjohnlev return (DDI_FAILURE); 487843e1988Sjohnlev ispec->intrspec_pri = 0; /* mark as un-initialized */ 488843e1988Sjohnlev break; 489843e1988Sjohnlev 490843e1988Sjohnlev case DDI_INTROP_GETPRI: 491843e1988Sjohnlev if (xpvd_get_priority(rdip, hdlp->ih_inum, &priority) != 492843e1988Sjohnlev DDI_SUCCESS) 493843e1988Sjohnlev return (DDI_FAILURE); 494843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: priority = 0x%x\n", 495843e1988Sjohnlev priority)); 496843e1988Sjohnlev *(int *)result = priority; 497843e1988Sjohnlev break; 498843e1988Sjohnlev 499843e1988Sjohnlev case DDI_INTROP_SETPRI: 500843e1988Sjohnlev /* Validate the interrupt priority passed */ 501843e1988Sjohnlev if (*(int *)result > LOCK_LEVEL) 502843e1988Sjohnlev return (DDI_FAILURE); 503843e1988Sjohnlev 504843e1988Sjohnlev /* Ensure that PSM is all initialized */ 505843e1988Sjohnlev if (psm_intr_ops == NULL) 506843e1988Sjohnlev return (DDI_FAILURE); 507843e1988Sjohnlev 508843e1988Sjohnlev /* Change the priority */ 509843e1988Sjohnlev if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 510843e1988Sjohnlev PSM_FAILURE) 511843e1988Sjohnlev return (DDI_FAILURE); 512843e1988Sjohnlev 513843e1988Sjohnlev ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum); 514843e1988Sjohnlev if (ispec == NULL) 515843e1988Sjohnlev return (DDI_FAILURE); 516843e1988Sjohnlev ispec->intrspec_pri = *(int *)result; 517843e1988Sjohnlev break; 518843e1988Sjohnlev 519843e1988Sjohnlev case DDI_INTROP_ADDISR: 520843e1988Sjohnlev /* update ispec */ 521843e1988Sjohnlev ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum); 522843e1988Sjohnlev if (ispec == NULL) 523843e1988Sjohnlev return (DDI_FAILURE); 524843e1988Sjohnlev ispec->intrspec_func = hdlp->ih_cb_func; 525843e1988Sjohnlev 526843e1988Sjohnlev break; 527843e1988Sjohnlev 528843e1988Sjohnlev case DDI_INTROP_REMISR: 529843e1988Sjohnlev ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum); 530843e1988Sjohnlev pdp = (struct xendev_ppd *)ddi_get_parent_data(rdip); 531843e1988Sjohnlev 532843e1988Sjohnlev ASSERT(pdp != NULL); 533843e1988Sjohnlev ASSERT(pdp->xd_evtchn != INVALID_EVTCHN); 534843e1988Sjohnlev 535843e1988Sjohnlev if (ispec) { 536843e1988Sjohnlev ispec->intrspec_vec = 0; 537843e1988Sjohnlev ispec->intrspec_func = (uint_t (*)()) 0; 538843e1988Sjohnlev } 539843e1988Sjohnlev pdp->xd_evtchn = INVALID_EVTCHN; 540843e1988Sjohnlev break; 541843e1988Sjohnlev 542843e1988Sjohnlev case DDI_INTROP_GETCAP: 543843e1988Sjohnlev if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 544843e1988Sjohnlev /* 545843e1988Sjohnlev * event channels are edge-triggered, maskable, 546843e1988Sjohnlev * and support int pending. 547843e1988Sjohnlev */ 548843e1988Sjohnlev *(int *)result = XENDEV_INTR_CAPABILITIES; 549843e1988Sjohnlev } else { 550843e1988Sjohnlev *(int *)result = 0; 551843e1988Sjohnlev return (DDI_FAILURE); 552843e1988Sjohnlev } 553843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd: GETCAP returned = %x\n", 554843e1988Sjohnlev *(int *)result)); 555843e1988Sjohnlev break; 556843e1988Sjohnlev case DDI_INTROP_SETCAP: 557843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: SETCAP cap=0x%x\n", 558843e1988Sjohnlev *(int *)result)); 559843e1988Sjohnlev if (psm_intr_ops == NULL) 560843e1988Sjohnlev return (DDI_FAILURE); 561843e1988Sjohnlev 562843e1988Sjohnlev if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 563843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 564843e1988Sjohnlev " returned failure\n")); 565843e1988Sjohnlev return (DDI_FAILURE); 566843e1988Sjohnlev } 567843e1988Sjohnlev break; 568843e1988Sjohnlev 569843e1988Sjohnlev case DDI_INTROP_ENABLE: 570843e1988Sjohnlev if (psm_intr_ops == NULL) 571843e1988Sjohnlev return (DDI_FAILURE); 572843e1988Sjohnlev 573843e1988Sjohnlev if (xpvd_enable_intr(rdip, hdlp, (int)hdlp->ih_inum) != 574843e1988Sjohnlev DDI_SUCCESS) 575843e1988Sjohnlev return (DDI_FAILURE); 576843e1988Sjohnlev 577843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: ENABLE vec=0x%x\n", 578843e1988Sjohnlev hdlp->ih_vector)); 579843e1988Sjohnlev break; 580843e1988Sjohnlev 581843e1988Sjohnlev case DDI_INTROP_DISABLE: 582843e1988Sjohnlev if (psm_intr_ops == NULL) 583843e1988Sjohnlev return (DDI_FAILURE); 584843e1988Sjohnlev xpvd_disable_intr(rdip, hdlp, hdlp->ih_inum); 585843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: DISABLE vec = %x\n", 586843e1988Sjohnlev hdlp->ih_vector)); 587843e1988Sjohnlev break; 588843e1988Sjohnlev 589843e1988Sjohnlev case DDI_INTROP_BLOCKENABLE: 590843e1988Sjohnlev case DDI_INTROP_BLOCKDISABLE: 591843e1988Sjohnlev return (DDI_FAILURE); 592843e1988Sjohnlev 593843e1988Sjohnlev case DDI_INTROP_SETMASK: 594843e1988Sjohnlev case DDI_INTROP_CLRMASK: 595551bc2a6Smrj #ifdef XPV_HVM_DRIVER 596551bc2a6Smrj return (DDI_ENOTSUP); 597551bc2a6Smrj #else 598843e1988Sjohnlev /* 599843e1988Sjohnlev * Handle this here 600843e1988Sjohnlev */ 601843e1988Sjohnlev if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 602843e1988Sjohnlev return (DDI_FAILURE); 603843e1988Sjohnlev if (intr_op == DDI_INTROP_SETMASK) { 604843e1988Sjohnlev ec_disable_irq(hdlp->ih_vector); 605843e1988Sjohnlev } else { 606843e1988Sjohnlev ec_enable_irq(hdlp->ih_vector); 607843e1988Sjohnlev } 608843e1988Sjohnlev break; 609551bc2a6Smrj #endif 610843e1988Sjohnlev case DDI_INTROP_GETPENDING: 611551bc2a6Smrj #ifdef XPV_HVM_DRIVER 612551bc2a6Smrj return (DDI_ENOTSUP); 613551bc2a6Smrj #else 614843e1988Sjohnlev if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 615843e1988Sjohnlev return (DDI_FAILURE); 616843e1988Sjohnlev *(int *)result = ec_pending_irq(hdlp->ih_vector); 617843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd: GETPENDING returned = %x\n", 618843e1988Sjohnlev *(int *)result)); 619843e1988Sjohnlev break; 620551bc2a6Smrj #endif 621843e1988Sjohnlev 622843e1988Sjohnlev case DDI_INTROP_NAVAIL: 623843e1988Sjohnlev *(int *)result = 1; 624843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd: NAVAIL returned = %x\n", 625843e1988Sjohnlev *(int *)result)); 626843e1988Sjohnlev break; 627843e1988Sjohnlev 628843e1988Sjohnlev default: 629843e1988Sjohnlev return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 630843e1988Sjohnlev } 631843e1988Sjohnlev 632843e1988Sjohnlev return (DDI_SUCCESS); 633843e1988Sjohnlev } 634843e1988Sjohnlev 635843e1988Sjohnlev 636843e1988Sjohnlev static int 637843e1988Sjohnlev xpvd_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp, int inum) 638843e1988Sjohnlev { 639843e1988Sjohnlev int vector; 640843e1988Sjohnlev ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 641843e1988Sjohnlev 642843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_enable_intr: hdlp %p inum %x\n", 643843e1988Sjohnlev (void *)hdlp, inum)); 644843e1988Sjohnlev 645843e1988Sjohnlev ihdl_plat_datap->ip_ispecp = xpvd_get_ispec(rdip, inum); 646843e1988Sjohnlev if (ihdl_plat_datap->ip_ispecp == NULL) 647843e1988Sjohnlev return (DDI_FAILURE); 648843e1988Sjohnlev 649843e1988Sjohnlev /* translate the interrupt if needed */ 650843e1988Sjohnlev (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector); 651843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_enable_intr: priority=%x vector=%x\n", 652843e1988Sjohnlev hdlp->ih_pri, vector)); 653843e1988Sjohnlev 654843e1988Sjohnlev /* Add the interrupt handler */ 655843e1988Sjohnlev if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 656843e1988Sjohnlev DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1, 657843e1988Sjohnlev hdlp->ih_cb_arg2, NULL, rdip)) 658843e1988Sjohnlev return (DDI_FAILURE); 659843e1988Sjohnlev 660843e1988Sjohnlev /* Note this really is an irq. */ 661843e1988Sjohnlev hdlp->ih_vector = (ushort_t)vector; 662843e1988Sjohnlev 663843e1988Sjohnlev return (DDI_SUCCESS); 664843e1988Sjohnlev } 665843e1988Sjohnlev 666843e1988Sjohnlev 667843e1988Sjohnlev static void 668843e1988Sjohnlev xpvd_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp, int inum) 669843e1988Sjohnlev { 670843e1988Sjohnlev int vector; 671843e1988Sjohnlev ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 672843e1988Sjohnlev 673843e1988Sjohnlev DDI_INTR_NEXDBG((CE_CONT, "xpvd_disable_intr: \n")); 674843e1988Sjohnlev ihdl_plat_datap->ip_ispecp = xpvd_get_ispec(rdip, inum); 675843e1988Sjohnlev if (ihdl_plat_datap->ip_ispecp == NULL) 676843e1988Sjohnlev return; 677843e1988Sjohnlev 678843e1988Sjohnlev /* translate the interrupt if needed */ 679843e1988Sjohnlev (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector); 680843e1988Sjohnlev 681843e1988Sjohnlev /* Disable the interrupt handler */ 682843e1988Sjohnlev rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector); 683843e1988Sjohnlev ihdl_plat_datap->ip_ispecp = NULL; 684843e1988Sjohnlev } 685843e1988Sjohnlev 686843e1988Sjohnlev /*ARGSUSED*/ 687843e1988Sjohnlev static int 688843e1988Sjohnlev xpvd_ctlops(dev_info_t *dip, dev_info_t *rdip, 689843e1988Sjohnlev ddi_ctl_enum_t ctlop, void *arg, void *result) 690843e1988Sjohnlev { 691843e1988Sjohnlev switch (ctlop) { 692843e1988Sjohnlev case DDI_CTLOPS_REPORTDEV: 693843e1988Sjohnlev if (rdip == (dev_info_t *)0) 694843e1988Sjohnlev return (DDI_FAILURE); 695843e1988Sjohnlev cmn_err(CE_CONT, "?%s@%s, %s%d\n", ddi_node_name(rdip), 696843e1988Sjohnlev ddi_get_name_addr(rdip), ddi_driver_name(rdip), 697843e1988Sjohnlev ddi_get_instance(rdip)); 698843e1988Sjohnlev return (DDI_SUCCESS); 699843e1988Sjohnlev 700843e1988Sjohnlev case DDI_CTLOPS_INITCHILD: 701843e1988Sjohnlev return (xpvd_initchild((dev_info_t *)arg)); 702843e1988Sjohnlev 703843e1988Sjohnlev case DDI_CTLOPS_UNINITCHILD: 704843e1988Sjohnlev return (xpvd_removechild((dev_info_t *)arg)); 705843e1988Sjohnlev 706843e1988Sjohnlev case DDI_CTLOPS_SIDDEV: 707843e1988Sjohnlev return (DDI_SUCCESS); 708843e1988Sjohnlev 709843e1988Sjohnlev case DDI_CTLOPS_REGSIZE: 710843e1988Sjohnlev case DDI_CTLOPS_NREGS: 711843e1988Sjohnlev return (DDI_FAILURE); 712843e1988Sjohnlev 713843e1988Sjohnlev case DDI_CTLOPS_POWER: { 714843e1988Sjohnlev return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 715843e1988Sjohnlev } 716843e1988Sjohnlev 717843e1988Sjohnlev default: 718843e1988Sjohnlev return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 719843e1988Sjohnlev } 720843e1988Sjohnlev 721843e1988Sjohnlev /* NOTREACHED */ 722843e1988Sjohnlev 723843e1988Sjohnlev } 724843e1988Sjohnlev 725843e1988Sjohnlev /* 726843e1988Sjohnlev * Assign the address portion of the node name 727843e1988Sjohnlev */ 728843e1988Sjohnlev static int 72906bbe1e0Sedp xpvd_name_child(dev_info_t *child, char *addr, int addrlen) 730843e1988Sjohnlev { 731843e1988Sjohnlev int *domain, *vdev; 732843e1988Sjohnlev uint_t ndomain, nvdev; 73306bbe1e0Sedp char *prop_str; 734843e1988Sjohnlev 735843e1988Sjohnlev /* 736843e1988Sjohnlev * i_xpvd_parse_devname() knows the formats used by this 737843e1988Sjohnlev * routine. If this code changes, so must that. 738843e1988Sjohnlev */ 739843e1988Sjohnlev 740843e1988Sjohnlev if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 741843e1988Sjohnlev "domain", &domain, &ndomain) != DDI_PROP_SUCCESS) 742843e1988Sjohnlev return (DDI_FAILURE); 743843e1988Sjohnlev ASSERT(ndomain == 1); 744843e1988Sjohnlev 745843e1988Sjohnlev /* 746843e1988Sjohnlev * Use "domain" and "vdev" properties (backend drivers). 747843e1988Sjohnlev */ 748843e1988Sjohnlev if (*domain != DOMID_SELF) { 749843e1988Sjohnlev if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 750843e1988Sjohnlev DDI_PROP_DONTPASS, "vdev", &vdev, &nvdev) 751843e1988Sjohnlev != DDI_PROP_SUCCESS) { 752843e1988Sjohnlev ddi_prop_free(domain); 753843e1988Sjohnlev return (DDI_FAILURE); 754843e1988Sjohnlev } 755843e1988Sjohnlev ASSERT(nvdev == 1); 756843e1988Sjohnlev 75706bbe1e0Sedp (void) snprintf(addr, addrlen, "%d,%d", domain[0], vdev[0]); 758843e1988Sjohnlev ddi_prop_free(vdev); 759843e1988Sjohnlev ddi_prop_free(domain); 760843e1988Sjohnlev return (DDI_SUCCESS); 761843e1988Sjohnlev } 762843e1988Sjohnlev ddi_prop_free(domain); 763843e1988Sjohnlev 764843e1988Sjohnlev /* 765843e1988Sjohnlev * Use "unit-address" property (frontend/softdev drivers). 766843e1988Sjohnlev */ 76706bbe1e0Sedp if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 76806bbe1e0Sedp "unit-address", &prop_str) != DDI_PROP_SUCCESS) 769551bc2a6Smrj return (DDI_FAILURE); 77006bbe1e0Sedp (void) strlcpy(addr, prop_str, addrlen); 77106bbe1e0Sedp ddi_prop_free(prop_str); 772843e1988Sjohnlev return (DDI_SUCCESS); 773843e1988Sjohnlev } 774843e1988Sjohnlev 775843e1988Sjohnlev static int 776843e1988Sjohnlev xpvd_initchild(dev_info_t *child) 777843e1988Sjohnlev { 77806bbe1e0Sedp char addr[80]; 779843e1988Sjohnlev 780843e1988Sjohnlev /* 781843e1988Sjohnlev * Pseudo nodes indicate a prototype node with per-instance 782843e1988Sjohnlev * properties to be merged into the real h/w device node. 783843e1988Sjohnlev */ 784843e1988Sjohnlev if (ndi_dev_is_persistent_node(child) == 0) { 785843e1988Sjohnlev ddi_set_parent_data(child, NULL); 786843e1988Sjohnlev 787843e1988Sjohnlev /* 788843e1988Sjohnlev * Try to merge the properties from this prototype 789843e1988Sjohnlev * node into real h/w nodes. 790843e1988Sjohnlev */ 791843e1988Sjohnlev if (ndi_merge_node(child, xpvd_name_child) == DDI_SUCCESS) { 792843e1988Sjohnlev /* 793843e1988Sjohnlev * Merged ok - return failure to remove the node. 794843e1988Sjohnlev */ 795843e1988Sjohnlev ddi_set_name_addr(child, NULL); 796843e1988Sjohnlev return (DDI_FAILURE); 797843e1988Sjohnlev } 798843e1988Sjohnlev 799843e1988Sjohnlev /* 800843e1988Sjohnlev * The child was not merged into a h/w node, 801843e1988Sjohnlev * but there's not much we can do with it other 802843e1988Sjohnlev * than return failure to cause the node to be removed. 803843e1988Sjohnlev */ 804843e1988Sjohnlev cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 805843e1988Sjohnlev ddi_get_name(child), ddi_get_name_addr(child), 806843e1988Sjohnlev ddi_get_name(child)); 807843e1988Sjohnlev ddi_set_name_addr(child, NULL); 808843e1988Sjohnlev return (DDI_NOT_WELL_FORMED); 809843e1988Sjohnlev } 810843e1988Sjohnlev 811843e1988Sjohnlev if (xvdi_init_dev(child) != DDI_SUCCESS) 812843e1988Sjohnlev return (DDI_FAILURE); 813843e1988Sjohnlev 81406bbe1e0Sedp if (xpvd_name_child(child, addr, sizeof (addr)) != DDI_SUCCESS) { 815843e1988Sjohnlev xvdi_uninit_dev(child); 816843e1988Sjohnlev return (DDI_FAILURE); 817843e1988Sjohnlev } 81806bbe1e0Sedp ddi_set_name_addr(child, addr); 819843e1988Sjohnlev 820843e1988Sjohnlev return (DDI_SUCCESS); 821843e1988Sjohnlev } 822843e1988Sjohnlev 823843e1988Sjohnlev static int 824843e1988Sjohnlev xpvd_removechild(dev_info_t *dip) 825843e1988Sjohnlev { 826843e1988Sjohnlev xvdi_uninit_dev(dip); 827843e1988Sjohnlev 828843e1988Sjohnlev ddi_set_name_addr(dip, NULL); 829843e1988Sjohnlev 830843e1988Sjohnlev /* 831843e1988Sjohnlev * Strip the node to properly convert it back to prototype 832843e1988Sjohnlev * form. 833843e1988Sjohnlev */ 834843e1988Sjohnlev ddi_remove_minor_node(dip, NULL); 835843e1988Sjohnlev 836843e1988Sjohnlev return (DDI_SUCCESS); 837843e1988Sjohnlev } 838843e1988Sjohnlev 839843e1988Sjohnlev static int 840843e1988Sjohnlev xpvd_bus_unconfig(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op, 841843e1988Sjohnlev void *device_name) 842843e1988Sjohnlev { 843843e1988Sjohnlev return (ndi_busop_bus_unconfig(parent, flag, op, device_name)); 844843e1988Sjohnlev } 845843e1988Sjohnlev 846843e1988Sjohnlev /* 847843e1988Sjohnlev * Given the name of a child of xpvd, determine the device class, 848843e1988Sjohnlev * domain and vdevnum to which it refers. 849843e1988Sjohnlev */ 850843e1988Sjohnlev static boolean_t 851843e1988Sjohnlev i_xpvd_parse_devname(char *name, xendev_devclass_t *devclassp, 852843e1988Sjohnlev domid_t *domp, int *vdevp) 853843e1988Sjohnlev { 854843e1988Sjohnlev int len = strlen(name) + 1; 855843e1988Sjohnlev char *device_name = i_ddi_strdup(name, KM_SLEEP); 856843e1988Sjohnlev char *cname = NULL, *caddr = NULL; 857843e1988Sjohnlev boolean_t ret; 858843e1988Sjohnlev 859843e1988Sjohnlev i_ddi_parse_name(device_name, &cname, &caddr, NULL); 860843e1988Sjohnlev 861843e1988Sjohnlev if ((cname == NULL) || (strlen(cname) == 0) || 862843e1988Sjohnlev (caddr == NULL) || (strlen(caddr) == 0)) { 863843e1988Sjohnlev ret = B_FALSE; 864843e1988Sjohnlev goto done; 865843e1988Sjohnlev } 866843e1988Sjohnlev 867843e1988Sjohnlev *devclassp = xendev_nodename_to_devclass(cname); 868843e1988Sjohnlev if (*devclassp < 0) { 869843e1988Sjohnlev ret = B_FALSE; 870843e1988Sjohnlev goto done; 871843e1988Sjohnlev } 872843e1988Sjohnlev 873843e1988Sjohnlev /* 874843e1988Sjohnlev * Parsing the address component requires knowledge of how 875843e1988Sjohnlev * xpvd_name_child() works. If that code changes, so must 876843e1988Sjohnlev * this. 877843e1988Sjohnlev */ 878843e1988Sjohnlev 879843e1988Sjohnlev /* Backend format is "<domain>,<vdev>". */ 8808793b36bSNick Todd if (sscanf(caddr, "%hu,%d", domp, vdevp) == 2) { 881843e1988Sjohnlev ret = B_TRUE; 882843e1988Sjohnlev goto done; 883843e1988Sjohnlev } 884843e1988Sjohnlev 885843e1988Sjohnlev /* Frontend format is "<vdev>". */ 886843e1988Sjohnlev *domp = DOMID_SELF; 88797869ac5Sjhd if (sscanf(caddr, "%d", vdevp) == 1) 888843e1988Sjohnlev ret = B_TRUE; 889843e1988Sjohnlev done: 890843e1988Sjohnlev kmem_free(device_name, len); 891843e1988Sjohnlev return (ret); 892843e1988Sjohnlev } 893843e1988Sjohnlev 894843e1988Sjohnlev /* 895843e1988Sjohnlev * xpvd_bus_config() 896843e1988Sjohnlev * 897843e1988Sjohnlev * BUS_CONFIG_ONE: 898843e1988Sjohnlev * Enumerate the exact instance of a driver. 899843e1988Sjohnlev * 900843e1988Sjohnlev * BUS_CONFIG_ALL: 901843e1988Sjohnlev * Enumerate all the instances of all the possible children (seen before 902843e1988Sjohnlev * and never seen before). 903843e1988Sjohnlev * 904843e1988Sjohnlev * BUS_CONFIG_DRIVER: 905843e1988Sjohnlev * Enumerate all the instances of a particular driver. 906843e1988Sjohnlev */ 907843e1988Sjohnlev static int 908843e1988Sjohnlev xpvd_bus_config(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op, 909843e1988Sjohnlev void *arg, dev_info_t **childp) 910843e1988Sjohnlev { 911843e1988Sjohnlev int circ; 912843e1988Sjohnlev char *cname = NULL; 913843e1988Sjohnlev 914843e1988Sjohnlev ndi_devi_enter(parent, &circ); 915843e1988Sjohnlev 916843e1988Sjohnlev switch (op) { 917843e1988Sjohnlev case BUS_CONFIG_ONE: { 918843e1988Sjohnlev xendev_devclass_t devclass; 919843e1988Sjohnlev domid_t dom; 920843e1988Sjohnlev int vdev; 921843e1988Sjohnlev 922843e1988Sjohnlev if (!i_xpvd_parse_devname(arg, &devclass, &dom, &vdev)) { 923843e1988Sjohnlev ndi_devi_exit(parent, circ); 924843e1988Sjohnlev return (NDI_FAILURE); 925843e1988Sjohnlev } 926843e1988Sjohnlev 927843e1988Sjohnlev *childp = xvdi_find_dev(parent, devclass, dom, vdev); 928843e1988Sjohnlev if (*childp == NULL) 929843e1988Sjohnlev *childp = xvdi_create_dev(parent, devclass, dom, vdev); 930843e1988Sjohnlev 931843e1988Sjohnlev ndi_devi_exit(parent, circ); 932843e1988Sjohnlev 933843e1988Sjohnlev if (*childp == NULL) 934843e1988Sjohnlev return (NDI_FAILURE); 935843e1988Sjohnlev else 936843e1988Sjohnlev return (ndi_busop_bus_config(parent, flag, 937843e1988Sjohnlev op, arg, childp, 0)); 938843e1988Sjohnlev } 939843e1988Sjohnlev 940843e1988Sjohnlev case BUS_CONFIG_DRIVER: { 941843e1988Sjohnlev xendev_devclass_t devclass = XEN_INVAL; 942843e1988Sjohnlev 943843e1988Sjohnlev cname = ddi_major_to_name((major_t)(uintptr_t)arg); 944843e1988Sjohnlev if (cname != NULL) 945843e1988Sjohnlev devclass = xendev_nodename_to_devclass(cname); 946843e1988Sjohnlev 947843e1988Sjohnlev if (devclass == XEN_INVAL) { 948843e1988Sjohnlev ndi_devi_exit(parent, circ); 949843e1988Sjohnlev return (NDI_FAILURE); 950843e1988Sjohnlev } else { 951843e1988Sjohnlev xendev_enum_class(parent, devclass); 952843e1988Sjohnlev ndi_devi_exit(parent, circ); 953843e1988Sjohnlev return (ndi_busop_bus_config(parent, flag, op, 954843e1988Sjohnlev arg, childp, 0)); 955843e1988Sjohnlev } 956843e1988Sjohnlev /* NOTREACHED */ 957843e1988Sjohnlev } 958843e1988Sjohnlev 959843e1988Sjohnlev case BUS_CONFIG_ALL: 960843e1988Sjohnlev xendev_enum_all(parent, B_FALSE); 961843e1988Sjohnlev ndi_devi_exit(parent, circ); 962843e1988Sjohnlev 963843e1988Sjohnlev return (ndi_busop_bus_config(parent, flag, op, 964843e1988Sjohnlev arg, childp, 0)); 965843e1988Sjohnlev 966843e1988Sjohnlev default: 967843e1988Sjohnlev ndi_devi_exit(parent, circ); 968843e1988Sjohnlev return (NDI_FAILURE); 969843e1988Sjohnlev } 970843e1988Sjohnlev } 971843e1988Sjohnlev 972843e1988Sjohnlev /*ARGSUSED*/ 973843e1988Sjohnlev static int 974843e1988Sjohnlev xpvd_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, 975843e1988Sjohnlev char *eventname, ddi_eventcookie_t *cookie) 976843e1988Sjohnlev { 977843e1988Sjohnlev return (ndi_event_retrieve_cookie(xpvd_ndi_event_handle, 978843e1988Sjohnlev rdip, eventname, cookie, NDI_EVENT_NOPASS)); 979843e1988Sjohnlev } 980843e1988Sjohnlev 981843e1988Sjohnlev /*ARGSUSED*/ 982843e1988Sjohnlev static int 983843e1988Sjohnlev xpvd_add_eventcall(dev_info_t *dip, dev_info_t *rdip, 984843e1988Sjohnlev ddi_eventcookie_t cookie, void (*callback)(dev_info_t *dip, 985843e1988Sjohnlev ddi_eventcookie_t cookie, void *arg, void *bus_impldata), 986843e1988Sjohnlev void *arg, ddi_callback_id_t *cb_id) 987843e1988Sjohnlev { 988843e1988Sjohnlev return (ndi_event_add_callback(xpvd_ndi_event_handle, 989843e1988Sjohnlev rdip, cookie, callback, arg, NDI_SLEEP, cb_id)); 990843e1988Sjohnlev } 991843e1988Sjohnlev 992843e1988Sjohnlev /*ARGSUSED*/ 993843e1988Sjohnlev static int 994843e1988Sjohnlev xpvd_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 995843e1988Sjohnlev { 996843e1988Sjohnlev return (ndi_event_remove_callback(xpvd_ndi_event_handle, 997843e1988Sjohnlev cb_id)); 998843e1988Sjohnlev } 999843e1988Sjohnlev 1000843e1988Sjohnlev /*ARGSUSED*/ 1001843e1988Sjohnlev static int 1002843e1988Sjohnlev xpvd_post_event(dev_info_t *dip, dev_info_t *rdip, 1003843e1988Sjohnlev ddi_eventcookie_t cookie, void *bus_impldata) 1004843e1988Sjohnlev { 1005843e1988Sjohnlev return (ndi_event_run_callbacks(xpvd_ndi_event_handle, rdip, 1006843e1988Sjohnlev cookie, bus_impldata)); 1007843e1988Sjohnlev } 1008