[MIPS] Add support for thread_suspension test

This change:

- implements NaClSignalContextUnsetClobberedRegisters() for MIPS,
- modifies Unwind for thread suspension to match what is needed for MIPS,
- adds MIPS-arch specific parts in suspend_test_guest.c and
  suspend_test_host.c.

BUG= run_thread_suspension_test fails for MIPS
TEST= run run_thread_suspension_test on MIPS-board

Review URL: https://codereview.chromium.org/121833002

git-svn-id: svn://svn.chromium.org/native_client/trunk/src/native_client@12571 fcba33aa-ac0c-11dd-b9e7-8d5594d729c2
diff --git a/src/trusted/service_runtime/arch/mips/nacl_syscall.S b/src/trusted/service_runtime/arch/mips/nacl_syscall.S
index 24e4b5f..8347d8d 100644
--- a/src/trusted/service_runtime/arch/mips/nacl_syscall.S
+++ b/src/trusted/service_runtime/arch/mips/nacl_syscall.S
@@ -108,6 +108,7 @@
   sw $t8, NACL_THREAD_CONTEXT_OFFSET_T8($a0)
   sw $sp, NACL_THREAD_CONTEXT_OFFSET_STACK_PTR($a0)
   sw $fp, NACL_THREAD_CONTEXT_OFFSET_FRAME_PTR($a0)
+DEFINE_GLOBAL_HIDDEN_IDENTIFIER(NaClSyscallSegRegsSaved):
 
   /* Restore the trusted stack */
   lw $sp, NACL_THREAD_CONTEXT_OFFSET_TRUSTED_STACK_PTR($a0)
diff --git a/src/trusted/service_runtime/arch/mips/sel_rt.c b/src/trusted/service_runtime/arch/mips/sel_rt.c
index b549662..adadca9 100644
--- a/src/trusted/service_runtime/arch/mips/sel_rt.c
+++ b/src/trusted/service_runtime/arch/mips/sel_rt.c
@@ -83,3 +83,27 @@
   sig_ctx->prog_ctr    = th_ctx->new_prog_ctr;
   sig_ctx->return_addr = 0;
 }
+
+
+void NaClSignalContextUnsetClobberedRegisters(
+    struct NaClSignalContext *sig_ctx) {
+  sig_ctx->zero        = 0;
+  sig_ctx->at          = 0;
+  sig_ctx->v0          = 0;
+  sig_ctx->v1          = 0;
+  sig_ctx->a0          = 0;
+  sig_ctx->a1          = 0;
+  sig_ctx->a2          = 0;
+  sig_ctx->a3          = 0;
+  sig_ctx->t0          = 0;
+  sig_ctx->t1          = 0;
+  sig_ctx->t2          = 0;
+  sig_ctx->t3          = 0;
+  sig_ctx->t4          = 0;
+  sig_ctx->t5          = 0;
+  sig_ctx->t9          = 0;
+  sig_ctx->k0          = 0;
+  sig_ctx->k1          = 0;
+  sig_ctx->global_ptr  = 0;
+  sig_ctx->return_addr = 0;
+}
diff --git a/src/trusted/service_runtime/thread_suspension_unwind.c b/src/trusted/service_runtime/thread_suspension_unwind.c
index 22f82f2..0bb274d 100644
--- a/src/trusted/service_runtime/thread_suspension_unwind.c
+++ b/src/trusted/service_runtime/thread_suspension_unwind.c
@@ -15,9 +15,6 @@
 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
 
 
-#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 || \
-    NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
-
 static void GetNaClSyscallSeg(struct NaClApp *nap,
                               uintptr_t *nacl_syscall_seg,
                               uintptr_t *nacl_syscall_seg_regs_saved) {
@@ -78,6 +75,13 @@
     regs->prog_ctr = NaClSandboxCodeAddr(nap, regs->lr);
     return 1;
   }
+#elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
+  if (regs->prog_ctr >= NACL_TRAMPOLINE_START &&
+      regs->prog_ctr < NACL_TRAMPOLINE_END) {
+    *unwind_case = NACL_UNWIND_in_trampoline;
+    regs->prog_ctr = NaClSandboxCodeAddr(nap, regs->return_addr);
+    return 1;
+  }
 #endif
 
 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
@@ -113,15 +117,19 @@
   if (regs->prog_ctr >= nacl_syscall_seg &&
       regs->prog_ctr < nacl_syscall_seg_regs_saved) {
     *unwind_case = NACL_UNWIND_in_syscallseg;
-    if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86) {
-      if (NACL_BUILD_SUBARCH == 32) {
-        /* Pop user + trampoline return addresses */
-        regs->stack_ptr += 4 + 8;
-      } else {
-        /* Pop user return address. */
-        regs->stack_ptr += 8;
-      }
-    }
+#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
+    /* Pop user + trampoline return addresses */
+    regs->stack_ptr += 4 + 8;
+#elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
+    /* Pop user return address. */
+    regs->stack_ptr += 8;
+#elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
+    /*
+     * On MIPS, $ra is not modified from the start of the trampoline to the
+     * point where registers are saved.
+     */
+    regs->prog_ctr = NaClSandboxCodeAddr(nap, regs->return_addr);
+#endif
     return 1;
   }
 
@@ -146,7 +154,10 @@
 
   if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 ||
       (NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm &&
-       *unwind_case != NACL_UNWIND_in_trampoline)) {
+       *unwind_case != NACL_UNWIND_in_trampoline) ||
+      (NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips &&
+       *unwind_case != NACL_UNWIND_in_trampoline &&
+       *unwind_case != NACL_UNWIND_in_syscallseg)) {
     /*
      * Read the return address from the untrusted stack.
      * NaClCopyInFromUser() can fault or return an error here, but only
@@ -164,16 +175,3 @@
     regs->prog_ctr = NaClSandboxCodeAddr(natp->nap, user_ret);
   }
 }
-
-#else
-
-/* TODO(mseaborn): Extend this to handle MIPS. */
-void NaClGetRegistersForContextSwitch(struct NaClAppThread *natp,
-                                      struct NaClSignalContext *regs,
-                                      enum NaClUnwindCase *unwind_case) {
-  UNREFERENCED_PARAMETER(natp);
-  UNREFERENCED_PARAMETER(regs);
-  *unwind_case = 0;
-}
-
-#endif
diff --git a/tests/thread_suspension/suspend_test_guest.c b/tests/thread_suspension/suspend_test_guest.c
index 2d2f847..1773ed5 100644
--- a/tests/thread_suspension/suspend_test_guest.c
+++ b/tests/thread_suspension/suspend_test_guest.c
@@ -105,6 +105,19 @@
       "str r0, [r0]\n"
       "spin_instruction:\n"
       "b spin_instruction\n");
+#elif defined(__mips__)
+  regs->a0 = (uintptr_t) test_shm;
+  ASM_WITH_REGS(
+      regs,
+      /* Align to ensure no NOPs are inserted in the code that follows. */
+      ".p2align 4\n"
+      /* Set "test_shm->var = test_shm" to indicate that we are ready. */
+      "and $a0, $a0, $t7\n"
+      "sw $a0, 0($a0)\n"
+      ".global spin_instruction\n"
+      "spin_instruction:\n"
+      "b spin_instruction\n"
+      "nop\n");
 #else
 # error Unsupported architecture
 #endif
@@ -161,6 +174,15 @@
         &call_regs,
         "bic r1, r1, #0xf000000f\n"
         "bx r1\n");
+#elif defined(__mips__)
+    call_regs.a0 = (uintptr_t) test_shm;  /* Set syscall argument */
+    call_regs.t9 = syscall_addr;  /* Scratch register */
+    call_regs.return_addr = (uintptr_t) ContinueAfterSyscall;
+    ASM_WITH_REGS(
+        &call_regs,
+        "and $t9, $t9, $t6\n"
+        "jr $t9\n"
+        "nop\n");
 #else
 # error Unsupported architecture
 #endif
@@ -193,6 +215,17 @@
         "bic r4, r4, #0xc000000f\n"
         "bx r4\n"
         ".popsection\n");
+#elif defined(__mips__)
+__asm__(".pushsection .text, \"ax\", %progbits\n"
+        ".p2align 4\n"
+        ".global SyscallReturnAddress\n"
+        "SyscallReturnAddress:\n"
+        "lui   $ra, %hi(SyscallReturnAddress)\n"
+        "addiu $ra, $ra, %lo(SyscallReturnAddress)\n"
+        "and $s0, $s0, $t6\n"
+        "jr $s0\n"
+        "nop\n"
+        ".popsection\n");
 #else
 # error Unsupported architecture
 #endif
@@ -220,6 +253,8 @@
   regs->r12 = syscall_addr;
 #elif defined(__arm__)
   regs->r4 = syscall_addr;
+#elif defined(__mips__)
+  regs->s0 = syscall_addr;
 #else
 # error Unsupported architecture
 #endif
diff --git a/tests/thread_suspension/suspend_test_host.c b/tests/thread_suspension/suspend_test_host.c
index 445ce8b..aacd272 100644
--- a/tests/thread_suspension/suspend_test_host.c
+++ b/tests/thread_suspension/suspend_test_host.c
@@ -339,6 +339,38 @@
   regs.r12 = 0x1234000c;
   /* Leave sp (r13) and lr (r14) alone for now. */
   regs.prog_ctr = test_shm->continue_after_suspension_func;
+#elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
+  /* Skip setting zero register because it's read-only. */
+  regs.at = 0x12340001;
+  regs.v0 = 0x12340002;
+  regs.v1 = 0x12340003;
+  regs.a0 = 0x12340004;
+  regs.a1 = 0x12340005;
+  regs.a2 = 0x12340006;
+  regs.a3 = 0x12340007;
+  regs.t0 = 0x12340008;
+  regs.t1 = 0x12340009;
+  regs.t2 = 0x12340010;
+  regs.t3 = 0x12340011;
+  regs.t4 = 0x12340012;
+  regs.t5 = 0x12340013;
+  /* Skip setting t6 and t7 because those are mask registers. */
+  regs.s0 = 0x12340014;
+  regs.s1 = 0x12340015;
+  regs.s2 = 0x12340016;
+  regs.s3 = 0x12340017;
+  regs.s4 = 0x12340018;
+  regs.s5 = 0x12340019;
+  regs.s6 = 0x12340020;
+  regs.s7 = 0x12340021;
+  /* Skip setting t8 because it holds thread pointer. */
+  regs.t9 = 0x12340022;
+  /* Skip setting k0 and k1 registers, they are used by kernel. */
+  regs.global_ptr  = 0x12340023;
+  /* Skip setting sp also. */
+  regs.frame_ptr   = 0x12340025;
+  regs.return_addr = 0x12340026;
+  regs.prog_ctr = test_shm->continue_after_suspension_func;
 #else
 # error Unsupported architecture
 #endif
@@ -431,15 +463,7 @@
       regs.prog_ctr = test_shm->expected_regs.prog_ctr;
       RegsUnsetNonCalleeSavedRegisters(&regs);
     }
-    /*
-     * TODO(mseaborn): Enable the RegsAssertEqual() check for MIPS
-     * once NaClGetRegistersForContextSwitch() is implemented for
-     * MIPS.
-     */
-    if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 ||
-        NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm) {
-      RegsAssertEqual(&regs, &test_shm->expected_regs);
-    }
+    RegsAssertEqual(&regs, &test_shm->expected_regs);
 
     NaClUntrustedThreadsResumeAll(nap);
   }