summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/arch/sparc32/switch.S
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/openbios/arch/sparc32/switch.S')
-rw-r--r--qemu/roms/openbios/arch/sparc32/switch.S154
1 files changed, 154 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/sparc32/switch.S b/qemu/roms/openbios/arch/sparc32/switch.S
new file mode 100644
index 000000000..d5b1b659d
--- /dev/null
+++ b/qemu/roms/openbios/arch/sparc32/switch.S
@@ -0,0 +1,154 @@
+#define __ASSEMBLY
+#include "psr.h"
+#include "asm/asi.h"
+#define ASI_BP ASI_M_BYPASS
+#define REGWIN_SZ 0x40
+
+ .globl __switch_context, __switch_context_nosave, __exit_context, halt
+
+ .text
+ .align 4
+
+#define STACKFRAME_SZ 0x60
+
+/* These are just handy. */
+#define _SV save %sp, -STACKFRAME_SZ, %sp
+#define _RS restore
+
+#define FLUSH_ALL_KERNEL_WINDOWS \
+ _SV; _SV; _SV; _SV; _SV; _SV; _SV; \
+ _RS; _RS; _RS; _RS; _RS; _RS; _RS;
+
+/*
+ * Switch execution context
+ * This saves registers in the stack, then
+ * switches the stack, and restores everything from the new stack.
+ * This function takes no argument. New stack pointer is
+ * taken from global variable __context, and old stack pointer
+ * is also saved to __context. This way we can just jump to
+ * this routine to get back to the original context.
+ */
+
+__switch_context:
+ FLUSH_ALL_KERNEL_WINDOWS
+ /* Save everything in stack */
+ st %fp, [%fp + 120 -144]
+ add %fp, -144, %fp
+ st %g1, [%fp + 4]
+ st %g2, [%fp + 8]
+ st %g3, [%fp + 12]
+ st %g4, [%fp + 16]
+ st %g5, [%fp + 20]
+ st %g6, [%fp + 24]
+ st %g7, [%fp + 28]
+
+ st %o0, [%fp + 32]
+ st %o1, [%fp + 36]
+ st %o2, [%fp + 40]
+ st %o3, [%fp + 44]
+ st %o4, [%fp + 48]
+ st %o5, [%fp + 52]
+ st %sp, [%fp + 56]
+ st %o7, [%fp + 60]
+
+ st %l0, [%fp + 64]
+ st %l1, [%fp + 68]
+ st %l2, [%fp + 72]
+ st %l3, [%fp + 76]
+ st %l4, [%fp + 80]
+ st %l5, [%fp + 84]
+ st %l6, [%fp + 88]
+ st %l7, [%fp + 92]
+
+ st %i0, [%fp + 96]
+ st %i1, [%fp + 100]
+ st %i2, [%fp + 104]
+ st %i3, [%fp + 108]
+ st %i4, [%fp + 112]
+ st %i5, [%fp + 116]
+ st %i7, [%fp + 124]
+
+ /* ctx->return_address: Return to caller */
+ st %o7, [%fp + 128]
+
+ /* Interrupts are not allowed... */
+
+ /* Turn on Supervisor, EnableFloating, and all the PIL bits.
+ * Also puts us in register window zero with traps off.
+ */
+#if 0
+ set (PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
+ wr %g2, 0x0, %psr
+#endif
+ set __context, %g1
+ /* Swap ctx pointer with %fp and jump*/
+ ba __set_context
+ swap [%g1], %fp
+__switch_context_nosave:
+ set __context, %g1
+ /* load %fp from ctx pointer */
+ ld [%g1], %fp
+__set_context:
+ /* Load all registers */
+ /* offset 0: %g0, no need to load */
+ ld [%fp + 4], %g1
+ ld [%fp + 8], %g2
+ ld [%fp + 12], %g3
+ ld [%fp + 16], %g4
+ ld [%fp + 20], %g5
+ ld [%fp + 24], %g6
+ ld [%fp + 28], %g7
+
+ /* offset 32: %o0, loaded from ctx->param */
+ ld [%fp + 36], %o1
+ ld [%fp + 40], %o2
+ ld [%fp + 44], %o3
+ ld [%fp + 48], %o4
+ ld [%fp + 52], %o5
+ ld [%fp + 56], %sp
+ /* offset 60: %o7, loaded from ctx->return_addr */
+
+ ld [%fp + 64], %l0
+ ld [%fp + 68], %l1
+ ld [%fp + 72], %l2
+ ld [%fp + 76], %l3
+ ld [%fp + 80], %l4
+ ld [%fp + 84], %l5
+ ld [%fp + 88], %l6
+ ld [%fp + 92], %l7
+
+ ld [%fp + 96], %i0
+ ld [%fp + 100], %i1
+ ld [%fp + 104], %i2
+ ld [%fp + 108], %i3
+ ld [%fp + 112], %i4
+ ld [%fp + 116], %i5
+ ld [%fp + 124], %i7
+
+ /* ctx->return_addr */
+ ld [%fp + 136], %o7
+
+ /* ctx->param */
+ ld [%fp + 140], %o0
+
+ /* ctx->pc, save %g1 to %y and load to %g1 */
+ mov %g1, %y
+ ld [%fp + 128], %g1
+ /* %fp last */
+ ld [%fp + 120], %fp
+ /* Finally, get the new %pc from %g1 and restore %g1*/
+ jmp %g1
+ mov %y, %g1
+
+ FLUSH_ALL_KERNEL_WINDOWS
+__exit_context:
+ /* Get back to the original context */
+ call __switch_context
+ nop
+
+ /* We get here if the other context attempt to switch to this
+ * dead context. This should not happen. */
+
+halt:
+ b halt
+ nop