[kernel] Unittests of kernel stack depth handling

Add a unittest that checks that we can receive interrupts and handle
mp_sync_exec calls when thread context has used up half of the kernel
stack.

The first test is simple and probablistic - it allocates a large
on-stack buffer and waits a bit for any (potential) interrupts. If an
interrupt were to take > 1/2 the kernel stack to handle, we would see an
architecture-specific stack overrun failure.

The second test is deterministic and ensures we can run an mp_sync_exec
handler (that signals an event) while half the kernel stack is used from
thread context by a buffer.

Bug: 64808 Panic on boot on the kasan bringup builder
Change-Id: I40d356197d38e039d999fbe09615921b08ab0a88
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/461084
Reviewed-by: Adrian Danis <adanis@google.com>
Commit-Queue: Venkatesh Srinivas <venkateshs@google.com>
diff --git a/zircon/kernel/tests/BUILD.gn b/zircon/kernel/tests/BUILD.gn
index 5c1131a..043deb8 100644
--- a/zircon/kernel/tests/BUILD.gn
+++ b/zircon/kernel/tests/BUILD.gn
@@ -79,6 +79,7 @@
       "fibo.cc",
       "interrupt_disable_tests.cc",
       "job_tests.cc",
+      "kstack_tests.cc",
       "lock_dep_tests.cc",
       "mem_tests.cc",
       "mmu_tests.cc",
diff --git a/zircon/kernel/tests/kstack_tests.cc b/zircon/kernel/tests/kstack_tests.cc
new file mode 100644
index 0000000..1a5ea9c
--- /dev/null
+++ b/zircon/kernel/tests/kstack_tests.cc
@@ -0,0 +1,122 @@
+// Copyright 2020 The Fuchsia Authors
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+
+#include <lib/unittest/unittest.h>
+
+#include <kernel/auto_preempt_disabler.h>
+#include <kernel/event.h>
+#include <kernel/mp.h>
+#include <kernel/thread.h>
+#include <lib/arch/intrin.h>
+
+namespace {
+
+// Test that the kernel is able to handle interrupts when half the kernel stack is used.
+bool kstack_interrupt_depth_test() {
+  BEGIN_TEST;
+  constexpr size_t kSize = DEFAULT_STACK_SIZE / 2;
+  volatile uint8_t buffer[kSize] = {};
+
+  // Spin for a bit while we have a large, active buffer on the kernel stack.
+  // This gives us a window for interrupts to occur; any that do will have to make do with half
+  // the stack consumed.
+  int i = 0;
+  zx_time_t now = current_time();
+  while (current_time() < now + ZX_MSEC(100)) {
+    buffer[i++ % kSize] = buffer[0];  // Touch the buffer to ensure it is not optimized out
+    arch::Yield();
+  }
+
+  END_TEST;
+}
+
+#if __has_feature(safe_stack)
+__attribute__((no_sanitize("safe-stack")))
+bool kstack_interrupt_depth_test_no_safestack() {
+  BEGIN_TEST;
+  constexpr size_t kSize = DEFAULT_STACK_SIZE / 2;
+  volatile uint8_t buffer[kSize] = {};
+
+  // Spin for a bit while we have a large, active buffer on the kernel stack.
+  // Just like the above test, though with safe-stack disabled.
+  int i = 0;
+  zx_time_t now = current_time();
+  while (current_time() < now + ZX_MSEC(100)) {
+    buffer[i++ % kSize] = buffer[0];  // Touch the buffer to ensure it is not optimized out
+    arch::Yield();
+  }
+
+  END_TEST;
+}
+#endif
+
+static unsigned get_num_cpus_online() {
+  unsigned count = 0;
+  cpu_mask_t online = mp_get_online_mask();
+  while (online) {
+    online >>= 1;
+    ++count;
+  }
+  return count;
+}
+
+struct context {
+  cpu_num_t cpu_to_wake;
+  ktl::atomic<bool> cpu_to_wake_ok;
+  ktl::atomic<bool> wake;
+};
+static int waiter_thread(void* arg) {
+  constexpr size_t kSize = DEFAULT_STACK_SIZE / 2;
+  volatile uint8_t buffer[kSize] = {};
+
+  context* const deferred = reinterpret_cast<context*>(arg);
+  AutoPreemptDisabler<APDInitialState::PREEMPT_DISABLED> preempt_disable;
+  // Lock our thread to the current CPU.
+  Thread::Current::Get()->SetCpuAffinity(cpu_num_to_mask(arch_curr_cpu_num()));
+  deferred->cpu_to_wake = arch_curr_cpu_num();
+  deferred->cpu_to_wake_ok.store(true);
+  buffer[1] = buffer[0];  // Touch the buffer to ensure it is not optimized out
+  for (;;) {
+    // Wait for a bit while we have a large, active buffer on the kernel stack.
+    // mp_sync_exec()'s callback will run on our CPU; we want to check that it succeeds even when
+    // kSize bytes of kernel stack space are consumed by (this) thread context.
+    if (deferred->wake.load() == true)
+      break;
+    arch::Yield();
+  }
+
+  return 0;
+}
+// Test that we can handle an mp_sync_exec callback while half the kernel stack is used.
+bool kstack_mp_sync_exec_test() {
+  BEGIN_TEST;
+  // We need 2 or more CPUs for this test.
+  if (get_num_cpus_online() < 2) {
+    printf("not enough online cpus\n");
+    END_TEST;
+  }
+
+  context deferred = { };
+  Thread* const thread = Thread::CreateEtc(nullptr, "waiter", waiter_thread, &deferred,
+                                           DEFAULT_PRIORITY, nullptr);
+  thread->Resume();
+  while (deferred.cpu_to_wake_ok.load() == false);
+  mp_sync_exec(MP_IPI_TARGET_MASK, cpu_num_to_mask(deferred.cpu_to_wake),
+               [](void* arg) { reinterpret_cast<ktl::atomic<bool>*>(arg)->store(true); }, &deferred.wake);
+
+  thread->Join(nullptr, ZX_TIME_INFINITE);
+  END_TEST;
+}
+
+}  // anonymous namespace
+
+UNITTEST_START_TESTCASE(kstack_tests)
+UNITTEST("kstack-interrupt-depth", kstack_interrupt_depth_test)
+#if __has_feature(safe_stack)
+UNITTEST("kstack-interrupt-depth-no-safestack", kstack_interrupt_depth_test_no_safestack)
+#endif
+UNITTEST("kstack-mp-sync-exec", kstack_mp_sync_exec_test)
+UNITTEST_END_TESTCASE(kstack_tests, "kstack", "kernel stack tests")