/* * Copyright (c) 2019-2020 Cobham Gaisler AB * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include GTEXT(z_sparc_arch_switch) GTEXT(z_sparc_context_switch) GTEXT(z_thread_entry_wrapper) /* * The routine z_sparc_context_switch() is called from arch_switch(), or from * the interrupt trap handler in case of preemption. The subtraction to get the * "old" thread from "switched_from" has already been performed and the "old" * thread is now in register %o1. We can address old->switch_handle in assembly * as: [%o1 + ___thread_t_switch_handle_OFFSET]. * * The switch_handle is written in z_sparc_context_switch() after the old * context has been saved. * * This is a leaf function, so only out registers * can be used without saving their context first. * * o0: new thread to restore * o1: old thread to save */ SECTION_FUNC(TEXT, z_sparc_context_switch) mov %y, %o4 st %o4, [%o1 + _thread_offset_to_y] std %l0, [%o1 + _thread_offset_to_l0_and_l1] std %l2, [%o1 + _thread_offset_to_l2] std %l4, [%o1 + _thread_offset_to_l4] std %l6, [%o1 + _thread_offset_to_l6] std %i0, [%o1 + _thread_offset_to_i0] std %i2, [%o1 + _thread_offset_to_i2] std %i4, [%o1 + _thread_offset_to_i4] std %i6, [%o1 + _thread_offset_to_i6] std %o6, [%o1 + _thread_offset_to_o6] rd %psr, %o4 st %o4, [%o1 + _thread_offset_to_psr] and %o4, PSR_CWP, %g3 /* %g3 = CWP */ andn %o4, PSR_ET, %g1 /* %g1 = psr with traps disabled */ wr %g1, %psr /* disable traps */ nop nop nop rd %wim, %g2 /* %g2 = wim */ mov 1, %g4 sll %g4, %g3, %g4 /* %g4 = wim mask for CW invalid */ .Lsave_frame_loop: sll %g4, 1, %g5 /* rotate wim left by 1 */ srl %g4, (CONFIG_SPARC_NWIN-1), %g4 or %g4, %g5, %g4 /* %g4 = wim if we do one restore */ /* if restore would not underflow, continue */ andcc %g4, %g2, %g0 /* window to flush? */ bnz .Ldone_flushing /* continue */ nop restore /* go one window back */ /* essentially the same as window overflow */ /* sp still points to task stack */ std %l0, [%sp + 0x00] std %l2, [%sp + 0x08] std %l4, [%sp + 0x10] std %l6, [%sp + 0x18] std %i0, [%sp + 0x20] std %i2, [%sp + 0x28] std %i4, [%sp + 0x30] std %i6, [%sp + 0x38] ba .Lsave_frame_loop nop .Ldone_flushing: /* * "wrpsr" is a delayed write instruction so wait three instructions * after the write before using non-global registers or instructions * affecting the CWP. */ wr %g1, %psr /* restore cwp */ nop nop nop add %g3, 1, %g2 /* calculate desired wim */ cmp %g2, (CONFIG_SPARC_NWIN-1) /* check if wim is in range */ bg,a .Lwim_overflow mov 0, %g2 .Lwim_overflow: mov 1, %g4 sll %g4, %g2, %g4 /* %g4 = new wim */ wr %g4, %wim nop nop nop /* * We have finished saving the "old" context and are also back in the * register window for which z_sparc_context_switch() was called. * * Now write the old thread into switch handle. * "old->switch_handle = old". */ st %o1, [%o1 + ___thread_t_switch_handle_OFFSET] ldd [%o0 + _thread_offset_to_y], %o4 mov %o4, %y /* restore local registers */ ldd [%o0 + _thread_offset_to_l0_and_l1], %l0 ldd [%o0 + _thread_offset_to_l2], %l2 ldd [%o0 + _thread_offset_to_l4], %l4 ldd [%o0 + _thread_offset_to_l6], %l6 /* restore input registers */ ldd [%o0 + _thread_offset_to_i0], %i0 ldd [%o0 + _thread_offset_to_i2], %i2 ldd [%o0 + _thread_offset_to_i4], %i4 ldd [%o0 + _thread_offset_to_i6], %i6 /* restore output registers */ ldd [%o0 + _thread_offset_to_o6], %o6 #ifdef CONFIG_THREAD_LOCAL_STORAGE ld [%o0 + _thread_offset_to_tls], %g7 #endif ld [%o0 + _thread_offset_to_psr], %g1 /* %g1 = new thread psr */ andn %g1, PSR_CWP, %g1 /* psr without cwp */ or %g1, %g3, %g1 /* psr with new cwp */ wr %g1, %psr /* restore status register and ET */ nop nop nop /* jump into thread */ jmp %o7 + 8 nop SECTION_FUNC(TEXT, z_thread_entry_wrapper) mov %g0, %o7 mov %i0, %o0 mov %i1, %o1 mov %i2, %o2 mov %i3, %o3 call z_thread_entry nop