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