| // 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 <string> |
| #include <utility> |
| |
| #include "absl/numeric/bits.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/string_view.h" |
| #include "mpact/sim/generic/arch_state.h" |
| #include "mpact/sim/generic/register.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace cheriot { |
| |
| using PermissionFormats = CheriotRegister::PermissionFormats; |
| |
| // Helper function to extract a bit range from a uint64_t given msb and lsb. |
| template <typename T> |
| static inline T Extract(T value, int msb, int lsb) { |
| T shift = lsb; |
| T mask = (1ULL << (msb - lsb + 1)) - 1; |
| return (value >> shift) & mask; |
| } |
| |
| CheriotRegister::CheriotRegister(generic::ArchState *state, |
| absl::string_view name) |
| : generic::Register<uint32_t>(state, name) { |
| ResetNull(); |
| } |
| |
| void CheriotRegister::ResetNull() { |
| // When called by the constructor, the data buffer may be nullptr. |
| if (data_buffer() != nullptr) { |
| data_buffer()->Set<uint32_t>(0, 0); |
| } |
| Clear(); |
| } |
| |
| void CheriotRegister::ResetMemoryRoot() { |
| set_base(0); |
| set_top(0x1'0000'0000ULL); |
| set_permissions_format(kMemoryCapReadWrite); |
| set_permissions(kPermitGlobal | kPermitLoad | kPermitStore | |
| kPermitLoadStoreCapability | kPermitStoreLocalCapability | |
| kPermitLoadGlobal | kPermitLoadMutable); |
| set_object_type(kUnsealed); |
| set_reserved(0); |
| set_tag(true); |
| is_dirty_ = true; |
| is_null_ = false; |
| exponent_ = 24; |
| raw_ = Compress(); |
| } |
| |
| void CheriotRegister::ResetExecuteRoot() { |
| set_base(0); |
| set_top(0x1'0000'0000ULL); |
| set_permissions_format(kExecutable); |
| set_permissions(kPermitGlobal | kPermitExecute | kPermitLoad | |
| kPermitLoadStoreCapability | kPermitLoadGlobal | |
| kPermitLoadMutable | kPermitAccessSystemRegisters); |
| set_object_type(kUnsealed); |
| set_reserved(0); |
| set_tag(true); |
| is_dirty_ = true; |
| is_null_ = false; |
| exponent_ = 24; |
| raw_ = Compress(); |
| } |
| |
| void CheriotRegister::ResetSealingRoot() { |
| set_base(0); |
| set_top(0x1'0000'0000ULL); |
| set_permissions_format(kSealing); |
| set_permissions(kPermitGlobal | kPermitSeal | kPermitUnseal | kUserPerm0); |
| set_object_type(kUnsealed); |
| set_reserved(0); |
| set_tag(true); |
| is_dirty_ = true; |
| is_null_ = false; |
| exponent_ = 24; |
| raw_ = Compress(); |
| } |
| |
| void CheriotRegister::Clear() { |
| set_base(0); |
| set_top(0); |
| set_permissions_format(kSealing); |
| set_permissions(0); |
| set_object_type(kUnsealed); |
| set_reserved(0); |
| set_tag(false); |
| is_dirty_ = true; |
| is_null_ = false; |
| raw_ = 0; |
| exponent_ = 0; |
| } |
| |
| void CheriotRegister::ClearPermissions(uint32_t permission_bits) { |
| set_permissions(permissions() & ~permission_bits); |
| set_permissions(ExpandPermissions(CompressPermissions())); |
| } |
| |
| bool CheriotRegister::SetBounds(uint32_t req_base, uint64_t req_length) { |
| if (is_null_) { |
| Expand(address(), 0, /*tag=*/false); |
| is_null_ = false; |
| } |
| // Compute the requested top based on base and length. |
| uint64_t new_top = static_cast<uint64_t>(req_base) + req_length; |
| uint32_t exp; |
| // Determine the appropriate exponent in order to perform proper rounding |
| // of base and top. |
| if (req_length >= 0x1'0000'0000ULL) { |
| exp = 24; |
| } else { |
| exp = 23 - absl::countl_zero(static_cast<uint32_t>(req_length) | 0x1ff); |
| if (exp > 14) exp = 24; |
| } |
| if (exp == 0) { |
| set_base(req_base); |
| set_top(req_base + req_length); |
| exponent_ = 0; |
| raw_ = Compress(); |
| return true; /* exact */ |
| } |
| |
| // Adjust base and top as needed. |
| uint64_t exp_mask = (1ULL << exp) - 1; |
| uint64_t new_base = req_base & ~exp_mask; |
| uint64_t new_top_correction = |
| static_cast<uint64_t>((new_top & exp_mask) != 0); |
| new_top &= ~exp_mask; |
| // Correct for any truncation in top if top was rounded down. |
| new_top += new_top_correction << exp; |
| // Recompute the length based on the rounded base and top. |
| uint64_t new_length = new_top - new_base; |
| uint32_t new_exp; |
| if (new_length >= 0x1'0000'0000ULL) { |
| new_exp = 24; |
| } else { |
| new_exp = 23 - absl::countl_zero(static_cast<uint32_t>(new_length) | 0x1ff); |
| if (new_exp > 14) new_exp = 24; |
| } |
| exponent_ = new_exp; |
| // Check if the rounding of base and top increased the length so much that it |
| // now requires a larger exponent. If so, recompute base and top. This can |
| // only happen once, so no need to recheck. |
| if (new_exp > exp) { |
| new_top = static_cast<uint64_t>(new_base) + new_length; |
| if (tag() && (new_top > 0x1'0000'0000ULL)) new_top = 0x1'0000'0000ULL; |
| exp_mask = (1ULL << new_exp) - 1; |
| // Adjust base and top as needed. |
| new_base &= ~exp_mask; |
| new_top_correction = static_cast<uint64_t>((new_top & exp_mask) != 0); |
| new_top &= ~exp_mask; |
| // Correct for any truncation. |
| new_top += new_top_correction << exp; |
| } |
| // Set the top and base. |
| set_top(new_top); |
| set_base(new_base); |
| raw_ = Compress(); |
| // If the address is not in bounds, clear the tag. |
| if ((address() > top()) || (address() < base())) Invalidate(); |
| // If the length and the base are the same as requested, the bounds were |
| // set exactly. |
| return (req_base == new_base) && (new_length == req_length); |
| } |
| |
| std::pair<uint32_t, uint64_t> CheriotRegister::ComputeBounds() { |
| if (is_null_) { |
| Expand(address(), 0, /*tag=*/false); |
| } |
| uint64_t base_bits = raw_ & 0x1ff; |
| uint64_t top_bits = (raw_ >> 9) & 0x1ff; |
| uint64_t a_mid = (address() >> exponent_) & 0x1ff; |
| uint64_t a_hi = (a_mid < base_bits ? 1 : 0); |
| uint64_t t_hi = top_bits < base_bits ? 1 : 0; |
| uint64_t c_b = 0 - a_hi; |
| uint64_t c_t = t_hi - a_hi; |
| uint64_t address64 = address(); |
| uint64_t a_top = address64 >> (exponent_ + 9); |
| uint64_t base = ((a_top + c_b) << (exponent_ + 9)) | (base_bits << exponent_); |
| base &= 0xffff'ffff; |
| uint64_t top = ((a_top + c_t) << (exponent_ + 9)) | (top_bits << exponent_); |
| top &= 0x1'ffff'ffffULL; |
| return std::make_pair(static_cast<uint32_t>(base), top); |
| } |
| |
| uint32_t CheriotRegister::Compress() const { |
| if (is_null_) return 0; |
| |
| uint32_t compressed = 0; |
| // Compute the compressed permissions representation. |
| uint32_t permissions_field = CompressPermissions(); |
| // Only store the low 3 bits of the object type. The fourth is implied based |
| // on the capability type. |
| compressed |= (object_type() & 0b111) << 22; |
| compressed |= permissions_field << 25; |
| compressed |= reserved_ << 31; |
| // If the expanded capability is not dirty, we don't have to re-do the |
| // exponent and top/base computations. |
| if (!is_dirty_) { |
| compressed |= raw_ & 0x3f'ffff; |
| return compressed; |
| } |
| // Compute exponent. |
| uint32_t exp; |
| if (length() >= 0x1'0000'0000ULL) { |
| exp = 24; |
| } else { |
| exp = 23 - absl::countl_zero(static_cast<uint32_t>(length()) | 0x1ff); |
| } |
| uint32_t exp_field = exp; |
| // Any exponent over 14 gets set to 24 (the max). |
| if (exp > 14) { |
| exp = 24; |
| exp_field = 0xf; |
| } |
| // Get the bounds. Note, there is no need for any specific rounding, since |
| // bounds are automatically rounded by SetBounds. |
| uint32_t top_field = (top() >> exp) & 0x1ff; |
| uint32_t base_field = (base() >> exp) & 0x1ff; |
| // Assemble the fields. |
| compressed |= base_field; |
| compressed |= top_field << 9; |
| compressed |= exp_field << 18; |
| return compressed; |
| } |
| |
| void CheriotRegister::Expand(uint32_t address, uint32_t compressed, bool tag) { |
| // Extract bit fields. |
| uint64_t top_9 = Extract(compressed, kTop[0], kTop[1]); |
| uint32_t base_9 = Extract(compressed, kBase[0], kBase[1]); |
| uint32_t exp = Extract(compressed, kExponent[0], kExponent[1]); |
| if (exp == 15) exp = 24; |
| uint64_t a_top = static_cast<uint64_t>(address) >> (exp + 9); |
| uint32_t a_mid = Extract(address, exp + 8, exp); |
| |
| // Compute correction factors. |
| uint64_t a_hi = a_mid < base_9 ? 1 : 0; |
| uint64_t t_hi = top_9 < base_9 ? 1 : 0; |
| uint64_t c_b = 0 - a_hi; |
| uint64_t c_t = t_hi - a_hi; |
| uint64_t new_base64 = ((a_top + c_b) << (exp + 9)) | (base_9 << exp); |
| uint64_t new_top = ((a_top + c_t) << (exp + 9)) | (top_9 << exp); |
| |
| // Only use the lower 32 bits. |
| uint64_t new_base = static_cast<uint32_t>(new_base64); |
| // Expand permissions. |
| uint32_t compressed_permissions = |
| Extract(compressed, kPermissions[0], kPermissions[1]); |
| uint32_t new_permissions = ExpandPermissions(compressed_permissions); |
| |
| // Set the fields. |
| if (tag) { |
| set_base(0); |
| set_top(0x1'0000'0000ULL); |
| (void)SetBounds(new_base, new_top - new_base64); |
| } else { |
| set_base(new_base); |
| set_top(new_top & 0x1'ffff'ffffULL); |
| } |
| exponent_ = exp; |
| uint32_t obj_type = Extract(compressed, kObjectType[0], kObjectType[1]); |
| if (obj_type && (new_permissions & kPermitExecute) == 0) { |
| // Bit 3 of the object type is implied by the capability type. For non |
| // execute capabilities, set it to one. |
| obj_type |= 0b1'000; |
| } |
| set_object_type(obj_type); |
| set_permissions(ExpandPermissions(compressed_permissions)); |
| set_reserved(Extract(compressed, kReserved[0], kReserved[1])); |
| set_tag(tag); |
| data_buffer()->Set<uint32_t>(0, address); |
| raw_ = compressed; |
| is_dirty_ = false; |
| is_null_ = false; |
| raw_ = compressed; |
| } |
| |
| void CheriotRegister::Validate() { |
| if (!tag() | is_null_) return; |
| |
| // The capability is still valid if the address is representable. |
| set_tag(IsRepresentable()); |
| } |
| |
| bool CheriotRegister::IsValid() const { |
| if (!tag() || is_null_) return false; |
| uint32_t address = data_buffer()->Get<uint32_t>(0); |
| return (address < top()) && (address >= base()); |
| } |
| |
| bool CheriotRegister::IsSealed() const { |
| 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) || |
| (object_type() == kSealedExecutable6) || |
| (object_type() == kSealedExecutable7); |
| } else { |
| return ((object_type() >= 9) && (object_type() <= 15)); |
| } |
| } |
| |
| absl::Status CheriotRegister::Seal(const CheriotRegister &source, |
| uint32_t obj_type) { |
| if (is_null_) { |
| Expand(address(), 0, /*tag=*/false); |
| is_null_ = false; |
| } |
| absl::Status status = absl::OkStatus(); |
| // Check that the conditions are correct for sealing the target capability. |
| if (!tag()) { |
| status = absl::InvalidArgumentError("Target is not a valid capability"); |
| } else if (IsSealed()) { |
| status = |
| absl::InvalidArgumentError("Cannot seal already sealed capability"); |
| } else if (!source.tag()) { |
| status = absl::InvalidArgumentError( |
| "Sealing capability is not a valid capability"); |
| } else if (source.IsSealed()) { |
| status = |
| absl::InvalidArgumentError("Cannot seal using a sealed capability"); |
| } else if ((source.permissions() & PermissionBits::kPermitSeal) == 0) { |
| status = absl::PermissionDeniedError("Missing sealing permission"); |
| } else if (!source.IsValid()) { |
| status = |
| absl::InvalidArgumentError("Sealing capability address out of range"); |
| } |
| |
| // Different sealing values for memory and execute capabilities. |
| if (status.ok()) { |
| if (HasPermission(PermissionBits::kPermitExecute)) { |
| switch (obj_type) { |
| case kSentry: |
| case kInterruptEnablingSentry: |
| case kInterruptDisablingSentry: |
| case kInterruptEnablingReturnSentry: |
| case kInterruptDisablingReturnSentry: |
| case kSealedExecutable6: |
| case kSealedExecutable7: |
| // The sealing type is ok. |
| break; |
| default: |
| // Invalidate if the capability does not have execute permission. |
| status = absl::InvalidArgumentError( |
| "Invalid object type for executable capability"); |
| break; |
| } |
| } else { |
| // Invalidate if the object type is out of range. |
| if ((obj_type <= 0b1000) || (obj_type > 0b1111)) { |
| status = absl::InvalidArgumentError( |
| "Invalid object type for non-execute capability"); |
| } |
| } |
| } |
| // Keep object type 1 wider than the compressed object type. |
| obj_type &= (1 << (kObjectType[0] - kObjectType[1] + 1 + 1)) - 1; |
| set_object_type(obj_type); |
| if (!status.ok()) Invalidate(); |
| return status; |
| } |
| |
| absl::Status CheriotRegister::Unseal(const CheriotRegister &source, |
| uint32_t obj_type) { |
| if (is_null_) { |
| Expand(address(), 0, /*tag=*/false); |
| is_null_ = false; |
| } |
| // Check that the conditions are correct for unsealing the target capability. |
| auto status = absl::OkStatus(); |
| if (!tag()) { |
| status = absl::InvalidArgumentError("Target is not a valid capability"); |
| } else if (IsUnsealed()) { |
| status = |
| absl::InvalidArgumentError("Cannot unseal already unsealed capability"); |
| } else if (!source.tag()) { |
| status = absl::InvalidArgumentError( |
| "Unsealing capability is not a valid capability"); |
| } else if (source.IsSealed()) { |
| status = |
| absl::InvalidArgumentError("Cannot unseal using a sealed capability"); |
| } else if ((source.permissions() & PermissionBits::kPermitUnseal) == 0) { |
| status = absl::PermissionDeniedError("Missing unsealing permission"); |
| } else if (!source.IsValid()) { |
| status = |
| absl::InvalidArgumentError("Unsealing capability address out of range"); |
| } else if (obj_type != object_type()) { |
| status = |
| absl::InvalidArgumentError("Unsealing capability object type mismatch"); |
| } |
| // Unseal the capability. |
| set_object_type(kUnsealed); |
| return status; |
| } |
| |
| bool CheriotRegister::IsUnsealed() const { |
| return tag() && (object_type() == kUnsealed); |
| } |
| |
| bool CheriotRegister::IsRepresentable() const { |
| if (exponent_ == 24) return true; |
| uint64_t address = data_buffer()->Get<uint32_t>(0); |
| uint64_t cap_base = base(); |
| return (cap_base <= address) && |
| (address < (cap_base + (1ULL << (exponent_ + 9)))); |
| } |
| |
| bool CheriotRegister::IsSentry() const { |
| return !is_null_ && (object_type() >= kSentry) && |
| (object_type() <= kInterruptEnablingReturnSentry); |
| } |
| |
| bool CheriotRegister::IsBackwardSentry() const { |
| return !is_null_ && ((object_type() == kInterruptEnablingReturnSentry) || |
| (object_type() == kInterruptDisablingReturnSentry)); |
| } |
| |
| void CheriotRegister::CopyFrom(const CheriotRegister &other) { |
| data_buffer()->CopyFrom(other.data_buffer()); |
| if (other.is_null_) { |
| Expand(address(), 0, /*tag=*/false); |
| return; |
| } |
| is_null_ = false; |
| set_tag(other.tag()); |
| set_top(other.top()); |
| set_base(other.base()); |
| set_permissions(other.permissions()); |
| set_object_type(other.object_type()); |
| set_reserved(other.reserved()); |
| exponent_ = other.exponent(); |
| is_dirty_ = other.is_dirty_; |
| raw_ = other.raw_; |
| } |
| |
| bool CheriotRegister::operator==(const CheriotRegister &other) const { |
| return (tag() == other.tag()) && (top() == other.top()) && |
| (base() == other.base()) && (permissions() == other.permissions()) && |
| (object_type() == other.object_type()) && |
| (reserved() == other.reserved()); |
| } |
| |
| bool CheriotRegister::IsMemoryEqual(const CheriotRegister &other) const { |
| return (address() == other.address()) && (Compress() == other.Compress()); |
| } |
| |
| void CheriotRegister::SetAddress(uint32_t address) { |
| if (!tag()) { |
| data_buffer()->Set<uint32_t>(0, address); |
| uint64_t mask = ~((1ULL << (exponent_ + 9)) - 1); |
| auto len = length(); |
| set_base(address & mask); |
| set_top(static_cast<uint64_t>(base()) + len); |
| return; |
| } |
| set_address(address); |
| } |
| |
| // Create a text representation of the capability. |
| std::string CheriotRegister::AsString() const { |
| std::string output; |
| absl::StrAppend(&output, absl::StrFormat("0x%08X", address())); |
| absl::StrAppend( |
| &output, " (v:", tag() ? "1 " : "0 ", absl::StrFormat("0x%08X", base()), |
| "-", absl::StrFormat("0x%09X", top()), |
| " l:", absl::StrFormat("0x%09X", length()), " o:", |
| absl::StrFormat( |
| "0x%X", |
| object_type() | |
| ((object_type() && !(permissions() & kPermitExecute)) ? 0x8 |
| : 0x0)), |
| " p:", permissions() & kPermitGlobal ? "G " : "- ", |
| permissions() & kPermitLoad ? "R" : "-", |
| permissions() & kPermitStore ? "W" : "-", |
| permissions() & kPermitLoadStoreCapability ? "c" : "-", |
| permissions() & kPermitLoadMutable ? "m" : "-", |
| permissions() & kPermitLoadGlobal ? "g" : "-", |
| permissions() & kPermitStoreLocalCapability ? "l " : "- ", |
| permissions() & kPermitExecute ? "X" : "-", |
| permissions() & kPermitAccessSystemRegisters ? "a " : "- ", |
| permissions() & kPermitSeal ? "S" : "-", |
| permissions() & kPermitUnseal ? "U" : "-", |
| permissions() & kUserPerm0 ? "0)" : "-)"); |
| return output; |
| } |
| |
| // Determine the current permission format. |
| PermissionFormats CheriotRegister::GetPermissionFormat() const { |
| if (is_null_) return kSealing; |
| // Check to see if it is an executable permission format. |
| if ((permissions() & kImpliedCapabilities[kExecutable]) == |
| kImpliedCapabilities[kExecutable]) { |
| return kExecutable; |
| } |
| |
| // Check to see if it is a mem_cap_rw format. |
| if ((permissions() & kImpliedCapabilities[kMemoryCapReadWrite]) == |
| kImpliedCapabilities[kMemoryCapReadWrite]) { |
| return kMemoryCapReadWrite; |
| } |
| |
| // Check to see if it is a mem_cap_ro format. |
| if ((permissions() & kImpliedCapabilities[kMemoryCapReadOnly]) == |
| kImpliedCapabilities[kMemoryCapReadOnly]) { |
| return kMemoryCapReadOnly; |
| } |
| |
| // Check to see if it is a mem_cap_wo format. |
| if ((permissions() & kImpliedCapabilities[kMemoryCapWriteOnly]) == |
| kImpliedCapabilities[kMemoryCapWriteOnly]) { |
| return kMemoryCapWriteOnly; |
| } |
| |
| // Check to see if is a memory data only format. |
| if ((permissions() & kPermitLoad) || (permissions() & kPermitStore)) { |
| return kMemoryDataOnly; |
| } |
| |
| return kSealing; |
| } |
| |
| uint32_t CheriotRegister::CompressPermissions() const { |
| // Determine the target format based on the currently set permissions. |
| if (is_null_) return 0; |
| |
| auto format = GetPermissionFormat(); |
| uint32_t compressed; |
| switch (format) { |
| case kMemoryCapReadWrite: |
| compressed = 0b0'11'000; |
| compressed |= ((permissions() & kPermitGlobal) != 0) << 5; |
| compressed |= ((permissions() & kPermitStoreLocalCapability) != 0) << 2; |
| compressed |= ((permissions() & kPermitLoadMutable) != 0) << 1; |
| compressed |= ((permissions() & kPermitLoadGlobal) != 0); |
| return compressed; |
| case kMemoryCapReadOnly: |
| compressed = 0b0'101'00; |
| compressed |= ((permissions() & kPermitGlobal) != 0) << 5; |
| compressed |= ((permissions() & kPermitLoadMutable) != 0) << 1; |
| compressed |= ((permissions() & kPermitLoadGlobal) != 0); |
| return compressed; |
| case kMemoryCapWriteOnly: |
| compressed = 0b0'10000; |
| compressed |= ((permissions() & kPermitGlobal) != 0) << 5; |
| return compressed; |
| case kExecutable: |
| compressed = 0b0'01'000; |
| compressed |= ((permissions() & kPermitGlobal) != 0) << 5; |
| compressed |= ((permissions() & kPermitAccessSystemRegisters) != 0) << 2; |
| compressed |= ((permissions() & kPermitLoadMutable) != 0) << 1; |
| compressed |= ((permissions() & kPermitLoadGlobal) != 0); |
| return compressed; |
| case kMemoryDataOnly: |
| compressed = 0b0'100'00; |
| compressed |= ((permissions() & kPermitGlobal) != 0) << 5; |
| compressed |= ((permissions() & kPermitLoad) != 0) << 1; |
| compressed |= ((permissions() & kPermitStore) != 0); |
| return compressed; |
| default: |
| compressed = 0b0'00'000; |
| compressed |= ((permissions() & kPermitGlobal) != 0) << 5; |
| compressed |= ((permissions() & kUserPerm0) != 0) << 2; |
| compressed |= ((permissions() & kPermitSeal) != 0) << 1; |
| compressed |= ((permissions() & kPermitUnseal) != 0); |
| return compressed; |
| } |
| } |
| |
| uint32_t CheriotRegister::ExpandPermissions(uint32_t compressed) const { |
| // Determine the source compressed format based on table lookup. |
| PermissionFormats format = kPermissionFormat[compressed & 0x1f]; |
| // First add the implied capabilities using table lookup. |
| uint32_t expanded = kImpliedCapabilities[format]; |
| // Add the global format if present. |
| expanded |= compressed & 0b1'00000 ? kPermitGlobal : 0; |
| // Perform permission expansion based on format using table lookup. |
| switch (format) { |
| case kSealing: |
| expanded |= kExpandSealed[compressed & 0b111]; |
| return expanded; |
| case kExecutable: |
| expanded |= kExpandExecutable[compressed & 0b111]; |
| return expanded; |
| case kMemoryCapWriteOnly: |
| // Only implied permissions, so just return the expanded value. |
| return expanded; |
| case kMemoryDataOnly: |
| expanded |= kExpandMemoryDataOnly[compressed & 0b11]; |
| return expanded; |
| case kMemoryCapReadOnly: |
| expanded |= kExpandMemoryCapReadOnly[compressed & 0b11]; |
| return expanded; |
| case kMemoryCapReadWrite: |
| expanded |= kExpandMemoryCapReadWrite[compressed & 0b111]; |
| return expanded; |
| } |
| } |
| |
| } // namespace cheriot |
| } // namespace sim |
| } // namespace mpact |