16495Sspeer /*
26495Sspeer * CDDL HEADER START
36495Sspeer *
46495Sspeer * The contents of this file are subject to the terms of the
56495Sspeer * Common Development and Distribution License (the "License").
66495Sspeer * You may not use this file except in compliance with the License.
76495Sspeer *
86495Sspeer * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96495Sspeer * or http://www.opensolaris.org/os/licensing.
106495Sspeer * See the License for the specific language governing permissions
116495Sspeer * and limitations under the License.
126495Sspeer *
136495Sspeer * When distributing Covered Code, include this CDDL HEADER in each
146495Sspeer * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156495Sspeer * If applicable, add the following below this CDDL HEADER, with the
166495Sspeer * fields enclosed by brackets "[]" replaced with your own identifying
176495Sspeer * information: Portions Copyright [yyyy] [name of copyright owner]
186495Sspeer *
196495Sspeer * CDDL HEADER END
206495Sspeer */
216495Sspeer
226495Sspeer /*
23*12011SSriharsha.Basavapatna@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
246495Sspeer * Use is subject to license terms.
256495Sspeer */
266495Sspeer
276495Sspeer #include <sys/modctl.h>
286495Sspeer #include <sys/prom_plat.h>
296495Sspeer #include <sys/ddi.h>
306495Sspeer #include <sys/sunddi.h>
316495Sspeer #include <sys/sunndi.h>
326495Sspeer #include <sys/ndi_impldefs.h>
336495Sspeer #include <sys/ddi_impldefs.h>
346495Sspeer #include <sys/ethernet.h>
356495Sspeer #include <sys/machsystm.h>
366495Sspeer #include <sys/hypervisor_api.h>
376495Sspeer #include <sys/mach_descrip.h>
386495Sspeer #include <sys/drctl.h>
396495Sspeer #include <sys/dr_util.h>
406495Sspeer #include <sys/mac.h>
416495Sspeer #include <sys/vnet.h>
426495Sspeer #include <sys/vnet_mailbox.h>
436495Sspeer #include <sys/vnet_common.h>
446495Sspeer #include <sys/hsvc.h>
456495Sspeer
466495Sspeer
476495Sspeer #define VDDS_MAX_RANGES 6 /* 6 possible VRs */
486495Sspeer #define VDDS_MAX_VRINTRS 8 /* limited to 8 intrs/VR */
496495Sspeer #define VDDS_MAX_INTR_NUM 64 /* 0-63 or valid */
506495Sspeer
516495Sspeer #define VDDS_INO_RANGE_START(x) (x * VDDS_MAX_VRINTRS)
526495Sspeer #define HVCOOKIE(c) ((c) & 0xFFFFFFFFF)
536495Sspeer #define NIUCFGHDL(c) ((c) >> 32)
546495Sspeer
556495Sspeer
566495Sspeer /* For "ranges" property */
576495Sspeer typedef struct vdds_ranges {
586495Sspeer uint32_t child_hi;
596495Sspeer uint32_t child_lo;
606495Sspeer uint32_t parent_hi;
616495Sspeer uint32_t parent_lo;
626495Sspeer uint32_t size_hi;
636495Sspeer uint32_t size_lo;
646495Sspeer } vdds_ranges_t;
656495Sspeer
666495Sspeer /* For "reg" property */
676495Sspeer typedef struct vdds_reg {
686495Sspeer uint32_t addr_hi;
696495Sspeer uint32_t addr_lo;
706495Sspeer uint32_t size_hi;
716495Sspeer uint32_t size_lo;
726495Sspeer } vdds_reg_t;
736495Sspeer
746495Sspeer /* For ddi callback argument */
756495Sspeer typedef struct vdds_cb_arg {
766495Sspeer dev_info_t *dip;
776495Sspeer uint64_t cookie;
786495Sspeer uint64_t macaddr;
797529SSriharsha.Basavapatna@Sun.COM uint32_t max_frame_size;
806495Sspeer } vdds_cb_arg_t;
816495Sspeer
826495Sspeer
836495Sspeer /* Functions exported to other files */
846495Sspeer void vdds_mod_init(void);
856495Sspeer void vdds_mod_fini(void);
866495Sspeer int vdds_init(vnet_t *vnetp);
876495Sspeer void vdds_cleanup(vnet_t *vnetp);
886495Sspeer void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg);
897819SRaghuram.Kothakota@Sun.COM void vdds_cleanup_hybrid_res(void *arg);
909647SWentao.Yang@Sun.COM void vdds_cleanup_hio(vnet_t *vnetp);
916495Sspeer
926495Sspeer /* Support functions to create/destory Hybrid device */
937529SSriharsha.Basavapatna@Sun.COM static dev_info_t *vdds_create_niu_node(uint64_t cookie,
947529SSriharsha.Basavapatna@Sun.COM uint64_t macaddr, uint32_t max_frame_size);
956495Sspeer static int vdds_destroy_niu_node(dev_info_t *niu_dip, uint64_t cookie);
967529SSriharsha.Basavapatna@Sun.COM static dev_info_t *vdds_create_new_node(vdds_cb_arg_t *cba,
976495Sspeer dev_info_t *pdip, int (*new_node_func)(dev_info_t *dip,
986495Sspeer void *arg, uint_t flags));
996495Sspeer static int vdds_new_nexus_node(dev_info_t *dip, void *arg, uint_t flags);
1006495Sspeer static int vdds_new_niu_node(dev_info_t *dip, void *arg, uint_t flags);
1016495Sspeer static dev_info_t *vdds_find_node(uint64_t cookie, dev_info_t *sdip,
1026495Sspeer int (*match_func)(dev_info_t *dip, void *arg));
1036495Sspeer static int vdds_match_niu_nexus(dev_info_t *dip, void *arg);
1046495Sspeer static int vdds_match_niu_node(dev_info_t *dip, void *arg);
1056495Sspeer static int vdds_get_interrupts(uint64_t cookie, int ino_range,
1066495Sspeer int *intrs, int *nintr);
1076495Sspeer
1086495Sspeer /* DDS message processing related functions */
1096495Sspeer static void vdds_process_dds_msg_task(void *arg);
1106495Sspeer static int vdds_send_dds_resp_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg, int ack);
1116495Sspeer static int vdds_send_dds_rel_msg(vnet_t *vnetp);
1126495Sspeer static void vdds_release_range_prop(dev_info_t *nexus_dip, uint64_t cookie);
1136495Sspeer
1146495Sspeer /* Functions imported from other files */
1156495Sspeer extern int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg);
11610309SSriharsha.Basavapatna@Sun.COM extern int vnet_hio_mac_init(vnet_t *vnetp, char *ifname);
11710309SSriharsha.Basavapatna@Sun.COM extern void vnet_hio_mac_cleanup(vnet_t *vnetp);
1186495Sspeer
1196495Sspeer /* HV functions that are used in this file */
1206495Sspeer extern uint64_t vdds_hv_niu_vr_getinfo(uint32_t hvcookie,
1216495Sspeer uint64_t *real_start, uint64_t *size);
1226495Sspeer extern uint64_t vdds_hv_niu_vr_get_txmap(uint32_t hvcookie, uint64_t *dma_map);
1236495Sspeer extern uint64_t vdds_hv_niu_vr_get_rxmap(uint32_t hvcookie, uint64_t *dma_map);
1246495Sspeer extern uint64_t vdds_hv_niu_vrtx_set_ino(uint32_t cookie, uint64_t vch_idx,
1256495Sspeer uint32_t ino);
1266495Sspeer extern uint64_t vdds_hv_niu_vrrx_set_ino(uint32_t cookie, uint64_t vch_idx,
1276495Sspeer uint32_t ino);
1286495Sspeer
1296495Sspeer
1306495Sspeer #ifdef DEBUG
1316495Sspeer
132*12011SSriharsha.Basavapatna@Sun.COM #define DEBUG_PRINTF debug_printf
133*12011SSriharsha.Basavapatna@Sun.COM
1346495Sspeer extern int vnet_dbglevel;
1356495Sspeer
1366495Sspeer static void
debug_printf(const char * fname,void * arg,const char * fmt,...)1376495Sspeer debug_printf(const char *fname, void *arg, const char *fmt, ...)
1386495Sspeer {
1396495Sspeer char buf[512];
1406495Sspeer va_list ap;
1416495Sspeer char *bufp = buf;
1426495Sspeer vnet_dds_info_t *vdds = arg;
1436495Sspeer
1446495Sspeer if (vdds != NULL) {
1456495Sspeer (void) sprintf(bufp, "vnet%d: %s: ",
1466495Sspeer vdds->vnetp->instance, fname);
1476495Sspeer } else {
1486495Sspeer (void) sprintf(bufp, "%s: ", fname);
1496495Sspeer }
1506495Sspeer bufp += strlen(bufp);
1516495Sspeer va_start(ap, fmt);
1526495Sspeer (void) vsprintf(bufp, fmt, ap);
1536495Sspeer va_end(ap);
1546495Sspeer cmn_err(CE_CONT, "%s\n", buf);
1556495Sspeer }
1566495Sspeer #endif
1576495Sspeer
1586495Sspeer /*
15911304SJanie.Lu@Sun.COM * Hypervisor N2/NIU services information:
16011304SJanie.Lu@Sun.COM *
16111304SJanie.Lu@Sun.COM * The list of HV versions that support NIU HybridIO. Note,
16211304SJanie.Lu@Sun.COM * the order is higher version to a lower version, as the
16311304SJanie.Lu@Sun.COM * registration is attempted in this order.
1646495Sspeer */
16511304SJanie.Lu@Sun.COM static hsvc_info_t niu_hsvc[] = {
16611304SJanie.Lu@Sun.COM {HSVC_REV_1, NULL, HSVC_GROUP_NIU, 2, 0, "vnet_dds"},
16711304SJanie.Lu@Sun.COM {HSVC_REV_1, NULL, HSVC_GROUP_NIU, 1, 1, "vnet_dds"}
1686495Sspeer };
1696495Sspeer
1707819SRaghuram.Kothakota@Sun.COM /*
17111304SJanie.Lu@Sun.COM * Index that points to the successful HV version that
17211304SJanie.Lu@Sun.COM * is registered.
17311304SJanie.Lu@Sun.COM */
17411304SJanie.Lu@Sun.COM static int niu_hsvc_index = -1;
17511304SJanie.Lu@Sun.COM
17611304SJanie.Lu@Sun.COM /*
1777819SRaghuram.Kothakota@Sun.COM * Lock to serialize the NIU device node related operations.
1787819SRaghuram.Kothakota@Sun.COM */
1797819SRaghuram.Kothakota@Sun.COM kmutex_t vdds_dev_lock;
1807819SRaghuram.Kothakota@Sun.COM
1816495Sspeer boolean_t vdds_hv_hio_capable = B_FALSE;
1826495Sspeer
1836495Sspeer /*
1846495Sspeer * vdds_mod_init -- one time initialization.
1856495Sspeer */
1866495Sspeer void
vdds_mod_init(void)1876495Sspeer vdds_mod_init(void)
1886495Sspeer {
18911304SJanie.Lu@Sun.COM int i;
1906495Sspeer int rv;
19111304SJanie.Lu@Sun.COM uint64_t minor = 0;
1926495Sspeer
1936495Sspeer /*
19411304SJanie.Lu@Sun.COM * Try register one by one from niu_hsvc.
1956495Sspeer */
19611304SJanie.Lu@Sun.COM for (i = 0; i < (sizeof (niu_hsvc) / sizeof (hsvc_info_t)); i++) {
19711304SJanie.Lu@Sun.COM rv = hsvc_register(&niu_hsvc[i], &minor);
19811304SJanie.Lu@Sun.COM if (rv == 0) {
19911304SJanie.Lu@Sun.COM if (minor == niu_hsvc[i].hsvc_minor) {
20011304SJanie.Lu@Sun.COM vdds_hv_hio_capable = B_TRUE;
20111304SJanie.Lu@Sun.COM niu_hsvc_index = i;
20211304SJanie.Lu@Sun.COM break;
20311304SJanie.Lu@Sun.COM } else {
20411304SJanie.Lu@Sun.COM (void) hsvc_unregister(&niu_hsvc[i]);
20511304SJanie.Lu@Sun.COM }
20611304SJanie.Lu@Sun.COM }
2076495Sspeer }
2087819SRaghuram.Kothakota@Sun.COM mutex_init(&vdds_dev_lock, NULL, MUTEX_DRIVER, NULL);
20911304SJanie.Lu@Sun.COM DBG2(NULL, "HV HIO capable=%d ver(%ld.%ld)", vdds_hv_hio_capable,
21011304SJanie.Lu@Sun.COM (niu_hsvc_index == -1) ? 0 : niu_hsvc[niu_hsvc_index].hsvc_major,
21111304SJanie.Lu@Sun.COM minor);
2126495Sspeer }
2136495Sspeer
2146495Sspeer /*
2156495Sspeer * vdds_mod_fini -- one time cleanup.
2166495Sspeer */
2176495Sspeer void
vdds_mod_fini(void)2186495Sspeer vdds_mod_fini(void)
2196495Sspeer {
22011304SJanie.Lu@Sun.COM if (niu_hsvc_index != -1) {
22111304SJanie.Lu@Sun.COM (void) hsvc_unregister(&niu_hsvc[niu_hsvc_index]);
22211304SJanie.Lu@Sun.COM }
2237819SRaghuram.Kothakota@Sun.COM mutex_destroy(&vdds_dev_lock);
2246495Sspeer }
2256495Sspeer
2266495Sspeer /*
2276495Sspeer * vdds_init -- vnet instance related DDS related initialization.
2286495Sspeer */
2296495Sspeer int
vdds_init(vnet_t * vnetp)2306495Sspeer vdds_init(vnet_t *vnetp)
2316495Sspeer {
2326495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
2336495Sspeer char qname[TASKQ_NAMELEN];
2346495Sspeer
2356495Sspeer vdds->vnetp = vnetp;
2366495Sspeer DBG1(vdds, "Initializing..");
2376495Sspeer (void) snprintf(qname, TASKQ_NAMELEN, "vdds_taskq%d", vnetp->instance);
2386495Sspeer if ((vdds->dds_taskqp = ddi_taskq_create(vnetp->dip, qname, 1,
2396495Sspeer TASKQ_DEFAULTPRI, 0)) == NULL) {
2406495Sspeer cmn_err(CE_WARN, "!vnet%d: Unable to create DDS task queue",
2416495Sspeer vnetp->instance);
2426495Sspeer return (ENOMEM);
2436495Sspeer }
2446495Sspeer mutex_init(&vdds->lock, NULL, MUTEX_DRIVER, NULL);
2456495Sspeer return (0);
2466495Sspeer }
2476495Sspeer
2486495Sspeer /*
2496495Sspeer * vdds_cleanup -- vnet instance related cleanup.
2506495Sspeer */
2516495Sspeer void
vdds_cleanup(vnet_t * vnetp)2526495Sspeer vdds_cleanup(vnet_t *vnetp)
2536495Sspeer {
2546495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
2556495Sspeer
2566495Sspeer DBG1(vdds, "Cleanup...");
2576495Sspeer /* Cleanup/destroy any hybrid resouce that exists */
2586495Sspeer vdds_cleanup_hybrid_res(vnetp);
2596495Sspeer
2606495Sspeer /* taskq_destroy will wait for all taskqs to complete */
2616495Sspeer ddi_taskq_destroy(vdds->dds_taskqp);
2626495Sspeer vdds->dds_taskqp = NULL;
2636495Sspeer mutex_destroy(&vdds->lock);
2646495Sspeer DBG1(vdds, "Cleanup complete");
2656495Sspeer }
2666495Sspeer
2676495Sspeer /*
2686495Sspeer * vdds_cleanup_hybrid_res -- Cleanup Hybrid resource.
2696495Sspeer */
2706495Sspeer void
vdds_cleanup_hybrid_res(void * arg)2717819SRaghuram.Kothakota@Sun.COM vdds_cleanup_hybrid_res(void *arg)
2726495Sspeer {
2737819SRaghuram.Kothakota@Sun.COM vnet_t *vnetp = arg;
2746495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
2756495Sspeer
2766495Sspeer DBG1(vdds, "Hybrid device cleanup...");
2776495Sspeer mutex_enter(&vdds->lock);
2786495Sspeer if (vdds->task_flags == VNET_DDS_TASK_ADD_SHARE) {
2796495Sspeer /*
2806495Sspeer * Task for ADD_SHARE is pending, simply
2816495Sspeer * cleanup the flags, the task will quit without
2826495Sspeer * any changes.
2836495Sspeer */
2846495Sspeer vdds->task_flags = 0;
2856495Sspeer DBG2(vdds, "Task for ADD is pending, clean flags only");
2866495Sspeer } else if ((vdds->hio_dip != NULL) && (vdds->task_flags == 0)) {
2876495Sspeer /*
2886495Sspeer * There is no task pending and a hybrid device
2896495Sspeer * is present, so dispatch a task to release the share.
2906495Sspeer */
2916495Sspeer vdds->task_flags = VNET_DDS_TASK_REL_SHARE;
2926495Sspeer (void) ddi_taskq_dispatch(vdds->dds_taskqp,
2936495Sspeer vdds_process_dds_msg_task, vnetp, DDI_NOSLEEP);
2946495Sspeer DBG2(vdds, "Dispatched a task to destroy HIO device");
2956495Sspeer }
2966495Sspeer /*
2976495Sspeer * Other possible cases include either DEL_SHARE or
2986495Sspeer * REL_SHARE as pending. In that case, there is nothing
2996495Sspeer * to do as a task is already pending to do the cleanup.
3006495Sspeer */
3016495Sspeer mutex_exit(&vdds->lock);
3026495Sspeer DBG1(vdds, "Hybrid device cleanup complete");
3036495Sspeer }
3046495Sspeer
3056495Sspeer /*
3069647SWentao.Yang@Sun.COM * vdds_cleanup_hio -- An interface to cleanup the hio resources before
3079647SWentao.Yang@Sun.COM * resetting the vswitch port.
3089647SWentao.Yang@Sun.COM */
3099647SWentao.Yang@Sun.COM void
vdds_cleanup_hio(vnet_t * vnetp)3109647SWentao.Yang@Sun.COM vdds_cleanup_hio(vnet_t *vnetp)
3119647SWentao.Yang@Sun.COM {
3129647SWentao.Yang@Sun.COM vnet_dds_info_t *vdds = &vnetp->vdds_info;
3139647SWentao.Yang@Sun.COM
3149647SWentao.Yang@Sun.COM /* Wait for any pending vdds tasks to complete */
3159647SWentao.Yang@Sun.COM ddi_taskq_wait(vdds->dds_taskqp);
3169647SWentao.Yang@Sun.COM vdds_cleanup_hybrid_res(vnetp);
3179647SWentao.Yang@Sun.COM /* Wait for the cleanup task to complete */
3189647SWentao.Yang@Sun.COM ddi_taskq_wait(vdds->dds_taskqp);
3199647SWentao.Yang@Sun.COM }
3209647SWentao.Yang@Sun.COM
3219647SWentao.Yang@Sun.COM /*
3226495Sspeer * vdds_process_dds_msg -- Process a DDS message.
3236495Sspeer */
3246495Sspeer void
vdds_process_dds_msg(vnet_t * vnetp,vio_dds_msg_t * dmsg)3256495Sspeer vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg)
3266495Sspeer {
3276495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
3286495Sspeer int rv;
3296495Sspeer
3306495Sspeer DBG1(vdds, "DDS message received...");
3316495Sspeer
3326495Sspeer if (dmsg->dds_class != DDS_VNET_NIU) {
3336495Sspeer DBG2(vdds, "Invalid class send NACK");
3346495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
3356495Sspeer return;
3366495Sspeer }
3376495Sspeer mutex_enter(&vdds->lock);
3386495Sspeer switch (dmsg->dds_subclass) {
3396495Sspeer case DDS_VNET_ADD_SHARE:
3406495Sspeer DBG2(vdds, "DDS_VNET_ADD_SHARE message...");
3416495Sspeer if ((vdds->task_flags != 0) || (vdds->hio_dip != NULL)) {
3426495Sspeer /*
3436495Sspeer * Either a task is already pending or
3446495Sspeer * a hybrid device already exists.
3456495Sspeer */
3466495Sspeer DWARN(vdds, "NACK: Already pending DDS task");
3476495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
3486495Sspeer mutex_exit(&vdds->lock);
3496495Sspeer return;
3506495Sspeer }
3516495Sspeer vdds->task_flags = VNET_DDS_TASK_ADD_SHARE;
3526495Sspeer bcopy(dmsg, &vnetp->vdds_info.dmsg, sizeof (vio_dds_msg_t));
3536495Sspeer DBG2(vdds, "Dispatching task for ADD_SHARE");
3546495Sspeer rv = ddi_taskq_dispatch(vdds->dds_taskqp,
3556495Sspeer vdds_process_dds_msg_task, vnetp, DDI_NOSLEEP);
3566495Sspeer if (rv != 0) {
3576495Sspeer /* Send NACK */
3586495Sspeer DBG2(vdds, "NACK: Failed to dispatch task");
3596495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
3606495Sspeer vdds->task_flags = 0;
3616495Sspeer }
3626495Sspeer break;
3636495Sspeer
3646495Sspeer case DDS_VNET_DEL_SHARE:
3656495Sspeer DBG2(vdds, "DDS_VNET_DEL_SHARE message...");
3666495Sspeer if (vdds->task_flags == VNET_DDS_TASK_ADD_SHARE) {
3676495Sspeer /*
3686495Sspeer * ADD_SHARE task still pending, simply clear
3696495Sspeer * task falgs and ACK.
3706495Sspeer */
3716495Sspeer DBG2(vdds, "ACK:ADD_SHARE task still pending");
3726495Sspeer vdds->task_flags = 0;
3736495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_TRUE);
3746495Sspeer mutex_exit(&vdds->lock);
3756495Sspeer return;
3766495Sspeer }
3776495Sspeer if ((vdds->task_flags == 0) && (vdds->hio_dip == NULL)) {
3786495Sspeer /* Send NACK */
3796495Sspeer DBG2(vdds, "NACK:No HIO device exists");
3806495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
3816495Sspeer mutex_exit(&vdds->lock);
3826495Sspeer return;
3836495Sspeer }
3846495Sspeer vdds->task_flags = VNET_DDS_TASK_DEL_SHARE;
3856495Sspeer bcopy(dmsg, &vdds->dmsg, sizeof (vio_dds_msg_t));
3866495Sspeer DBG2(vdds, "Dispatching DEL_SHARE task");
3876495Sspeer rv = ddi_taskq_dispatch(vdds->dds_taskqp,
3886495Sspeer vdds_process_dds_msg_task, vnetp, DDI_NOSLEEP);
3896495Sspeer if (rv != 0) {
3906495Sspeer /* Send NACK */
3916495Sspeer DBG2(vdds, "NACK: failed to dispatch task");
3926495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
3936495Sspeer vdds->task_flags = 0;
3946495Sspeer }
3956495Sspeer break;
3966495Sspeer case DDS_VNET_REL_SHARE:
3976495Sspeer DBG2(vdds, "Reply for REL_SHARE reply=%d",
3986495Sspeer dmsg->tag.vio_subtype);
3996495Sspeer break;
4006495Sspeer default:
4016495Sspeer DWARN(vdds, "Discarding Unknown DDS message");
4026495Sspeer break;
4036495Sspeer }
4046495Sspeer mutex_exit(&vdds->lock);
4056495Sspeer }
4066495Sspeer
4076495Sspeer /*
4086495Sspeer * vdds_process_dds_msg_task -- Called from a taskq to process the
4096495Sspeer * DDS message.
4106495Sspeer */
4116495Sspeer static void
vdds_process_dds_msg_task(void * arg)4126495Sspeer vdds_process_dds_msg_task(void *arg)
4136495Sspeer {
4146495Sspeer vnet_t *vnetp = arg;
4156495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
4166495Sspeer vio_dds_msg_t *dmsg = &vdds->dmsg;
4176495Sspeer dev_info_t *dip;
4187529SSriharsha.Basavapatna@Sun.COM uint32_t max_frame_size;
4196495Sspeer uint64_t hio_cookie;
4206495Sspeer int rv;
4216495Sspeer
4226495Sspeer DBG1(vdds, "DDS task started...");
4236495Sspeer mutex_enter(&vdds->lock);
4246495Sspeer switch (vdds->task_flags) {
4256495Sspeer case VNET_DDS_TASK_ADD_SHARE:
4266495Sspeer DBG2(vdds, "ADD_SHARE task...");
4276495Sspeer hio_cookie = dmsg->msg.share_msg.cookie;
4287529SSriharsha.Basavapatna@Sun.COM /*
4297529SSriharsha.Basavapatna@Sun.COM * max-frame-size value need to be set to
4307529SSriharsha.Basavapatna@Sun.COM * the full ethernet frame size. That is,
4317529SSriharsha.Basavapatna@Sun.COM * header + payload + checksum.
4327529SSriharsha.Basavapatna@Sun.COM */
4337529SSriharsha.Basavapatna@Sun.COM max_frame_size = vnetp->mtu +
4347529SSriharsha.Basavapatna@Sun.COM sizeof (struct ether_vlan_header) + ETHERFCSL;
4356495Sspeer dip = vdds_create_niu_node(hio_cookie,
4367529SSriharsha.Basavapatna@Sun.COM dmsg->msg.share_msg.macaddr, max_frame_size);
4376495Sspeer if (dip == NULL) {
4386495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
4396495Sspeer DERR(vdds, "Failed to create HIO node");
4406495Sspeer } else {
4416495Sspeer vdds->hio_dip = dip;
4426495Sspeer vdds->hio_cookie = hio_cookie;
44311119SSriharsha.Basavapatna@Sun.COM (void) snprintf(vdds->hio_ifname,
44411119SSriharsha.Basavapatna@Sun.COM sizeof (vdds->hio_ifname), "%s%d",
44511119SSriharsha.Basavapatna@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip));
44610309SSriharsha.Basavapatna@Sun.COM
44710309SSriharsha.Basavapatna@Sun.COM rv = vnet_hio_mac_init(vnetp, vdds->hio_ifname);
44810309SSriharsha.Basavapatna@Sun.COM if (rv != 0) {
44910309SSriharsha.Basavapatna@Sun.COM /* failed - cleanup, send failed DDS message */
45010309SSriharsha.Basavapatna@Sun.COM DERR(vdds, "HIO mac init failed, cleaning up");
45110309SSriharsha.Basavapatna@Sun.COM rv = vdds_destroy_niu_node(dip, hio_cookie);
45210309SSriharsha.Basavapatna@Sun.COM if (rv == 0) {
45310309SSriharsha.Basavapatna@Sun.COM /* use DERR to print by default */
45410309SSriharsha.Basavapatna@Sun.COM DERR(vdds, "Successfully destroyed"
45510309SSriharsha.Basavapatna@Sun.COM " Hybrid node");
45610309SSriharsha.Basavapatna@Sun.COM } else {
45710309SSriharsha.Basavapatna@Sun.COM cmn_err(CE_WARN, "vnet%d:Failed to "
45810309SSriharsha.Basavapatna@Sun.COM "destroy Hybrid node",
45910309SSriharsha.Basavapatna@Sun.COM vnetp->instance);
46010309SSriharsha.Basavapatna@Sun.COM }
46110309SSriharsha.Basavapatna@Sun.COM vdds->hio_dip = NULL;
46210309SSriharsha.Basavapatna@Sun.COM vdds->hio_cookie = 0;
46310309SSriharsha.Basavapatna@Sun.COM (void) vdds_send_dds_resp_msg(vnetp,
46410309SSriharsha.Basavapatna@Sun.COM dmsg, B_FALSE);
46510309SSriharsha.Basavapatna@Sun.COM } else {
46610309SSriharsha.Basavapatna@Sun.COM (void) vdds_send_dds_resp_msg(vnetp,
46710309SSriharsha.Basavapatna@Sun.COM dmsg, B_TRUE);
46810309SSriharsha.Basavapatna@Sun.COM }
4696495Sspeer /* DERR used only print by default */
4706495Sspeer DERR(vdds, "Successfully created HIO node");
4716495Sspeer }
4726495Sspeer break;
4736495Sspeer
4746495Sspeer case VNET_DDS_TASK_DEL_SHARE:
4756495Sspeer DBG2(vdds, "DEL_SHARE task...");
4766495Sspeer if (vnetp->vdds_info.hio_dip == NULL) {
4776495Sspeer DBG2(vdds, "NACK: No HIO device destroy");
4786495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE);
4796495Sspeer } else {
48010309SSriharsha.Basavapatna@Sun.COM vnet_hio_mac_cleanup(vnetp);
4816495Sspeer rv = vdds_destroy_niu_node(vnetp->vdds_info.hio_dip,
4826495Sspeer vdds->hio_cookie);
4836495Sspeer if (rv == 0) {
4846495Sspeer /* use DERR to print by default */
4856495Sspeer DERR(vdds, "Successfully destroyed"
4866495Sspeer " Hybrid node");
4876495Sspeer } else {
4886495Sspeer cmn_err(CE_WARN, "vnet%d:Failed to "
4896495Sspeer "destroy Hybrid node", vnetp->instance);
4906495Sspeer }
4916495Sspeer /* TODO: send ACK even for failure? */
4926495Sspeer DBG2(vdds, "ACK: HIO device destroyed");
4936495Sspeer (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_TRUE);
4946495Sspeer vdds->hio_dip = 0;
4956495Sspeer vdds->hio_cookie = 0;
4966495Sspeer }
4976495Sspeer break;
4986495Sspeer case VNET_DDS_TASK_REL_SHARE:
4996495Sspeer DBG2(vdds, "REL_SHARE task...");
5006495Sspeer if (vnetp->vdds_info.hio_dip != NULL) {
50110309SSriharsha.Basavapatna@Sun.COM vnet_hio_mac_cleanup(vnetp);
5026495Sspeer rv = vdds_destroy_niu_node(vnetp->vdds_info.hio_dip,
5036495Sspeer vdds->hio_cookie);
5046495Sspeer if (rv == 0) {
5056495Sspeer DERR(vdds, "Successfully destroyed "
5066495Sspeer "Hybrid node");
5076495Sspeer } else {
5086495Sspeer cmn_err(CE_WARN, "vnet%d:Failed to "
5096495Sspeer "destroy HIO node", vnetp->instance);
5106495Sspeer }
5116495Sspeer /* TODO: failure case */
5126495Sspeer (void) vdds_send_dds_rel_msg(vnetp);
5136495Sspeer vdds->hio_dip = 0;
5146495Sspeer vdds->hio_cookie = 0;
5156495Sspeer }
5166495Sspeer break;
5176495Sspeer default:
5186495Sspeer break;
5196495Sspeer }
5206495Sspeer vdds->task_flags = 0;
5216495Sspeer mutex_exit(&vdds->lock);
5226495Sspeer }
5236495Sspeer
5246495Sspeer /*
5256495Sspeer * vdds_send_dds_rel_msg -- Send a DDS_REL_SHARE message.
5266495Sspeer */
5276495Sspeer static int
vdds_send_dds_rel_msg(vnet_t * vnetp)5286495Sspeer vdds_send_dds_rel_msg(vnet_t *vnetp)
5296495Sspeer {
5306495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
5316495Sspeer vio_dds_msg_t vmsg;
5326495Sspeer dds_share_msg_t *smsg = &vmsg.msg.share_msg;
5336495Sspeer int rv;
5346495Sspeer
5356495Sspeer DBG1(vdds, "Sending DDS_VNET_REL_SHARE message");
5366495Sspeer vmsg.tag.vio_msgtype = VIO_TYPE_CTRL;
5376495Sspeer vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO;
5386495Sspeer vmsg.tag.vio_subtype_env = VIO_DDS_INFO;
5396495Sspeer /* vio_sid filled by the LDC module */
5406495Sspeer vmsg.dds_class = DDS_VNET_NIU;
5416495Sspeer vmsg.dds_subclass = DDS_VNET_REL_SHARE;
5426495Sspeer vmsg.dds_req_id = (++vdds->dds_req_id);
5436495Sspeer smsg->macaddr = vnet_macaddr_strtoul(vnetp->curr_macaddr);
5446495Sspeer smsg->cookie = vdds->hio_cookie;
5456495Sspeer rv = vnet_send_dds_msg(vnetp, &vmsg);
5466495Sspeer return (rv);
5476495Sspeer }
5486495Sspeer
5496495Sspeer /*
5506495Sspeer * vdds_send_dds_resp_msg -- Send a DDS response message.
5516495Sspeer */
5526495Sspeer static int
vdds_send_dds_resp_msg(vnet_t * vnetp,vio_dds_msg_t * dmsg,int ack)5536495Sspeer vdds_send_dds_resp_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg, int ack)
5546495Sspeer {
5556495Sspeer vnet_dds_info_t *vdds = &vnetp->vdds_info;
5566495Sspeer int rv;
5576495Sspeer
5586495Sspeer DBG1(vdds, "Sending a response mesage=%d", ack);
5596495Sspeer if (ack == B_TRUE) {
5606495Sspeer dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK;
5616495Sspeer dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS;
5626495Sspeer } else {
5636495Sspeer dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK;
5646495Sspeer dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL;
5656495Sspeer }
5666495Sspeer rv = vnet_send_dds_msg(vnetp, dmsg);
5676495Sspeer return (rv);
5686495Sspeer }
5696495Sspeer
5706495Sspeer /*
5716495Sspeer * vdds_create_niu_node -- Create NIU Hybrid node. The NIU nexus
5726495Sspeer * node also created if it doesn't exist already.
5736495Sspeer */
5746495Sspeer dev_info_t *
vdds_create_niu_node(uint64_t cookie,uint64_t macaddr,uint32_t max_frame_size)5757529SSriharsha.Basavapatna@Sun.COM vdds_create_niu_node(uint64_t cookie, uint64_t macaddr, uint32_t max_frame_size)
5766495Sspeer {
5776495Sspeer dev_info_t *nexus_dip;
5786495Sspeer dev_info_t *niu_dip;
5797529SSriharsha.Basavapatna@Sun.COM vdds_cb_arg_t cba;
5806495Sspeer
5816495Sspeer DBG1(NULL, "Called");
5826495Sspeer
5836495Sspeer if (vdds_hv_hio_capable == B_FALSE) {
5846495Sspeer return (NULL);
5856495Sspeer }
5867819SRaghuram.Kothakota@Sun.COM mutex_enter(&vdds_dev_lock);
5876495Sspeer /* Check if the nexus node exists already */
5886495Sspeer nexus_dip = vdds_find_node(cookie, ddi_root_node(),
5896495Sspeer vdds_match_niu_nexus);
5906495Sspeer if (nexus_dip == NULL) {
5916495Sspeer /*
5926495Sspeer * NIU nexus node not found, so create it now.
5936495Sspeer */
5947529SSriharsha.Basavapatna@Sun.COM cba.dip = NULL;
5957529SSriharsha.Basavapatna@Sun.COM cba.cookie = cookie;
5967529SSriharsha.Basavapatna@Sun.COM cba.macaddr = macaddr;
5977529SSriharsha.Basavapatna@Sun.COM cba.max_frame_size = max_frame_size;
5987529SSriharsha.Basavapatna@Sun.COM nexus_dip = vdds_create_new_node(&cba, NULL,
5996495Sspeer vdds_new_nexus_node);
6006495Sspeer if (nexus_dip == NULL) {
6017819SRaghuram.Kothakota@Sun.COM mutex_exit(&vdds_dev_lock);
6026495Sspeer return (NULL);
6036495Sspeer }
6046495Sspeer }
6056495Sspeer DBG2(NULL, "nexus_dip = 0x%p", nexus_dip);
6066495Sspeer
6076495Sspeer /* Check if NIU node exists already before creating one */
6086495Sspeer niu_dip = vdds_find_node(cookie, nexus_dip,
6096495Sspeer vdds_match_niu_node);
6106495Sspeer if (niu_dip == NULL) {
6117529SSriharsha.Basavapatna@Sun.COM cba.dip = NULL;
6127529SSriharsha.Basavapatna@Sun.COM cba.cookie = cookie;
6137529SSriharsha.Basavapatna@Sun.COM cba.macaddr = macaddr;
6147529SSriharsha.Basavapatna@Sun.COM cba.max_frame_size = max_frame_size;
6157529SSriharsha.Basavapatna@Sun.COM niu_dip = vdds_create_new_node(&cba, nexus_dip,
6166495Sspeer vdds_new_niu_node);
6176495Sspeer /*
6187819SRaghuram.Kothakota@Sun.COM * Hold the niu_dip to prevent it from
6197819SRaghuram.Kothakota@Sun.COM * detaching.
6206495Sspeer */
6217819SRaghuram.Kothakota@Sun.COM if (niu_dip != NULL) {
6227819SRaghuram.Kothakota@Sun.COM e_ddi_hold_devi(niu_dip);
6237819SRaghuram.Kothakota@Sun.COM } else {
6247819SRaghuram.Kothakota@Sun.COM DWARN(NULL, "niumx/network node creation failed");
6257819SRaghuram.Kothakota@Sun.COM }
6267819SRaghuram.Kothakota@Sun.COM } else {
6277819SRaghuram.Kothakota@Sun.COM DWARN(NULL, "niumx/network node already exists(dip=0x%p)",
6287819SRaghuram.Kothakota@Sun.COM niu_dip);
6296495Sspeer }
6307819SRaghuram.Kothakota@Sun.COM /* release the hold that was done in find/create */
6317819SRaghuram.Kothakota@Sun.COM if ((niu_dip != NULL) && (e_ddi_branch_held(niu_dip)))
6327819SRaghuram.Kothakota@Sun.COM e_ddi_branch_rele(niu_dip);
6337819SRaghuram.Kothakota@Sun.COM if (e_ddi_branch_held(nexus_dip))
6347819SRaghuram.Kothakota@Sun.COM e_ddi_branch_rele(nexus_dip);
6357819SRaghuram.Kothakota@Sun.COM mutex_exit(&vdds_dev_lock);
6367819SRaghuram.Kothakota@Sun.COM DBG1(NULL, "returning niu_dip=0x%p", niu_dip);
6376495Sspeer return (niu_dip);
6386495Sspeer }
6396495Sspeer
6406495Sspeer /*
6416495Sspeer * vdds_destroy_niu_node -- Destroy the NIU node.
6426495Sspeer */
6436495Sspeer int
vdds_destroy_niu_node(dev_info_t * niu_dip,uint64_t cookie)6446495Sspeer vdds_destroy_niu_node(dev_info_t *niu_dip, uint64_t cookie)
6456495Sspeer {
6466495Sspeer int rv;
6476495Sspeer dev_info_t *fdip = NULL;
6486495Sspeer dev_info_t *nexus_dip = ddi_get_parent(niu_dip);
6496495Sspeer
6506495Sspeer
6516495Sspeer DBG1(NULL, "Called");
6527819SRaghuram.Kothakota@Sun.COM ASSERT(nexus_dip != NULL);
6537819SRaghuram.Kothakota@Sun.COM mutex_enter(&vdds_dev_lock);
6547819SRaghuram.Kothakota@Sun.COM
6557819SRaghuram.Kothakota@Sun.COM if (!e_ddi_branch_held(niu_dip))
6567819SRaghuram.Kothakota@Sun.COM e_ddi_branch_hold(niu_dip);
6577819SRaghuram.Kothakota@Sun.COM /*
6587819SRaghuram.Kothakota@Sun.COM * As we are destroying now, release the
6597819SRaghuram.Kothakota@Sun.COM * hold that was done in during the creation.
6607819SRaghuram.Kothakota@Sun.COM */
6617819SRaghuram.Kothakota@Sun.COM ddi_release_devi(niu_dip);
6626495Sspeer rv = e_ddi_branch_destroy(niu_dip, &fdip, 0);
6636495Sspeer if (rv != 0) {
6646495Sspeer DERR(NULL, "Failed to destroy niumx/network node dip=0x%p",
6656495Sspeer niu_dip);
6666495Sspeer if (fdip != NULL) {
6677819SRaghuram.Kothakota@Sun.COM ddi_release_devi(fdip);
6686495Sspeer }
6697819SRaghuram.Kothakota@Sun.COM rv = EBUSY;
6707819SRaghuram.Kothakota@Sun.COM goto dest_exit;
6716495Sspeer }
6726495Sspeer /*
6736495Sspeer * Cleanup the parent's ranges property set
6746495Sspeer * for this Hybrid device.
6756495Sspeer */
6766495Sspeer vdds_release_range_prop(nexus_dip, cookie);
6777819SRaghuram.Kothakota@Sun.COM
6787819SRaghuram.Kothakota@Sun.COM dest_exit:
6797819SRaghuram.Kothakota@Sun.COM mutex_exit(&vdds_dev_lock);
6807819SRaghuram.Kothakota@Sun.COM DBG1(NULL, "returning rv=%d", rv);
6817819SRaghuram.Kothakota@Sun.COM return (rv);
6826495Sspeer }
6836495Sspeer
6846495Sspeer /*
6856495Sspeer * vdds_match_niu_nexus -- callback function to verify a node is the
6866495Sspeer * NIU nexus node.
6876495Sspeer */
6886495Sspeer static int
vdds_match_niu_nexus(dev_info_t * dip,void * arg)6896495Sspeer vdds_match_niu_nexus(dev_info_t *dip, void *arg)
6906495Sspeer {
6916495Sspeer vdds_cb_arg_t *warg = (vdds_cb_arg_t *)arg;
6926495Sspeer vdds_reg_t *reg_p;
6936495Sspeer char *name;
6946495Sspeer uint64_t hdl;
6956495Sspeer uint_t reglen;
6966495Sspeer int rv;
6976495Sspeer
6986495Sspeer if (dip == ddi_root_node()) {
6996495Sspeer return (DDI_WALK_CONTINUE);
7006495Sspeer }
7016495Sspeer
7026495Sspeer name = ddi_node_name(dip);
7036495Sspeer if (strcmp(name, "niu") != 0) {
7046495Sspeer return (DDI_WALK_CONTINUE);
7056495Sspeer }
7066495Sspeer rv = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
7076495Sspeer DDI_PROP_DONTPASS, "reg", (int **)®_p, ®len);
7086495Sspeer if (rv != DDI_PROP_SUCCESS) {
7096495Sspeer DWARN(NULL, "Failed to get reg property dip=0x%p", dip);
7106495Sspeer return (DDI_WALK_CONTINUE);
7116495Sspeer }
7126495Sspeer
7136495Sspeer hdl = reg_p->addr_hi & 0x0FFFFFFF;
7146495Sspeer ddi_prop_free(reg_p);
7156495Sspeer
7166495Sspeer DBG2(NULL, "Handle = 0x%lx dip=0x%p", hdl, dip);
7176495Sspeer if (hdl == NIUCFGHDL(warg->cookie)) {
7186495Sspeer /* Hold before returning */
7197819SRaghuram.Kothakota@Sun.COM if (!e_ddi_branch_held(dip))
7207819SRaghuram.Kothakota@Sun.COM e_ddi_branch_hold(dip);
7216495Sspeer warg->dip = dip;
7226495Sspeer DBG2(NULL, "Found dip = 0x%p", dip);
7236495Sspeer return (DDI_WALK_TERMINATE);
7246495Sspeer }
7256495Sspeer return (DDI_WALK_CONTINUE);
7266495Sspeer }
7276495Sspeer
7286495Sspeer /*
7296495Sspeer * vdds_match_niu_node -- callback function to verify a node is the
7306495Sspeer * NIU Hybrid node.
7316495Sspeer */
7326495Sspeer static int
vdds_match_niu_node(dev_info_t * dip,void * arg)7336495Sspeer vdds_match_niu_node(dev_info_t *dip, void *arg)
7346495Sspeer {
7356495Sspeer vdds_cb_arg_t *warg = (vdds_cb_arg_t *)arg;
7366495Sspeer char *name;
7376495Sspeer vdds_reg_t *reg_p;
7386495Sspeer uint_t reglen;
7396495Sspeer int rv;
7406495Sspeer uint32_t addr_hi;
7416495Sspeer
7426495Sspeer name = ddi_node_name(dip);
7436495Sspeer if (strcmp(name, "network") != 0) {
7446495Sspeer return (DDI_WALK_CONTINUE);
7456495Sspeer }
7466495Sspeer rv = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
7476495Sspeer DDI_PROP_DONTPASS, "reg", (int **)®_p, ®len);
7486495Sspeer if (rv != DDI_PROP_SUCCESS) {
7496495Sspeer DWARN(NULL, "Failed to get reg property dip=0x%p", dip);
7506495Sspeer return (DDI_WALK_CONTINUE);
7516495Sspeer }
7526495Sspeer
7536495Sspeer addr_hi = reg_p->addr_hi;
7546495Sspeer DBG1(NULL, "addr_hi = 0x%x dip=0x%p", addr_hi, dip);
7556495Sspeer ddi_prop_free(reg_p);
7566495Sspeer if (addr_hi == HVCOOKIE(warg->cookie)) {
7576495Sspeer warg->dip = dip;
7587819SRaghuram.Kothakota@Sun.COM if (!e_ddi_branch_held(dip))
7597819SRaghuram.Kothakota@Sun.COM e_ddi_branch_hold(dip);
7606495Sspeer DBG1(NULL, "Found dip = 0x%p", dip);
7616495Sspeer return (DDI_WALK_TERMINATE);
7626495Sspeer }
7636495Sspeer return (DDI_WALK_CONTINUE);
7646495Sspeer }
7656495Sspeer
7666495Sspeer /*
7676495Sspeer * vdds_new_nexus_node -- callback function to set all the properties
7686495Sspeer * a new NIU nexus node.
7696495Sspeer */
7706495Sspeer static int
vdds_new_nexus_node(dev_info_t * dip,void * arg,uint_t flags)7716495Sspeer vdds_new_nexus_node(dev_info_t *dip, void *arg, uint_t flags)
7726495Sspeer {
7736495Sspeer vdds_cb_arg_t *cba = (vdds_cb_arg_t *)arg;
7746495Sspeer char *compat[] = { "SUNW,niumx" };
7756495Sspeer vdds_ranges_t *rangesp;
7766495Sspeer vdds_reg_t reg;
7776495Sspeer uint64_t nranges;
7786495Sspeer int n;
7796495Sspeer
7806495Sspeer DBG1(NULL, "Called dip=0x%p, flags=0x%X", dip, flags);
7816495Sspeer
7826495Sspeer /* create "niu" property */
7836495Sspeer if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "name", "niu") !=
7846495Sspeer DDI_SUCCESS) {
7856495Sspeer DERR(NULL, "Failed to create name property(dip=0x%p)", dip);
7866495Sspeer return (DDI_WALK_ERROR);
7876495Sspeer }
7886495Sspeer
7896495Sspeer /* create "compatible" property */
7906495Sspeer if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible",
7916495Sspeer compat, 1) != DDI_SUCCESS) {
7926495Sspeer DERR(NULL, "Failed to create compatible property(dip=0x%p)",
7936495Sspeer dip);
7946495Sspeer return (DDI_WALK_ERROR);
7956495Sspeer }
7966495Sspeer
7976495Sspeer /* create "device_type" property */
7986495Sspeer if (ndi_prop_update_string(DDI_DEV_T_NONE, dip,
7996495Sspeer "device_type", "sun4v") != DDI_SUCCESS) {
8006495Sspeer DERR(NULL, "Failed to create device_type property(dip=0x%p)",
8016495Sspeer dip);
8026495Sspeer return (DDI_WALK_ERROR);
8036495Sspeer }
8046495Sspeer
8056495Sspeer /*
8066495Sspeer * create "reg" property. The first 28 bits of
8076495Sspeer * 'addr_hi' are NIU cfg_handle, the 0xc in 28-31 bits
8086495Sspeer * indicates non-cacheable config.
8096495Sspeer */
8106495Sspeer reg.addr_hi = 0xc0000000 | NIUCFGHDL(cba->cookie);
8116495Sspeer reg.addr_lo = 0;
8126495Sspeer reg.size_hi = 0;
8136495Sspeer reg.size_lo = 0;
8146495Sspeer if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
8156495Sspeer "reg", (int *)®, sizeof (reg)/sizeof (int)) != DDI_SUCCESS) {
8166495Sspeer DERR(NULL, "Failed to create reg property(dip=0x%p)", dip);
8176495Sspeer return (DDI_WALK_ERROR);
8186495Sspeer }
8196495Sspeer
8206495Sspeer /*
8216495Sspeer * Create VDDS_MAX_RANGES so that they are already in place
8226495Sspeer * before the children are created. While creating the child
8236495Sspeer * we just modify one of this ranges entries.
8246495Sspeer */
8256495Sspeer nranges = VDDS_MAX_RANGES; /* One range for each VR */
8266495Sspeer rangesp = (vdds_ranges_t *)kmem_zalloc(
8276495Sspeer (sizeof (vdds_ranges_t) * nranges), KM_SLEEP);
8286495Sspeer
8296495Sspeer for (n = 0; n < nranges; n++) {
8306495Sspeer /* zero all child_hi/lo */
8316495Sspeer rangesp[n].child_hi = 0;
8326495Sspeer rangesp[n].child_lo = 0;
8336495Sspeer }
8346495Sspeer
8356495Sspeer if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges",
8366495Sspeer (int *)rangesp, (nranges * 6)) != DDI_SUCCESS) {
8376495Sspeer DERR(NULL, "Failed to create ranges property(dip=0x%p)", dip);
8386495Sspeer kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges));
8396495Sspeer return (DDI_WALK_ERROR);
8406495Sspeer }
8416495Sspeer
8426495Sspeer /* create "#size-cells" property */
8436495Sspeer if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
8446495Sspeer "#size-cells", 2) != DDI_SUCCESS) {
8456495Sspeer DERR(NULL, "Failed to create #size-cells property(dip=0x%p)",
8466495Sspeer dip);
8476495Sspeer kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges));
8486495Sspeer return (DDI_WALK_ERROR);
8496495Sspeer }
8506495Sspeer
8516495Sspeer /* create "#address-cells" property */
8526495Sspeer if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
8536495Sspeer "#address-cells", 2) != DDI_SUCCESS) {
8546495Sspeer DERR(NULL, "Failed to create #address-cells prop(dip=0x%p)",
8556495Sspeer dip);
8566495Sspeer kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges));
8576495Sspeer return (DDI_WALK_ERROR);
8586495Sspeer }
8596495Sspeer
8606495Sspeer kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges));
8616495Sspeer cba->dip = dip;
8626495Sspeer DBG1(NULL, "Returning (dip=0x%p)", dip);
8636495Sspeer return (DDI_WALK_TERMINATE);
8646495Sspeer }
8656495Sspeer
8666495Sspeer /*
8676495Sspeer * vdds_new_niu_node -- callback function to create a new NIU Hybrid node.
8686495Sspeer */
8696495Sspeer static int
vdds_new_niu_node(dev_info_t * dip,void * arg,uint_t flags)8706495Sspeer vdds_new_niu_node(dev_info_t *dip, void *arg, uint_t flags)
8716495Sspeer {
8726495Sspeer vdds_cb_arg_t *cba = (vdds_cb_arg_t *)arg;
8736495Sspeer char *compat[] = { "SUNW,niusl" };
8746495Sspeer uint8_t macaddrbytes[ETHERADDRL];
8756495Sspeer int interrupts[VDDS_MAX_VRINTRS];
8766495Sspeer vdds_ranges_t *prng;
8776495Sspeer vdds_ranges_t *prp;
8786495Sspeer vdds_reg_t reg;
8796495Sspeer dev_info_t *pdip;
8806495Sspeer uint64_t start;
8816495Sspeer uint64_t size;
8826495Sspeer int prnglen;
8836495Sspeer int nintr = 0;
8846495Sspeer int nrng;
8856495Sspeer int rnum;
8866495Sspeer int rv;
8876495Sspeer
8886495Sspeer DBG1(NULL, "Called dip=0x%p flags=0x%X", dip, flags);
8896495Sspeer pdip = ddi_get_parent(dip);
8906495Sspeer
8916495Sspeer if (pdip == NULL) {
8926495Sspeer DWARN(NULL, "Failed to get parent dip(dip=0x%p)", dip);
8936495Sspeer return (DDI_WALK_ERROR);
8946495Sspeer }
8956495Sspeer
8966495Sspeer /* create "network" property */
8976495Sspeer if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "name", "network") !=
8986495Sspeer DDI_SUCCESS) {
8996495Sspeer DERR(NULL, "Failed to create name property(dip=0x%p)", dip);
9006495Sspeer return (DDI_WALK_ERROR);
9016495Sspeer }
9026495Sspeer
9036495Sspeer /*
9046495Sspeer * create "niutype" property, it is set to n2niu to
9056495Sspeer * indicate NIU Hybrid node.
9066495Sspeer */
9076495Sspeer if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "niutype",
9086495Sspeer "n2niu") != DDI_SUCCESS) {
9096495Sspeer DERR(NULL, "Failed to create niuopmode property(dip=0x%p)",
9106495Sspeer dip);
9116495Sspeer return (DDI_WALK_ERROR);
9126495Sspeer }
9136495Sspeer
9146495Sspeer /* create "compatible" property */
9156495Sspeer if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible",
9166495Sspeer compat, 1) != DDI_SUCCESS) {
9176495Sspeer DERR(NULL, "Failed to create compatible property(dip=0x%p)",
9186495Sspeer dip);
9196495Sspeer return (DDI_WALK_ERROR);
9206495Sspeer }
9216495Sspeer
9226495Sspeer /* create "device_type" property */
9236495Sspeer if (ndi_prop_update_string(DDI_DEV_T_NONE, dip,
9246495Sspeer "device_type", "network") != DDI_SUCCESS) {
9256495Sspeer DERR(NULL, "Failed to create device_type property(dip=0x%p)",
9266495Sspeer dip);
9276495Sspeer return (DDI_WALK_ERROR);
9286495Sspeer }
9296495Sspeer
9306495Sspeer /* create "reg" property */
9316495Sspeer if (vdds_hv_niu_vr_getinfo(HVCOOKIE(cba->cookie),
9326495Sspeer &start, &size) != H_EOK) {
9336495Sspeer DERR(NULL, "Failed to get vrinfo for cookie(0x%lX)",
9346495Sspeer cba->cookie);
9356495Sspeer return (DDI_WALK_ERROR);
9366495Sspeer }
9376495Sspeer reg.addr_hi = HVCOOKIE(cba->cookie);
9386495Sspeer reg.addr_lo = 0;
9396495Sspeer reg.size_hi = 0;
9406495Sspeer reg.size_lo = size;
9416495Sspeer
9426495Sspeer if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
9436495Sspeer (int *)®, sizeof (reg) / sizeof (int)) != DDI_SUCCESS) {
9446495Sspeer DERR(NULL, "Failed to create reg property(dip=0x%p)", dip);
9456495Sspeer return (DDI_WALK_ERROR);
9466495Sspeer }
9476495Sspeer
9486495Sspeer /*
9496495Sspeer * Modify the parent's ranges property to map the "reg" property
9506495Sspeer * of the new child.
9516495Sspeer */
9526495Sspeer if ((rv = ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
9536495Sspeer "ranges", (caddr_t)&prng, &prnglen)) != DDI_SUCCESS) {
9546495Sspeer DERR(NULL,
9556495Sspeer "Failed to get parent's ranges property(pdip=0x%p) rv=%d",
9566495Sspeer pdip, rv);
9576495Sspeer return (DDI_WALK_ERROR);
9586495Sspeer }
9596495Sspeer nrng = prnglen/(sizeof (vdds_ranges_t));
9606495Sspeer /*
9616495Sspeer * First scan all ranges to see if a range corresponding
9626495Sspeer * to this virtual NIU exists already.
9636495Sspeer */
9646495Sspeer for (rnum = 0; rnum < nrng; rnum++) {
9656495Sspeer prp = &prng[rnum];
9666495Sspeer if (prp->child_hi == HVCOOKIE(cba->cookie)) {
9676495Sspeer break;
9686495Sspeer }
9696495Sspeer }
9706495Sspeer if (rnum == nrng) {
9716495Sspeer /* Now to try to find an empty range */
9726495Sspeer for (rnum = 0; rnum < nrng; rnum++) {
9736495Sspeer prp = &prng[rnum];
9746495Sspeer if (prp->child_hi == 0) {
9756495Sspeer break;
9766495Sspeer }
9776495Sspeer }
9786495Sspeer }
9796495Sspeer if (rnum == nrng) {
9806495Sspeer DERR(NULL, "No free ranges entry found");
9816495Sspeer return (DDI_WALK_ERROR);
9826495Sspeer }
9836495Sspeer
9846495Sspeer /*
9856495Sspeer * child_hi will have HV cookie as HV cookie is more like
9866495Sspeer * a port in the HybridIO.
9876495Sspeer */
9886495Sspeer prp->child_hi = HVCOOKIE(cba->cookie);
9896495Sspeer prp->child_lo = 0;
9906495Sspeer prp->parent_hi = 0x80000000 | (start >> 32);
9916495Sspeer prp->parent_lo = start & 0x00000000FFFFFFFF;
9926495Sspeer prp->size_hi = (size >> 32);
9936495Sspeer prp->size_lo = size & 0x00000000FFFFFFFF;
9946495Sspeer
9956495Sspeer if (ndi_prop_update_int_array(DDI_DEV_T_NONE, pdip, "ranges",
9966495Sspeer (int *)prng, (nrng * 6)) != DDI_SUCCESS) {
9976495Sspeer DERR(NULL, "Failed to update parent ranges prop(pdip=0x%p)",
9986495Sspeer pdip);
9996495Sspeer return (DDI_WALK_ERROR);
10006495Sspeer }
10016495Sspeer kmem_free((void *)prng, prnglen);
10026495Sspeer
10036495Sspeer vnet_macaddr_ultostr(cba->macaddr, macaddrbytes);
10046495Sspeer
10056495Sspeer /*
10066495Sspeer * create "local-mac-address" property, this will be same as
10076495Sspeer * the vnet's mac-address.
10086495Sspeer */
10096495Sspeer if (ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, "local-mac-address",
10106495Sspeer macaddrbytes, ETHERADDRL) != DDI_SUCCESS) {
10116495Sspeer DERR(NULL, "Failed to update mac-addresses property(dip=0x%p)",
10126495Sspeer dip);
10136495Sspeer return (DDI_WALK_ERROR);
10146495Sspeer }
10156495Sspeer
10166495Sspeer rv = vdds_get_interrupts(cba->cookie, rnum, interrupts, &nintr);
10176495Sspeer if (rv != 0) {
10186495Sspeer DERR(NULL, "Failed to get interrupts for cookie=0x%lx",
10196495Sspeer cba->cookie);
10206495Sspeer return (DDI_WALK_ERROR);
10216495Sspeer }
10226495Sspeer
10236495Sspeer /* create "interrupts" property */
10246495Sspeer if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts",
10256495Sspeer interrupts, nintr) != DDI_SUCCESS) {
10266495Sspeer DERR(NULL, "Failed to update interrupts property(dip=0x%p)",
10276495Sspeer dip);
10286495Sspeer return (DDI_WALK_ERROR);
10296495Sspeer }
10306495Sspeer
10316495Sspeer
10327529SSriharsha.Basavapatna@Sun.COM /* create "max_frame_size" property */
10337529SSriharsha.Basavapatna@Sun.COM if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "max-frame-size",
10347529SSriharsha.Basavapatna@Sun.COM cba->max_frame_size) != DDI_SUCCESS) {
10357529SSriharsha.Basavapatna@Sun.COM DERR(NULL, "Failed to update max-frame-size property(dip=0x%p)",
10367529SSriharsha.Basavapatna@Sun.COM dip);
10377529SSriharsha.Basavapatna@Sun.COM return (DDI_WALK_ERROR);
10387529SSriharsha.Basavapatna@Sun.COM }
10397529SSriharsha.Basavapatna@Sun.COM
10406495Sspeer cba->dip = dip;
10416495Sspeer DBG1(NULL, "Returning dip=0x%p", dip);
10426495Sspeer return (DDI_WALK_TERMINATE);
10436495Sspeer }
10446495Sspeer
10456495Sspeer
10466495Sspeer /*
10476495Sspeer * vdds_find_node -- A common function to find a NIU nexus or NIU node.
10486495Sspeer */
10496495Sspeer static dev_info_t *
vdds_find_node(uint64_t cookie,dev_info_t * sdip,int (* match_func)(dev_info_t * dip,void * arg))10506495Sspeer vdds_find_node(uint64_t cookie, dev_info_t *sdip,
10516495Sspeer int (*match_func)(dev_info_t *dip, void *arg))
10526495Sspeer {
10536495Sspeer vdds_cb_arg_t arg;
10546495Sspeer dev_info_t *pdip;
10556495Sspeer int circ;
10566495Sspeer
10576495Sspeer DBG1(NULL, "Called cookie=%lx\n", cookie);
10586495Sspeer
10596495Sspeer arg.dip = NULL;
10606495Sspeer arg.cookie = cookie;
10616495Sspeer
10626495Sspeer if (pdip = ddi_get_parent(sdip)) {
10636495Sspeer ndi_devi_enter(pdip, &circ);
10646495Sspeer }
10656495Sspeer
10666495Sspeer ddi_walk_devs(sdip, match_func, (void *)&arg);
10676495Sspeer if (pdip != NULL) {
10686495Sspeer ndi_devi_exit(pdip, circ);
10696495Sspeer }
10706495Sspeer
10716495Sspeer DBG1(NULL, "Returning dip=0x%p", arg.dip);
10726495Sspeer return (arg.dip);
10736495Sspeer }
10746495Sspeer
10756495Sspeer /*
10766495Sspeer * vdds_create_new_node -- A common function to create NIU nexus/NIU node.
10776495Sspeer */
10786495Sspeer static dev_info_t *
vdds_create_new_node(vdds_cb_arg_t * cbap,dev_info_t * pdip,int (* new_node_func)(dev_info_t * dip,void * arg,uint_t flags))10797529SSriharsha.Basavapatna@Sun.COM vdds_create_new_node(vdds_cb_arg_t *cbap, dev_info_t *pdip,
10806495Sspeer int (*new_node_func)(dev_info_t *dip, void *arg, uint_t flags))
10816495Sspeer {
10826495Sspeer devi_branch_t br;
10837819SRaghuram.Kothakota@Sun.COM int rv;
10846495Sspeer
10857529SSriharsha.Basavapatna@Sun.COM DBG1(NULL, "Called cookie=0x%lx", cbap->cookie);
10866495Sspeer
10877529SSriharsha.Basavapatna@Sun.COM br.arg = (void *)cbap;
10886495Sspeer br.type = DEVI_BRANCH_SID;
10896495Sspeer br.create.sid_branch_create = new_node_func;
10906495Sspeer br.devi_branch_callback = NULL;
10916495Sspeer
10927819SRaghuram.Kothakota@Sun.COM if (pdip == NULL) {
10936495Sspeer pdip = ddi_root_node();
10947819SRaghuram.Kothakota@Sun.COM }
10956495Sspeer DBG1(NULL, "calling e_ddi_branch_create");
10966495Sspeer if ((rv = e_ddi_branch_create(pdip, &br, NULL,
10976495Sspeer DEVI_BRANCH_CHILD | DEVI_BRANCH_CONFIGURE))) {
10986495Sspeer DERR(NULL, "e_ddi_branch_create failed=%d", rv);
10996495Sspeer return (NULL);
11006495Sspeer }
11017529SSriharsha.Basavapatna@Sun.COM DBG1(NULL, "Returning(dip=0x%p", cbap->dip);
11027529SSriharsha.Basavapatna@Sun.COM return (cbap->dip);
11036495Sspeer }
11046495Sspeer
11056495Sspeer /*
11066495Sspeer * vdds_get_interrupts -- A function that binds ino's to channels and
11076495Sspeer * then provides them to create interrupts property.
11086495Sspeer */
11096495Sspeer static int
vdds_get_interrupts(uint64_t cookie,int ino_range,int * intrs,int * nintr)11106495Sspeer vdds_get_interrupts(uint64_t cookie, int ino_range, int *intrs, int *nintr)
11116495Sspeer {
11126495Sspeer uint32_t hvcookie = HVCOOKIE(cookie);
11136495Sspeer uint64_t txmap;
11146495Sspeer uint64_t rxmap;
11156495Sspeer uint32_t ino = VDDS_INO_RANGE_START(ino_range);
11166495Sspeer int rv;
11176495Sspeer uint64_t i;
11186495Sspeer
11196495Sspeer *nintr = 0;
11206495Sspeer rv = vdds_hv_niu_vr_get_txmap(hvcookie, &txmap);
11216495Sspeer if (rv != H_EOK) {
11226495Sspeer DWARN(NULL, "Failed to get txmap for hvcookie=0x%X rv=%d\n",
11236495Sspeer hvcookie, rv);
11246495Sspeer return (EIO);
11256495Sspeer }
11266495Sspeer rv = vdds_hv_niu_vr_get_rxmap(hvcookie, &rxmap);
11276495Sspeer if (rv != H_EOK) {
11286495Sspeer DWARN(NULL, "Failed to get rxmap for hvcookie=0x%X, rv=%d\n",
11296495Sspeer hvcookie, rv);
11306495Sspeer return (EIO);
11316495Sspeer }
11326495Sspeer /* Check if the number of total channels to be more than 8 */
11336495Sspeer for (i = 0; i < 4; i++) {
11346495Sspeer if (rxmap & (((uint64_t)0x1) << i)) {
11356495Sspeer rv = vdds_hv_niu_vrrx_set_ino(hvcookie, i, ino);
11366495Sspeer if (rv != H_EOK) {
11376495Sspeer DWARN(NULL, "Failed to get Rx ino for "
11386495Sspeer "hvcookie=0x%X vch_idx=0x%lx rv=%d\n",
11396495Sspeer hvcookie, i, rv);
11406495Sspeer return (EIO);
11416495Sspeer }
11426495Sspeer DWARN(NULL,
11436495Sspeer "hvcookie=0x%X RX vch_idx=0x%lx ino=0x%X\n",
11446495Sspeer hvcookie, i, ino);
11456495Sspeer *intrs = ino;
11466495Sspeer ino++;
11476495Sspeer } else {
11486495Sspeer *intrs = VDDS_MAX_INTR_NUM;
11496495Sspeer }
11506495Sspeer intrs++;
11516495Sspeer *nintr += 1;
11526495Sspeer }
11536495Sspeer for (i = 0; i < 4; i++) {
11546495Sspeer if (txmap & (((uint64_t)0x1) << i)) {
11556495Sspeer rv = vdds_hv_niu_vrtx_set_ino(hvcookie, i, ino);
11566495Sspeer if (rv != H_EOK) {
11576495Sspeer DWARN(NULL, "Failed to get Tx ino for "
11586495Sspeer "hvcookie=0x%X vch_idx=0x%lx rv=%d\n",
11596495Sspeer hvcookie, i, rv);
11606495Sspeer return (EIO);
11616495Sspeer }
11626495Sspeer DWARN(NULL, "hvcookie=0x%X TX vch_idx=0x%lx ino=0x%X\n",
11636495Sspeer hvcookie, i, ino);
11646495Sspeer *intrs = ino;
11656495Sspeer ino++;
11666495Sspeer } else {
11676495Sspeer *intrs = VDDS_MAX_INTR_NUM;
11686495Sspeer }
11696495Sspeer intrs++;
11706495Sspeer *nintr += 1;
11716495Sspeer }
11726495Sspeer return (0);
11736495Sspeer }
11746495Sspeer
11756495Sspeer /*
11766495Sspeer * vdds_release_range_prop -- cleanups an entry in the ranges property
11776495Sspeer * corresponding to a cookie.
11786495Sspeer */
11796495Sspeer static void
vdds_release_range_prop(dev_info_t * nexus_dip,uint64_t cookie)11806495Sspeer vdds_release_range_prop(dev_info_t *nexus_dip, uint64_t cookie)
11816495Sspeer {
11826495Sspeer vdds_ranges_t *prng;
11836495Sspeer vdds_ranges_t *prp;
11846495Sspeer int prnglen;
11856495Sspeer int nrng;
11866495Sspeer int rnum;
11876495Sspeer boolean_t success = B_FALSE;
11886495Sspeer int rv;
11896495Sspeer
11906495Sspeer if ((rv = ddi_getlongprop(DDI_DEV_T_ANY, nexus_dip, DDI_PROP_DONTPASS,
11916495Sspeer "ranges", (caddr_t)&prng, &prnglen)) != DDI_SUCCESS) {
11926495Sspeer DERR(NULL,
11936495Sspeer "Failed to get nexus ranges property(dip=0x%p) rv=%d",
11946495Sspeer nexus_dip, rv);
11956495Sspeer return;
11966495Sspeer }
11976495Sspeer nrng = prnglen/(sizeof (vdds_ranges_t));
11986495Sspeer for (rnum = 0; rnum < nrng; rnum++) {
11996495Sspeer prp = &prng[rnum];
12006495Sspeer if (prp->child_hi == HVCOOKIE(cookie)) {
12016495Sspeer prp->child_hi = 0;
12026495Sspeer success = B_TRUE;
12036495Sspeer break;
12046495Sspeer }
12056495Sspeer }
12066495Sspeer if (success) {
12076495Sspeer if (ndi_prop_update_int_array(DDI_DEV_T_NONE, nexus_dip,
12086495Sspeer "ranges", (int *)prng, (nrng * 6)) != DDI_SUCCESS) {
12096495Sspeer DERR(NULL,
12106495Sspeer "Failed to update nexus ranges prop(dip=0x%p)",
12116495Sspeer nexus_dip);
12126495Sspeer }
12136495Sspeer }
12146495Sspeer }
1215