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());