/* * ========== Copyright Header Begin ========================================== * * Hypervisor Software File: svc_vbsc.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 * conditions are met: * * - 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 * any nuclear facility. * * ========== Copyright Header End ============================================ */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ .ident "@(#)svc_vbsc.s 1.28 07/05/29 SMI" .file "svc_vbsc.s" #if defined(CONFIG_SVC) && defined(CONFIG_VBSC_SVC) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * vbsc_send_polled - send a command to vbsc using polled I/O * * If VBSC does not accept the packet then * * %g1 - cmd[0] * %g2 - cmd[1] * %g3 - cmd[2] * %g7 - return address */ ENTRY_NP(vbsc_send_polled) .vbsc_send_polled_resend: setx FPGA_Q3OUT_BASE, %g4, %g5 setx FPGA_BASE + FPGA_SRAM_BASE, %g4, %g6 lduh [%g5 + FPGA_Q_BASE], %g4 add %g4, %g6, %g6 !! %g6 = sram buffer words stx %g3, [%g6 + (2 * 8)] stx %g2, [%g6 + (1 * 8)] stx %g1, [%g6 + (0 * 8)] mov 1, %g4 stb %g4, [%g5 + FPGA_Q_SEND] /* * Wait for a non-zero status. If we get an ACK then we're done. * Otherwise re-send the packet. Failure is not an option, even * to hv_abort we need to send a message to vbsc. So keep trying. */ .vbsc_send_polled_wait_for_ack: ldub [%g5 + FPGA_Q_STATUS], %g4 andcc %g4, (QINTR_ACK | QINTR_NACK | QINTR_BUSY | QINTR_ABORT), %g4 bz,pn %xcc, .vbsc_send_polled_wait_for_ack nop btst QINTR_ACK, %g4 bz,pt %xcc, .vbsc_send_polled_resend nop stb %g4, [%g5 + FPGA_Q_STATUS] ! clear status bits HVRET SET_SIZE(vbsc_send_polled) /* * vbsc_hv_start - notify VBSC that the hypervisor has started * * %g7 return address * * Called from setup environment */ ENTRY_NP(vbsc_hv_start) mov %g7, %o3 setx VBSC_HV_START, %g2, %g1 mov 0, %g2 mov 0, %g3 HVCALL(vbsc_send_polled) mov %o3, %g7 HVRET SET_SIZE(vbsc_hv_start) /* * vbsc_hv_abort - notify VBSC that hv has aborted * * %g1 contains reason for the abort * %g7 return address */ ENTRY_NP(vbsc_hv_abort) mov %g1, %g2 setx VBSC_HV_ABORT, %g3, %g1 mov 0, %g3 HVCALL(vbsc_send_polled) /* spin until the vbsc powers us down */ ba . nop SET_SIZE(vbsc_hv_abort) /* * vbsc_hv_plxreset - notify VBSC that the hypervisor has requested * a special reset due to PLX link training problems. VBSC knows to * do this quietly and prevent it from looping forever. * * %g1 plx link failure bitmask * %g7 return address * * Called from setup environment */ ENTRY_NP(vbsc_hv_plxreset) mov %g7, %o3 mov %g1, %g2 setx VBSC_HV_PLXRESET, %g3, %g1 mov 0, %g3 HVCALL(vbsc_send_polled) mov %o3, %g7 HVRET SET_SIZE(vbsc_hv_plxreset) /* * vbsc_guest_start - notify VBSC that a guest has started * * %g7 return address */ ENTRY_NP(vbsc_guest_start) mov %g7, %o3 setx VBSC_GUEST_ON, %g2, %g1 GUEST_STRUCT(%o2) set GUEST_GID, %o4 ldx [%o2 + %o4], %g2 add %g2, XPID_GUESTBASE, %g2 mov 0, %g3 HVCALL(vbsc_send_polled) mov %o3, %g7 HVRET SET_SIZE(vbsc_guest_start) /* * vbsc_guest_exit - notify VBSC that a guest has exited * * arg0 exit code (%o0) * -- * does not return */ ENTRY_NP(vbsc_guest_exit) setx VBSC_GUEST_OFF, %g2, %g1 GUEST_STRUCT(%o2) set GUEST_GID, %o4 ldx [%o2 + %o4], %g2 add %g2, XPID_GUESTBASE, %g2 ldx [%o2 + GUEST_TOD_OFFSET], %g3 HVCALL(vbsc_send_polled) /* spin until the vbsc powers us down */ ba . nop SET_SIZE(vbsc_guest_exit) /* * vbsc_guest_sir - notify vbsc that a guest requested a reset * * -- * does not return */ ENTRY_NP(vbsc_guest_sir) setx VBSC_GUEST_RESET, %g2, %g1 GUEST_STRUCT(%o2) set GUEST_GID, %o4 ldx [%o2 + %o4], %g2 add %g2, XPID_GUESTBASE, %g2 ldx [%o2 + GUEST_TOD_OFFSET], %g3 HVCALL(vbsc_send_polled) /* spin until the vbsc powers us down */ ba . nop SET_SIZE(vbsc_guest_sir) /* * vbsc_guest_wdexpire - notify vbsc that a guest's watchdog timer expired * Service Processor policy dictates what happens next. * * %g1: guestp * %g7: return address * -- */ ENTRY_NP(vbsc_guest_wdexpire) mov %g1, %g6 setx VBSC_GUEST_WDEXPIRE, %g2, %g1 set GUEST_GID, %g5 ldx [%g6 + %g5], %g2 add %g2, XPID_GUESTBASE, %g2 ldx [%g6 + GUEST_TOD_OFFSET], %g3 ba,a vbsc_send_polled ! tail call, returns to caller SET_SIZE(vbsc_guest_wdexpire) /* * vbsc_guest_tod_offset - notify VBSC of a guest's TOD offset * We don't retry here, failures are ignored. * * %g1 guestp * %g7 return address * * Clobbers %g1-6 * Called from guest hcall environment */ /* FIXME: Use a better buffer to send from ... */ /* Better yet this function / operation goes away entirely for LDoms */ ENTRY_NP(vbsc_guest_tod_offset) VCPU_STRUCT(%g2) inc CPU_SCR0, %g2 set GUEST_GID, %g3 ldx [%g1 + %g3], %g3 add %g3, XPID_GUESTBASE, %g3 stx %g3, [%g2 + 0x8] ldx [%g1 + GUEST_TOD_OFFSET], %g3 stx %g3, [%g2 + 0x10] setx VBSC_GUEST_TODOFFSET, %g4, %g3 stx %g3, [%g2 + 0x0] ROOT_STRUCT(%g1) ldx [%g1 + CONFIG_VBSC_SVCH], %g1 mov 8 * 3, %g3 !! %g1 svch !! %g2 buf !! %g3 length ba,pt %xcc, svc_internal_send ! tail call, returns to caller nop SET_SIZE(vbsc_guest_tod_offset) #define SIM_IRU_DIAG_ERPT(erpt_vbsc, reg1, reg2) \ set ERPT_TYPE_CPU, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_REPORT_TYPE] ;\ setx 0x10000000000001, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_EHDL] ;\ setx 0x002a372a4, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_STICK] ;\ setx 0x3e002310000607, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_CPUVER] ;\ setx 0x000000000, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_CPUSERIAL] ;\ setx 0x000010000, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_SPARC_AFSR] ;\ setx 0x000830550, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_SPARC_AFAR] ;\ setx 0x400000402, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_TSTATE] ;\ setx 0x000000800, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_HTSTATE] ;\ setx 0x000800610, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_TPC] ;\ set 0x0, reg1 ;\ stuh reg1, [erpt_vbsc + EVBSC_CPUID] ;\ set 0x63, reg1 ;\ stuh reg1, [erpt_vbsc + EVBSC_TT] ;\ set 0x1, reg1 ;\ stub reg1, [erpt_vbsc + EVBSC_TL] ;\ set 0x3, reg1 ;\ stub reg1, [erpt_vbsc + EVBSC_ERREN] #define SIM_IRC_DIAG_ERPT(erpt_vbsc, reg1, reg2) \ set ERPT_TYPE_CPU, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_REPORT_TYPE] ;\ setx 0x10000000000002, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_EHDL] ;\ setx 0x002a372a4, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_STICK] ;\ setx 0x3e002310000607, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_CPUVER] ;\ setx 0x000000000, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_CPUSERIAL] ;\ setx 0x000020000, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_SPARC_AFSR] ;\ setx 0x000830550, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_SPARC_AFAR] ;\ setx 0x400000402, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_TSTATE] ;\ setx 0x000000800, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_HTSTATE] ;\ setx 0x000800610, reg2, reg1 ;\ stx reg1, [erpt_vbsc + EVBSC_TPC] ;\ set 0x0, reg1 ;\ stuh reg1, [erpt_vbsc + EVBSC_CPUID] ;\ set 0x63, reg1 ;\ stuh reg1, [erpt_vbsc + EVBSC_TT] ;\ set 0x1, reg1 ;\ stub reg1, [erpt_vbsc + EVBSC_TL] ;\ set 0x3, reg1 ;\ stub reg1, [erpt_vbsc + EVBSC_ERREN] /* * vbsc_rx * * %g1 callback cookie (guest struct?XXX) * %g2 svc pointer * %g7 return address */ ENTRY(vbsc_rx) ROOT_STRUCT(%g1) ldx [%g1 + CONFIG_VBSC_SVCH], %g1 mov %g7, %g6 PRINT("vbsc_rx: "); PRINTX(%g1); PRINT("\r\n"); mov %g6, %g7 /* * We don't defer packets so clear the recv pending flag. * This is called on the cpu handling the interrupts so * the contents of the buffer will not get clobbered until * we return. */ ld [%g2 + SVC_CTRL_STATE], %g3 andn %g3, SVC_FLAGS_RI, %g3 st %g3, [%g2 + SVC_CTRL_STATE] ! clear RECV pending ldx [%g2 + SVC_CTRL_RECV + SVC_LINK_PA], %g2 inc SVC_PKT_SIZE, %g2 ! skip the header /* * Dispatch command */ ldub [%g2 + 6], %g4 cmp %g4, VBSC_CMD_GUEST_STATE be,pn %xcc, gueststatecmd cmp %g4, VBSC_CMD_HV be,pn %xcc, hvcmd cmp %g4, VBSC_CMD_READMEM be,pn %xcc, dbgrd cmp %g4, VBSC_CMD_WRITEMEM be,pn %xcc, dbgwr cmp %g4, VBSC_CMD_SENDERR be,pn %xcc, dbg_send_error nop vbsc_rx_finished: HVRET /* * hvcmd - Hypervisor Command */ hvcmd: ldub [%g2 + 7], %g4 cmp %g4, 'I' be,pn %xcc, hvcmd_ping nop ba,a vbsc_rx_finished /* * hvcmd_ping - nop, just respond */ hvcmd_ping: /* FIXME: find better scratch buffer */ VCPU_STRUCT(%g2) inc CPU_SCR0, %g2 setx VBSC_ACK(VBSC_CMD_HV, 'I'), %g4, %g3 stx %g3, [%g2 + 0x0] rdpr %tpc, %g3 st %g3, [%g2 + 0xc] srlx %g3, 32, %g3 st %g3, [%g2 + 0x8] ba,pt %xcc, svc_internal_send ! returns to caller!!!! mov 16, %g3 ! len /* * gueststatecmd - Request from VBSC to change guest state */ gueststatecmd: !! %g2 = incoming packet ldub [%g2 + 7], %g4 cmp %g4, GUEST_STATE_CMD_SHUTREQ be,pn %xcc, 1f cmp %g4, GUEST_STATE_CMD_DCOREREQ be,pn %xcc, 1f nop ba,a vbsc_rx_finished 1: /* * The use of XID is deprecated. The target of * the request is always the control domain. */ CTRL_DOMAIN(%g1, %g3, %g4) cmp %g1, 0 be,pn %xcc, vbsc_rx_finished nop /* * Check if the current vcpu is in the control * domain and running. If so, the state command * can be executed on the local strand. */ VCPU_STRUCT(%g3) !! %g1 = control domain guestp !! %g2 = incoming packet !! %g3 = current vcpup ldx [%g3 + CPU_GUEST], %g4 cmp %g1, %g4 bne,pn %xcc, reroutecmd nop ldx [%g3 + CPU_STATUS], %g4 cmp %g4, CPU_STATE_RUNNING bne,pn %xcc, reroutecmd nop ba,a executecmd reroutecmd: !! %g1 = control domain guestp !! %g2 = incoming packet /* * Loop through the vcpus in the control domain * until one is found that is running. If none * are found, the domain is not in an appropriate * state for the request so the request is dropped. */ mov 0, %g4 !! %g4 = current index 1: cmp %g4, (NVCPUS - 1) bgu,pn %xcc, vbsc_rx_finished ! no appropriate vcpu, ignore request nop sllx %g4, GUEST_VCPUS_SHIFT, %g3 add %g1, %g3, %g3 add %g3, GUEST_VCPUS, %g3 ldx [%g3], %g3 brz,a %g3, 1b ! skip any empty entries inc %g4 !! %g3 = current vcpup ! check the vcpu state ldx [%g3 + CPU_STATUS], %g5 cmp %g5, CPU_STATE_RUNNING bne,a %xcc, 1b inc %g4 executecmd: !! %g2 = incoming packet !! %g3 = target vcpup VCPU2STRAND_STRUCT(%g3, %g4) !! %g4 = target strandp ldub [%g2 + 7], %g5 cmp %g5, GUEST_STATE_CMD_SHUTREQ be,pn %xcc, hvcmd_guest_shutdown_request cmp %g5, GUEST_STATE_CMD_DCOREREQ be,pn %xcc, hvcmd_guest_dcore_request nop ba,a vbsc_rx_finished hvcmd_guest_shutdown_request: !! %g2 = incoming packet !! %g3 = target vcpup !! %g4 = target strandp #ifdef DEBUG mov %g7, %g5 PRINT("Control Domain shutdown request, target strand=0x") ldub [%g4 + STRAND_ID], %g6 PRINTX(%g6) PRINT("\r\n") mov %g5, %g7 #endif /* DEBUG */ ! retrieve the command argument from the packet ldx [%g2 + 0x10], %g2 !! %g2 = command argument (grace period in seconds) /* * Perform the action locally if the current * strand is the specified target. */ STRAND_STRUCT(%g5) cmp %g4, %g5 be,a,pt %xcc, guest_shutdown ! tail call, does not return here mov %g2, %g1 ! the required argument /* * Send a mondo to the specified strand to * perform the action. */ add %g4, STRAND_HV_TXMONDO, %g5 !! %g5 = strand mondop ! setup mondo structure mov HXCMD_GUEST_SHUTDOWN, %g6 stx %g6, [%g5 + HVM_CMD] STRAND_STRUCT(%g6) stx %g6, [%g5 + HVM_FROM_STRANDP] stx %g3, [%g5 + HVM_ARGS + HVM_GUESTCMD_VCPUP] stx %g2, [%g5 + HVM_ARGS + HVM_GUESTCMD_ARG] ! send the mondo mov %g4, %g1 ! arg1 = target strandp mov %g5, %g2 ! arg2 = strand mondop STRAND_PUSH(%g7, %g3, %g4) HVCALL(hvmondo_send) STRAND_POP(%g7, %g3) ba,a vbsc_rx_finished hvcmd_guest_dcore_request: !! %g2 = incoming packet !! %g3 = target vcpup !! %g4 = target strandp #ifdef DEBUG mov %g7, %g5 PRINT("Control Domain panic request, target strand=0x") ldub [%g4 + STRAND_ID], %g6 PRINTX(%g6) PRINT("\r\n") mov %g5, %g7 #endif /* DEBUG */ /* * Perform the action locally if the current * strand is the specified target. */ STRAND_STRUCT(%g5) cmp %g4, %g5 be,a,pt %xcc, guest_panic ! tail call, does not return here nop /* * Send a mondo to the specified strand to * perform the action. */ add %g4, STRAND_HV_TXMONDO, %g5 !! %g5 = strand mondop ! setup mondo structure mov HXCMD_GUEST_PANIC, %g6 stx %g6, [%g5 + HVM_CMD] STRAND_STRUCT(%g6) stx %g6, [%g5 + HVM_FROM_STRANDP] stx %g3, [%g5 + HVM_ARGS + HVM_GUESTCMD_VCPUP] ! send the mondo mov %g4, %g1 ! arg1 = target strandp mov %g5, %g2 ! arg2 = strand mondop STRAND_PUSH(%g7, %g3, %g4) HVCALL(hvmondo_send) STRAND_POP(%g7, %g3) ba,a vbsc_rx_finished /* * dbgrd - perform read transaction on behalf of vbsc */ dbgrd: ldx [%g2 + 8], %g3 ! ADDR ldub [%g2 + 7], %g6 ! size sub %g6, '0', %g6 ldub [%g2 + 5], %g4 ! asi? cmp %g4, 'A' bne,pt %xcc, 1f sllx %g6, 2, %g6 ! offset add %g6, 4*4, %g6 ! offset of ASIs srlx %g3, 56, %g4 and %g4, 0xff, %g4 wr %g4, %asi sllx %g3, 8, %g3 srlx %g3, 8, %g3 ! bits 0-56 1: ba 1f rd %pc, %g4 ldub [%g3], %g6 lduh [%g3], %g6 ld [%g3], %g6 ldx [%g3], %g6 lduba [%g3] %asi, %g6 lduha [%g3] %asi, %g6 lda [%g3] %asi, %g6 ldxa [%g3] %asi, %g6 1: add %g4, 4, %g4 jmp %g4 + %g6 ! CTI COUPLE!! ba 1f ! CTI COUPLE!! nop ! NEVER EXECUTED!! DONT DELETE 1: ba 1f rd %pc, %g2 .word 0 ! data buffer - upper 32 bits .word 0 ! data buffer - lower 32 bits 1: add %g2, 4, %g2 ! buf st %g6, [%g2 + 4] ! low bits srlx %g6, 32, %g6 st %g6, [%g2 + 0] ! upper bits ba svc_internal_send ! returns to caller!!!! mov 8, %g3 ! len /* * dbgwr - perform write transaction on behalf of vbsc */ dbgwr: ldx [%g2 + 0x10], %g3 ! ADDR ldub [%g2 + 7], %g6 ! size sub %g6, '0', %g6 ldub [%g2 + 5], %g1 ! asi? cmp %g1, 'A' bne,pt %xcc, 1f sllx %g6, 2, %g6 ! offset add %g6, 4*4, %g6 ! offset of ASIs srlx %g3, 56, %g4 and %g4, 0xff, %g4 wr %g4, %asi sllx %g3, 8, %g3 srlx %g3, 8, %g3 ! bits0-56 1: ba 1f rd %pc, %g4 stb %g1, [%g3] sth %g1, [%g3] st %g1, [%g3] stx %g1, [%g3] stba %g1, [%g3] %asi stha %g1, [%g3] %asi sta %g1, [%g3] %asi stxa %g1, [%g3] %asi 1: add %g4, 4, %g4 ldx [%g2 + 8], %g1 ! get data jmp %g4 + %g6 ! CTI COUPLE!! jmp %g7 + 4 ! All done. nop ! NEVER EXECUTED!!! /* * dbg_send_error - send a fake error transaction back to vbsc */ dbg_send_error: #if 0 #ifdef DEBUG /* * Fill the error reports with valid information to * help test interaction with the FERG on the vbsc */ mov %g7, %g6 STRAND_STRUCT(%g3) add %g3, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g4 SIM_IRC_DIAG_ERPT(%g4, %g5, %g7) add %g3, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g4 SIM_IRU_DIAG_ERPT(%g4, %g5, %g7) PRINT("\r\n") mov %g6, %g7 #endif CPU_PUSH(%g7, %g1, %g2, %g3) STRAND_STRUCT(%g1) add %g1, STRAND_CE_RPT + STRAND_UNSENT_PKT, %g2 ldx [%g1 + STRAND_CONFIGP], %g6 #ifdef DEBUG /* * Send one error and mark another buffer to be * sent */ mov 1, %g7 stx %g7, [%g6 + CONFIG_ERRS_TO_SEND] set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g3 add %g1, %g3, %g3 stx %g7, [%g3] #endif add %g1, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g1 mov EVBSC_SIZE, %g3 HVCALL(send_diag_erpt) CPU_POP(%g7, %g1, %g2, %g3) #endif HVRET SET_SIZE(vbsc_rx) /* * vbsc_tx * * %g1 callback cookie * %g2 packet * %g7 return address */ ENTRY(vbsc_tx) mov %g7, %g6 PRINT("vbsc_tx: ") PRINTX(%g1) PRINT("\r\n") mov %g6, %g7 HVRET SET_SIZE(vbsc_tx) /* * vbsc_puts - print string on hypervisor console * * %g1 string pointer * %g7 return address */ ENTRY_NP(vbsc_puts) CPU_PUSH(%g7, %g2, %g3, %g4) CPU_PUSH(%g6, %g2, %g3, %g4) CPU_PUSH(%g5, %g2, %g3, %g4) CPU_PUSH(%l0, %g2, %g3, %g4) mov %g1, %l0 !! %l0 = string pointer 1: mov 8, %g5 mov 0, %g2 !! %g5 = loop count !! %g2 = debug-puthex arg1 2: ldub [%l0], %g3 brz,pn %g3, .sendndone nop sllx %g2, 8, %g2 or %g2, %g3, %g2 ! arg1 = (arg1 << 8) | char deccc %g5 bnz,pt %xcc, 2b inc %l0 3: setx VBSC_HV_PUTCHARS, %g3, %g1 HVCALL(vbsc_send_polled) ba,pt %xcc, 1b nop .sendndone: setx VBSC_HV_PUTCHARS, %g3, %g1 HVCALL(vbsc_send_polled) CPU_POP(%l0, %g1, %g2, %g3) CPU_POP(%g5, %g1, %g2, %g3) CPU_POP(%g6, %g1, %g2, %g3) CPU_POP(%g7, %g1, %g2, %g3) HVRET SET_SIZE(vbsc_puts) /* * vbsc_putx - print hex number on hypervisor console * * %g1 number * %g7 return address */ ENTRY_NP(vbsc_putx) mov %g1, %g2 setx VBSC_HV_PUTHEX, %g3, %g1 ba,a vbsc_send_polled /* tail call, returns directly to caller */ SET_SIZE(vbsc_putx) #endif /* CONFIG_SVC && CONFIG_VBSC_SVC */