1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21/* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26#pragma ident "%Z%%M% %I% %E% SMI" 27 28/* 29 * This file contains the low-level DMV interrupt 30 * handler for IDN cross-domain interrupts. 31 */ 32 33#if defined(lint) 34#include <sys/types.h> 35#endif /* lint */ 36 37#include <sys/asm_linkage.h> 38#include <sys/machasi.h> 39#include <sys/privregs.h> 40#include <sys/intreg.h> 41#include <sys/machthread.h> 42 43#include <sys/idn.h> 44 45#if !defined(lint) 46#include "idn_offsets.h" 47#endif /* !lint */ 48 49#define IDN_MONDO 50 51/* 52 * The IDN_DMV_CPU_SHIFT is based on the sizeof (idn_dmv_cpu_t) 53 * which must be a power of 2 to optimize calculating our 54 * entry into idn_dmv_cpu[]. 55 */ 56#define IDN_DMV_CPU_SHIFT 4 57 58/* 59 *-------------------------------------------------------- 60 */ 61#if defined(lint) 62 63/* 64 * Would be nice to use init_mondo, but unforunately 65 * it assumes the first arg is 32-bits. 66 */ 67/*ARGSUSED*/ 68void 69idnxf_init_mondo(uint64_t arg0, uint64_t arg1, uint64_t arg2) 70{} 71 72#else /* lint */ 73 74 .global _idn_dispatch_status_busy 75_idn_dispatch_status_busy: 76 .asciz "ASI_INTR_DISPATCH_STATUS error: busy" 77 .align 4 78 79 ENTRY_NP(idnxf_init_mondo) 80#ifdef DEBUG 81 ! 82 ! IDSR should not be busy at the moment - borrowed from init_mondo 83 ! 84 ldxa [%g0]ASI_INTR_DISPATCH_STATUS, %g1 85 btst IDSR_BUSY, %g1 86 bz,pt %xcc, 1f 87 mov ASI_INTR_DISPATCH, %asi 88 sethi %hi(_idn_dispatch_status_busy), %o0 89 call panic 90 or %o0, %lo(_idn_dispatch_status_busy), %o0 91#endif /* DEBUG */ 92 93 mov ASI_INTR_DISPATCH, %asi 941: 95 stxa %o0, [IDDR_0]%asi ! dmv_word0 96 stxa %o1, [IDDR_1]%asi ! dmv_word1 97 stxa %o2, [IDDR_2]%asi ! dmv_word2 98 99 retl 100 membar #Sync 101 102 SET_SIZE(idnxf_init_mondo) 103 104#endif /* lint */ 105/* 106 *-------------------------------------------------------- 107 */ 108#if defined(lint) 109 110/* 111 * Unfortunately, send_mondo is rather picky about getting 112 * a result from the cpu it sends an interrupt to. If it 113 * doesn't get a result within a specific timeframe it 114 * will panic! For IDN that's not cool since a cpu hungup 115 * in one could ultimately result in the demise of a cpu 116 * in another domain. Instead of getting our panties in 117 * a bind, we simply bail out. 118 */ 119/*ARGSUSED*/ 120int 121idnxf_send_mondo(int upaid) 122{ return (0); } 123 124#else /* lint */ 125 126 .seg ".data" 127 128 .global _idn_send_mondo_failure 129_idn_send_mondo_failure: 130 .word 0 131 132 .seg ".text" 133 ENTRY(idnxf_send_mondo) 134 ! 135 ! NOTE: 136 ! This is stolen from send_mondo. The changes 137 ! are those ifdef'd with IDN_MONDO 138 ! 139 ! construct the interrupt dispatch command register in %g1 140 ! also, get the dispatch out as SOON as possible 141 ! (initial analysis puts the minimum dispatch time at around 142 ! 30-60 cycles. hence, we try to get the dispatch out quickly 143 ! and then start the rapid check loop). 144 ! 145 rd %tick, %o4 ! baseline tick 146 sll %o0, IDCR_PID_SHIFT, %g1 ! IDCR<18:14> = upa port id 147 or %g1, IDCR_OFFSET, %g1 ! IDCR<13:0> = 0x70 148 stxa %g0, [%g1]ASI_INTR_DISPATCH ! interrupt vector dispatch 149#if defined(SF_ERRATA_54) 150 membar #Sync ! store must occur before load 151 mov 0x20, %g3 ! UDBH Control Register Read 152 ldxa [%g3]ASI_SDB_INTR_R, %g0 153#endif 154 membar #Sync 155 clr %o2 ! clear NACK counter 156 clr %o3 ! clear BUSY counter 157 158 ! 159 ! how long, in ticks, are we willing to wait completely 160 ! 161 sethi %hi(xc_tick_limit), %g2 162 ldx [%g2 + %lo(xc_tick_limit)], %g2 163 add %g2, %o4, %o5 ! compute the limit value 164 165 ! 166 ! check the dispatch status 167 ! 168.check_dispatch: 169 ldxa [%g0]ASI_INTR_DISPATCH_STATUS, %o1 170 brz,pn %o1, .dispatch_complete 171 rd %tick, %g5 172 173 ! 174 ! see if we've gone beyond the limit 175 ! (can tick ever overflow?) 176 ! 177.timeout_primed: 178 sub %o5, %g5, %g2 ! limit - tick < 0 if timeout 179 brgez,pt %g2, .check_busy 180 inc %o3 ! bump the BUSY counter 181 182#ifdef IDN_MONDO 183 ! 184 ! Within the context of IDN we don't want 185 ! to panic just because we can't send_mondo. 186 ! Clear the dispatch register and increment 187 ! our count of failures. 188 ! 189 stxa %g0, [%g1]ASI_INTR_DISPATCH 190 sethi %hi(_idn_send_mondo_failure), %o0 191 ld [%o0 + %lo(_idn_send_mondo_failure)], %o1 192 inc %o1 193 st %o1, [%o0 + %lo(_idn_send_mondo_failure)] 194 retl 195 mov -1, %o0 ! return (-1) 196#else /* IDN_MONDO */ 197 ! 198 ! time to die, see if we are already panicing 199 ! 200 mov %o0, %o1 ! save target 201 sethi %hi(_send_mondo_nack), %o0 202 or %o0, %lo(_send_mondo_nack), %o0 203 sethi %hi(panicstr), %g2 204 ldn [%g2 + %lo(panicstr)], %g2 205 brnz %g2, .dispatch_complete ! skip if already in panic 206 nop 207 call panic 208 nop 209#endif /* IDN_MONDO */ 210 211.check_busy: 212 btst IDSR_BUSY, %o1 ! was it BUSY? 213 bnz,pt %xcc, .check_dispatch 214 nop 215 216 ! 217 ! we weren't busy, we must have been NACK'd 218 ! wait a while and send again 219 ! (this might need jitter) 220 ! 221 sethi %hi(sys_clock_mhz), %g2 222 lduw [%g2 + %lo(sys_clock_mhz)], %g2 223 rd %tick, %g4 224 add %g2, %g4, %g2 225.delay: 226 cmp %g2, %g4 227 bgu,pt %xcc, .delay 228 rd %tick, %g4 229 230 stxa %g0, [%g1]ASI_INTR_DISPATCH ! interrupt vector dispatch 231#if defined(SF_ERRATA_54) 232 membar #Sync ! store must occur before load 233 ldxa [%g3]ASI_SDB_INTR_R, %g0 234#endif 235 membar #Sync 236 clr %o3 ! reset BUSY counter 237 ba .check_dispatch 238 inc %o2 ! bump the NACK counter 239 240.dispatch_complete: 241#ifndef IDN_MONDO 242#ifdef SEND_MONDO_STATS 243 ! 244 ! Increment the appropriate entry in a send_mondo timeout array 245 ! x_entry[CPU][MSB]++; 246 sub %g5, %o4, %g5 ! how long did we wait? 247 clr %o1 ! o1 is now bit counter 2481: orcc %g5, %g0, %g0 ! any bits left? 249 srlx %g5, 1, %g5 ! bits to the right 250 bne,a,pt %xcc, 1b 251 add %o1, 4, %o1 ! pointer increment 252 253 ! 254 ! now compute the base of the x_early entry for our cpu 255 ! 256 CPU_INDEX(%o0, %g5) 257 sll %o0, 8, %o0 ! 64 * 4 258 add %o0, %o1, %o1 ! %o0 = &[CPU][delay] 259 260 ! 261 ! and increment the appropriate value 262 ! 263 sethi %hi(x_early), %o0 264 or %o0, %lo(x_early), %o0 265 ld [%o0 + %o1], %g5 266 inc %g5 267 st %g5, [%o0 + %o1] 268#endif /* SEND_MONDO_STATS */ 269#endif /* !IDN_MONDO */ 270 retl 271#ifdef IDN_MONDO 272 mov %g0, %o0 ! return (0) 273#else /* IDN_MONDO */ 274 nop 275#endif /* IDN_MONDO */ 276 SET_SIZE(idnxf_send_mondo) 277 278#endif /* lint */ 279/* 280 *-------------------------------------------------------- 281 */ 282#if defined(lint) 283 284/*ARGSUSED*/ 285void 286idn_dmv_handler(void *arg) 287{} 288 289#else /* lint */ 290 291 ENTRY_NP(idn_dmv_handler) 292 ! 293 ! On entry: 294 ! g1 = idn_dmv_data 295 ! g2 = word 0 296 ! 297 ldx [%g1 + IDN_DMV_QBASE], %g4 ! g4 = idn_dmv_qbase 298 add %g1, IDN_DMV_CPU, %g3 ! g3 = &idn_dmv_cpu[0] 299 300 CPU_INDEX(%g6, %g5) ! g6 = cpuid 301 302 ! 303 ! g5 = cur = idn_dmv_cpu[cpuid] 304 ! 305 sll %g6, IDN_DMV_CPU_SHIFT, %g6 ! g6 = cpuid * 8 306 add %g3, IDN_DMV_CURRENT, %g3 307 ld [%g6 + %g3], %g5 308 ! 309 ! g5 = idn_dmv_cpu[cpuid].idn_dmv_current 310 ! offset from idn_dmv_qbase 311 ! 312 or %g5, %g0, %g5 ! get to 64-bits 313 add %g5, %g4, %g5 ! g5 = idn_dmv_current 314 ! actual address 315 ldstub [%g5 + IV_INUSE], %g7 ! cur->iv_inuse = 0xff 316 brz,pt %g7, 1f ! did we get it? 317 sub %g3, IDN_DMV_CURRENT, %g4 318 319 ! 320 ! Queue is FULL. Drop interrupt. 321 ! 322 add %g4, IDN_DMV_LOSTINTR, %g3 323 ld [%g6 + %g3], %g2 324 ! 325 ! g2 = idn_dmv_cpu[cpuid].idn_iv_lostintr++ 326 ! 327 inc %g2 328 set dmv_finish_intr, %g4 329 st %g2, [%g3 + %g6] 330 jmp %g4 331 mov -1, %g1 332 ! 333 ! not reached 334 ! 335 3361: 337 add %g4, IDN_DMV_ACTIVE, %g7 338 ! 339 ! Move current pointer to next one. 340 ! idn_dmv_current[cpuid] = cur->iv_next 341 ! 342 ld [%g5 + IV_NEXT], %g4 343 st %g4, [%g3 + %g6] 344 345 ! 346 ! Start filling in structure with data. 347 ! 348 stx %g2, [%g5 + IV_HEAD] 349 350 mov IRDR_1, %g2 351 mov IRDR_2, %g4 352 ldxa [%g2]ASI_INTR_RECEIVE, %g2 ! g2 = xargs[0,1] 353 ldxa [%g4]ASI_INTR_RECEIVE, %g4 ! g4 = xargs[2,3] 354 355 stx %g2, [%g5 + IV_XARGS0] 356 stx %g4, [%g5 + IV_XARGS2] 357 358 membar #StoreLoad|#StoreStore 359 360 clrb [%g5 + IV_READY] ! cur->iv_ready = 0 (unlocked) 361 362 ! 363 ! See if we're already active, i.e. have things 364 ! queued. If so, don't bother generating a soft 365 ! interrupt. IDN interrupts could exhaust the 366 ! intr_vec structs for the given cpu and that code 367 ! doesn't know how to survive with intr_vec structs! 368 ! 369 ldstub [%g6 + %g7], %g7 ! idn_dmv_active = 0xff 370 brz,a,pt %g7, 2f 371 ldx [%g1 + IDN_SOFT_INUM], %g7 ! g7 = idn_soft_inum 372 mov -1, %g7 3732: 374 375 ! 376 ! Setup to cause an IDN soft interrupt to occur, 377 ! (if necessary). 378 ! 379 set dmv_finish_intr, %g3 380 jmp %g3 381 mov %g7, %g1 382 383 SET_SIZE(idn_dmv_handler) 384 385#endif /* lint */ 386