|  | // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  | library fuchsia.sysmem; | 
|  |  | 
|  | using zx; | 
|  |  | 
|  | /// SecureMem | 
|  | /// | 
|  | /// The client is sysmem.  The server is securemem driver. | 
|  | /// | 
|  | /// TEE - Trusted Execution Environment. | 
|  | /// | 
|  | /// REE - Rich Execution Environment. | 
|  | /// | 
|  | /// Enables sysmem to call the securemem driver to get any secure heaps | 
|  | /// configured via the TEE (or via the securemem driver), and set any physical | 
|  | /// secure heaps configured via sysmem. | 
|  | /// | 
|  | /// Presently, dynamically-allocated secure heaps are configured via sysmem, as | 
|  | /// it starts quite early during boot and can successfully reserve contiguous | 
|  | /// physical memory.  Presently, fixed-location secure heaps are configured via | 
|  | /// TEE, as the plumbing goes from the bootloader to the TEE.  However, this | 
|  | /// protocol intentionally doesn't care which heaps are dynamically-allocated | 
|  | /// and which are fixed-location. | 
|  | protocol SecureMem { | 
|  | /// Gets the physical address and length of any secure heap whose physical | 
|  | /// range is configured via the TEE. | 
|  | /// | 
|  | /// Presently, these will be fixed physical addresses and lengths, with the | 
|  | /// location plumbed via the TEE. | 
|  | /// | 
|  | /// This is preferred over RegisterHeap() when there isn't any special | 
|  | /// heap-specific per-VMO setup or teardown required. | 
|  | /// | 
|  | /// The physical range must be secured/protected by the TEE before the | 
|  | /// securemem driver responds to this request with success. | 
|  | /// | 
|  | /// Sysmem should only call this once.  Returning zero heaps is not a | 
|  | /// failure. | 
|  | /// | 
|  | /// Errors: | 
|  | ///  * ZX_ERR_BAD_STATE - called more than once. | 
|  | ///  * ZX_ERR_INTERNAL - generic internal error (such as in communication | 
|  | ///    with TEE which doesn't generate zx_status_t errors). | 
|  | ///  * other errors are possible, such as from communication failures or | 
|  | ///    server propagation of zx_status_t failures | 
|  | GetPhysicalSecureHeaps() -> (struct { | 
|  | heaps SecureHeapsAndRanges; | 
|  | }) error zx.status; | 
|  |  | 
|  | /// This request from sysmem to the securemem driver gets the properties of | 
|  | /// a protected/secure heap. | 
|  | /// | 
|  | /// This only handles heaps with a single contiguous physical extent. | 
|  | /// | 
|  | /// The heap's entire physical range is indicated in case this request needs | 
|  | /// some physical space to auto-detect how many ranges are REE-usable.  Any | 
|  | /// temporary HW protection ranges will be deleted before this request | 
|  | /// completes. | 
|  | @available(added=9) | 
|  | GetPhysicalSecureHeapProperties(struct { | 
|  | entire_heap SecureHeapAndRange; | 
|  | }) -> (struct { | 
|  | properties SecureHeapProperties; | 
|  | }) error zx.status; | 
|  |  | 
|  | @available(added=7, deprecated=8, removed=9) | 
|  | GetPhysicalSecureHeapProperties(struct { | 
|  | heap HeapType; | 
|  | }) -> (struct { | 
|  | properties SecureHeapProperties; | 
|  | }) error zx.status; | 
|  |  | 
|  | /// This request from sysmem to the securemem driver conveys a physical | 
|  | /// range to add, for a heap whose physical range(s) are set up via | 
|  | /// sysmem. | 
|  | /// | 
|  | /// Only sysmem can call this because only sysmem is handed the client end | 
|  | /// of a FIDL channel serving this protocol, via RegisterSecureMem().  The | 
|  | /// securemem driver is the server end of this protocol. | 
|  | /// | 
|  | /// The securemem driver must configure all the covered offsets as protected | 
|  | /// before responding to this message with success. | 
|  | /// | 
|  | /// On failure, the securemem driver must ensure the protected range was not | 
|  | /// created. | 
|  | /// | 
|  | /// Sysmem must only call this up to once if dynamic_protection_ranges | 
|  | /// false. | 
|  | /// | 
|  | /// If dynamic_protection_ranges is true, sysmem can call this multiple | 
|  | /// times as long as the current number of ranges never exceeds | 
|  | /// max_protected_range_count. | 
|  | /// | 
|  | /// The caller must not attempt to add a range that matches an | 
|  | /// already-existing range.  Added ranges can overlap each other as long as | 
|  | /// no two ranges match exactly. | 
|  | /// | 
|  | /// Errors: | 
|  | ///   * ZX_ERR_BAD_STATE - called more than once when | 
|  | ///     !dynamic_protection_ranges.  Adding a heap that would cause overall | 
|  | ///     heap count to exceed max_protected_range_count. | 
|  | ///   * ZX_ERR_INVALID_ARGS - unexpected heap, or range that doesn't conform | 
|  | ///     to protected_range_granularity. | 
|  | ///   * ZX_ERR_INTERNAL - generic internal error (such as in communication | 
|  | ///     with TEE which doesn't generate zx_status_t errors). | 
|  | ///   * other errors are possible, such as from communication failures or | 
|  | ///     server propagation of zx_status_t failures. | 
|  | AddSecureHeapPhysicalRange(struct { | 
|  | heap_range SecureHeapAndRange; | 
|  | }) -> (struct {}) error zx.status; | 
|  |  | 
|  | /// This request from sysmem to the securemem driver conveys a physical | 
|  | /// range to delete, for a heap whose physical range(s) are set up via | 
|  | /// sysmem. | 
|  | /// | 
|  | /// Only sysmem can call this because only sysmem is handed the client end | 
|  | /// of a FIDL channel serving this protocol, via RegisterSecureMem().  The | 
|  | /// securemem driver is the server end of this protocol. | 
|  | /// | 
|  | /// The securemem driver must configure all the covered offsets as not | 
|  | /// protected before responding to this message with success. | 
|  | /// | 
|  | /// On failure, the securemem driver must ensure the protected range was not | 
|  | /// deleted. | 
|  | /// | 
|  | /// Sysmem must not call this if dynamic_protection_ranges false. | 
|  | /// | 
|  | /// If dynamic_protection_ranges is true, sysmem can call this repeatedly, | 
|  | /// on various ranges that exist at the time of the call. | 
|  | /// | 
|  | /// If any portion of the range being deleted is not also covered by another | 
|  | /// protected range, then any ongoing DMA to any part of the entire range | 
|  | /// may be interrupted / may fail, potentially in a way that's disruptive to | 
|  | /// the entire system (bus lockup or similar, depending on device details). | 
|  | /// Therefore, the caller must ensure that no ongoing DMA is occurring to | 
|  | /// any portion of the range being deleted, unless the caller has other | 
|  | /// active ranges covering every block of the range being deleted.  Ongoing | 
|  | /// DMA to/from blocks outside the range being deleted is never impacted by | 
|  | /// the deletion. | 
|  | /// | 
|  | /// Errors: | 
|  | ///   * ZX_ERR_BAD_STATE - called when !dynamic_protection_ranges. | 
|  | ///   * ZX_ERR_INVALID_ARGS - unexpected heap, or range that doesn't conform | 
|  | ///     to protected_range_granularity. | 
|  | ///   * ZX_ERR_INTERNAL - generic internal error (such as in communication | 
|  | ///     with TEE which doesn't generate zx_status_t errors). | 
|  | ///   * ZX_ERR_NOT_FOUND - the specified range is not found. | 
|  | ///   * other errors are possible, such as from communication failures or | 
|  | ///     server propagation of zx_status_t failures. | 
|  | DeleteSecureHeapPhysicalRange(struct { | 
|  | heap_range SecureHeapAndRange; | 
|  | }) -> (struct {}) error zx.status; | 
|  |  | 
|  | /// This request from sysmem to the securemem driver conveys a physical | 
|  | /// range to modify and its new base and length, for a heap whose physical | 
|  | /// range(s) are set up via sysmem. | 
|  | /// | 
|  | /// Only sysmem can call this because only sysmem is handed the client end | 
|  | /// of a FIDL channel serving this protocol, via RegisterSecureMem().  The | 
|  | /// securemem driver is the server end of this protocol. | 
|  | /// | 
|  | /// The securemem driver must configure the range to cover only the new | 
|  | /// offsets before responding to this message with success. | 
|  | /// | 
|  | /// On failure, the securemem driver must ensure the range was not changed. | 
|  | /// | 
|  | /// Sysmem must not call this if dynamic_protection_ranges false.  Sysmem | 
|  | /// must not call this if !is_mod_protected_range_available. | 
|  | /// | 
|  | /// If dynamic_protection_ranges is true, sysmem can call this repeatedly, | 
|  | /// on various ranges that exist at the time of the call. | 
|  | /// | 
|  | /// The range must only be modified at one end or the other, but not both. | 
|  | /// If the range is getting shorter, and the un-covered blocks are not | 
|  | /// covered by other active ranges, any ongoing DMA to the entire range | 
|  | /// that's geting shorter may fail in a way that disrupts the entire system | 
|  | /// (bus lockup or similar), so the caller must ensure that no DMA is | 
|  | /// ongoing to any portion of a range that is getting shorter, unless the | 
|  | /// blocks being un-covered by the modification to this range are all | 
|  | /// covered by other active ranges, in which case no disruption to ongoing | 
|  | /// DMA will occur. | 
|  | /// | 
|  | /// If a range is modified to become <= zero length, the range is deleted. | 
|  | /// | 
|  | /// Errors: | 
|  | ///   * ZX_ERR_BAD_STATE - called when !dynamic_protection_ranges. | 
|  | ///   * ZX_ERR_INVALID_ARGS - unexpected heap, or old_range or new_range | 
|  | ///     that doesn't conform to protected_range_granularity, or old_range | 
|  | ///     and new_range differ in both begin and end (disallowed). | 
|  | ///   * ZX_ERR_INTERNAL - generic internal error (such as in communication | 
|  | ///     with TEE which doesn't generate zx_status_t errors). | 
|  | ///   * ZX_ERR_NOT_FOUND - the specified range is not found. | 
|  | ///   * other errors are possible, such as from communication failures or | 
|  | ///     server propagation of zx_status_t failures. | 
|  | ModifySecureHeapPhysicalRange(struct { | 
|  | range_modification SecureHeapAndRangeModification; | 
|  | }) -> (struct {}) error zx.status; | 
|  |  | 
|  | /// Zero a sub-range of a currently-existing physical range added via | 
|  | /// AddSecureHeapPhysicalRange().  The sub-range must be fully covered by | 
|  | /// exactly one physical range, and must not overlap with any other | 
|  | /// physical range. | 
|  | /// | 
|  | /// is_covering_range_explicit - When true, the covering range must be one | 
|  | ///     of the ranges explicitly created via AddSecureHeapPhysicalRange(), | 
|  | ///     possibly modified since.  When false, the covering range must not | 
|  | ///     be one of the ranges explicitly created via | 
|  | ///     AddSecureHeapPhysicalRange(), but the covering range must exist as | 
|  | ///     a covering range not created via AddSecureHeapPhysicalRange().  The | 
|  | ///     covering range is typically the entire physical range (or a range | 
|  | ///     which covers even more) of a heap configured by the TEE and whose | 
|  | ///     configuration is conveyed to sysmem via GetPhysicalSecureHeaps(). | 
|  | /// | 
|  | /// Ongoing DMA is not disrupted by this request. | 
|  | ZeroSubRange(struct { | 
|  | is_covering_range_explicit bool; | 
|  | heap_range SecureHeapAndRange; | 
|  | }) -> (struct {}) error zx.status; | 
|  | }; | 
|  |  | 
|  | type SecureHeapProperties = table { | 
|  | /// The HeapType is repeated here for convenience. | 
|  | 1: heap HeapType; | 
|  |  | 
|  | /// If true, more than one call to SetPhysicalSecureHeap() for the same | 
|  | /// heap is allowed.  If false, only one SetPhyscialSecureHeap() call is | 
|  | /// allowed, and no calls to DeleteSecureHeapPhysicalRange() or | 
|  | /// ModifySecureHeapPhysicalRange() are allowed.  Even when this is false, | 
|  | /// the SecureMem server (driver) is still responsible for de-protecting | 
|  | /// just before warm reboot if protected ranges would not otherwise be | 
|  | /// cleaned up during a warm reboot. | 
|  | 2: dynamic_protection_ranges bool; | 
|  |  | 
|  | /// The granularity of protection ranges.  If the granularity of start is | 
|  | /// different than granularity of end or length, then this is the max | 
|  | /// granularity value among those values. | 
|  | /// | 
|  | /// This must be a power of 2.  The client must not request ranges that | 
|  | /// specify smaller granularity. | 
|  | /// | 
|  | /// This must be at least zx_system_page_size() even if the HW can do | 
|  | /// smaller granularity. | 
|  | 3: protected_range_granularity uint32; | 
|  |  | 
|  | /// The SecureMem server should not count reserved ranges that the SecureMem | 
|  | /// server uses internally to get from range set A to range set B, if the | 
|  | /// SecureMem server needs to do any emulation of that sort.  Normally such | 
|  | /// emulation by the SecureMem server is unnecessary.  If any ranges are | 
|  | /// reserved by the SecureMem server, those reserved ranges are not | 
|  | /// available for use by the SecureMem client. | 
|  | /// | 
|  | /// If the number of ranges is limited only by available memory, it's ok for | 
|  | /// the SecureMem server to report 0xFFFFFFFFFFFFFFFF for this value.  The | 
|  | /// field must still be set.  As usual, the SecureMem server should ensure | 
|  | /// that SetPhysicalSecureHeapRanges() succeeds or fails atomically (either | 
|  | /// fully updates or rolls back before completing). | 
|  | 4: max_protected_range_count uint64; | 
|  |  | 
|  | /// Iff true, ModifySecureHeapPhysicalRange() is implemented.  Calling | 
|  | /// ModifySecureHeapPhysicalRange() when is_mod_protected_range_available | 
|  | /// is false is prohibited.  Don't attempt to detect availability of | 
|  | /// ModifySecureHeapPhysicalRange() by calling it to see if it fails; it | 
|  | /// may ZX_PANIC(). | 
|  | 5: is_mod_protected_range_available bool; | 
|  | }; | 
|  |  | 
|  | const MAX_HEAPS_COUNT uint32 = 32; | 
|  | const MAX_RANGES_COUNT uint32 = 128; | 
|  |  | 
|  | type SecureHeapRange = table { | 
|  | /// Must be aligned to at least heap_range_granularity. | 
|  | 1: physical_address uint64; | 
|  |  | 
|  | /// Must be aligned to at least heap_range_granularity. | 
|  | 2: size_bytes uint64; | 
|  | }; | 
|  |  | 
|  | type SecureHeapAndRanges = table { | 
|  | /// This is which secure/protected heap. | 
|  | 1: heap HeapType; | 
|  |  | 
|  | /// The list of physical ranges.  This list must be sorted by | 
|  | /// physical_address (lower first), and must not have any overlapping | 
|  | /// ranges.  Ranges that are directly adjacent are allowed (not | 
|  | /// overlapping). | 
|  | 2: ranges vector<SecureHeapRange>:MAX_RANGES_COUNT; | 
|  | }; | 
|  |  | 
|  | type SecureHeapsAndRanges = table { | 
|  | 1: heaps vector<SecureHeapAndRanges>:MAX_HEAPS_COUNT; | 
|  | }; | 
|  |  | 
|  | type SecureHeapAndRange = table { | 
|  | 1: heap HeapType; | 
|  | 2: range SecureHeapRange; | 
|  | }; | 
|  |  | 
|  | type SecureHeapAndRangeModification = table { | 
|  | 1: heap HeapType; | 
|  | 2: old_range SecureHeapRange; | 
|  | 3: new_range SecureHeapRange; | 
|  | }; |