Allows setting a core version from command line and renode properties.
The version is specified as an integer, with 100 = 1.00, 150 = 1.50, etc.

Behavior of jal/jalr are changed for core versions greater than 100 (default).
In higher versions, when the destination register is not "ra", then an
unsealed return capability is returned as opposed to a sealed one.

PiperOrigin-RevId: 702487323
Change-Id: I856b7bcf0b1fb6da694486d6de8d0dde41de7029
diff --git a/cheriot/BUILD b/cheriot/BUILD
index 365d1c2..b574d8d 100644
--- a/cheriot/BUILD
+++ b/cheriot/BUILD
@@ -47,6 +47,11 @@
     values = {"cpu": "aarch64"},
 )
 
+config_setting(
+    name = "darwin_aarch64_cpu",
+    values = {"cpu": "darwin_arm64"},
+)
+
 mpact_isa_decoder(
     name = "riscv_cheriot_isa",
     src = "riscv_cheriot.isa",
@@ -266,12 +271,16 @@
         "riscv_cheriot_vector_reduction_instructions.h",
         "riscv_cheriot_vector_unary_instructions.h",
     ],
-    copts = [
-        "-O3",
-        "-ffp-model=strict",
-    ] + select({
-        "darwin_arm64_cpu": [],
-        "//conditions:default": ["-fprotect-parens"],
+    copts = select({
+        "darwin_aarch64_cpu": [
+            "-O3",
+            "-ffp-model=strict",
+        ],
+        "//conditions:default": [
+            "-O3",
+            "-ffp-model=strict",
+            "-fprotect-parens",
+        ],
     }),
     deps = [
         ":cheriot_state",
@@ -304,12 +313,16 @@
         "riscv_cheriot_vector_fp_reduction_instructions.h",
         "riscv_cheriot_vector_fp_unary_instructions.h",
     ],
-    copts = [
-        "-O3",
-        "-ffp-model=strict",
-    ] + select({
-        "darwin_arm64_cpu": [],
-        "//conditions:default": ["-fprotect-parens"],
+    copts = select({
+        "darwin_aarch64_cpu": [
+            "-O3",
+            "-ffp-model=strict",
+        ],
+        "//conditions:default": [
+            "-O3",
+            "-ffp-model=strict",
+            "-fprotect-parens",
+        ],
     }),
     deps = [
         ":cheriot_state",
diff --git a/cheriot/cheriot_register.cc b/cheriot/cheriot_register.cc
index d964e33..9ab85ef 100644
--- a/cheriot/cheriot_register.cc
+++ b/cheriot/cheriot_register.cc
@@ -315,11 +315,11 @@
   if (is_null_ || !tag()) return false;
 
   if (HasPermission(kPermitExecute)) {
-    return (object_type() == kSentry) ||
-           (object_type() == kInterruptEnablingSentry) ||
-           (object_type() == kInterruptDisablingSentry) ||
-           (object_type() == kInterruptEnablingReturnSentry) ||
-           (object_type() == kInterruptDisablingReturnSentry) ||
+    return (object_type() == kInterruptInheritingSentry) ||
+           (object_type() == kInterruptEnablingForwardSentry) ||
+           (object_type() == kInterruptDisablingForwardSentry) ||
+           (object_type() == kInterruptEnablingBackwardSentry) ||
+           (object_type() == kInterruptDisablingBackwardSentry) ||
            (object_type() == kSealedExecutable6) ||
            (object_type() == kSealedExecutable7);
   } else {
@@ -357,11 +357,11 @@
   if (status.ok()) {
     if (HasPermission(PermissionBits::kPermitExecute)) {
       switch (obj_type) {
-        case kSentry:
-        case kInterruptEnablingSentry:
-        case kInterruptDisablingSentry:
-        case kInterruptEnablingReturnSentry:
-        case kInterruptDisablingReturnSentry:
+        case kInterruptInheritingSentry:
+        case kInterruptEnablingForwardSentry:
+        case kInterruptDisablingForwardSentry:
+        case kInterruptEnablingBackwardSentry:
+        case kInterruptDisablingBackwardSentry:
         case kSealedExecutable6:
         case kSealedExecutable7:
           // The sealing type is ok.
@@ -433,13 +433,13 @@
 }
 
 bool CheriotRegister::IsSentry() const {
-  return !is_null_ && (object_type() >= kSentry) &&
-         (object_type() <= kInterruptEnablingReturnSentry);
+  return !is_null_ && (object_type() >= kInterruptInheritingSentry) &&
+         (object_type() <= kInterruptEnablingBackwardSentry);
 }
 
 bool CheriotRegister::IsBackwardSentry() const {
-  return !is_null_ && ((object_type() == kInterruptEnablingReturnSentry) ||
-                       (object_type() == kInterruptDisablingReturnSentry));
+  return !is_null_ && ((object_type() == kInterruptEnablingBackwardSentry) ||
+                       (object_type() == kInterruptDisablingBackwardSentry));
 }
 
 void CheriotRegister::CopyFrom(const CheriotRegister &other) {
diff --git a/cheriot/cheriot_register.h b/cheriot/cheriot_register.h
index c62ddbb..647b58c 100644
--- a/cheriot/cheriot_register.h
+++ b/cheriot/cheriot_register.h
@@ -69,11 +69,11 @@
   // Special object types.
   enum ObjectType : uint32_t {
     kUnsealed = 0,
-    kSentry = 1,
-    kInterruptDisablingSentry = 2,
-    kInterruptEnablingSentry = 3,
-    kInterruptDisablingReturnSentry = 4,
-    kInterruptEnablingReturnSentry = 5,
+    kInterruptInheritingSentry = 1,
+    kInterruptDisablingForwardSentry = 2,
+    kInterruptEnablingForwardSentry = 3,
+    kInterruptDisablingBackwardSentry = 4,
+    kInterruptEnablingBackwardSentry = 5,
     kSealedExecutable6 = 6,
     kSealedExecutable7 = 7,
     kReserved8 = 8,
diff --git a/cheriot/cheriot_renode.cc b/cheriot/cheriot_renode.cc
index 4ab981b..5269cf7 100644
--- a/cheriot/cheriot_renode.cc
+++ b/cheriot/cheriot_renode.cc
@@ -109,6 +109,8 @@
 constexpr std::string_view kBaseName = "Mpact.Cheriot";
 constexpr std::string_view kRvvName = "Mpact.CheriotRvv";
 constexpr std::string_view kRvvFpName = "Mpact.CheriotRvvFp";
+// Core version.
+constexpr std::string_view kCoreVersion = "coreVersion";
 
 CheriotRenode::CheriotRenode(std::string name, MemoryInterface *renode_sysbus)
     : name_(name), renode_sysbus_(renode_sysbus) {}
@@ -151,7 +153,7 @@
     std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out);
     std::string serialized;
     if (!proto_file.good() || !google::protobuf::TextFormat::PrintToString(
-                                  *component_proto.get(), &serialized)) {
+                                  *component_proto, &serialized)) {
       LOG(ERROR) << "Failed to write proto to file";
     } else {
       proto_file << serialized;
@@ -388,6 +390,8 @@
         do_inst_profile = value != 0;
       } else if (name == kMemProfile) {
         mem_profiler_->set_is_enabled(value != 0);
+      } else if (name == kCoreVersion) {
+        cheriot_state_->set_core_version(value);
       } else {
         LOG(ERROR) << "Unknown config name: " << name << " "
                    << config_values[i];
diff --git a/cheriot/cheriot_state.h b/cheriot/cheriot_state.h
index 6045039..2b082b9 100644
--- a/cheriot/cheriot_state.h
+++ b/cheriot/cheriot_state.h
@@ -169,6 +169,7 @@
 
 class CheriotState : public generic::ArchState {
  public:
+  static constexpr int kVersion0Dot5 = 50;
   static int constexpr kCapRegQueueSizeMask = 0x11;
   static constexpr uint32_t kCheriExceptionCode = 0x1c;
   static constexpr char kCregPrefix[] = "c";
@@ -323,6 +324,9 @@
   bool MustRevoke(uint32_t address) const;
 
   // Accessors.
+  // Core version.
+  int core_version() const { return core_version_; }
+  void set_core_version(int version) { core_version_ = version; }
   // Returns true if an interrupt is available for the core to take or false
   // otherwise.
   inline bool is_interrupt_available() const { return is_interrupt_available_; }
@@ -431,6 +435,9 @@
 
  private:
   InterruptCode PickInterrupt(uint32_t interrupts);
+  // Core version. Expressed as an integer where as version * 100. Thus
+  // version 1.0 is 100, and 1.5 is 150. Default is 0.5 (or 50).
+  int core_version_ = kVersion0Dot5;
   // A map from register name to entry in the mtval register.
   absl::flat_hash_map<std::string, uint32_t> cap_index_map_;
   // These are root capabilities
diff --git a/cheriot/mpact_cheriot.cc b/cheriot/mpact_cheriot.cc
index ced488d..ef14699 100644
--- a/cheriot/mpact_cheriot.cc
+++ b/cheriot/mpact_cheriot.cc
@@ -81,6 +81,8 @@
 using ::mpact::sim::util::InstructionProfiler;
 using ::mpact::sim::util::TaggedMemoryUseProfiler;
 
+// Flat to specify core version.
+ABSL_FLAG(int, core_version, 100, "Core version");
 // Flags for specifying interactive mode.
 ABSL_FLAG(bool, i, false, "Interactive mode");
 ABSL_FLAG(bool, interactive, false, "Interactive mode");
@@ -278,6 +280,7 @@
   }
   CheriotState cheriot_state("CherIoT", data_memory,
                              static_cast<AtomicMemoryOpInterface *>(router));
+  cheriot_state.set_core_version(absl::GetFlag(FLAGS_core_version));
 
   DecoderInterface *decoder = nullptr;
   if (absl::GetFlag(FLAGS_rvv_fp)) {
@@ -604,7 +607,7 @@
   std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out);
   std::string serialized;
   if (!proto_file.good() || !google::protobuf::TextFormat::PrintToString(
-                                *component_proto.get(), &serialized)) {
+                                *component_proto, &serialized)) {
     LOG(ERROR) << "Failed to write proto to file";
   } else {
     proto_file << serialized;
diff --git a/cheriot/riscv_cheriot.bin_fmt b/cheriot/riscv_cheriot.bin_fmt
index 0d2cdf2..9a9fdf2 100644
--- a/cheriot/riscv_cheriot.bin_fmt
+++ b/cheriot/riscv_cheriot.bin_fmt
@@ -225,7 +225,8 @@
   cheriot_gettype  : R2Type : func7 == 0x7f, func5 == 0x01, func3 == 0, opcode == 0x5b;
   cheriot_incaddr  : RType : func7 == 0x11, func3 == 0, opcode == 0x5b;
   cheriot_incaddrimm : IType : func3 == 0x1, opcode == 0x5b;
-  cheriot_jal      : JType : rd != 0, opcode == 0x6f;
+  cheriot_jal      : JType : rd > 1, opcode == 0x6f;
+  cheriot_jal_cra  : JType : rd == 1, opcode == 0x6f;
   cheriot_j        : JType : rd == 0, opcode == 0x6f;
   // For jalr and jr, need to differentiate when the destination (jalr) or the
   // source (jr) designates the cra register.
@@ -487,7 +488,7 @@
   cswsp     : CSS: func3 == 0b110, op == 0b10;
   cscsp     : CSS: func3 == 0b111, op == 0b10;
   cheriot_cj        : CJ : func3 == 0b101, op == 0b01;
-  cheriot_cjal      : CJ : func3 == 0b001, op == 0b01;
+  cheriot_cjal_cra  : CJ : func3 == 0b001, op == 0b01;
   cheriot_cjr       : CR : func4 == 0b1000, rs1 > 1, rs2 == 0, op == 0b10;
   cheriot_cjr_cra   : CR : func4 == 0b1000, rs1 == 1, rs2 == 0, op == 0b10;
   cheriot_cjalr_cra : CR : func4 == 0b1001, rs1 != 0, rs2 == 0, op == 0b10;
diff --git a/cheriot/riscv_cheriot.isa b/cheriot/riscv_cheriot.isa
index 3dbf475..1af2abe 100644
--- a/cheriot/riscv_cheriot.isa
+++ b/cheriot/riscv_cheriot.isa
@@ -205,6 +205,9 @@
     cheriot_incaddrimm{: cs1, I_imm12 : cd},
       disasm: "cincoffset", "%cd, %cs1, 0x%(I_imm12:08x)",
       semfunc: "&CheriotCIncAddr";
+    cheriot_jal_cra{: J_imm20 : cd},
+      disasm: "cjal", "%cd, 0x%(J_imm20:08x)",
+      semfunc: "&CheriotCJalCra";
     cheriot_jal{: J_imm20 : cd},
       disasm: "cjal", "%cd, 0x%(J_imm20:08x)",
       semfunc: "&CheriotCJal";
@@ -522,6 +525,9 @@
     cheriot_cjal{: I_cj_imm11, x0 :  x1},
       disasm: "c.jal", "%(@+I_cj_imm11:08x)",
       semfunc: "&CheriotCJal";
+    cheriot_cjal_cra{: I_cj_imm11, x0 :  x1},
+      disasm: "c.jal", "%(@+I_cj_imm11:08x)",
+      semfunc: "&CheriotCJalCra";
     cheriot_cjr{: crs1, x0 :},
       disasm: "c.jr", "%crs1",
       semfunc: "&CheriotCJr";
diff --git a/cheriot/riscv_cheriot_instructions.cc b/cheriot/riscv_cheriot_instructions.cc
index 1bf26e0..06d20a1 100644
--- a/cheriot/riscv_cheriot_instructions.cc
+++ b/cheriot/riscv_cheriot_instructions.cc
@@ -200,9 +200,31 @@
   cd->CopyFrom(*pcc);
   cd->set_address(instruction->address() + instruction->size());
   bool interrupt_enable = state->mstatus()->mie();
+  if (state->core_version() == CheriotState::kVersion0Dot5) {
+    (void)cd->Seal(*state->sealing_root(),
+                   interrupt_enable
+                       ? CapReg::kInterruptEnablingBackwardSentry
+                       : CapReg::kInterruptDisablingBackwardSentry);
+  }
+  // Update pcc.
+  pcc->set_address(new_pc);
+  state->set_branch(true);
+}
+
+void CheriotCJalCra(const Instruction *instruction) {
+  auto *state = static_cast<CheriotState *>(instruction->state());
+  auto offset = generic::GetInstructionSource<uint32_t>(instruction, 0);
+  uint64_t new_pc = offset + instruction->address();
+  auto *pcc = state->pcc();
+  if (!CheriotCJChecks(instruction, new_pc, pcc)) return;
+  // Update link register.
+  auto *cd = GetCapDest(instruction, 0);
+  cd->CopyFrom(*pcc);
+  cd->set_address(instruction->address() + instruction->size());
+  bool interrupt_enable = state->mstatus()->mie();
   (void)cd->Seal(*state->sealing_root(),
-                 interrupt_enable ? CapReg::kInterruptEnablingReturnSentry
-                                  : CapReg::kInterruptDisablingReturnSentry);
+                 interrupt_enable ? CapReg::kInterruptEnablingBackwardSentry
+                                  : CapReg::kInterruptDisablingBackwardSentry);
   // Update pcc.
   pcc->set_address(new_pc);
   state->set_branch(true);
@@ -233,11 +255,12 @@
   ok |= !has_dest && uses_ra && cs1->IsBackwardSentry();
   ok |= !has_dest && !uses_ra &&
         ((cs1->object_type() == CapReg::kUnsealed) ||
-         (cs1->object_type() == CapReg::kSentry));
-  ok |= has_dest && ((cs1->object_type() == CapReg::kUnsealed) ||
-                     (cs1->object_type() == CapReg::kSentry));
+         (cs1->object_type() == CapReg::kInterruptInheritingSentry));
+  ok |=
+      has_dest && ((cs1->object_type() == CapReg::kUnsealed) ||
+                   (cs1->object_type() == CapReg::kInterruptInheritingSentry));
   ok |= has_dest && uses_ra && (cs1->object_type() >= CapReg::kUnsealed) &&
-        (cs1->object_type() <= CapReg::kInterruptEnablingSentry);
+        (cs1->object_type() <= CapReg::kInterruptEnablingForwardSentry);
   if ((cs1->IsSealed() && offset != 0) || !ok) {
     state->HandleCheriRegException(instruction, instruction->address(),
                                    EC::kCapExSealViolation, cs1);
@@ -275,23 +298,25 @@
     state->temp_reg()->set_address(instruction->address() +
                                    instruction->size());
     bool interrupt_enable = (mstatus->GetUint32() & 0b1000) != 0;
-    auto status = state->temp_reg()->Seal(
-        *state->sealing_root(), interrupt_enable
-                                    ? CapReg::kInterruptEnablingReturnSentry
-                                    : CapReg::kInterruptDisablingReturnSentry);
-    if (!status.ok()) {
-      LOG(ERROR) << "Failed to seal: " << status;
-      return;
+    if ((state->core_version() == CheriotState::kVersion0Dot5) || uses_ra) {
+      auto status = state->temp_reg()->Seal(
+          *state->sealing_root(),
+          interrupt_enable ? CapReg::kInterruptEnablingBackwardSentry
+                           : CapReg::kInterruptDisablingBackwardSentry);
+      if (!status.ok()) {
+        LOG(ERROR) << "Failed to seal: " << status;
+        return;
+      }
     }
   }
   // Update pcc.
   pcc->CopyFrom(*cs1);
   // If the new pcc is a sentry, unseal and set/clear mie accordingly.
   if (pcc->IsSentry()) {
-    if (pcc->object_type() != CapReg::kSentry) {
+    if (pcc->object_type() != CapReg::kInterruptInheritingSentry) {
       bool interrupt_enable =
-          (pcc->object_type() == CapReg::kInterruptEnablingSentry) ||
-          (pcc->object_type() == CapReg::kInterruptEnablingReturnSentry);
+          (pcc->object_type() == CapReg::kInterruptEnablingForwardSentry) ||
+          (pcc->object_type() == CapReg::kInterruptEnablingBackwardSentry);
       mstatus->set_mie(interrupt_enable);
       mstatus->Submit();
     }
@@ -490,9 +515,9 @@
   uint32_t object_type = cs2->address();
   bool permitted_otype = false;
   switch (object_type) {
-    case CapReg::kSentry:
-    case CapReg::kInterruptDisablingSentry:
-    case CapReg::kInterruptEnablingSentry:
+    case CapReg::kInterruptInheritingSentry:
+    case CapReg::kInterruptDisablingForwardSentry:
+    case CapReg::kInterruptEnablingForwardSentry:
     case CapReg::kSealedExecutable6:
     case CapReg::kSealedExecutable7:
       permitted_otype = cs1->HasPermission(CapReg::kPermitExecute);
diff --git a/cheriot/riscv_cheriot_instructions.h b/cheriot/riscv_cheriot_instructions.h
index ec59f87..8988908 100644
--- a/cheriot/riscv_cheriot_instructions.h
+++ b/cheriot/riscv_cheriot_instructions.h
@@ -72,6 +72,7 @@
 // This instruction takes 1 source (an offset), and one destination (the link
 // capability). The pcc is implied.
 void CheriotCJal(const Instruction *instruction);
+void CheriotCJalCra(const Instruction *instruction);
 // This instruction takes 1 source (an offset). The pcc is implied.
 void CheriotCJ(const Instruction *instruction);
 // This instruction takes 2 sources and one destination operand. Source 0 is the
diff --git a/cheriot/test/BUILD b/cheriot/test/BUILD
index 3616d1d..42440bb 100644
--- a/cheriot/test/BUILD
+++ b/cheriot/test/BUILD
@@ -21,6 +21,11 @@
     values = {"cpu": "darwin_arm64"},
 )
 
+config_setting(
+    name = "darwin_aarch64_cpu",
+    values = {"cpu": "darwin_arm64"},
+)
+
 cc_test(
     name = "cheriot_register_test",
     size = "small",
@@ -413,11 +418,12 @@
     srcs = [
         "riscv_cheriot_vector_fp_unary_instructions_test.cc",
     ],
-    copts = [
-        "-ffp-model=strict",
-    ] + select({
-        "darwin_arm64_cpu": [],
-        "//conditions:default": ["-fprotect-parens"],
+    copts = select({
+        "darwin_aarch64_cpu": ["-ffp-model=strict"],
+        "//conditions:default": [
+            "-ffp-model=strict",
+            "-fprotect-parens",
+        ],
     }),
     deps = [
         ":riscv_cheriot_vector_fp_test_utilities",
@@ -443,11 +449,12 @@
     srcs = [
         "riscv_cheriot_vector_fp_instructions_test.cc",
     ],
-    copts = [
-        "-ffp-model=strict",
-    ] + select({
-        "darwin_arm64_cpu": [],
-        "//conditions:default": ["-fprotect-parens"],
+    copts = select({
+        "darwin_aarch64_cpu": ["-ffp-model=strict"],
+        "//conditions:default": [
+            "-ffp-model=strict",
+            "-fprotect-parens",
+        ],
     }),
     deps = [
         ":riscv_cheriot_vector_fp_test_utilities",
@@ -473,11 +480,12 @@
     srcs = [
         "riscv_cheriot_vector_fp_compare_instructions_test.cc",
     ],
-    copts = [
-        "-ffp-model=strict",
-    ] + select({
-        "darwin_arm64_cpu": [],
-        "//conditions:default": ["-fprotect-parens"],
+    copts = select({
+        "darwin_aarch64_cpu": ["-ffp-model=strict"],
+        "//conditions:default": [
+            "-ffp-model=strict",
+            "-fprotect-parens",
+        ],
     }),
     deps = [
         ":riscv_cheriot_vector_fp_test_utilities",
@@ -500,11 +508,12 @@
     srcs = [
         "riscv_cheriot_vector_fp_reduction_instructions_test.cc",
     ],
-    copts = [
-        "-ffp-model=strict",
-    ] + select({
-        "darwin_arm64_cpu": [],
-        "//conditions:default": ["-fprotect-parens"],
+    copts = select({
+        "darwin_aarch64_cpu": ["-ffp-model=strict"],
+        "//conditions:default": [
+            "-ffp-model=strict",
+            "-fprotect-parens",
+        ],
     }),
     deps = [
         ":riscv_cheriot_vector_fp_test_utilities",
diff --git a/cheriot/test/cheriot_register_test.cc b/cheriot/test/cheriot_register_test.cc
index 2753ffd..262dd30 100644
--- a/cheriot/test/cheriot_register_test.cc
+++ b/cheriot/test/cheriot_register_test.cc
@@ -346,11 +346,11 @@
     cap_reg()->ResetExecuteRoot();
     auto status = cap_reg()->Seal(*seal_cap_reg, i);
     // If the object type is out of range for data, expect failure.
-    if ((i == ObjectType::kSentry) ||
-        (i == ObjectType::kInterruptDisablingSentry) ||
-        (i == ObjectType::kInterruptEnablingSentry) ||
-        (i == ObjectType::kInterruptDisablingReturnSentry) ||
-        (i == ObjectType::kInterruptEnablingReturnSentry) ||
+    if ((i == ObjectType::kInterruptInheritingSentry) ||
+        (i == ObjectType::kInterruptDisablingForwardSentry) ||
+        (i == ObjectType::kInterruptEnablingForwardSentry) ||
+        (i == ObjectType::kInterruptDisablingBackwardSentry) ||
+        (i == ObjectType::kInterruptEnablingBackwardSentry) ||
         (i == ObjectType::kSealedExecutable6) ||
         (i == ObjectType::kSealedExecutable7)) {
       EXPECT_TRUE(status.ok()) << status.message();
@@ -373,7 +373,9 @@
   // Try with a sealing root with cleared tag.
   seal_cap_reg->ResetSealingRoot();
   seal_cap_reg->Invalidate();
-  EXPECT_FALSE(cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry).ok());
+  EXPECT_FALSE(cap_reg()
+                   ->Seal(*seal_cap_reg, ObjectType::kInterruptInheritingSentry)
+                   .ok());
   cap_reg()->ResetExecuteRoot();
   seal_cap_reg->ResetSealingRoot();
   // Seal the sealing capability. This should succeed.
@@ -388,7 +390,8 @@
   seal_cap_reg->ResetSealingRoot();
   seal_cap_reg->ClearPermissions(PermissionBits::kPermitSeal);
   cap_reg()->ResetExecuteRoot();
-  status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry);
+  status =
+      cap_reg()->Seal(*seal_cap_reg, ObjectType::kInterruptInheritingSentry);
   EXPECT_FALSE(status.ok());
   EXPECT_EQ(status.code(), absl::StatusCode::kPermissionDenied);
   EXPECT_THAT(status.message(),
@@ -396,15 +399,18 @@
   // Try sealing a null capability.
   seal_cap_reg->ResetSealingRoot();
   cap_reg()->ResetNull();
-  status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry);
+  status =
+      cap_reg()->Seal(*seal_cap_reg, ObjectType::kInterruptInheritingSentry);
   EXPECT_FALSE(status.ok());
   EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
   EXPECT_THAT(status.message(),
               testing::HasSubstr("Target is not a valid capability"));
   // Try sealing twice.
   cap_reg()->ResetExecuteRoot();
-  CHECK_OK(cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry));
-  status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry);
+  CHECK_OK(
+      cap_reg()->Seal(*seal_cap_reg, ObjectType::kInterruptInheritingSentry));
+  status =
+      cap_reg()->Seal(*seal_cap_reg, ObjectType::kInterruptInheritingSentry);
   EXPECT_FALSE(status.ok());
   EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
   EXPECT_THAT(status.message(),
diff --git a/cheriot/test/riscv_cheriot_encoding_test.cc b/cheriot/test/riscv_cheriot_encoding_test.cc
index 4cc6346..8b58730 100644
--- a/cheriot/test/riscv_cheriot_encoding_test.cc
+++ b/cheriot/test/riscv_cheriot_encoding_test.cc
@@ -158,7 +158,7 @@
 constexpr uint32_t kCsd = 0b111'000'000'00'000'00;
 // constexpr uint32_t kCdsd = 0b101'000'000'00'000'00;
 constexpr uint32_t kCheriotCj = 0b101'00000000000'01;
-constexpr uint32_t kCheriotCjal = 0b001'00000000000'01;
+constexpr uint32_t kCheriotCjalCra = 0b001'00000000000'01;
 constexpr uint32_t kCheriotCjr = 0b100'0'00000'00000'10;
 constexpr uint32_t kCheriotCjalr = 0b100'1'00000'00000'10;
 constexpr uint32_t kCbeqz = 0b110'000'000'00000'01;
@@ -206,6 +206,7 @@
 };
 
 constexpr int kRdValue = 1;
+constexpr int kNonRaValue = 2;
 
 static uint32_t SetRd(uint32_t iword, uint32_t rdval) {
   return (iword | ((rdval & 0x1f) << 7));
@@ -248,6 +249,9 @@
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kLui);
   enc_->ParseInstruction(SetRd(kCheriotJal, kRdValue));
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
+            OpcodeEnum::kCheriotJalCra);
+  enc_->ParseInstruction(SetRd(kCheriotJal, kNonRaValue));
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
             OpcodeEnum::kCheriotJal);
   enc_->ParseInstruction(SetRd(kCheriotJalr, kRdValue));
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
@@ -445,9 +449,9 @@
   enc_->ParseInstruction(kCheriotCj);
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
             OpcodeEnum::kCheriotCj);
-  enc_->ParseInstruction(kCheriotCjal);
+  enc_->ParseInstruction(kCheriotCjalCra);
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
-            OpcodeEnum::kCheriotCjal);
+            OpcodeEnum::kCheriotCjalCra);
   enc_->ParseInstruction(Set16Rd(kCheriotCjr, 1));
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
             OpcodeEnum::kCheriotCjrCra);
@@ -543,6 +547,9 @@
             OpcodeEnum::kCheriotIncaddrimm);
   enc_->ParseInstruction(SetRd(kCheriotJal, kRdValue));
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
+            OpcodeEnum::kCheriotJalCra);
+  enc_->ParseInstruction(SetRd(kCheriotJal, kNonRaValue));
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
             OpcodeEnum::kCheriotJal);
   enc_->ParseInstruction(SetRd(kCheriotJalr, kRdValue));
   EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
diff --git a/cheriot/test/riscv_cheriot_instructions_test.cc b/cheriot/test/riscv_cheriot_instructions_test.cc
index 0173b83..3271a58 100644
--- a/cheriot/test/riscv_cheriot_instructions_test.cc
+++ b/cheriot/test/riscv_cheriot_instructions_test.cc
@@ -64,6 +64,8 @@
 using ::mpact::sim::cheriot::CheriotCGetType;
 using ::mpact::sim::cheriot::CheriotCIncAddr;
 using ::mpact::sim::cheriot::CheriotCJal;
+using ::mpact::sim::cheriot::CheriotCJalCra;
+using ::mpact::sim::cheriot::CheriotCJalr;
 using ::mpact::sim::cheriot::CheriotCJalrCra;
 using ::mpact::sim::cheriot::CheriotCLc;
 using ::mpact::sim::cheriot::CheriotCLcChild;
@@ -98,6 +100,8 @@
 constexpr int kDataSeal10 = 10;
 constexpr int kInstSizeNormal = 4;
 
+constexpr int kVersion1Dot0 = 100;
+
 // Test fixture.
 class RiscVCheriotInstructionsTest : public ::testing::Test {
  protected:
@@ -544,8 +548,8 @@
 }
 
 // Jump and link - no traps.
-TEST_F(RiscVCheriotInstructionsTest, CJal) {
-  inst()->set_semantic_function(&CheriotCJal);
+TEST_F(RiscVCheriotInstructionsTest, CJalCra) {
+  inst()->set_semantic_function(&CheriotCJalCra);
   AppendCapabilityOperands(inst(), {kC1}, {kC3});
   state()->pcc()->set_address(inst()->address());
   c1_reg()->set_address(0x200);
@@ -558,7 +562,7 @@
   EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
   EXPECT_TRUE(c3_reg()->tag());
   EXPECT_TRUE(c3_reg()->IsSentry());
-  EXPECT_EQ(c3_reg()->object_type(), OT::kInterruptEnablingReturnSentry);
+  EXPECT_EQ(c3_reg()->object_type(), OT::kInterruptEnablingBackwardSentry);
   EXPECT_TRUE(state()->pcc()->tag());
   // Set interrupt enable to false.
   state()->mstatus()->set_mie(0);
@@ -570,13 +574,13 @@
   EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
   EXPECT_TRUE(c3_reg()->tag());
   EXPECT_TRUE(c3_reg()->IsSentry());
-  EXPECT_EQ(c3_reg()->object_type(), OT::kInterruptDisablingReturnSentry);
+  EXPECT_EQ(c3_reg()->object_type(), OT::kInterruptDisablingBackwardSentry);
   EXPECT_TRUE(state()->pcc()->tag());
 }
 
 // Jump and link - out of bounds error.
-TEST_F(RiscVCheriotInstructionsTest, CJalOutOfBounds) {
-  inst()->set_semantic_function(&CheriotCJal);
+TEST_F(RiscVCheriotInstructionsTest, CJalCraOutOfBounds) {
+  inst()->set_semantic_function(&CheriotCJalCra);
   AppendCapabilityOperands(inst(), {kC1}, {kC3});
   state()->pcc()->set_address(inst()->address());
   c1_reg()->set_address(0x200);
@@ -590,8 +594,8 @@
 
 // Jump and link - misaligned (jumping to 2 byte aligned address with no
 // compact instructions).
-TEST_F(RiscVCheriotInstructionsTest, CJalMisaligned) {
-  inst()->set_semantic_function(&CheriotCJal);
+TEST_F(RiscVCheriotInstructionsTest, CJalCraMisaligned) {
+  inst()->set_semantic_function(&CheriotCJalCra);
   AppendCapabilityOperands(inst(), {kC1}, {kC3});
   state()->pcc()->set_address(inst()->address());
   state()->misa()->Set(state()->misa()->AsUint64() & ~*ISA::kCompressed);
@@ -608,8 +612,67 @@
   EXPECT_EQ(inst(), trap_inst());
 }
 
+// Jump and link - rd != ra.
+TEST_F(RiscVCheriotInstructionsTest, CJalNonCra) {
+  inst()->set_semantic_function(&CheriotCJal);
+  AppendCapabilityOperands(inst(), {kC1}, {kC3});
+  state()->pcc()->set_address(inst()->address());
+  c1_reg()->set_address(0x200);
+  // Set interrupt enable to true.
+  state()->mstatus()->set_mie(1);
+  state()->mstatus()->Submit();
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(c3_reg()->tag());
+  EXPECT_TRUE(c3_reg()->IsSentry());
+  EXPECT_EQ(c3_reg()->object_type(), OT::kInterruptEnablingBackwardSentry);
+  EXPECT_TRUE(state()->pcc()->tag());
+
+  // Set interrupt enable to false.
+  state()->mstatus()->set_mie(0);
+  state()->mstatus()->Submit();
+  state()->pcc()->set_address(inst()->address());
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(c3_reg()->tag());
+  EXPECT_TRUE(c3_reg()->IsSentry());
+  EXPECT_EQ(c3_reg()->object_type(), OT::kInterruptDisablingBackwardSentry);
+  EXPECT_TRUE(state()->pcc()->tag());
+
+  // Now set core version to 1.0 - this behavior should return an unsealed
+  // capability.
+  state()->set_core_version(kVersion1Dot0);
+  // Set interrupt enable to true.
+  state()->mstatus()->set_mie(1);
+  state()->mstatus()->Submit();
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(c3_reg()->tag());
+  EXPECT_FALSE(c3_reg()->IsSentry());
+  EXPECT_EQ(c3_reg()->object_type(), OT::kUnsealed);
+  EXPECT_TRUE(state()->pcc()->tag());
+
+  // Set interrupt enable to false.
+  state()->mstatus()->set_mie(0);
+  state()->mstatus()->Submit();
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(c3_reg()->tag());
+  EXPECT_FALSE(c3_reg()->IsSentry());
+  EXPECT_EQ(c3_reg()->object_type(), OT::kUnsealed);
+  EXPECT_TRUE(state()->pcc()->tag());
+}
+
 // Jump and link register (capability) indirect - no traps, unsealed source.
-TEST_F(RiscVCheriotInstructionsTest, CJalr) {
+TEST_F(RiscVCheriotInstructionsTest, CJalrCra) {
   inst()->set_semantic_function(&CheriotCJalrCra);
   AppendCapabilityOperands(inst(), {kC1, kC2}, {kCra});
   state()->pcc()->set_address(inst()->address());
@@ -628,7 +691,7 @@
   EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
   EXPECT_TRUE(cra_reg()->tag());
   EXPECT_TRUE(cra_reg()->IsSentry());
-  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptEnablingReturnSentry);
+  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptEnablingBackwardSentry);
   EXPECT_TRUE(state()->pcc()->tag());
   // Set interrupt enable to false.
   state()->mstatus()->set_mie(0);
@@ -640,12 +703,77 @@
   EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
   EXPECT_TRUE(cra_reg()->tag());
   EXPECT_TRUE(cra_reg()->IsSentry());
-  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptDisablingReturnSentry);
+  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptDisablingBackwardSentry);
+  EXPECT_TRUE(state()->pcc()->tag());
+}
+
+// Jump and link register (capability) indirect - no traps, unsealed source.
+// Non cra register used for destination.
+TEST_F(RiscVCheriotInstructionsTest, CJalr) {
+  inst()->set_semantic_function(&CheriotCJalr);
+  AppendCapabilityOperands(inst(), {kC1, kC2}, {kCra});
+  state()->pcc()->set_address(inst()->address());
+  // Set up the destination capability.
+  c1_reg()->ResetExecuteRoot();
+  c1_reg()->set_address(kInstAddress + 0x100);
+  c1_reg()->SetBounds(kInstAddress, 0x400);
+  // Set offset.
+  c2_reg()->set_address(0x100);
+  // Set interrupt enable to true.
+  state()->mstatus()->set_mie(1);
+  state()->mstatus()->Submit();
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(cra_reg()->tag());
+  EXPECT_TRUE(cra_reg()->IsSentry());
+  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptEnablingBackwardSentry);
+  EXPECT_TRUE(state()->pcc()->tag());
+  // Set interrupt enable to false.
+  state()->mstatus()->set_mie(0);
+  state()->mstatus()->Submit();
+  state()->pcc()->set_address(inst()->address());
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(cra_reg()->tag());
+  EXPECT_TRUE(cra_reg()->IsSentry());
+  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptDisablingBackwardSentry);
+  EXPECT_TRUE(state()->pcc()->tag());
+
+  // Set core version to 1.0 - this behavior should return an unsealed
+  // capability.
+  state()->set_core_version(kVersion1Dot0);
+
+  // Set interrupt enable to true.
+  state()->mstatus()->set_mie(1);
+  state()->mstatus()->Submit();
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(cra_reg()->tag());
+  EXPECT_FALSE(cra_reg()->IsSentry());
+  EXPECT_EQ(cra_reg()->object_type(), OT::kUnsealed);
+  EXPECT_TRUE(state()->pcc()->tag());
+  // Set interrupt enable to false.
+  state()->mstatus()->set_mie(0);
+  state()->mstatus()->Submit();
+  state()->pcc()->set_address(inst()->address());
+  inst()->Execute(nullptr);
+  EXPECT_FALSE(trap_taken()) << "ec: " << std::hex << trap_exception_code()
+                             << " value: " << trap_value();
+  EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
+  EXPECT_TRUE(cra_reg()->tag());
+  EXPECT_FALSE(cra_reg()->IsSentry());
+  EXPECT_EQ(cra_reg()->object_type(), OT::kUnsealed);
   EXPECT_TRUE(state()->pcc()->tag());
 }
 
 // Jump and link register (capability) indirect - no traps, sentry.
-TEST_F(RiscVCheriotInstructionsTest, CJalrSentry) {
+TEST_F(RiscVCheriotInstructionsTest, CJalrCraSentry) {
   inst()->set_semantic_function(&CheriotCJalrCra);
   AppendCapabilityOperands(inst(), {kC1, kC2}, {kCra});
   state()->pcc()->set_address(inst()->address());
@@ -654,7 +782,7 @@
   c1_reg()->set_address(kInstAddress + 0x200);
   c1_reg()->SetBounds(kInstAddress, 0x400);
   (void)c1_reg()->Seal(*(state()->sealing_root()),
-                       OT::kInterruptEnablingSentry);
+                       OT::kInterruptEnablingForwardSentry);
   // Set offset to zero (because c1_reg is sealed).
   c2_reg()->set_address(0);
   // Set interrupt enable to false.
@@ -667,14 +795,14 @@
   EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
   EXPECT_TRUE(cra_reg()->tag());
   EXPECT_TRUE(cra_reg()->IsSentry());
-  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptDisablingReturnSentry);
+  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptDisablingBackwardSentry);
   EXPECT_TRUE(state()->pcc()->tag());
   // Set up the destination capability.
   c1_reg()->ResetExecuteRoot();
   c1_reg()->SetBounds(kInstAddress, 0x400);
   c1_reg()->set_address(kInstAddress + 0x200);
   (void)c1_reg()->Seal(*(state()->sealing_root()),
-                       OT::kInterruptDisablingSentry);
+                       OT::kInterruptDisablingForwardSentry);
   // Set interrupt enable to true.
   state()->mstatus()->set_mie(1);
   state()->mstatus()->Submit();
@@ -686,12 +814,12 @@
   EXPECT_EQ(state()->pcc()->address(), inst()->address() + 0x200);
   EXPECT_TRUE(cra_reg()->tag());
   EXPECT_TRUE(cra_reg()->IsSentry());
-  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptEnablingReturnSentry);
+  EXPECT_EQ(cra_reg()->object_type(), OT::kInterruptEnablingBackwardSentry);
   EXPECT_TRUE(state()->pcc()->tag());
 }
 
 // Verify an unset tag generates a tag violation exception.
-TEST_F(RiscVCheriotInstructionsTest, CJalrTagViolation) {
+TEST_F(RiscVCheriotInstructionsTest, CJalrCraTagViolation) {
   inst()->set_semantic_function(&CheriotCJalrCra);
   AppendCapabilityOperands(inst(), {kC1, kC2}, {kCra});
   state()->pcc()->set_address(inst()->address());
@@ -718,7 +846,7 @@
 
 // For a jalr with a sentry, the immediate has to be zero or it will cause
 // an exception. Make sure the exception happens.
-TEST_F(RiscVCheriotInstructionsTest, CJalrSentryNonZeroImmediate) {
+TEST_F(RiscVCheriotInstructionsTest, CJalrCraSentryNonZeroImmediate) {
   inst()->set_semantic_function(&CheriotCJalrCra);
   AppendCapabilityOperands(inst(), {kC1, kC2}, {kCra});
   state()->pcc()->set_address(inst()->address());
@@ -727,7 +855,7 @@
   c1_reg()->set_address(kInstAddress + 0x200);
   c1_reg()->SetBounds(kInstAddress, 0x400);
   (void)c1_reg()->Seal(*(state()->sealing_root()),
-                       OT::kInterruptEnablingSentry);
+                       OT::kInterruptEnablingForwardSentry);
   // Set offset to non-zero - should cause an exception.
   c2_reg()->set_address(0x100);
   inst()->Execute(nullptr);
@@ -745,7 +873,7 @@
 
 // If the source capability does not have execute permission, there should
 // be an exception.
-TEST_F(RiscVCheriotInstructionsTest, CJalrExecuteViolation) {
+TEST_F(RiscVCheriotInstructionsTest, CJalrCraExecuteViolation) {
   inst()->set_semantic_function(&CheriotCJalrCra);
   AppendCapabilityOperands(inst(), {kC1, kC2}, {kCra});
   state()->pcc()->set_address(inst()->address());
@@ -773,7 +901,7 @@
 
 // If the architecture does not have compact instructions, then misaligned
 // access on two byte boundary should cause an exception.
-TEST_F(RiscVCheriotInstructionsTest, CJalrAlignmentViolation) {
+TEST_F(RiscVCheriotInstructionsTest, CJalrCraAlignmentViolation) {
   inst()->set_semantic_function(&CheriotCJalrCra);
   AppendCapabilityOperands(inst(), {kC1, kC2}, {kC3});
   state()->pcc()->set_address(inst()->address());
@@ -1228,7 +1356,7 @@
   c1_reg()->set_address(kMemAddress);
   c2_reg()->set_address(0x200);
   c3_reg()->ResetExecuteRoot();
-  c3_reg()->set_object_type(CheriotRegister::kInterruptDisablingReturnSentry);
+  c3_reg()->set_object_type(CheriotRegister::kInterruptDisablingBackwardSentry);
   c3_reg()->ClearPermissions(PB::kPermitGlobal);
   EXPECT_TRUE(c3_reg()->IsBackwardSentry());
   inst()->Execute(nullptr);
@@ -1245,7 +1373,7 @@
 
   // Now with global flag on the stored capability.
   c3_reg()->ResetExecuteRoot();
-  c3_reg()->set_object_type(CheriotRegister::kInterruptDisablingReturnSentry);
+  c3_reg()->set_object_type(CheriotRegister::kInterruptDisablingBackwardSentry);
   EXPECT_TRUE(c3_reg()->IsBackwardSentry());
   inst()->Execute(nullptr);
   EXPECT_FALSE(trap_taken());
@@ -1321,8 +1449,9 @@
     inst()->Execute(nullptr);
     EXPECT_EQ(c3_reg()->object_type(), o_type & 0b111);
     // For executable object types the tag should be true.
-    if ((o_type == OT::kSentry) || (o_type == OT::kInterruptDisablingSentry) ||
-        (o_type == OT::kInterruptEnablingSentry) ||
+    if ((o_type == OT::kInterruptInheritingSentry) ||
+        (o_type == OT::kInterruptDisablingForwardSentry) ||
+        (o_type == OT::kInterruptEnablingForwardSentry) ||
         (o_type == OT::kSealedExecutable6) ||
         (o_type == OT::kSealedExecutable7)) {
       EXPECT_TRUE(c3_reg()->tag());