* ========== Copyright Header Begin ==========================================
* Hypervisor Software File: ssi.s
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* - Do no alter or remove copyright notices
* - Redistribution and use of this software in source and binary forms, with
* or without modification, are permitted provided that the following
* - Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* This software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or maintenance of
* ========== Copyright Header End ============================================
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
.ident "@(#)ssi.s 1.4 07/06/20 SMI"
#include <sys/asm_linkage.h>
FPGA_MBOX_INT_DISABLE(IRQ_QUEUE_IN|IRQ_QUEUE_OUT|IRQ_LDC_OUT, %g2, %g3)
DEVINST2COOKIE(%g4, %g1, %g1, %g5, badtrap)
HVCALL(fpga_uart_mondo_receive)
#endif /* CONFIG_FPGA_UART */
* Move SSI interrupts from CPU %g1 onto CPU %g2
ENTRY(ssi_redistribute_interrupts)
ldx [%g4 + INT_MAN + INT_MAN_DEV_OFF(NCUDEV_SSIERR)], %g5
srl %g5, INTRTGT_DEVSHIFT, %g5
and %g5, INTRTGT_CPUMASK, %g5
! %g5 current interrupt target
* The CPU in error does not have any interrupts targetted at
mov %g2, %g1 ! %g1 new interrupt target
sllx %g1, 1 * INTRTGT_DEVSHIFT, %g2
sllx %g1, 2 * INTRTGT_DEVSHIFT, %g3
stx %g3, [%g2 + CONFIG_INTRTGT]
* setup the map registers for the SSI
* %g1 intrtgt CPUID array (8-bits per INT_MAN target)
srl %g1, INTRTGT_DEVSHIFT, %g1 ! get dev1 bits in bottom
and %g1, INTRTGT_CPUMASK, %g3
sllx %g3, INT_MAN_CPU_SHIFT, %g3 ! int_man.cpu
or %g3, VECINTR_SSIERR, %g3 ! int_man.vecnum
stx %g3, [%g4 + INT_MAN + INT_MAN_DEV_OFF(NCUDEV_SSIERR)]
srl %g1, INTRTGT_DEVSHIFT, %g1 ! get dev2 bits in bottom
and %g1, INTRTGT_CPUMASK, %g3
sllx %g3, INT_MAN_CPU_SHIFT, %g3 ! int_man.cpu
or %g3, VECINTR_FPGA, %g3 ! int_man.vecnum
stx %g3, [%g4 + INT_MAN + INT_MAN_DEV_OFF(NCUDEV_SSI)]
SET_SIZE(ssi_redistribute_interrupts)
* Handles the LDC interrupt from the SP.
ENTRY_NP(svc_ldc_tx_intr)
setx FPGA_LDCIN_BASE, %g3, %g5
ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv
! If no more interrupts are pending, exit
brz,pn %g3, .svc_ldc_tx_intr_exit
* The RECV register bits [30:16] are for data notifications
* for the corresponding channel.
* The RECV register bits [14:0] are for space available
* notifications for the corresponding channel.
* RECV register bit[31] is for STATE_CHG notification.
! Find the first bit set in the RECV register
! %g3 RECV register contents
* Clear bit %g4 in RECV register (%g3)
st %g6, [%g5 + FPGA_LDC_RECV_REG] ! RW1C
cmp %g4, FPGA_LDC_RECV_TX_CHANNELS ! space notification
cmp %g4, FPGA_LDC_RECV_RX_CHANNELS ! data notification
sub %g4, FPGA_LDC_RECV_TX_CHANNELS + 1, %g4 ! bit 16 -> channel 0 ...
HVCALL(svc_ldc_reset_intr)
setx FPGA_LDCIN_BASE, %g3, %g5
ba,pt %xcc, .ldc_recv_intrs
ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv
PRINT("LDX TX data available\r\n")
HVCALL(svc_ldc_data_available)
setx FPGA_LDCIN_BASE, %g3, %g5
ba,pt %xcc, .ldc_recv_intrs
ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv
PRINT("LDX TX space available\r\n")
HVCALL(svc_ldc_space_available)
setx FPGA_LDCIN_BASE, %g3, %g5
ba,pt %xcc, .ldc_recv_intrs
ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv
SET_SIZE(svc_ldc_tx_intr)
* Handles the LDC BUSY interrupt from the SP which indicates that the
* SP has just sent us one or more LDC packets across the SRAM.
* For guest<->SP connections, we simply check to see if there is
* any data available on any of the incoming SRAM queues. If so,
* we send notification to the appropriate guest (if the guest's queue
* %g7 - holds calling pc value.
ENTRY_NP(svc_ldc_data_available)
ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array
mulx %g4, SP_LDC_ENDPOINT_SIZE, %g2
add %g1, %g2, %g1 ! pointer to SP endpoint
! %g1 = SP endpoint which received the notification
ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open?
brz %g2, .exit_ldc_rx_channel
.one_more_ldc_rx_channel:
! check to see whether there are any packets
! available on this channel.
ldx [ %g1 + SP_LDC_RX_QD_PA ], %g2
ldub [ %g2 + SRAM_LDC_HEAD ], %g3
ldub [ %g2 + SRAM_LDC_TAIL ], %g4
be %xcc, .exit_ldc_rx_channel ! nothing to pick up
ldub [ %g1 + SP_LDC_TARGET_TYPE ], %g3
cmp %g3, LDC_GUEST_ENDPOINT
! This is a SP<->HV channel so we must read out each packet
! and call the appropriate callback routine for each one.
! NOTE: There is no need to grab the RX_LOCK in this situation
! because we are executing with FPGA interrupts off and this
! is the only routine used for receiving data from the SRAM
! since it is a SP<->HV channel.
! snapshot queue state into our scratch register area
! since we will be copying the data in possibly several
ldx [ %g1 + SP_LDC_RX_QD_PA ], %g5
ldub [ %g5 + SRAM_LDC_HEAD ], %g3
LDC_SRAM_IDX_TO_OFFSET(%g3)
stw %g3, [ %g1 + SP_LDC_RX_SCR_TXHEAD ] ! TX head
ldub [ %g5 + SRAM_LDC_TAIL ], %g3
LDC_SRAM_IDX_TO_OFFSET(%g3)
stw %g3, [ %g1 + SP_LDC_RX_SCR_TXTAIL ] ! TX tail
set (SRAM_LDC_QENTRY_SIZE * SRAM_LDC_ENTRIES_PER_QUEUE), %g3
stx %g3, [ %g1 + SP_LDC_RX_SCR_TXSIZE ] ! TX size
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM)
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM)
lduw [ %g1 + SP_LDC_RX_SCR_TXHEAD ], %g2
lduw [ %g1 + SP_LDC_RX_SCR_TXTAIL ], %g3
ldx [ %g1 + SP_LDC_RX_SCR_TXSIZE ], %g4
LDC_QUEUE_DATA_AVAILABLE(%g2, %g3, %g4)
LDC_SRAM_OFFSET_TO_IDX(%g3)
! %g3 = packets of data to copy
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM)
brlez %g3, .done_read_sram_pkts
ldx [ %g1 + SP_LDC_RX_Q_DATA_PA ], %g5 ! queue data PA
add %g2, %g5, %g2 ! PA of TX queue data
add %g1, SP_LDC_RX_SCR_PKT, %g4 ! PA of RX scratch buffer
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM)
! %g5 = SRAM Queue Data PA (CONFIG_SPLIT_SRAM)
LDC_COPY_PKT_FROM_SRAM(%g2, %g4, %g3, %g6)
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor)
! Now we need to update our scratchpad head pointer
sub %g2, %g5, %g2 ! New TX head offset
ldx [ %g1 + SP_LDC_RX_SCR_TXSIZE ], %g6
move %xcc, 0, %g2 ! check for wrap around
stw %g2, [ %g1 + SP_LDC_RX_SCR_TXHEAD ]
ldx [%g3 + CPU_ROOT], %g3
ldx [%g3 + CONFIG_HV_LDCS], %g3 ! get HV endpoint array
ldx [%g1 + SP_LDC_TARGET_CHANNEL], %g6 ! and target endpoint
mulx %g6, LDC_ENDPOINT_SIZE, %g4
add %g3, %g4, %g4 ! and its struct
ldx [%g4 + LDC_RX_CB], %g6 ! get the callback
brz,pn %g6, .done_read_sram_pkts ! if none, drop pkt
STRAND_PUSH(%g1, %g2, %g3)
STRAND_PUSH(%g7, %g2, %g3)
add %g1, SP_LDC_RX_SCR_PKT, %g2 ! payload
ldx [%g4 + LDC_RX_CBARG], %g1 ! load the argument
jmp %g6 ! invoke callback
! Assume all %g registers clobbered
#ifndef CONFIG_SPLIT_SRAM
ldx [%g1 + SP_LDC_RX_QD_PA], %g5
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM)
! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM)
lduw [ %g1 + SP_LDC_RX_SCR_TXHEAD ], %g3
LDC_SRAM_OFFSET_TO_IDX(%g3)
ldx [%g1 + SP_LDC_RX_QD_PA], %g5 ! queue data PA
stb %g3, [ %g5 + SRAM_LDC_HEAD ] ! commit the new TX head
STRAND_PUSH(%g1, %g2, %g3)
! %g1 target endpoint (clobbered)
LDC_SEND_SP_INTR(%g1, %g3, %g4, SP_LDC_SPACE)
! At this point, since we just updated the SRAM head index, we
! need re-read the head/tail value from SRAM and make sure no new
! packets were added while we were processing the last one.
ba,a .one_more_ldc_rx_channel
ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3
brz %g3, .exit_ldc_rx_channel
ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g6
mulx %g6, LDC_ENDPOINT_SIZE, %g4
set GUEST_LDC_ENDPOINT, %g5
! See if we need to send an interrupt to the recipient
ldx [ %g3 + LDC_RX_MAPREG + LDC_MAPREG_CPUP ], %g6
! if no target CPU specified, is there a vdev interrupt we
ldx [ %g3 + LDC_RX_VINTR_COOKIE ], %g6
brz,pn %g6, .exit_ldc_rx_channel ! if not, we are done.
STRAND_PUSH(%g7, %g2, %g4)
HVCALL(vdev_intr_generate)
! %g6 = target cpu struct
! Only need to send notification if guest RX queue is empty.
! No synchronization issues with respect to lost notification here
! because the guest's rx_set_qhead routine pulls data from the SRAM
! after updating the head pointer with the guest specified value.
lduw [ %g3 + LDC_RX_QHEAD ], %g4
lduw [ %g3 + LDC_RX_QTAIL ], %g5
bne %xcc, .exit_ldc_rx_channel
! %g6 = target cpu struct
STRAND_PUSH(%g7, %g2, %g4)
HVCALL(hv_ldc_cpu_notify)
SET_SIZE(svc_ldc_data_available)
* Handles the LDC ACK interrupt from the SP which indicates that the
* SP has freed up some room in the SRAM LDC queues for us so that we
* may once again send more packets if needed.
* %g4 - channel which received the notification
* %g7 - holds calling pc value.
ENTRY_NP(svc_ldc_space_available)
STRAND_PUSH(%g7, %g1, %g2)
ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array
mulx %g4, SP_LDC_ENDPOINT_SIZE, %g2
add %g1, %g2, %g1 ! pointer to SP endpoint
clr %g6 ! SP notification flag
! %g1 = SP endpoint which received t henotication
! %g6 = 0 (notification flag)
ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open?
brz %g2, .exit_ldc_tx_channel
! If this is a SP <-> HV connection (guest ptr==NULL) then
! there is nothing really to do.
ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3
brz %g3, .exit_ldc_tx_channel
ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g4
mulx %g4, LDC_ENDPOINT_SIZE, %g4
set GUEST_LDC_ENDPOINT, %g5
! %g6 = (notification flag)
! Nothing to send if guest TX queue is empty
lduw [ %g3 + LDC_TX_QHEAD ], %g4
lduw [ %g3 + LDC_TX_QTAIL ], %g5
be %xcc, .exit_ldc_tx_channel
STRAND_PUSH(%g2, %g1, %g4) ! save guest endpoint
add %g3, SP_LDC_TX_LOCK, %g5
SPINLOCK_ENTER(%g5, %g1, %g4)
HVCALL(sram_ldc_push_data) ! %g3 (sp endpoint) preserved
add %g3, SP_LDC_TX_LOCK, %g5
movrnz %g2, 1, %g3 ! %g2 = send interrupt flag
! %g6 = (notification flag)
STRAND_POP(%g2, %g3) ! restore guest endpoint
! %g2 = sender's (guest) endpoint
! We might need to send a 'queue no longer full' interrupt
ldub [ %g2 + LDC_TXQ_FULL ], %g3
brz,pt %g3, .exit_ldc_tx_channel
stb %g0, [ %g2 + LDC_TXQ_FULL ]
ldx [%g2 + LDC_RX_VINTR_COOKIE], %g2
brz %g2, .exit_ldc_tx_channel
STRAND_PUSH(%g1, %g3, %g4)
STRAND_PUSH(%g6, %g3, %g4)
HVCALL(vdev_intr_generate)
! Done checking all SRAM queues
bz %xcc, 1f ! skip TX notification if flag is clear
! %g1 target endpoint (clobbered)
LDC_SEND_SP_INTR(%g1, %g6, %g4, SP_LDC_DATA)
SET_SIZE(svc_ldc_space_available)
* Called when the SP sends us notification for a channel reset so that
* we can forward the notification interrupt to a guest if necessary.
* %g7 - holds calling pc value.
ENTRY_NP(svc_ldc_reset_intr)
ldx [%g1 + CONFIG_SP_LDC_MAX_CID], %g2
ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array
mulx %g2, SP_LDC_ENDPOINT_SIZE, %g2
add %g1, %g2, %g1 ! pointer to last SP endpoint
ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open?
brz %g2, .next_ldc_sram_channel
! check to see whether there is a reset notification pending
ldx [ %g1 + SP_LDC_TX_QD_PA ], %g2
ldub [ %g2 + SRAM_LDC_STATE_NOTIFY ], %g3
brz,pt %g3, .next_ldc_sram_channel
! reset notification is pending for this channel
stb %g0, [ %g2 + SRAM_LDC_STATE_NOTIFY ] ! clear the flag
! For SP<->HV connections (guest ptr==NULL), there is nothing to
ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3
brz %g3, .next_ldc_sram_channel
ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g4
mulx %g4, LDC_ENDPOINT_SIZE, %g4
set GUEST_LDC_ENDPOINT, %g5
! skip notification if no CPU is specified to handle interrupts
ldx [ %g3 + LDC_RX_MAPREG + LDC_MAPREG_CPUP ], %g6
brz,pn %g6, .next_ldc_sram_channel
! %g6 = target cpu struct
STRAND_PUSH(%g1, %g2, %g4)
STRAND_PUSH(%g7, %g2, %g4)
STRAND_PUSH(%g6, %g2, %g4)
HVCALL(hv_ldc_cpu_notify)
ldx [%g2 + CONFIG_SP_LDCS], %g2 ! first SP endpoint
cmp %g1, %g2 ! did we just process it?
bgu,pt %xcc, .one_more_sram_channel
sub %g1, SP_LDC_ENDPOINT_SIZE, %g1
SET_SIZE(svc_ldc_reset_intr)