| /* |
| * 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. |
| */ |
| |
| #ifndef MPACT_CHERIOT__CHERIOT_REGISTER_H_ |
| #define MPACT_CHERIOT__CHERIOT_REGISTER_H_ |
| |
| // This file contains the definition of the Capability class used to implement |
| // the CHERI RiscV IoT capability. |
| |
| #include <cstdint> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/status/status.h" |
| #include "absl/strings/string_view.h" |
| #include "mpact/sim/generic/register.h" |
| |
| // This file defines the CHERI RiscV 64 bit capability register for use in |
| // a unified register file as described in CHERIoT. |
| |
| namespace mpact { |
| namespace sim { |
| namespace cheriot { |
| |
| class CheriotState; |
| |
| class CheriotRegister : public generic::Register<uint32_t> { |
| public: |
| // Set of permission bits in uncompressed view. |
| enum PermissionBits : uint32_t { |
| kPermitNone = 0, |
| kPermitGlobal = 1 << 0, |
| kPermitLoadGlobal = 1 << 1, |
| kPermitStore = 1 << 2, |
| kPermitLoadMutable = 1 << 3, |
| kPermitStoreLocalCapability = 1 << 4, |
| kPermitLoad = 1 << 5, |
| kPermitLoadStoreCapability = 1 << 6, |
| kPermitAccessSystemRegisters = 1 << 7, |
| kPermitExecute = 1 << 8, |
| kPermitUnseal = 1 << 9, |
| kPermitSeal = 1 << 10, |
| kUserPerm0 = 1 << 11, |
| kPermitMask = (1 << 12) - 1, |
| }; |
| // In the compressed representation of the capability, the permission bits |
| // are stored in one of the below compressed formats. |
| enum PermissionFormats : uint32_t { |
| kMemoryCapReadWrite, |
| kMemoryCapReadOnly, |
| kMemoryCapWriteOnly, |
| kMemoryDataOnly, |
| kExecutable, |
| kSealing, |
| }; |
| // Special object types. |
| enum ObjectType : uint32_t { |
| kUnsealed = 0, |
| kSentry = 1, |
| kInterruptDisablingSentry = 2, |
| kInterruptEnablingSentry = 3, |
| kInterruptDisablingReturnSentry = 4, |
| kInterruptEnablingReturnSentry = 5, |
| kSealedExecutable6 = 6, |
| kSealedExecutable7 = 7, |
| kReserved8 = 8, |
| // 9-15 are sealed non-executable capabilities. |
| }; |
| |
| static constexpr uint32_t kNullCapability = 0; |
| static constexpr unsigned kCapabilitySizeInBytes = 8; |
| static constexpr unsigned kGranuleShift = 3; |
| |
| // Value type of this register class. |
| using ValueType = uint32_t; |
| |
| // {msb, lsb} pairs of fields in the compressed capability. |
| // Bit layout: |
| // |3|3 2|2 2|2 1|1 | | |
| // |1|0 5|4 2|1 8|7 9|8 0| |
| // R perm otype exp base top |
| // |
| static constexpr int kBase[2] = {8, 0}; |
| static constexpr int kTop[2] = {17, 9}; |
| static constexpr int kExponent[2] = {21, 18}; |
| static constexpr int kObjectType[2] = {24, 22}; |
| static constexpr int kPermissions[2] = {30, 25}; |
| static constexpr int kReserved[2] = {31, 31}; |
| |
| // Constructor. Default constructors and assignment are disabled. |
| CheriotRegister(generic::ArchState *state, absl::string_view name); |
| CheriotRegister() = delete; |
| CheriotRegister(const CheriotRegister &) = delete; |
| CheriotRegister &operator=(const CheriotRegister &) = delete; |
| |
| // These functions set the capability register to a known state, either the |
| // null capability, or one of the three root capabilities. All capabilities |
| // have to be derived from a root capability. |
| void ResetNull(); |
| void ResetMemoryRoot(); |
| void ResetExecuteRoot(); |
| void ResetSealingRoot(); |
| // Return true if the capability register has the given permission. |
| bool HasPermission(uint32_t permission_bits) const { |
| return (permissions() & permission_bits) != 0; |
| } |
| // Clears the capability as a null capability, but does not change the |
| // address. |
| void Clear(); |
| // Removes one or more permission from the current capability. |
| void ClearPermissions(uint32_t permission_bits); |
| // Clear the tag - invalidates the capability. |
| void ClearTag(); |
| // Compute the bounds. |
| std::pair<uint32_t, uint64_t> ComputeBounds(); |
| // Get the compressed representation of the capability. |
| uint32_t Compress() const; |
| // Expand the compressed capability representation. |
| void Expand(uint32_t address, uint32_t compressed, bool tag); |
| // If the address is out of range, invalidate the tag. |
| void Validate(); |
| // Return true if the current capability is valid, i.e., tag is true and |
| // the address is in range. |
| bool IsValid() const; |
| // Is representable. |
| bool IsRepresentable() const; |
| // Seal (unseal) the current capability based on permissions and address field |
| // in source. Returns ok status if the operation is successful. |
| absl::Status Seal(const CheriotRegister &source, uint32_t obj_type); |
| absl::Status Unseal(const CheriotRegister &source, uint32_t obj_type); |
| // Returns true if the capability is sealed, i.e., that the object type is set |
| // to a valid, non-reserved object type. |
| bool IsSealed() const; |
| // Returns true if the capability is unsealed, i.e., that the object type is |
| // zero. |
| bool IsUnsealed() const; |
| // Returns true if the capability is a sentry. |
| bool IsSentry() const; |
| // Returns true if the capability is a backward sentry. |
| bool IsBackwardSentry() const; |
| // Clears the tag. |
| void Invalidate() { set_tag(false); } |
| // 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); |
| // 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()) && |
| (top() >= (uint64_t)cap_address + (uint64_t)size); |
| } |
| // Copy fields from other capability register. |
| void CopyFrom(const CheriotRegister &other); |
| // Equal operator. |
| bool operator==(const CheriotRegister &other) const; |
| bool IsMemoryEqual(const CheriotRegister &other) const; |
| // Text representation. |
| std::string AsString() const; |
| // Update address with change to base and top as needed. |
| void SetAddress(uint32_t address); |
| // Accessors. |
| bool tag() const { return is_null_ ? false : tag_; } |
| void set_tag(bool tag) { tag_ = tag; } |
| |
| uint32_t address() const { return data_buffer()->Get<uint32_t>(0); } |
| void set_address(uint32_t address) { |
| data_buffer()->Set<uint32_t>(0, address); |
| Validate(); |
| } |
| |
| uint64_t top() const { return is_null_ ? address() & ~0x1ffULL : top_; } |
| uint32_t base() const { return is_null_ ? address() & ~0x1ffULL : base_; } |
| uint64_t length() const { |
| // Length is only 33 bits, so mask off the value. |
| return is_null_ ? 0 : (top_ - base_) & 0x1'ffff'ffffULL; |
| } |
| uint32_t exponent() const { return is_null_ ? 0 : exponent_; } |
| uint32_t permissions() const { return is_null_ ? 0 : permissions_; } |
| void set_permissions(uint32_t permissions) { permissions_ = permissions; } |
| |
| uint32_t object_type() const { return is_null_ ? 0 : object_type_; } |
| void set_object_type(uint32_t object_type) { |
| object_type_ = object_type & 0xf; |
| } |
| |
| uint32_t reserved() const { return is_null_ ? 0 : reserved_; } |
| void set_reserved(uint32_t reserved) { reserved_ = reserved & 0x1; } |
| |
| bool is_null() const { return is_null_; } |
| void set_is_null() { is_null_ = true; } |
| |
| private: |
| // These are the capabilities in each compressed capability permission format |
| // that are writable. |
| static constexpr uint32_t kWritableCapabilites[] = { |
| /* kMemoryCapReadWrite */ kPermitGlobal | kPermitStoreLocalCapability | |
| kPermitLoadMutable | kPermitLoadGlobal, |
| /* kMemoryCapReadOnly */ kPermitGlobal | kPermitLoadMutable | |
| kPermitLoadGlobal, |
| /* kMemoryCapWriteOnly */ kPermitGlobal, |
| /* kMemoryDataOnly */ kPermitGlobal | kPermitLoad | kPermitStore, |
| /* kExecutable */ kPermitGlobal | kPermitAccessSystemRegisters | |
| kPermitLoadMutable | kPermitLoadGlobal, |
| /* kSealing */ kPermitGlobal | kUserPerm0 | kPermitSeal | kPermitUnseal, |
| }; |
| |
| // The different compressed capability permission formats have different sets |
| // of implied capabilities. |
| static constexpr uint32_t kImpliedCapabilities[] = { |
| /* kMemoryCapReadWrite */ kPermitLoad | kPermitLoadStoreCapability | |
| kPermitStore, |
| /* kMemoryCapReadOnly */ kPermitLoad | kPermitLoadStoreCapability, |
| /* kMemoryCapWriteOnly */ kPermitStore | kPermitLoadStoreCapability, |
| /* kMemoryDataOnly */ 0, |
| /* kExecutable */ kPermitExecute | kPermitLoad | |
| kPermitLoadStoreCapability, |
| /* kSealing */ 0, |
| }; |
| |
| // Decoding table for compressed permission formats. |
| static constexpr PermissionFormats kPermissionFormat[] = { |
| /* 00000 */ kSealing, |
| /* 00001 */ kSealing, |
| /* 00010 */ kSealing, |
| /* 00011 */ kSealing, |
| /* 00100 */ kSealing, |
| /* 00101 */ kSealing, |
| /* 00110 */ kSealing, |
| /* 00111 */ kSealing, |
| /* 01000 */ kExecutable, |
| /* 01001 */ kExecutable, |
| /* 01010 */ kExecutable, |
| /* 01011 */ kExecutable, |
| /* 01100 */ kExecutable, |
| /* 01101 */ kExecutable, |
| /* 01110 */ kExecutable, |
| /* 01111 */ kExecutable, |
| /* 10000 */ kMemoryCapWriteOnly, |
| /* 10001 */ kMemoryDataOnly, |
| /* 10010 */ kMemoryDataOnly, |
| /* 10011 */ kMemoryDataOnly, |
| /* 10100 */ kMemoryCapReadOnly, |
| /* 10101 */ kMemoryCapReadOnly, |
| /* 10110 */ kMemoryCapReadOnly, |
| /* 10111 */ kMemoryCapReadOnly, |
| /* 11000 */ kMemoryCapReadWrite, |
| /* 11001 */ kMemoryCapReadWrite, |
| /* 11010 */ kMemoryCapReadWrite, |
| /* 11011 */ kMemoryCapReadWrite, |
| /* 11100 */ kMemoryCapReadWrite, |
| /* 11101 */ kMemoryCapReadWrite, |
| /* 11110 */ kMemoryCapReadWrite, |
| /* 11111 */ kMemoryCapReadWrite, |
| }; |
| |
| // Expansion table for permissions in the sealing format. |
| static constexpr uint32_t kExpandSealed[8] = { |
| kPermitNone, |
| kPermitUnseal, |
| kPermitSeal, |
| kPermitUnseal | kPermitSeal, |
| kUserPerm0, |
| kUserPerm0 | kPermitUnseal, |
| kUserPerm0 | kPermitSeal, |
| kUserPerm0 | kPermitUnseal | kPermitSeal, |
| }; |
| // Expansion table for permissions in the executable format. |
| static constexpr uint32_t kExpandExecutable[8] = { |
| kPermitNone, |
| kPermitLoadGlobal, |
| kPermitLoadMutable, |
| kPermitLoadMutable | kPermitLoadGlobal, |
| kPermitAccessSystemRegisters, |
| kPermitAccessSystemRegisters | kPermitLoadGlobal, |
| kPermitAccessSystemRegisters | kPermitLoadMutable, |
| kPermitAccessSystemRegisters | kPermitLoadMutable | kPermitLoadGlobal, |
| }; |
| // Expansion table for permissions in the memory data only format. |
| static constexpr uint32_t kExpandMemoryDataOnly[4] = { |
| kPermitNone, |
| kPermitStore, |
| kPermitLoad, |
| kPermitStore | kPermitLoad, |
| }; |
| // Expansion table for permissions in the memory cap read only format. |
| static constexpr uint32_t kExpandMemoryCapReadOnly[4] = { |
| kPermitNone, |
| kPermitLoadGlobal, |
| kPermitLoadMutable, |
| kPermitLoadMutable | kPermitLoadGlobal, |
| }; |
| // Expansion table for permissions in the memory cap read/write format. |
| static constexpr uint32_t kExpandMemoryCapReadWrite[8] = { |
| kPermitNone, |
| kPermitLoadGlobal, |
| kPermitLoadMutable, |
| kPermitLoadMutable | kPermitLoadGlobal, |
| kPermitStoreLocalCapability, |
| kPermitStoreLocalCapability | kPermitLoadGlobal, |
| kPermitStoreLocalCapability | kPermitLoadMutable, |
| kPermitStoreLocalCapability | kPermitLoadMutable | kPermitLoadGlobal, |
| }; |
| // Get the permission format based on the current permissions. |
| PermissionFormats GetPermissionFormat() const; |
| // Return the current permissions in compressed form. |
| uint32_t CompressPermissions() const; |
| // Return the expanded view of the given compressed form of permissions. |
| uint32_t ExpandPermissions(uint32_t compressed) const; |
| |
| // If top or base is changed, set is_dirty_ so that the values get properly |
| // compressed if written to memory. |
| void set_top(uint64_t top) { |
| top_ = top; |
| is_dirty_ = true; |
| } |
| void set_base(uint32_t base) { |
| base_ = base; |
| is_dirty_ = true; |
| } |
| |
| PermissionFormats permissions_format() const { return permissions_format_; } |
| void set_permissions_format(PermissionFormats format) { |
| permissions_format_ = format; |
| } |
| bool tag_ = false; |
| uint64_t top_; |
| uint32_t base_; |
| // Stores the 12 permissions. |
| uint32_t permissions_; |
| PermissionFormats permissions_format_; |
| // Stores the 3 bit object type. |
| uint32_t object_type_; |
| uint32_t reserved_; |
| bool is_dirty_ = false; |
| bool is_null_ = false; |
| uint32_t raw_ = 0xdeadbeef; |
| uint32_t exponent_ = 0; |
| }; |
| |
| } // namespace cheriot |
| } // namespace sim |
| } // namespace mpact |
| |
| #endif // MPACT_CHERIOT__CHERIOT_REGISTER_H_ |