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