/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This file contains the low-level DMV interrupt * handler for IDN cross-domain interrupts. */ #if defined(lint) #include #endif /* lint */ #include #include #include #include #include #include #if !defined(lint) #include "idn_offsets.h" #endif /* !lint */ #define IDN_MONDO /* * The IDN_DMV_CPU_SHIFT is based on the sizeof (idn_dmv_cpu_t) * which must be a power of 2 to optimize calculating our * entry into idn_dmv_cpu[]. */ #define IDN_DMV_CPU_SHIFT 4 /* *-------------------------------------------------------- */ #if defined(lint) /* * Would be nice to use init_mondo, but unforunately * it assumes the first arg is 32-bits. */ /*ARGSUSED*/ void idnxf_init_mondo(uint64_t arg0, uint64_t arg1, uint64_t arg2) {} #else /* lint */ .global _idn_dispatch_status_busy _idn_dispatch_status_busy: .asciz "ASI_INTR_DISPATCH_STATUS error: busy" .align 4 ENTRY_NP(idnxf_init_mondo) #ifdef DEBUG ! ! IDSR should not be busy at the moment - borrowed from init_mondo ! ldxa [%g0]ASI_INTR_DISPATCH_STATUS, %g1 btst IDSR_BUSY, %g1 bz,pt %xcc, 1f mov ASI_INTR_DISPATCH, %asi sethi %hi(_idn_dispatch_status_busy), %o0 call panic or %o0, %lo(_idn_dispatch_status_busy), %o0 #endif /* DEBUG */ mov ASI_INTR_DISPATCH, %asi 1: stxa %o0, [IDDR_0]%asi ! dmv_word0 stxa %o1, [IDDR_1]%asi ! dmv_word1 stxa %o2, [IDDR_2]%asi ! dmv_word2 retl membar #Sync SET_SIZE(idnxf_init_mondo) #endif /* lint */ /* *-------------------------------------------------------- */ #if defined(lint) /* * Unfortunately, send_mondo is rather picky about getting * a result from the cpu it sends an interrupt to. If it * doesn't get a result within a specific timeframe it * will panic! For IDN that's not cool since a cpu hungup * in one could ultimately result in the demise of a cpu * in another domain. Instead of getting our panties in * a bind, we simply bail out. */ /*ARGSUSED*/ int idnxf_send_mondo(int upaid) { return (0); } #else /* lint */ .seg ".data" .global _idn_send_mondo_failure _idn_send_mondo_failure: .word 0 .seg ".text" ENTRY(idnxf_send_mondo) ! ! NOTE: ! This is stolen from send_mondo. The changes ! are those ifdef'd with IDN_MONDO ! ! construct the interrupt dispatch command register in %g1 ! also, get the dispatch out as SOON as possible ! (initial analysis puts the minimum dispatch time at around ! 30-60 cycles. hence, we try to get the dispatch out quickly ! and then start the rapid check loop). ! rd %tick, %o4 ! baseline tick sll %o0, IDCR_PID_SHIFT, %g1 ! IDCR<18:14> = upa port id or %g1, IDCR_OFFSET, %g1 ! IDCR<13:0> = 0x70 stxa %g0, [%g1]ASI_INTR_DISPATCH ! interrupt vector dispatch #if defined(SF_ERRATA_54) membar #Sync ! store must occur before load mov 0x20, %g3 ! UDBH Control Register Read ldxa [%g3]ASI_SDB_INTR_R, %g0 #endif membar #Sync clr %o2 ! clear NACK counter clr %o3 ! clear BUSY counter ! ! how long, in ticks, are we willing to wait completely ! sethi %hi(xc_tick_limit), %g2 ldx [%g2 + %lo(xc_tick_limit)], %g2 add %g2, %o4, %o5 ! compute the limit value ! ! check the dispatch status ! .check_dispatch: ldxa [%g0]ASI_INTR_DISPATCH_STATUS, %o1 brz,pn %o1, .dispatch_complete rd %tick, %g5 ! ! see if we've gone beyond the limit ! (can tick ever overflow?) ! .timeout_primed: sub %o5, %g5, %g2 ! limit - tick < 0 if timeout brgez,pt %g2, .check_busy inc %o3 ! bump the BUSY counter #ifdef IDN_MONDO ! ! Within the context of IDN we don't want ! to panic just because we can't send_mondo. ! Clear the dispatch register and increment ! our count of failures. ! stxa %g0, [%g1]ASI_INTR_DISPATCH sethi %hi(_idn_send_mondo_failure), %o0 ld [%o0 + %lo(_idn_send_mondo_failure)], %o1 inc %o1 st %o1, [%o0 + %lo(_idn_send_mondo_failure)] retl mov -1, %o0 ! return (-1) #else /* IDN_MONDO */ ! ! time to die, see if we are already panicing ! mov %o0, %o1 ! save target sethi %hi(_send_mondo_nack), %o0 or %o0, %lo(_send_mondo_nack), %o0 sethi %hi(panicstr), %g2 ldn [%g2 + %lo(panicstr)], %g2 brnz %g2, .dispatch_complete ! skip if already in panic nop call panic nop #endif /* IDN_MONDO */ .check_busy: btst IDSR_BUSY, %o1 ! was it BUSY? bnz,pt %xcc, .check_dispatch nop ! ! we weren't busy, we must have been NACK'd ! wait a while and send again ! (this might need jitter) ! sethi %hi(sys_clock_mhz), %g2 lduw [%g2 + %lo(sys_clock_mhz)], %g2 rd %tick, %g4 add %g2, %g4, %g2 .delay: cmp %g2, %g4 bgu,pt %xcc, .delay rd %tick, %g4 stxa %g0, [%g1]ASI_INTR_DISPATCH ! interrupt vector dispatch #if defined(SF_ERRATA_54) membar #Sync ! store must occur before load ldxa [%g3]ASI_SDB_INTR_R, %g0 #endif membar #Sync clr %o3 ! reset BUSY counter ba .check_dispatch inc %o2 ! bump the NACK counter .dispatch_complete: #ifndef IDN_MONDO #ifdef SEND_MONDO_STATS ! ! Increment the appropriate entry in a send_mondo timeout array ! x_entry[CPU][MSB]++; sub %g5, %o4, %g5 ! how long did we wait? clr %o1 ! o1 is now bit counter 1: orcc %g5, %g0, %g0 ! any bits left? srlx %g5, 1, %g5 ! bits to the right bne,a,pt %xcc, 1b add %o1, 4, %o1 ! pointer increment ! ! now compute the base of the x_early entry for our cpu ! CPU_INDEX(%o0, %g5) sll %o0, 8, %o0 ! 64 * 4 add %o0, %o1, %o1 ! %o0 = &[CPU][delay] ! ! and increment the appropriate value ! sethi %hi(x_early), %o0 or %o0, %lo(x_early), %o0 ld [%o0 + %o1], %g5 inc %g5 st %g5, [%o0 + %o1] #endif /* SEND_MONDO_STATS */ #endif /* !IDN_MONDO */ retl #ifdef IDN_MONDO mov %g0, %o0 ! return (0) #else /* IDN_MONDO */ nop #endif /* IDN_MONDO */ SET_SIZE(idnxf_send_mondo) #endif /* lint */ /* *-------------------------------------------------------- */ #if defined(lint) /*ARGSUSED*/ void idn_dmv_handler(void *arg) {} #else /* lint */ ENTRY_NP(idn_dmv_handler) ! ! On entry: ! g1 = idn_dmv_data ! g2 = word 0 ! ldx [%g1 + IDN_DMV_QBASE], %g4 ! g4 = idn_dmv_qbase add %g1, IDN_DMV_CPU, %g3 ! g3 = &idn_dmv_cpu[0] CPU_INDEX(%g6, %g5) ! g6 = cpuid ! ! g5 = cur = idn_dmv_cpu[cpuid] ! sll %g6, IDN_DMV_CPU_SHIFT, %g6 ! g6 = cpuid * 8 add %g3, IDN_DMV_CURRENT, %g3 ld [%g6 + %g3], %g5 ! ! g5 = idn_dmv_cpu[cpuid].idn_dmv_current ! offset from idn_dmv_qbase ! or %g5, %g0, %g5 ! get to 64-bits add %g5, %g4, %g5 ! g5 = idn_dmv_current ! actual address ldstub [%g5 + IV_INUSE], %g7 ! cur->iv_inuse = 0xff brz,pt %g7, 1f ! did we get it? sub %g3, IDN_DMV_CURRENT, %g4 ! ! Queue is FULL. Drop interrupt. ! add %g4, IDN_DMV_LOSTINTR, %g3 ld [%g6 + %g3], %g2 ! ! g2 = idn_dmv_cpu[cpuid].idn_iv_lostintr++ ! inc %g2 set dmv_finish_intr, %g4 st %g2, [%g3 + %g6] jmp %g4 mov -1, %g1 ! ! not reached ! 1: add %g4, IDN_DMV_ACTIVE, %g7 ! ! Move current pointer to next one. ! idn_dmv_current[cpuid] = cur->iv_next ! ld [%g5 + IV_NEXT], %g4 st %g4, [%g3 + %g6] ! ! Start filling in structure with data. ! stx %g2, [%g5 + IV_HEAD] mov IRDR_1, %g2 mov IRDR_2, %g4 ldxa [%g2]ASI_INTR_RECEIVE, %g2 ! g2 = xargs[0,1] ldxa [%g4]ASI_INTR_RECEIVE, %g4 ! g4 = xargs[2,3] stx %g2, [%g5 + IV_XARGS0] stx %g4, [%g5 + IV_XARGS2] membar #StoreLoad|#StoreStore clrb [%g5 + IV_READY] ! cur->iv_ready = 0 (unlocked) ! ! See if we're already active, i.e. have things ! queued. If so, don't bother generating a soft ! interrupt. IDN interrupts could exhaust the ! intr_vec structs for the given cpu and that code ! doesn't know how to survive with intr_vec structs! ! ldstub [%g6 + %g7], %g7 ! idn_dmv_active = 0xff brz,a,pt %g7, 2f ldx [%g1 + IDN_SOFT_INUM], %g7 ! g7 = idn_soft_inum mov -1, %g7 2: ! ! Setup to cause an IDN soft interrupt to occur, ! (if necessary). ! set dmv_finish_intr, %g3 jmp %g3 mov %g7, %g1 SET_SIZE(idn_dmv_handler) #endif /* lint */