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