wrsm_trap.s revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* TL1 trap handler for DMV interrupts generated by WCIs
*
* This handler places incoming interrupts onto the appropriate receive
* queue for waiting handlers based on the interrupt mondo. It also
* directly handles "small put interrupts"; it writes <64 chunks of data
* passed by the interrupt into the memory segment, again based on the
* interrupt mondo.
*/
#if !defined(lint)
#include <sys/asm_linkage.h>
#include <sys/privregs.h>
#include <sys/machthread.h>
#include <sys/machparam.h>
#include <sys/wrsm_config.h>
#include <sys/wrsm_intr.h>
#include <sys/wrsm_intr_impl.h>
#include <wrsm_offsets.h>
#endif /* lint */
/*
* The definitions of the interrupt recieve and send registers
* (IRDR_x and IDDR_x) are different for Cheetah than for spitfire
* so the values in intreg.h won't work.
*/
#define IRDR_0 0x40
#define IRDR_1 0x48
#define IRDR_2 0x50
#define IRDR_3 0x58
#define IRDR_4 0x60
#define IRDR_5 0x68
#define IRDR_6 0x80
#define IRDR_7 0x88
#define CMMU_MONDO_SHIFT 32
#define CMMU_MONDO_MASK 0xffff
#define RING_EMPTY 1
#define RING_FULL 2
#define PFN_INVALID -1
#if defined(lint)
/* ARGSUSED */
void
{}
#endif /* lint */
#if !defined(lint)
.seg ".data"
.align 8
.word 0, 0
#ifdef DEBUG
.align 8
.word 0, 0
.align 8
.word 0, 0
.align 8
.word 0, 0
.align 8
.word 0, 0xbadd
.align 8
.word 0, 0xcafe
.align 8
.word 0, 0
#endif /* DEBUG */
#ifdef WRSM_STORE_TRAP_DATA
.align 8
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
#endif /* WRSM_STORE_TRAP_DATA */
!
!
.seg ".text"
#ifdef DEBUG
#endif
/*
* The 16-bit index into the recvq_table is in bits 47 through
* 32 of IRDR_0
*/
bne 0f
! triggering a softint
0:
/*
* If the recvq exportseg field is set, this must be a small put
* interrupt; parse and handle message here. Otherwise it is
* a standard packet to be put onto a recvq.
*/
/*
* %g3 has exportseg pointer
*/
/*
* no longer need %g1, %g3 or %g6
* still don't need need %g5
*
* %g4 has pfn_list pointer
*
* %g2 has word 0 of the incoming mondo vector (IRDR_0), which
* contains the first 8 bytes of small put message.
*
* Parse message and find the length to write, and offset into
* message putdata field holding first byte. Both of these values
* are in IRDR_0 (bytes 0-7).
*/
/*
* If no bytes to write, finished small put interrupt handling.
* Skip to cleanup.
*/
/*
* %g1 holds the number of bytes left to write
* %g2 holds the message putdata offset to write
* %g4 has pfn_list pointer
*
* still don't need %g3, %g5 or %g6
*
* Calculate the physical address to start at. The segment offset
* is translated into the page backing the segment offset. The
* exact physical address is this page plus the offset into this
* page.
*/
/* calculate offset into the page */
6:
/*
* While (len > 0) {}
*
* Loop through the data in putdata, writing it to physical memory
* using the largest possible sized atomic write in each case: 8,
* 4, 2 or 1 byte chunks. The alignment of the write location and
* number of bytes to write determines the largest usable write to
* use.
*
* The data in the putdata buffer is guaranteed be aligned so that
* each Mondo register falls onto an aligned 8 bytes in the
* segment.
*
* %g1 holds the number of bytes left to write
* %g2 holds the message putdata offset to write
* %g4 holds the address of the current pfn_list entry
* %g5 holds the physical address to write to
* still don't need %g3 or %g6
*/
/*
* The maximum amount of bytes that can be transfered via a DMV is
* is 48 bytes. The data payload can span two Dcache lines. Invalidate
* two D$ lines starting at %g5. We do not have to mask %g5 as the
* lower 5 bits of the physical address are ignored.
*/
/*
* Read in appropriate message data based on the offset into the
* message putdata field. Offset 0 of the putdata starts at byte
* 16 in the message buffer, or IRDR_2.
*/
/*
* must be the last register (bytes 40 - 47 of putdata)
*/
/*
* %g3 now IRDR_X register that needs to be read
*
* Read the IRDR_X register into %g3
* Calculate the number of valid bytes in register.
* First, subtract off bytes at the start of the register
* that shouldn't be written (this only happens for IRDR_2).
*/
/*
* %g3 holds the data to write, with the valid bytes in the
* least significant bytes of the register
*
* %g6 holds the number valid bytes
*
* The number of bytes from this register to be written must be no
* more than the lesser of # valid bytes (%g6) and bytes left (%g1).
*/
/*
* Adjust for the number of bytes in %g1. Shift register to the
* right to get the valid bytes in the lower part of the register.
*/
/*
* %g3 holds the data to write, with the valid bytes in the
* least significant bytes of the register
*
* %g6 holds the number of valid bytes
*
* The maximum number of bytes to write is the lesser of the
* number of valid bytes (%g6) and the number of bytes that
* can be written atomicly at this offset.
*/
/*
* %g6 holds the number of valid bytes
* %g7 holds the max atomic write bytes
*
* save the lesser of %g6 and %g7 in %g7
*/
/*
* %g3 holds the data to write, with the valid bytes in the
* least significant bytes of the register
*
* %g6 holds the number of valid bytes
*
* %g7 holds max number of bytes to write
*/
write_one: /* write 1 byte */
/*
* %g6 hold number of excess bytes (shift right to get rid of them)
*/
write_two: /* write 2 bytes */
/*
* %g6 hold number of excess bytes (shift right to get rid of them)
*/
write_four: /* write 4 bytes */
/*
* %g6 hold number of excess bytes (shift right to get rid of them)
*/
write_eight: /* write 8 bytes */
/*
* If no bytes left to write, finished small put interrupt handling.
* Skip to cleanup.
*/
/*
* Determine whether there are any bytes left on the current page.
* If not, get the next page from the pfn_list, and loop.
*/
/*
* Handle recvq messages
*/
1:
! 0 -15 size
!
/*
* NOTE: num_items does not include the interrupt serviced
* g4 (head) was not yet incremented
*/
6:
/* num_items >= high_water_mark */
ba 9b ! }
8:
#ifdef DEBUG
#endif /* DEBUG */
1:
#ifdef DEBUG
#endif
/*
* If recvq->drainer_next != NULL then this recvq is
* already on a service list, so don't queue it again
*/
2:
! list (PSL)
add %g3, DRAINER_PSL, %g2 ! g2 = &drainer->psl
ldx [%g2], %g4 ! g4 = drainer->psl
3:
mov %g6, %g5 ! g5 = revcq
stx %g4, [%g6 + DRAINER_NEXT] ! recvq->dranr_next=dranr->psl
mov %g4, %g1 ! %g1 has old value of psl
! casx does the following as an atomic:
! } else { /* Else... */
! }
! store took place iff g4==g5 after the casx instruction.
casxa [%g2]ASI_N, %g4, %g5 ! drainer->psl = recvq
cmp %g4, %g5 ! if psl hasn't changed...
4:
cmp %g1, WRSM_INTR_PSL_IDLE
bne,pn %xcc, 5f
sub %g0, 1, %g1
#ifdef DEBUG
! Keep track of the number of softints triggered
set wrsm_trap_softint_count, %g5
ldx [%g5], %g2
inc %g2
stx %g2, [%g5]
#endif
! Get the softint inum from the drainer structure
ld [%g3 + DRAINER_INUM], %g1
5:
set dmv_finish_intr, %g5
jmp %g5
nop
#ifdef WRSM_STORE_TRAP_DATA
30:
set wrsm_trap_data, %g7
stx %g3, [%g7+0]
stx %g4, [%g7+8]
stx %g5, [%g7+16]
stx %g6, [%g7+24]
! If no secondary interrupt, return -1
set -1, %g1
sra %g1, 0, %g1
! Return
set dmv_finish_intr, %g5
jmp %g5
nop
#endif
SET_SIZE(wrsm_tl1_handler)
#endif /* !lint */