Add ecall/mret test

Change-Id: I5c1bfa67843f3ef03bcfe308db564dadfb8b713f
diff --git a/tests/kelvin_isa/BUILD b/tests/kelvin_isa/BUILD
index 1584d83..5861d6a 100644
--- a/tests/kelvin_isa/BUILD
+++ b/tests/kelvin_isa/BUILD
@@ -36,6 +36,17 @@
 )
 
 kelvin_test(
+    name = "ecall",
+    srcs = [
+        "ecall.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+)
+
+kelvin_test(
     name = "acset",
     srcs = [
         "acset.cc",
diff --git a/tests/kelvin_isa/ecall.cc b/tests/kelvin_isa/ecall.cc
new file mode 100644
index 0000000..55ba750
--- /dev/null
+++ b/tests/kelvin_isa/ecall.cc
@@ -0,0 +1,86 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+static volatile uint32_t isr_ok = 0;
+
+extern "C" {
+
+void isr_wrapper(void);
+__attribute__((naked)) void isr_wrapper() {
+  asm volatile(
+      "csrr t0, mepc \n"
+      "addi t0, t0, 4 \n"
+      "csrw mepc, t0 \n"
+      "csrr t0, mcause \n"
+      "li t1, 11 \n"
+      "bne t0, t1, 0f \n"
+      "lw t0, 0(%[isr_ok]) \n"
+      "addi t0, t0, 1 \n"
+      "sw t0, 0(%[isr_ok]) \n"
+      "0: mret \n"
+      : /* outputs */
+      : /* inputs */[isr_ok] "r"(&isr_ok));
+}
+
+void bad_isr(void);
+__attribute__((naked)) void bad_isr() { asm volatile("ebreak \n"); }
+
+__attribute__((naked, aligned(256))) void isr_vector_table() {
+  asm volatile(
+      "j isr_wrapper \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n"
+      "j bad_isr \n");
+}
+
+int main(int argc, char** argv) {
+  asm volatile("csrw mtvec, %0" ::"rK"((uint32_t)(&isr_vector_table)));
+  asm volatile("ecall");
+  if (isr_ok == 0) {
+    asm volatile("ebreak");
+  }
+
+  return 0;
+}
+}