1/* 2 * Copyright (c) 2019-2020 Cobham Gaisler AB 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7#include <zephyr/toolchain.h> 8#include <zephyr/linker/sections.h> 9#include <offsets_short.h> 10#include <zephyr/arch/sparc/sparc.h> 11 12GTEXT(z_sparc_arch_switch) 13GTEXT(z_sparc_context_switch) 14GTEXT(z_thread_entry_wrapper) 15 16/* 17 * The routine z_sparc_context_switch() is called from arch_switch(), or from 18 * the interrupt trap handler in case of preemption. The subtraction to get the 19 * "old" thread from "switched_from" has already been performed and the "old" 20 * thread is now in register %o1. We can address old->switch_handle in assembly 21 * as: [%o1 + ___thread_t_switch_handle_OFFSET]. 22 * 23 * The switch_handle is written in z_sparc_context_switch() after the old 24 * context has been saved. 25 * 26 * This is a leaf function, so only out registers 27 * can be used without saving their context first. 28 * 29 * o0: new thread to restore 30 * o1: old thread to save 31 */ 32SECTION_FUNC(TEXT, z_sparc_context_switch) 33 mov %y, %o4 34 st %o4, [%o1 + _thread_offset_to_y] 35 std %l0, [%o1 + _thread_offset_to_l0_and_l1] 36 std %l2, [%o1 + _thread_offset_to_l2] 37 std %l4, [%o1 + _thread_offset_to_l4] 38 std %l6, [%o1 + _thread_offset_to_l6] 39 std %i0, [%o1 + _thread_offset_to_i0] 40 std %i2, [%o1 + _thread_offset_to_i2] 41 std %i4, [%o1 + _thread_offset_to_i4] 42 std %i6, [%o1 + _thread_offset_to_i6] 43 44 std %o6, [%o1 + _thread_offset_to_o6] 45 46 rd %psr, %o4 47 st %o4, [%o1 + _thread_offset_to_psr] 48 49 and %o4, PSR_CWP, %g3 /* %g3 = CWP */ 50 andn %o4, PSR_ET, %g1 /* %g1 = psr with traps disabled */ 51 wr %g1, %psr /* disable traps */ 52 nop 53 nop 54 nop 55 56 rd %wim, %g2 /* %g2 = wim */ 57 mov 1, %g4 58 sll %g4, %g3, %g4 /* %g4 = wim mask for CW invalid */ 59 60.Lsave_frame_loop: 61 sll %g4, 1, %g5 /* rotate wim left by 1 */ 62 srl %g4, (CONFIG_SPARC_NWIN-1), %g4 63 or %g4, %g5, %g4 /* %g4 = wim if we do one restore */ 64 65 /* if restore would not underflow, continue */ 66 andcc %g4, %g2, %g0 /* window to flush? */ 67 bnz .Ldone_flushing /* continue */ 68 nop 69 restore /* go one window back */ 70 71 /* essentially the same as window overflow */ 72 /* sp still points to task stack */ 73 std %l0, [%sp + 0x00] 74 std %l2, [%sp + 0x08] 75 std %l4, [%sp + 0x10] 76 std %l6, [%sp + 0x18] 77 std %i0, [%sp + 0x20] 78 std %i2, [%sp + 0x28] 79 std %i4, [%sp + 0x30] 80 std %i6, [%sp + 0x38] 81 ba .Lsave_frame_loop 82 nop 83 84.Ldone_flushing: 85 /* 86 * "wrpsr" is a delayed write instruction so wait three instructions 87 * after the write before using non-global registers or instructions 88 * affecting the CWP. 89 */ 90 wr %g1, %psr /* restore cwp */ 91 nop 92 nop 93 nop 94 add %g3, 1, %g2 /* calculate desired wim */ 95 cmp %g2, (CONFIG_SPARC_NWIN-1) /* check if wim is in range */ 96 bg,a .Lwim_overflow 97 mov 0, %g2 98 99.Lwim_overflow: 100 101 mov 1, %g4 102 sll %g4, %g2, %g4 /* %g4 = new wim */ 103 wr %g4, %wim 104 nop 105 nop 106 nop 107 108 /* 109 * We have finished saving the "old" context and are also back in the 110 * register window for which z_sparc_context_switch() was called. 111 * 112 * Now write the old thread into switch handle. 113 * "old->switch_handle = old". 114 */ 115 st %o1, [%o1 + ___thread_t_switch_handle_OFFSET] 116 117 ldd [%o0 + _thread_offset_to_y], %o4 118 mov %o4, %y 119 120 /* restore local registers */ 121 ldd [%o0 + _thread_offset_to_l0_and_l1], %l0 122 ldd [%o0 + _thread_offset_to_l2], %l2 123 ldd [%o0 + _thread_offset_to_l4], %l4 124 ldd [%o0 + _thread_offset_to_l6], %l6 125 126 /* restore input registers */ 127 ldd [%o0 + _thread_offset_to_i0], %i0 128 ldd [%o0 + _thread_offset_to_i2], %i2 129 ldd [%o0 + _thread_offset_to_i4], %i4 130 ldd [%o0 + _thread_offset_to_i6], %i6 131 132 /* restore output registers */ 133 ldd [%o0 + _thread_offset_to_o6], %o6 134#ifdef CONFIG_THREAD_LOCAL_STORAGE 135 ld [%o0 + _thread_offset_to_tls], %g7 136#endif 137 138 ld [%o0 + _thread_offset_to_psr], %g1 /* %g1 = new thread psr */ 139 140 andn %g1, PSR_CWP, %g1 /* psr without cwp */ 141 or %g1, %g3, %g1 /* psr with new cwp */ 142 wr %g1, %psr /* restore status register and ET */ 143 nop 144 nop 145 nop 146 147 /* jump into thread */ 148 jmp %o7 + 8 149 nop 150 151SECTION_FUNC(TEXT, z_thread_entry_wrapper) 152 mov %g0, %o7 153 mov %i0, %o0 154 mov %i1, %o1 155 mov %i2, %o2 156 mov %i3, %o3 157 call z_thread_entry 158 nop 159