* ========== Copyright Header Begin ==========================================
* Hypervisor Software File: cyclic.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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
.ident "@(#)cyclic.s 1.9 07/03/23 SMI"
#include <sys/asm_linkage.h>
* Function: cyclic_add_rel & cyclic_add_abs
* These functions register a handler to be called at a time
* specified by relative or absolute ticks.
* The maximum real time for the counter depends on the core frequency.
* Assuming a 1.0 GHz tick, the rollover will occur in ~292 yrs.
* Since this opens the door for unrealistic timeout values, we shall,
* by default, limit the input delta to a more realistic value. This
* value, in days, is set by default to one year plus one day (367).
* It is then converted to system ticks in the function start_master.
* A handler is automatically removed from the queue when the
* %g1: tick time (relative or absolute)
* %g1: status (0=success, 1=full)
* Function: cyclic_add_rel
* This entry registers a handler to be called after a delay
rd STICK, %g5 ! current time
add %g1, %g5, %g1 ! + delta = abs time
/* Fall thru into cyclic_add_abs() */
* Function: cyclic_add_abs
* This entry registers a handler to be called at a time
/* Fall thru into cyclic_add() core function*/
/* This is the core function cyclic_add() */
stx %g2, [%g6 + STRAND_CY_HANDLER] ! save handler address
stx %g3, [%g6 + STRAND_CY_ARG0] ! save handler args
stx %g4, [%g6 + STRAND_CY_ARG1] ! save handler args
ldx [%g6 + STRAND_CY_CB_LAST_TICK], %g2
brnz,a %g2, .cya_ret ! full: return error
STRAND2CONFIG_STRUCT(%g6, %g5) ! ->config
ldx [%g6 + STRAND_CY_CB_TICK], %g2 ! first tick
brnz,pt %g2, .cya_1 ! not empty: continue
rd STICK, %g3 ! get current time
stx %g3, [%g6 + STRAND_CY_T0] ! empty: set t0
wrhpr %g0, -1, %hstick_cmpr ! inhibit the compare interrupt
ldx [%g5 + CONFIG_CYCLIC_MAXD], %g5 ! max delta
subcc %g1, %g5, %g2 ! delta
bleu %xcc, .cya_3 ! in the past, let t0 check fix
cmp %g2, %g5 ! input within range?
bgu,a %xcc, .cya_3 ! yes: anul next
add %g3, %g5, %g1 ! no: use max value!
ldx [%g6 + STRAND_CY_T0], %g4 ! normalize input to T0
movlu %xcc, %g0, %g1 ! reverse - set to minimum
add %g6, STRAND_CY_CB, %g4 ! ->cb[0]
add %g4, CB_LAST, %g5 ! ->cb[last]
dec CB_SIZE, %g4 ! annul next inc
ldx [%g4 + CB_HANDLER], %g3 ! cb[i].handler
ldx [%g4 + CB_TICK], %g2 ! cb[i].tick
brz,pn %g3, .cya_7 ! empty: store here
cmp %g1, %g2 ! input less than delta?
bge,a %xcc, .cya_4 ! no: check next
sub %g1, %g2, %g1 ! adjust input
sub %g2, %g1, %g2 ! adjust current for insert
stx %g2, [%g4 + CB_TICK] ! store
.cya_5: ! shift remaining up
ldx [%g5 + CB_HANDLER], %g2 ! next handler
brz,a,pt %g2, .cya_6 ! open slot: skip
cmp %g4, %g5 ! (at cb[i]?)
ldx [%g5 + CB_TICK], %g3 ! tick
stx %g2, [%g5 + CB_SIZE + CB_HANDLER]
ldx [%g5 + CB_ARG0], %g2 ! arg0
stx %g3, [%g5 + CB_SIZE + CB_TICK]
ldx [%g5 + CB_ARG1], %g3 ! arg1
stx %g2, [%g5 + CB_SIZE + CB_ARG0]
stx %g3, [%g5 + CB_SIZE + CB_ARG1] ! ..
bnz,a,pt %xcc, .cya_5 ! no
dec CB_SIZE, %g5 ! check next
* Store new entry at %g4:
stx %g1, [%g4 + CB_TICK] ! store tick
ldx [%g6 + STRAND_CY_HANDLER],%g2 ! saved handler
stx %g2, [%g4 + CB_HANDLER] ! store
ldx [%g6 + STRAND_CY_ARG0], %g2 ! saved arg0
stx %g2, [%g4 + CB_ARG0] ! store
ldx [%g6 + STRAND_CY_ARG1], %g2 ! saved arg1
stx %g2, [%g4 + CB_ARG1] ! store
* Setup new timer interrupt:
ldx [%g6 + STRAND_CY_T0], %g1 ! T0 (abs base for cyclic[].tick)
ldx [%g6 + STRAND_CY_CB_TICK], %g2 ! Td (next delta time)
add %g1, %g2, %g4 ! Tn = T0 + Td (next int time)
set EXIT_NTICK, %g5 ! #tick needed to exit
rd STICK, %g3 ! current time
add %g3, %g5, %g3 ! Tm (minimum int time)
cmp %g3, %g4 ! Tn = max(Tn, Tm)
wrhpr %g4, %hstick_cmpr ! start the clock running
clr %g1 ! return status = success
* Function: cyclic_remove
* This function removes an entry from the cyclic timer
* input: %g2 = handler address, if zero - remove head entry
* ToDo: ???? if first entry: disable int
add %g4, STRAND_CY_CB, %g4 ! ->cb[0]
brz,pt %g2, .cyr_2 ! input == 0: do head
ldx [%g4 + CB_HANDLER], %g3 ! address
brz %g3, .cyr_9 ! null: input not found!
cmp %g2, %g3 ! match input?
bnz,a %xcc, .cyr_1 ! no: keep looking
inc CB_SIZE, %g4 ! => next entry
ldx [%g4 + CB_TICK], %g3 ! Td of entry removed
ldx [%g4 + CB_SIZE + CB_TICK], %g2 ! Td of next entry
add %g3, %g2, %g3 ! adjust next time
stx %g3, [%g4 + CB_SIZE + CB_TICK] ! ..
ldx [%g4 + CB_SIZE + CB_HANDLER], %g2 ! shift remainder down
ldx [%g4 + CB_SIZE + CB_TICK], %g3 ! ..
stx %g2, [%g4 + CB_HANDLER] ! ..
brz,a,pn %g2, .cyr_8 ! (done: addr==0, tick=0)
stx %g0, [%g4 + CB_TICK] ! ..
ldx [%g4 + CB_SIZE + CB_ARG0], %g2 ! ..
stx %g3, [%g4 + CB_TICK] ! ..
ldx [%g4 + CB_SIZE + CB_ARG1], %g3 ! ..
stx %g2, [%g4 + CB_ARG0] ! ..
stx %g3, [%g4 + CB_ARG1] ! ..
stx %g0, [%g4 + CB_ARG0] ! zero arg0
stx %g0, [%g4 + CB_ARG1] ! zero arg1
* Function: cyclic_handler_pop
* This function pops first element off the queue. It uses the
* handler address as a valid flag. Time Zero (T0) is updated
* to the next time base (T0 + cyclic_tick[0]).
ENTRY_NP(cyclic_handler_pop)
STRAND_STRUCT(%g6) ! ->strand
add %g6, STRAND_CY_CB, %g5 ! ->cb[0]
ldx [%g5 + CB_HANDLER], %g4 ! cb[0].handler
brz,a,pn %g4, .cyp_9 ! null: return g4=null
clrx [%g6 + STRAND_CY_T0] ! reset time basis
ldx [%g6 + STRAND_CY_T0], %g3 ! T0
ldx [%g5 + CB_TICK], %g2 ! cb[0].tick
add %g3, %g2, %g3 ! next T0
stx %g3, [%g6 + STRAND_CY_T0]
ldx [%g5 + CB_ARG0], %g1 ! cb[0].arg0
ldx [%g5 + CB_ARG1], %g2 ! cb[0].arg1
ldx [%g5 + CB_SIZE + CB_HANDLER], %g6 ! shift rest down
stx %g6, [%g5 + CB_HANDLER] ! ..
brz,a %g6, .cyp_8 ! done: addr, tick = 0
clrx [%g5 + CB_TICK] ! ..
ldx [%g5 + CB_SIZE + CB_TICK], %g6 ! ..
stx %g6, [%g5 + CB_TICK] ! ..
ldx [%g5 + CB_SIZE + CB_ARG0], %g6 ! ..
stx %g6, [%g5 + CB_ARG0] ! ..
ldx [%g5 + CB_SIZE + CB_ARG1], %g6 ! ..
stx %g6, [%g5 + CB_ARG1] ! ..
clrx [%g5 + CB_ARG0] ! zero arg0
clrx [%g5 + CB_ARG1] ! zero arg1
SET_SIZE(cyclic_handler_pop)
* Hstick interrupt service routine.
* This function is called when the compare interrupt fires.
* Note that %hstick_cmpr has been disabled in the trap handler.
HVCALL(cyclic_handler_pop) ! %g1-4: arg0, arg1, t0, handler
brz,a,pn %g4, .hsi_8 ! no cyclic: return
jmp %g4 ! call handler(arg0, arg1, t0)
rd %pc, %g7 ! assume all regs are clobbered!!
* This test should (must) fail, if not there is a logic error.
* It is here as a fail-safe - add 'warning' code later
rdhpr %hstick_cmpr, %g1 ! did callback re-enable?
brgez %g1, .hsi_8 ! yes: clear int & return
STRAND_STRUCT(%g6) ! ->strand
ldx [%g6 + STRAND_CY_CB_HANDLER], %g2 ! first handler
brz,a %g2, .hsi_8 ! empty: clear int & return
ldx [%g6 + STRAND_CY_CB_TICK], %g2 ! Td (next delta time)
ldx [%g6 + STRAND_CY_T0], %g4 ! T0 (abs base for cyclic[].tick)
rd STICK, %g3 ! Tc = current time
mov HSTICK_RET, %g1 ! Tr = #tick needed to Retry
add %g3, %g1, %g3 ! Tint_min = Tc + Tr
add %g4, %g2, %g4 ! Tint_next = T0 + Td
cmp %g3, %g4 ! is Tint_min > Tint_next ?
movgu %xcc, %g3, %g4 ! yes: Tn = Tint_min
wrhpr %g4, %hstick_cmpr ! set the clock
* Reenable our interrupt. Clear hintp:HSP
* cyclic_callback_template
* Hstick interrupt callback function template:
* Called to get actual handler callback address (avoid relocation problems)
ENTRY_NP(cyclic_callback_template)
RETURN_HANDLER_ADDRESS(%g2) ! in %g2
* Callback from interrupt:
* %g3: t0 - interrupt tick time
.callback_entry: /* This is the actual function entry */
SET_SIZE(cyclic_callback_template)