blob: 2753ffd2fd9ab76177ab901ad2609262665ca4bf [file] [log] [blame]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cheriot/cheriot_register.h"
#include <cstdint>
#include <limits>
#include <memory>
#include "absl/log/check.h"
#include "absl/random/random.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "googlemock/include/gmock/gmock.h"
#include "mpact/sim/generic/arch_state.h"
#include "mpact/sim/generic/operand_interface.h"
// This file contains tests for the 32 bit RiscVCapabilityRegisters (CHERIoT).
namespace {
using ::mpact::sim::cheriot::CheriotRegister;
using ::mpact::sim::generic::ArchState;
using ::mpact::sim::generic::SourceOperandInterface;
using ObjectType = CheriotRegister::ObjectType;
using PermissionBits = CheriotRegister::PermissionBits;
static constexpr uint64_t kBase = 0x1'0011;
class MockArchState : public ArchState {
public:
MockArchState(absl::string_view id, SourceOperandInterface* pc_op)
: ArchState(id, pc_op) {}
explicit MockArchState(absl::string_view id) : MockArchState(id, nullptr) {}
};
// Test fixture.
class CheriotRegisterTest : public ::testing::Test {
protected:
CheriotRegisterTest() {
arch_state_ = new MockArchState("test");
cap_reg_ = new CheriotRegister(arch_state_, "test");
}
~CheriotRegisterTest() override {
delete cap_reg_;
delete arch_state_;
}
CheriotRegister* cap_reg() { return cap_reg_; }
absl::BitGen bitgen_;
MockArchState* arch_state_;
CheriotRegister* cap_reg_;
};
// Verify Reset().
TEST_F(CheriotRegisterTest, Reset) {
// Register value should be 0 on reset.
EXPECT_EQ(cap_reg()->data_buffer()->Get<uint32_t>(0), 0);
// The capability should be the null capability.
EXPECT_FALSE(cap_reg()->tag());
EXPECT_EQ(cap_reg()->base(), 0);
EXPECT_EQ(cap_reg()->length(), 0);
EXPECT_EQ(cap_reg()->object_type(), ObjectType::kUnsealed);
EXPECT_EQ(cap_reg()->permissions(), 0);
EXPECT_FALSE(cap_reg()->IsValid());
EXPECT_FALSE(cap_reg()->IsUnsealed());
EXPECT_FALSE(cap_reg()->IsSealed());
// Update values, then reset, then re-verify.
cap_reg()->ResetMemoryRoot();
(void)cap_reg()->SetBounds(0xabcd'0000, 0x10'0000);
cap_reg()->set_object_type(ObjectType::kUnsealed);
cap_reg()->ResetNull();
// The capability should be the null capability.
EXPECT_FALSE(cap_reg()->tag());
EXPECT_EQ(cap_reg()->base(), 0);
EXPECT_EQ(cap_reg()->length(), 0);
EXPECT_EQ(cap_reg()->object_type(), ObjectType::kUnsealed);
EXPECT_EQ(cap_reg()->permissions(), 0);
EXPECT_FALSE(cap_reg()->IsValid());
EXPECT_FALSE(cap_reg()->IsUnsealed());
EXPECT_FALSE(cap_reg()->IsSealed());
}
// Verify ResetRoot to see that the capability becomes a memory root capability.
TEST_F(CheriotRegisterTest, ResetMemoryRoot) {
// The capability is null at first.
cap_reg()->ResetMemoryRoot();
// Verify that it is a root capability.
EXPECT_TRUE(cap_reg()->tag());
EXPECT_EQ(cap_reg()->base(), 0);
EXPECT_EQ(cap_reg()->length(), 0x1'0000'0000ULL);
EXPECT_EQ(cap_reg()->object_type(), ObjectType::kUnsealed);
EXPECT_EQ(
cap_reg()->permissions(),
(PermissionBits::kPermitGlobal | PermissionBits::kPermitLoad |
PermissionBits::kPermitStore |
PermissionBits::kPermitLoadStoreCapability |
PermissionBits::kPermitStoreLocalCapability |
PermissionBits::kPermitLoadGlobal | PermissionBits::kPermitLoadMutable));
EXPECT_EQ(cap_reg()->data_buffer()->Get<uint32_t>(0), 0);
EXPECT_TRUE(cap_reg()->IsValid());
EXPECT_TRUE(cap_reg()->IsUnsealed());
EXPECT_FALSE(cap_reg()->IsSealed());
}
// Verify ResetRoot to see that the capability becomes a memory root capability.
TEST_F(CheriotRegisterTest, ResetExecuteRoot) {
// The capability is null at first.
cap_reg()->ResetExecuteRoot();
// Verify that it is a root capability.
EXPECT_TRUE(cap_reg()->tag());
EXPECT_EQ(cap_reg()->base(), 0);
EXPECT_EQ(cap_reg()->length(), 0x1'0000'0000ULL);
EXPECT_EQ(cap_reg()->object_type(), ObjectType::kUnsealed);
EXPECT_EQ(
cap_reg()->permissions(),
(PermissionBits::kPermitGlobal | PermissionBits::kPermitExecute |
PermissionBits::kPermitLoad |
PermissionBits::kPermitLoadStoreCapability |
PermissionBits::kPermitLoadGlobal | PermissionBits::kPermitLoadMutable |
PermissionBits::kPermitAccessSystemRegisters));
EXPECT_EQ(cap_reg()->data_buffer()->Get<uint32_t>(0), 0);
EXPECT_TRUE(cap_reg()->IsValid());
EXPECT_TRUE(cap_reg()->IsUnsealed());
EXPECT_FALSE(cap_reg()->IsSealed());
}
// Verify ResetRoot to see that the capability becomes a memory root capability.
TEST_F(CheriotRegisterTest, ResetSealingRoot) {
// The capability is null at first.
cap_reg()->ResetSealingRoot();
// Verify that it is a root capability.
EXPECT_TRUE(cap_reg()->tag());
EXPECT_EQ(cap_reg()->base(), 0);
EXPECT_EQ(cap_reg()->length(), 0x1'0000'0000ULL);
EXPECT_EQ(cap_reg()->object_type(), ObjectType::kUnsealed);
EXPECT_EQ(cap_reg()->permissions(),
(PermissionBits::kPermitGlobal | PermissionBits::kPermitSeal |
PermissionBits::kPermitUnseal | PermissionBits::kUserPerm0));
EXPECT_EQ(cap_reg()->data_buffer()->Get<uint32_t>(0), 0);
EXPECT_TRUE(cap_reg()->IsValid());
EXPECT_TRUE(cap_reg()->IsUnsealed());
}
TEST_F(CheriotRegisterTest, CompressNull) {
// The initial value of the capability is null. Verify that it matches the
// compressed, and memory compressed values.
EXPECT_EQ(cap_reg()->Compress(), CheriotRegister::kNullCapability);
}
TEST_F(CheriotRegisterTest, SetBounds) {
for (uint32_t length_exp = 0; length_exp <= 32; length_exp++) {
cap_reg()->ResetMemoryRoot();
uint64_t length = 1ULL << length_exp;
uint64_t top = kBase + length;
if (top > 0x1'0000'0000ULL) {
length = 0x1'0000'0000ULL - kBase;
}
bool is_exact = cap_reg()->SetBounds(kBase, length);
// The bounds are exact if the length exponent is < 10 for the given base.
EXPECT_EQ(is_exact, length_exp < 9) << length_exp;
if (is_exact) {
EXPECT_EQ(length, cap_reg()->length());
EXPECT_EQ(kBase, cap_reg()->base());
} else {
EXPECT_LE(length, cap_reg()->length());
EXPECT_GE(kBase, cap_reg()->base());
}
}
}
TEST_F(CheriotRegisterTest, ClearPermissions) {
// Memory root permissions.
cap_reg()->ResetMemoryRoot();
// Remove permission bits in order according to state transition diagram
// in section 7.13 of the CherIoT documentation.
for (uint32_t i :
{PermissionBits::kPermitGlobal,
PermissionBits::kPermitStoreLocalCapability,
PermissionBits::kPermitLoadMutable, PermissionBits::kPermitLoadGlobal,
PermissionBits::kPermitLoadStoreCapability, PermissionBits::kPermitLoad,
PermissionBits::kPermitStore}) {
auto permissions = cap_reg()->permissions();
cap_reg()->ClearPermissions(i);
auto new_permissions = cap_reg()->permissions();
auto diff = new_permissions ^ permissions;
if (permissions & i) {
EXPECT_EQ(diff, i);
} else {
EXPECT_EQ(diff, 0);
}
}
// Execute root permissions.
cap_reg()->ResetExecuteRoot();
for (uint32_t i : {
PermissionBits::kPermitGlobal,
PermissionBits::kPermitAccessSystemRegisters,
PermissionBits::kPermitLoadGlobal,
PermissionBits::kPermitLoadMutable,
PermissionBits::kPermitExecute,
PermissionBits::kPermitLoadStoreCapability,
PermissionBits::kPermitLoad,
}) {
auto permissions = cap_reg()->permissions();
cap_reg()->ClearPermissions(i);
auto new_permissions = cap_reg()->permissions();
auto diff = new_permissions ^ permissions;
if (permissions & i) {
EXPECT_EQ(diff, i);
} else {
EXPECT_EQ(diff, 0);
}
}
// Sealing root permissions.
cap_reg()->ResetSealingRoot();
for (uint32_t i = PermissionBits::kPermitGlobal;
i <= PermissionBits::kUserPerm0; i <<= 1) {
auto permissions = cap_reg()->permissions();
cap_reg()->ClearPermissions(i);
auto new_permissions = cap_reg()->permissions();
auto diff = new_permissions ^ permissions;
if (permissions & i) {
EXPECT_EQ(diff, i);
} else {
EXPECT_EQ(diff, 0);
}
}
}
TEST_F(CheriotRegisterTest, Invalidate) {
cap_reg()->ResetNull();
EXPECT_FALSE(cap_reg()->IsValid());
cap_reg()->ResetExecuteRoot();
EXPECT_TRUE(cap_reg()->IsValid());
cap_reg()->Invalidate();
EXPECT_FALSE(cap_reg()->IsValid());
cap_reg()->ResetMemoryRoot();
EXPECT_TRUE(cap_reg()->IsValid());
cap_reg()->Invalidate();
EXPECT_FALSE(cap_reg()->IsValid());
cap_reg()->ResetSealingRoot();
EXPECT_TRUE(cap_reg()->IsValid());
cap_reg()->Invalidate();
EXPECT_FALSE(cap_reg()->IsValid());
}
TEST_F(CheriotRegisterTest, SealDataCapabilities) {
// Create a sealing capability.
auto seal_cap_reg = std::make_unique<CheriotRegister>(arch_state_, "seal");
seal_cap_reg->ResetSealingRoot();
// Try sealing with different object types.
for (uint32_t i = ObjectType::kUnsealed; i <= 16; i++) {
// Set cap_reg top be a memory root capability.
cap_reg()->ResetMemoryRoot();
auto status = cap_reg()->Seal(*seal_cap_reg, i);
// Check to see if i is one of the correct object types, and check the
// status accordingly.
if ((i >= 9) && (i <= 15)) {
EXPECT_TRUE(status.ok()) << status.message();
EXPECT_TRUE(cap_reg()->IsSealed())
<< i << ": " << cap_reg()->tag() << " " << cap_reg()->object_type();
EXPECT_FALSE(cap_reg()->IsUnsealed())
<< i << ": " << cap_reg()->tag() << " " << cap_reg()->object_type();
} else {
EXPECT_FALSE(cap_reg()->IsSealed())
<< i << ": " << cap_reg()->tag() << " " << cap_reg()->object_type();
EXPECT_TRUE(!cap_reg()->tag() || cap_reg()->IsUnsealed())
<< i << ": " << cap_reg()->tag() << " " << cap_reg()->object_type();
}
}
// Change bounds of sealing capability to > than valid object types.
(void)seal_cap_reg->SetBounds(0x100, 0x100);
cap_reg()->ResetMemoryRoot();
auto status = cap_reg()->Seal(*seal_cap_reg, 9);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(), testing::HasSubstr("out of range"))
<< status.message();
// Try with a sealing root with cleared tag.
seal_cap_reg->ResetSealingRoot();
seal_cap_reg->Invalidate();
EXPECT_FALSE(cap_reg()->Seal(*seal_cap_reg, 9).ok());
cap_reg()->ResetMemoryRoot();
seal_cap_reg->ResetSealingRoot();
// Seal the sealing capability. This should succeed.
CHECK_OK(seal_cap_reg->Seal(*seal_cap_reg, 10));
// Now try to seal using the sealed sealing capability. That should fail.
status = cap_reg()->Seal(*seal_cap_reg, 10);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(),
testing::HasSubstr("Cannot seal using a sealed capability"))
<< status.message();
// Try to use a capability without sealing permission.
seal_cap_reg->ResetSealingRoot();
seal_cap_reg->ClearPermissions(PermissionBits::kPermitSeal);
cap_reg()->ResetMemoryRoot();
status = cap_reg()->Seal(*seal_cap_reg, 10);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kPermissionDenied);
EXPECT_THAT(status.message(),
testing::HasSubstr("Missing sealing permission"))
<< status.message();
// Try sealing a null capability.
seal_cap_reg->ResetSealingRoot();
cap_reg()->ResetNull();
status = cap_reg()->Seal(*seal_cap_reg, 10);
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()->ResetMemoryRoot();
CHECK_OK(cap_reg()->Seal(*seal_cap_reg, 10));
status = cap_reg()->Seal(*seal_cap_reg, 10);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(),
testing::HasSubstr("Cannot seal already sealed capability"))
<< status.message();
}
TEST_F(CheriotRegisterTest, SealExecuteCapability) {
// Create a sealing capability.
auto seal_cap_reg = std::make_unique<CheriotRegister>(arch_state_, "seal");
seal_cap_reg->ResetSealingRoot();
// Try sealing with different object types.
for (uint32_t i = ObjectType::kUnsealed; i <= 16; i++) {
// Set cap_reg top be a memory root capability.
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) ||
(i == ObjectType::kSealedExecutable6) ||
(i == ObjectType::kSealedExecutable7)) {
EXPECT_TRUE(status.ok()) << status.message();
EXPECT_TRUE(cap_reg()->IsSealed());
EXPECT_FALSE(cap_reg()->IsUnsealed());
} else {
EXPECT_FALSE(status.ok());
EXPECT_FALSE(cap_reg()->IsSealed());
}
}
// Change bounds of sealing capability to > than valid object types.
(void)seal_cap_reg->SetBounds(0x100, 0x1000);
cap_reg()->ResetExecuteRoot();
auto status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSealedExecutable6);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(
status.message(),
testing::HasSubstr("Sealing capability is not a valid capability"));
// 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());
cap_reg()->ResetExecuteRoot();
seal_cap_reg->ResetSealingRoot();
// Seal the sealing capability. This should succeed.
CHECK_OK(seal_cap_reg->Seal(*seal_cap_reg, 10));
// Now try to seal using the sealed sealing capability. That should fail.
status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSealedExecutable6);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(),
testing::HasSubstr("Cannot seal using a sealed capability"));
// Try to use a capability without sealing permission.
seal_cap_reg->ResetSealingRoot();
seal_cap_reg->ClearPermissions(PermissionBits::kPermitSeal);
cap_reg()->ResetExecuteRoot();
status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kPermissionDenied);
EXPECT_THAT(status.message(),
testing::HasSubstr("Missing sealing permission"));
// Try sealing a null capability.
seal_cap_reg->ResetSealingRoot();
cap_reg()->ResetNull();
status = cap_reg()->Seal(*seal_cap_reg, ObjectType::kSentry);
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);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(),
testing::HasSubstr("Cannot seal already sealed capability"));
}
TEST_F(CheriotRegisterTest, CopyFrom) {
constexpr uint32_t kAddress = 0xdeadbeef;
auto cap_reg_copy = std::make_unique<CheriotRegister>(arch_state_, "copy");
// Copy from null capability.
cap_reg()->ResetNull();
cap_reg()->data_buffer()->Set<uint32_t>(0, kAddress);
cap_reg()->set_object_type(ObjectType::kReserved8);
cap_reg()->set_reserved(1);
cap_reg_copy->CopyFrom(*cap_reg());
EXPECT_FALSE(cap_reg_copy->IsValid());
EXPECT_EQ(cap_reg_copy->data_buffer()->Get<uint32_t>(0), kAddress);
EXPECT_EQ(cap_reg_copy->tag(), cap_reg()->tag());
EXPECT_EQ(cap_reg_copy->top(), cap_reg()->top());
EXPECT_EQ(cap_reg_copy->base(), cap_reg()->base());
EXPECT_EQ(cap_reg_copy->length(), cap_reg()->length());
EXPECT_EQ(cap_reg_copy->permissions(), cap_reg()->permissions());
EXPECT_EQ(cap_reg_copy->object_type(), cap_reg()->object_type());
EXPECT_EQ(cap_reg_copy->reserved(), cap_reg()->reserved());
// Copy from memory root capability.
cap_reg()->ResetMemoryRoot();
cap_reg()->data_buffer()->Set<uint32_t>(0, kAddress);
cap_reg()->set_object_type(ObjectType::kReserved8);
cap_reg()->set_reserved(1);
(void)cap_reg()->SetBounds(kBase, kAddress + 1);
cap_reg_copy->CopyFrom(*cap_reg());
EXPECT_TRUE(cap_reg_copy->IsValid());
EXPECT_EQ(cap_reg_copy->data_buffer()->Get<uint32_t>(0), kAddress);
EXPECT_EQ(cap_reg_copy->tag(), cap_reg()->tag());
EXPECT_EQ(cap_reg_copy->top(), cap_reg()->top());
EXPECT_EQ(cap_reg_copy->base(), cap_reg()->base());
EXPECT_EQ(cap_reg_copy->length(), cap_reg()->length());
EXPECT_EQ(cap_reg_copy->permissions(), cap_reg()->permissions());
EXPECT_EQ(cap_reg_copy->object_type(), cap_reg()->object_type());
// Copy from execute root capability.
cap_reg()->ResetExecuteRoot();
cap_reg()->data_buffer()->Set<uint32_t>(0, kAddress);
cap_reg()->set_object_type(ObjectType::kReserved8);
cap_reg()->set_reserved(1);
(void)cap_reg()->SetBounds(kBase, kAddress + 1);
cap_reg_copy->CopyFrom(*cap_reg());
EXPECT_TRUE(cap_reg_copy->IsValid());
EXPECT_EQ(cap_reg_copy->data_buffer()->Get<uint32_t>(0), kAddress);
EXPECT_EQ(cap_reg_copy->tag(), cap_reg()->tag());
EXPECT_EQ(cap_reg_copy->top(), cap_reg()->top());
EXPECT_EQ(cap_reg_copy->base(), cap_reg()->base());
EXPECT_EQ(cap_reg_copy->length(), cap_reg()->length());
EXPECT_EQ(cap_reg_copy->permissions(), cap_reg()->permissions());
EXPECT_EQ(cap_reg_copy->object_type(), cap_reg()->object_type());
// Copy from sealing root capability.
cap_reg()->ResetSealingRoot();
cap_reg()->data_buffer()->Set<uint32_t>(0, kAddress);
cap_reg()->set_object_type(ObjectType::kReserved8);
cap_reg()->set_reserved(1);
(void)cap_reg()->SetBounds(kBase, kAddress + 1);
cap_reg_copy->CopyFrom(*cap_reg());
EXPECT_TRUE(cap_reg_copy->IsValid());
EXPECT_EQ(cap_reg_copy->data_buffer()->Get<uint32_t>(0), kAddress);
EXPECT_EQ(cap_reg_copy->tag(), cap_reg()->tag());
EXPECT_EQ(cap_reg_copy->top(), cap_reg()->top());
EXPECT_EQ(cap_reg_copy->base(), cap_reg()->base());
EXPECT_EQ(cap_reg_copy->length(), cap_reg()->length());
EXPECT_EQ(cap_reg_copy->permissions(), cap_reg()->permissions());
EXPECT_EQ(cap_reg_copy->object_type(), cap_reg()->object_type());
}
// Test compress/expand of bounds.
TEST_F(CheriotRegisterTest, CompressExpand) {
// First some random combinations.
for (int i = 0; i < 1000; i++) {
cap_reg()->ResetMemoryRoot();
// Generate random address and compressed capability.
uint32_t address = absl::Uniform(absl::IntervalClosed, bitgen_, 0ULL,
std::numeric_limits<uint32_t>::max());
uint32_t compressed = absl::Uniform(absl::IntervalClosed, bitgen_, 0ULL,
std::numeric_limits<uint32_t>::max());
// Expand the capability, get the base and top, then compress it again.
cap_reg()->Expand(address, compressed, true);
uint32_t base = cap_reg()->base();
uint64_t top = cap_reg()->top();
uint32_t re_compressed = cap_reg()->Compress();
// The starting compressed value should be the same as the re-compressed.
EXPECT_EQ(re_compressed, compressed) << absl::StrCat(
"address: ", absl::Hex(address, absl::kZeroPad8),
" compressed: ", absl::Hex(compressed, absl::kZeroPad8),
" re-compressed: ", absl::Hex(re_compressed, absl::kZeroPad8));
cap_reg()->ResetMemoryRoot();
// Expand the re-compressed capability. The base and top should be the same.
cap_reg()->Expand(address, re_compressed, true);
EXPECT_EQ(base, cap_reg()->base()) << absl::StrCat(
"address: ", absl::Hex(address, absl::kZeroPad8),
" compressed: ", absl::Hex(compressed, absl::kZeroPad8),
" re-compressed: ", absl::Hex(re_compressed, absl::kZeroPad8));
EXPECT_EQ(top, cap_reg()->top()) << absl::StrCat(
"address: ", absl::Hex(address, absl::kZeroPad8),
" compressed: ", absl::Hex(compressed, absl::kZeroPad8),
" re-compressed: ", absl::Hex(re_compressed, absl::kZeroPad8));
}
for (uint32_t length_exp = 0; length_exp <= 32; length_exp++) {
cap_reg()->ResetMemoryRoot();
uint64_t length = 1ULL << length_exp;
cap_reg()->data_buffer()->Set<uint32_t>(0, kBase);
uint64_t top = kBase + length;
if (top > 0x1'0000'0000) {
length = 0x1'0000'0000 - kBase;
}
// Set bounds.
(void)cap_reg()->SetBounds(kBase, length);
// Get the base, top, and length.
uint32_t cap_base = cap_reg()->base();
uint64_t cap_top = cap_reg()->top();
uint64_t cap_length = cap_reg()->length();
// Compress the capability.
uint32_t compressed = cap_reg()->Compress();
// Expand the capability. Make sure the base, top, and length are the same.
cap_reg()->Expand(kBase, compressed, true);
EXPECT_TRUE(cap_reg()->IsValid())
<< absl::StrCat("length_exp: ", length_exp);
EXPECT_EQ(cap_reg()->base(), cap_base)
<< absl::StrCat(length_exp, " base: ", absl::Hex(cap_reg()->base()),
" cap_base: ", absl::Hex(cap_base));
EXPECT_EQ(cap_reg()->top(), cap_top)
<< absl::StrCat(length_exp, " top: ", absl::Hex(cap_reg()->top()),
" cap_top: ", absl::Hex(cap_top));
EXPECT_EQ(cap_reg()->length(), cap_length)
<< absl::StrCat(length_exp, " length: ", absl::Hex(cap_reg()->length()),
" cap_length: ", absl::Hex(cap_length));
}
}
} // namespace