No public description PiperOrigin-RevId: 875775834 Change-Id: I768beb10532557c7af267c2e4e7c2b186700b86d
diff --git a/cheriot/cheriot_register.cc b/cheriot/cheriot_register.cc index 1b528e1..fe46f1e 100644 --- a/cheriot/cheriot_register.cc +++ b/cheriot/cheriot_register.cc
@@ -14,6 +14,7 @@ #include "cheriot/cheriot_register.h" +#include <algorithm> #include <cstdint> #include <string> #include <utility> @@ -186,6 +187,45 @@ return (req_base == new_base) && (new_length == req_length); } +void CheriotRegister::SetBoundsRoundDown(uint32_t req_base, + uint64_t req_length) { + if (is_null_) { + Expand(address(), 0, /*tag=*/false); + is_null_ = false; + } + // If the requested length is 0, set the base and top to the requested base. + if (req_length == 0) { + set_base(req_base); + set_top(req_base); + exponent_ = 0; + raw_ = Compress(); + return; + } + // Compute the exponent that will be used. Note, the largest exponent is 14. + uint64_t ext_base = req_base; + uint32_t trunc_length = static_cast<uint32_t>(req_length); + uint32_t exp_l = 31 - absl::countl_zero(trunc_length); + uint32_t exp_b = absl::countr_zero(ext_base); + uint32_t exp = std::min(14u, std::min(exp_l, exp_b)); + // Reduce the requested length to the nearest representable length. + uint64_t new_length; + // First check if the requested length is larger than the maximum + // representable length for the exponent, and if so, set the length to the + // maximum representable length. Otherwise, perform any rounding down of the + // length that may be required based on the exponent. + if (req_length > 511 * (1ULL << exp)) { + new_length = 511 * (1ULL << exp); + } else { + new_length = (req_length >> exp) << exp; + } + // Recompute the top based on the rounded length. + uint64_t new_top = req_base + new_length; + set_base(req_base); + set_top(new_top); + exponent_ = exp; + raw_ = Compress(); +} + std::pair<uint32_t, uint64_t> CheriotRegister::ComputeBounds() { if (is_null_) { Expand(address(), 0, /*tag=*/false);
diff --git a/cheriot/cheriot_register.h b/cheriot/cheriot_register.h index 9cfc569..ee6f74d 100644 --- a/cheriot/cheriot_register.h +++ b/cheriot/cheriot_register.h
@@ -156,6 +156,9 @@ // Set bounds, return true if they're precise, i.e., that the base and length // do not have to be rounded, false otherwise. bool SetBounds(uint32_t req_base, uint64_t req_length); + // Set bounds, but make sure the length is set to the largest value less than + // or equal to the requested length that preserves the alignment of the base. + void SetBoundsRoundDown(uint32_t req_base, uint64_t req_length); // Return true if the address is in bounds of the capability. bool IsInBounds(uint32_t cap_address, uint32_t size) const { return (cap_address >= base()) &&
diff --git a/cheriot/riscv_cheriot.bin_fmt b/cheriot/riscv_cheriot.bin_fmt index 3859033..3f0b95d 100644 --- a/cheriot/riscv_cheriot.bin_fmt +++ b/cheriot/riscv_cheriot.bin_fmt
@@ -249,6 +249,7 @@ cheriot_setaddr : RType : func7 == 0x10, func3 == 0, opcode == 0x5b; cheriot_setbounds : RType : func7 == 0x08, func3 == 0, opcode == 0x5b; cheriot_setboundsexact : RType : func7 == 0x09, func3 == 0, opcode == 0x5b; + cheriot_setboundsrounddown: RType : func7 == 0x0a, func3 == 0, opcode == 0x5b; cheriot_setboundsimm : IType : func3 == 0x02, opcode == 0x5b; cheriot_setequalexact : RType : func7 == 0x21, func3 == 0, opcode == 0x5b; cheriot_sethigh : RType : func7 == 0x16, func3 == 0, opcode == 0x5b; @@ -455,21 +456,21 @@ csw : CS : func3 == 0b110, op == 0b00; csc : CS : func3 == 0b111, op == 0b00; cnop : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 == 0, op == 0b01; - chint : CI : func3 == 0b000, imm6 != 0, rs1 == 0, op == 0b01; + !chint : CI : func3 == 0b000, imm6 != 0, rs1 == 0, op == 0b01; caddi : CI : func3 == 0b000, imm6 != 0, rd != 0, op == 0b01; - chint : CI : func3 == 0b000, imm6 == 0, rd != 0, op == 0b01; + !chint : CI : func3 == 0b000, imm6 == 0, rd != 0, op == 0b01; cli : CI : func3 == 0b010, rd != 0, op == 0b01; - chint : CI : func3 == 0b010, rd == 0, op == 0b01; + !chint : CI : func3 == 0b010, rd == 0, op == 0b01; caddi16sp : CI : func3 == 0b011, ci_imm10 != 0, rd == 2, op == 0b01; clui : CI : func3 == 0b011, rd != 0, rd != 2, imm18 != 0, op == 0b01; - chint : CI : func3 == 0b011, rd == 0, imm18 != 0, op == 0b01; + !chint : CI : func3 == 0b011, rd == 0, imm18 != 0, op == 0b01; // TODO(torerik): The following two instructions should have imm1 == 0 and // imm5 != 0 instead of imm6 != 0 in the constraints, but that has been // temporarily removed so as to make it easier to verify in TestRIG csrli : CSH : func3 == 0b100, op2 == 0b00, uimm6 != 0, op == 0b01; - chint : CSH : func3 == 0b100, op2 == 0b00, uimm6 == 0, op == 0b01; + !chint : CSH : func3 == 0b100, op2 == 0b00, uimm6 == 0, op == 0b01; csrai : CSH : func3 == 0b100, op2 == 0b01, uimm6 != 0, op == 0b01; - chint : CSH : func3 == 0b100, op2 == 0b01, uimm6 == 0, op == 0b01; + !chint : CSH : func3 == 0b100, op2 == 0b01, uimm6 == 0, op == 0b01; candi : CSH : func3 == 0b100, op2 == 0b10, op == 0b01; csub : CA : func6 == 0b100'011, func2 == 0b00, op == 0b01; cxor : CA : func6 == 0b100'011, func2 == 0b01, op == 0b01; @@ -478,14 +479,14 @@ cbeqz : CB : func3 == 0b110, op == 0b01; cbnez : CB : func3 == 0b111, op == 0b01; cslli : CI : func3 == 0b000, imm1 == 0, imm5 != 0, rs1 != 0, op == 0b10; - chint : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 != 0, op == 0b10; - chint : CI : func3 == 0b000, imm6 == 0, op == 0b10; + !chint : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 != 0, op == 0b10; + !chint : CI : func3 == 0b000, imm6 == 0, op == 0b10; clwsp : CI : func3 == 0b010, rd != 0, op == 0b10; clcsp : CI : func3 == 0b011, rd != 0, op == 0b10; cmv : CR : func4 == 0b1000, rs1 != 0, rs2 != 0, op == 0b10; cebreak : Inst16Format : func3 == 0b100, bits == 0b1'00000'00000, op == 0b10; cadd : CR : func4 == 0b1001, rs1 != 0, rs2 != 0, op == 0b10; - chint : CR : func4 == 0b1001, rs1 == 0, rs2 != 0, op == 0b10; + !chint : CR : func4 == 0b1001, rs1 == 0, rs2 != 0, op == 0b10; cswsp : CSS: func3 == 0b110, op == 0b10; cscsp : CSS: func3 == 0b111, op == 0b10; cheriot_cj : CJ : func3 == 0b101, op == 0b01;
diff --git a/cheriot/riscv_cheriot.isa b/cheriot/riscv_cheriot.isa index 469a6c3..44e1e74 100644 --- a/cheriot/riscv_cheriot.isa +++ b/cheriot/riscv_cheriot.isa
@@ -259,6 +259,9 @@ cheriot_setboundsexact{: cs1, rs2 : cd}, disasm: "csetboundsexact", "%cd, %cs1, %rs2", semfunc: "&CheriotCSetBoundsExact"; + cheriot_setboundsrounddown{: cs1, rs2 : cd}, + disasm: "csetboundsrounddown", "%cd, %cs1, %rs2", + semfunc: "&CheriotCSetBoundsRoundDown"; cheriot_setboundsimm{: cs1, I_uimm12 : cd}, disasm: "csetboundsimm", "%cd, %cs1, 0x%(I_uimm12:08x)", semfunc: "&CheriotCSetBounds";
diff --git a/cheriot/riscv_cheriot_instructions.cc b/cheriot/riscv_cheriot_instructions.cc index aa5f10e..ca2d896 100644 --- a/cheriot/riscv_cheriot_instructions.cc +++ b/cheriot/riscv_cheriot_instructions.cc
@@ -589,6 +589,24 @@ if (!exact || !valid) cd->Invalidate(); } +void CheriotCSetBoundsRoundDown(const Instruction* instruction) { + auto* cs1 = GetCapSource(instruction, 0); + auto rs2 = generic::GetInstructionSource<uint32_t>(instruction, 1); + auto* cd = GetCapDest(instruction, 0); + bool valid = true; + auto cs1_address = cs1->address(); + // If cs1 is sealed, then invalidate the capability. + if (cs1->IsSealed()) valid = false; + // If outside the requested representable range, invalidate. + auto [cs1_base, cs1_top] = cs1->ComputeBounds(); + auto new_top = + static_cast<uint64_t>(cs1_address) + static_cast<uint64_t>(rs2); + valid &= (cs1_address >= cs1_base) && (new_top <= cs1_top); + cd->CopyFrom(*cs1); + cd->SetBoundsRoundDown(cs1_address, rs2); + if (!valid) cd->Invalidate(); +} + void CheriotCSetEqualExact(const Instruction* instruction) { auto* cs1 = GetCapSource(instruction, 0); auto* cs2 = GetCapSource(instruction, 1);
diff --git a/cheriot/riscv_cheriot_instructions.h b/cheriot/riscv_cheriot_instructions.h index cdd3b66..1db22f4 100644 --- a/cheriot/riscv_cheriot_instructions.h +++ b/cheriot/riscv_cheriot_instructions.h
@@ -126,6 +126,10 @@ // source capability, source 1 is an integer value. Destination 0 is the // target capability. void CheriotCSetBoundsExact(const Instruction* instruction); +// This takes 2 source operands and 1 destination operand. Source 0 is the +// source capability, source 1 is an integer value. Destination 0 is the +// target capability. +void CheriotCSetBoundsRoundDown(const Instruction* instruction); // This takes 2 source operands and 1 destination operand. Sources 0 and 1 // are capabilities. Destination 0 is an integer register. void CheriotCSetEqualExact(const Instruction* instruction);
diff --git a/cheriot/test/riscv_cheriot_instructions_test.cc b/cheriot/test/riscv_cheriot_instructions_test.cc index 0dfc98c..35086f1 100644 --- a/cheriot/test/riscv_cheriot_instructions_test.cc +++ b/cheriot/test/riscv_cheriot_instructions_test.cc
@@ -77,6 +77,7 @@ using ::mpact::sim::cheriot::CheriotCSetAddr; using ::mpact::sim::cheriot::CheriotCSetBounds; using ::mpact::sim::cheriot::CheriotCSetBoundsExact; +using ::mpact::sim::cheriot::CheriotCSetBoundsRoundDown; using ::mpact::sim::cheriot::CheriotCSetEqualExact; using ::mpact::sim::cheriot::CheriotCSetHigh; using ::mpact::sim::cheriot::CheriotCSpecialR; @@ -1575,6 +1576,42 @@ EXPECT_FALSE(c3_reg()->tag()); } +TEST_F(RiscVCheriotInstructionsTest, CSetBoundsRoundDown) { + inst()->set_semantic_function(&CheriotCSetBoundsRoundDown); + AppendCapabilityOperands(inst(), {kC1, kC2}, {kC3}); + c1_reg()->ResetMemoryRoot(); + // Set the requested new base. + uint32_t base = kMemAddress << 10; + c1_reg()->set_address(base); + for (uint32_t len = 1; len < 0x8000'0000; len <<= 1) { + // Set the requested new length. + c2_reg()->set_address(len); + inst()->Execute(nullptr); + // The bounds will be no larger than requested. + EXPECT_EQ(c3_reg()->base(), base); + EXPECT_LE(c3_reg()->length(), len); + EXPECT_TRUE(c3_reg()->tag()); + } + // Request length outside the capability. + c1_reg()->SetBounds(kMemAddress, 0x200); + c1_reg()->set_address(base); + c2_reg()->set_address(0x400); + inst()->Execute(nullptr); + EXPECT_EQ(c3_reg()->base(), base); + EXPECT_EQ(c3_reg()->length(), 0x400); + EXPECT_FALSE(c3_reg()->tag()); + // Sealed capability. + c1_reg()->ResetMemoryRoot(); + EXPECT_TRUE(c1_reg()->Seal(*(state()->sealing_root()), kDataSeal10).ok()); + c1_reg()->SetBounds(kMemAddress, 0x800); + c1_reg()->set_address(base); + c2_reg()->set_address(0x400); + inst()->Execute(nullptr); + EXPECT_EQ(c3_reg()->base(), base); + EXPECT_EQ(c3_reg()->length(), 0x400); + EXPECT_FALSE(c3_reg()->tag()); +} + TEST_F(RiscVCheriotInstructionsTest, CSetBoundsExact) { inst()->set_semantic_function(&CheriotCSetBoundsExact); AppendCapabilityOperands(inst(), {kC1, kC2}, {kC3});