[OpenBIOS] [PATCH] SPARC32: implement full context switch logic in switch_to()

Mark Cave-Ayland mark.cave-ayland at ilande.co.uk
Sun Aug 21 17:13:21 CEST 2016


Make sure that the whole CPU state is saved/restored during switch_to()
to enable a clean context switch and return. Since we have this logic we
can also re-use it for initial entry into OpenBIOS.

Finally also update context.h to reflect the new CPU state stucture.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland at ilande.co.uk>
---
 arch/sparc32/boot.h     |    2 +-
 arch/sparc32/context.c  |    8 +-
 arch/sparc32/context.h  |    6 +-
 arch/sparc32/cpustate.h |  160 ++++++++++++++++++++++++++++++++++++++++
 arch/sparc32/entry.S    |   26 ++++++-
 arch/sparc32/switch.S   |  187 +++++++++++++++--------------------------------
 6 files changed, 249 insertions(+), 140 deletions(-)
 create mode 100644 arch/sparc32/cpustate.h

diff --git a/arch/sparc32/boot.h b/arch/sparc32/boot.h
index 55e391a..9bfc07e 100644
--- a/arch/sparc32/boot.h
+++ b/arch/sparc32/boot.h
@@ -10,7 +10,7 @@
 int linux_load(struct sys_info *info, const char *file, const char *cmdline);
 
 // context.c
-extern struct context *__context;
+extern struct context * volatile __context;
 unsigned int start_elf(unsigned long entry_point, unsigned long param);
 
 // boot.c
diff --git a/arch/sparc32/context.c b/arch/sparc32/context.c
index d4d8530..555f628 100644
--- a/arch/sparc32/context.c
+++ b/arch/sparc32/context.c
@@ -24,7 +24,6 @@ void __exit_context(void); /* assembly routine */
  * to start us up.
  */
 static struct context main_ctx = {
-    .regs[REG_SP] = (uint32_t) &_estack - 96,
     .pc = (uint32_t) start_main,
     .npc = (uint32_t) start_main + 4,
     .return_addr = (uint32_t) __exit_context,
@@ -32,7 +31,7 @@ static struct context main_ctx = {
 
 /* This is used by assembly routine to load/store the context which
  * it is to switch/switched.  */
-struct context *__context = &main_ctx;
+struct context * volatile __context = &main_ctx;
 
 /* Stack for loaded ELF image */
 static uint8_t image_stack[IMAGE_STACK_SIZE];
@@ -66,7 +65,8 @@ init_context(uint8_t *stack, uint32_t stack_size, int num_params)
 
     ctx = (struct context *)
 	(stack + stack_size - (sizeof(*ctx) + num_params*sizeof(uint32_t)));
-    memset(ctx, 0, sizeof(*ctx));
+    /* Use valid window state from startup */
+    memcpy(ctx, &main_ctx, sizeof(struct context));
 
     /* Fill in reasonable default for flat memory model */
     ctx->regs[REG_SP] = virt_to_phys(SP_LOC(ctx));
@@ -106,7 +106,7 @@ unsigned int start_elf(unsigned long entry_point, unsigned long param)
 
     ctx = init_context(image_stack, sizeof image_stack, 1);
     ctx->pc = entry_point;
-    ctx->param[0] = param;
+    ctx->regs[REG_O0] = param;
 
     ctx = switch_to(ctx);
     return ctx->regs[REG_O0];
diff --git a/arch/sparc32/context.h b/arch/sparc32/context.h
index 8689d56..ef454d0 100644
--- a/arch/sparc32/context.h
+++ b/arch/sparc32/context.h
@@ -3,11 +3,11 @@
 
 struct context {
     /* General registers */
-    uint32_t regs[32];
+    uint32_t regs[148];
     uint32_t pc;
     uint32_t npc;
-#define REG_O0 8
-#define REG_SP 14
+#define REG_O0 12
+#define REG_SP 18
 #define SP_LOC(ctx) (&(ctx)->regs[REG_SP])
     /* Flags */
     /* Optional stack contents */
diff --git a/arch/sparc32/cpustate.h b/arch/sparc32/cpustate.h
new file mode 100644
index 0000000..1dab6ad
--- /dev/null
+++ b/arch/sparc32/cpustate.h
@@ -0,0 +1,160 @@
+/*
+ *   Save/restore CPU state macros
+ *
+ *   Copyright (C) 2016 Mark Cave-Ayland (mark.cave-ayland at ilande.co.uk>)
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   version 2
+ *
+ */
+
+#include "autoconf.h"
+
+#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;
+
+
+#define SAVE_CPU_GENERAL_STATE(type) \
+        /* Save general state into context at %g1 */ \
+        rd      %psr, %g4; \
+        st      %g4, [%g1]; \
+        rd      %wim, %g4; \
+        st      %g4, [%g1 + 0x4];
+
+#define SAVE_CPU_WINDOW_STATE(type) \
+        /* Save window state into context at %g1 */ \
+        st      %o0, [%g1 + 0x30]; \
+        st      %o1, [%g1 + 0x34]; \
+        st      %o2, [%g1 + 0x38]; \
+        st      %o3, [%g1 + 0x3c]; \
+        st      %o4, [%g1 + 0x40]; \
+        st      %o5, [%g1 + 0x44]; \
+        st      %o6, [%g1 + 0x48]; \
+        st      %o7, [%g1 + 0x4c]; \
+        \
+        set     nwindows, %g6; \
+        ld      [%g6], %g6;         /* nwindows */ \
+        mov     %g6, %g5; \
+        sub     %g5, 1, %g5;        /* mask */ \
+        \
+        rd      %psr, %g4; \
+        and     %g4, %g5, %g4;      /* window */ \
+        \
+        rd      %psr, %g3; \
+        srl     %g3, 5, %g3; \
+        sll     %g3, 5, %g3;        /* psr hi */ \
+        \
+        mov     %g1, %g2; \
+        add     %g2, 0x50, %g2; \
+        \
+save_cpu_window_##type: \
+        mov     %g3, %g7; \
+        or      %g7, %g4, %g7; \
+        wr      %g7, %psr; \
+        \
+        st      %l0, [%g2]; \
+        st      %l1, [%g2 + 0x4]; \
+        st      %l2, [%g2 + 0x8]; \
+        st      %l3, [%g2 + 0xc]; \
+        st      %l4, [%g2 + 0x10]; \
+        st      %l5, [%g2 + 0x14]; \
+        st      %l6, [%g2 + 0x18]; \
+        st      %l7, [%g2 + 0x1c]; \
+        st      %i0, [%g2 + 0x20]; \
+        st      %i1, [%g2 + 0x24]; \
+        st      %i2, [%g2 + 0x28]; \
+        st      %i3, [%g2 + 0x2c]; \
+        st      %i4, [%g2 + 0x30]; \
+        st      %i5, [%g2 + 0x34]; \
+        st      %i6, [%g2 + 0x38]; \
+        st      %i7, [%g2 + 0x3c]; \
+        dec     %g4; \
+        and     %g4, %g5, %g4; \
+        subcc   %g6, 1, %g6; \
+        bne     save_cpu_window_##type; \
+         add    %g2, 0x40, %g2; \
+        \
+        /* Get back to the correct window */ \
+        ld      [%g1], %g2; \
+        wr      %g2, %psr;
+
+#define SAVE_CPU_STATE(type) \
+        SAVE_CPU_GENERAL_STATE(type); \
+        SAVE_CPU_WINDOW_STATE(type);
+        
+
+#define RESTORE_CPU_GENERAL_STATE(type) \
+        /* Restore window state from context at %g1 */ \
+        ld      [%g1], %g2; \
+        wr      %g2, %psr; \
+        ld      [%g1 + 0x4], %g2; \
+        wr      %g2, %wim;
+        
+#define RESTORE_CPU_WINDOW_STATE(type) \
+        /* Restore window state from context at %g1 */ \
+        set     nwindows, %g6; \
+        ld      [%g6], %g6;         /* nwindows */ \
+        mov     %g6, %g5; \
+        sub     %g5, 1, %g5;        /* mask */ \
+        \
+        rd      %psr, %g4; \
+        and     %g4, %g5, %g4;      /* window */ \
+        \
+        rd      %psr, %g3; \
+        srl     %g3, 5, %g3; \
+        sll     %g3, 5, %g3;        /* psr hi */ \
+        \
+        mov     %g1, %g2; \
+        add     %g2, 0x50, %g2; \
+        \
+restore_cpu_window_##type: \
+        mov     %g3, %g7; \
+        or      %g7, %g4, %g7; \
+        wr      %g7, %psr; \
+        \
+        ld      [%g2], %l0; \
+        ld      [%g2 + 0x4], %l1; \
+        ld      [%g2 + 0x8], %l2; \
+        ld      [%g2 + 0xc], %l3; \
+        ld      [%g2 + 0x10], %l4; \
+        ld      [%g2 + 0x14], %l5; \
+        ld      [%g2 + 0x18], %l6; \
+        ld      [%g2 + 0x1c], %l7; \
+        ld      [%g2 + 0x20], %i0; \
+        ld      [%g2 + 0x24], %i1; \
+        ld      [%g2 + 0x28], %i2; \
+        ld      [%g2 + 0x2c], %i3; \
+        ld      [%g2 + 0x30], %i4; \
+        ld      [%g2 + 0x34], %i5; \
+        ld      [%g2 + 0x38], %i6; \
+        ld      [%g2 + 0x3c], %i7; \
+        dec     %g4; \
+        and     %g4, %g5, %g4; \
+        subcc   %g6, 1, %g6; \
+        bne     restore_cpu_window_##type; \
+         add    %g2, 0x40, %g2; \
+        \
+        /* Get back to the correct window */ \
+        ld      [%g1], %g2; \
+        wr      %g2, %psr; \
+        \
+        ld      [%g1 + 0x30], %o0; \
+        ld      [%g1 + 0x34], %o1; \
+        ld      [%g1 + 0x38], %o2; \
+        ld      [%g1 + 0x3c], %o3; \
+        ld      [%g1 + 0x40], %o4; \
+        ld      [%g1 + 0x44], %o5; \
+        ld      [%g1 + 0x48], %o6; \
+        ld      [%g1 + 0x4c], %o7;
+
+#define RESTORE_CPU_STATE(type) \
+        RESTORE_CPU_GENERAL_STATE(type); \
+        RESTORE_CPU_WINDOW_STATE(type);
diff --git a/arch/sparc32/entry.S b/arch/sparc32/entry.S
index 72cb338..82aa88e 100644
--- a/arch/sparc32/entry.S
+++ b/arch/sparc32/entry.S
@@ -11,6 +11,7 @@
 #include "psr.h"
 #include "asm/asi.h"
 #include "asm/crs.h"
+#include "cpustate.h"
 #define NO_QEMU_PROTOS
 #define NO_OPENBIOS_PROTOS
 #include "arch/common/fw_cfg.h"
@@ -30,7 +31,7 @@
 
 #define WRITE_PAUSE    nop; nop; nop; /* Have to do this after %wim/%psr chg */
 
-        .globl	entry, _entry
+        .globl	entry, _entry, nwindows
 
 	.section ".text", "ax"
 	.align	8
@@ -69,6 +70,9 @@
 	 * Bottom
 	 */
 
+nwindows:
+        .word 0
+        
 /*
  * Entry point
  * We start execution from here.
@@ -460,6 +464,9 @@ highmem:
         wr      %g1, 0x0, %wim                  ! make window 1 invalid
         WRITE_PAUSE
 
+        set     nwindows, %g2                   ! store nwindows
+        st      %g3, [%g2]
+        
         cmp     %g3, 0x7
         bne     1f
          nop
@@ -490,8 +497,21 @@ highmem:
         wr	%g3, PSR_ET, %psr
         WRITE_PAUSE
 
-        set     0, %fp
-        call    __switch_context_nosave
+        /* Set up a default context */
+        set     __context, %g1
+        ld      [%g1], %g1
+        
+        SAVE_CPU_GENERAL_STATE(entry)
+        SAVE_CPU_WINDOW_STATE(entry)
+        
+        /* Set up local stack pointer */
+        set     _estack - 0x40, %sp
+        
+        /* And for the main context */
+        add     %sp, -0x260, %g2 
+        st      %g2, [%g1 + 0x48]
+
+        call    __switch_context
          nop
 
         /* We get here when the main context switches back to
diff --git a/arch/sparc32/switch.S b/arch/sparc32/switch.S
index d5b1b65..e4dbc9a 100644
--- a/arch/sparc32/switch.S
+++ b/arch/sparc32/switch.S
@@ -1,23 +1,14 @@
 #define __ASSEMBLY
 #include "psr.h"
 #include "asm/asi.h"
+#include "cpustate.h"
 #define ASI_BP ASI_M_BYPASS
 #define REGWIN_SZ   0x40
 
-	.globl	__switch_context, __switch_context_nosave, __exit_context, halt
+        .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;
+        .text
+        .align 4
 
 /*
  * Switch execution context
@@ -31,124 +22,62 @@
 
 __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]
+        
+        /* Save everything in stack */        
+        st      %g1, [%sp - 0x260 + 0x14]
+        st      %g2, [%sp - 0x260 + 0x18]
+        st      %g3, [%sp - 0x260 + 0x1c]
+        st      %g4, [%sp - 0x260 + 0x20]
+        st      %g5, [%sp - 0x260 + 0x24]
+        st      %g6, [%sp - 0x260 + 0x28]
+        st      %g7, [%sp - 0x260 + 0x2c]
+
+        mov     %sp, %g1
+        add     %g1, -0x260, %g1
+        
+        SAVE_CPU_STATE(switch)
+
+        /* Return PC value */
+        mov     %o7, %g2
+        add     %g2, 4, %g2
+        st      %g2, [%sp - 0x260 + 0x250]
+
+        /* swap context */
+        set     __context, %g3
+        ld      [%g3], %g2
+        st      %g1, [%g3]
+        mov     %g2, %g1
+        
+        ba      __set_context
+         nop
 
-        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:
+        FLUSH_ALL_KERNEL_WINDOWS
+        
         set     __context, %g1
-        /* load %fp from ctx pointer */
-        ld      [%g1], %fp
+        ld      [%g1], %g1
+        
 __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
+        RESTORE_CPU_STATE(switch)
+        
+        /* Restore globals */
+        mov     %g1, %g2
+        add     %g2, 0x14, %g2
+        st      %g2, [%sp - 96]
+        
+        ld      [%g1 + 0x18], %g2
+        ld      [%g1 + 0x1c], %g3
+        ld      [%g1 + 0x20], %g4
+        ld      [%g1 + 0x24], %g5
+        ld      [%g1 + 0x28], %g6
+        ld      [%g1 + 0x2c], %g7
+
+        /* Finally, load new %pc */
+        ld      [%g1 + 0x250], %g1
+        jmpl    %g1, %o7
+         ld     [%sp - 96], %g1
+        
 __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
+        /* Get back to the original context */
+        ba    __switch_context
+         nop
-- 
1.7.10.4




More information about the OpenBIOS mailing list