class ProtectedRanges
Defined at line 438 of file ../../src/sysmem/server/protected_ranges.h
The algorithmic goals of this class can be summarized as:
* Incrementally change from range set A to range set B, by generating add, delete, modify
steps.
* Ensure that all offsets in the intersection of A and B remain continuously usable for
protected DMA during the steps (and not accessible by REE CPU). Pages not in A or not in B
may not be usable.
* Never try to have more extant ranges than the set limit.
* Maximize (to the degree implemented) the minimum number of pages that are outside any range
during the overall A to B sequence. In other words, make the worst-case moment during the
sequence of steps be only as bad as it needs to be in terms of how many bytes are under
ranges (more bytes under ranges is worse, since we can't loan those ranges back to the rest
of the system).
To that end,
* We'll never delete or modify to remove any offsets from a range unless the offsets being
removed are fully covered by other currently-extant ranges, or the entire range being deleted
or modified has no overlap with any offsets in the intersection of A and B. This is because
removing offsets from a range is allowed to make the entire range temporarily unusable
including offsets that overlap with other extant ranges, unless _all_ offsets being removed
are covered by another extant range.
* We'll prioritize removal of offsets from ranges over adding offsets to ranges, while staying
under the set range count limit. If we're forced to add offsets to ranges to stay under the
range count limit and we have multiple options, we'll choose the option that minimizes the
number of additional offsets that are under ranges. If we have multiple options for how to
remove offsets from ranges, we'll pick the option that maximizes the number of offsets that
we're removing from ranges. When possible while staying under the range count limit, we
choose to remove offsets from ranges before we add offsets to ranges.
The intent is for this class to (ideally) handle range set updates such that there is no need for
a securemem driver to hold ranges in reserve to emulate steps requested by this class. As of
this comment, there are no known securemem drivers that need to hold ranges in reserve to emulate
steps requested by this class.
For now this is optimized for readability more than efficiency, but if we encounter HW with
unlimited HW protection ranges, it may make sense to revisit the algorithm aspects.
Checking that invariants are actually true is left to protected_ranges_test.cc and
ProtectedRangesTest_MiniStress, so that we can avoid doing repeated time-consuming invariant
checks in debug builds. The ProtectedRangesTest_MiniStress test verifies that we maintain the
invariants properly given many pseudo-random upper range requests.
Public Methods
void ProtectedRanges (ProtectedRangesControl * ranges_control, bool disable_dynamic)
Defined at line 254 of file ../../src/sysmem/server/protected_ranges.cc
void ~ProtectedRanges ()
Defined at line 276 of file ../../src/sysmem/server/protected_ranges.cc
void DiscardAllRanges ()
If the SecureMem driver cleanly disconnects, we know that all protected ranges are gone. This
allows ~ProtectedRanges to run without asserting.
Defined at line 278 of file ../../src/sysmem/server/protected_ranges.cc
uint64_t max_logical_ranges ()
Defined at line 280 of file ../../src/sysmem/server/protected_ranges.cc
bool AddRange (const Range & range)
This method attempts to add an additional range to the set of requested protected ranges. The
requested protected ranges are the ranges that need to all be continuously covered by a limited
number of HW-supported ranges.
If this returns true, the added range is now usable as a protected range. If this returns
false, the range was not added and no attempt should be made to use the range as a protected
range. Do not use ranges that happen to be protected due to being in the set of HW-backed
protected ranges at any given moment, as HW-backed protected ranges may be adjusted at any
time, and the implementation is free to cause DMA glitches in ranges that are not in the set
of required protected ranges.
This method typically will succeed even if there are more ranges added via AddRange() than the
number of HW-backed ranges. In this case, at least one of the HW-backed ranges is used to
cover more than one required range. This can lead to "extra" pages in between required ranges
which are HW-protected despite not being used as protected ranges. These pages are still
considered "used" in terms of ProtectedRangesControl.UseRange() and UnUseRange(), but must not
be used for protected DMA, as the implementation is free to disrupt protected DMA to/from any
such protected gap.
During this call, outgoing callbacks to ranges_control _may_ be made to effect the change. The
outgoing calls can in some cases be more numerous and change other ranges, as the HW-backed
ranges are being re-optimized to some extent during this call.
To finish optimizing ranges, the caller should call StepTowardOptimalRanges() until it returns
true, typically with a timer delay in between calls to avoid churning loaned pages too fast.
range - the range to add to the raw set of ranges that must be protected.
Defined at line 282 of file ../../src/sysmem/server/protected_ranges.cc
void DeleteRange (const Range & range)
This method removes a range from the set of required protected ranges. See also AddRange().
This method can't fail. If the system is too broken to delete a required range, this method
will ZX_PANIC() instead of returning. A hard reboot will result.
During this call, outgoing callbacks to ranges_control _may_ be made to effect the change. The
outgoing calls can in some cases be more numerous and change other ranges, as the HW-backed
ranges are being re-optimized to some extent during this call.
To finish optimizing ranges, the caller should call StepTowardOptimalRanges() until it returns
true, typically with a timer delay in between calls to avoid churning loaned pages too fast.
range - the range to remove from the raw set of ranges that must be protected.
Defined at line 349 of file ../../src/sysmem/server/protected_ranges.cc
const Ranges & requested_ranges ()
requested ranges
Defined at line 530 of file ../../src/sysmem/server/protected_ranges.h
const Ranges & required_ranges ()
requested_ranges() processed to align to block boundaries
Defined at line 533 of file ../../src/sysmem/server/protected_ranges.h
const Ranges & coalesced_required_ranges ()
required_ranges() processed to merge overlaps and barely-touching ranges
Defined at line 536 of file ../../src/sysmem/server/protected_ranges.h
const Ranges & interior_unused_ranges ()
coalesced_required_ranges() gaps
Defined at line 539 of file ../../src/sysmem/server/protected_ranges.h
const Ranges & largest_interior_unused_ranges ()
interior_unused_ranges() processed to keep only the largest gaps
Defined at line 542 of file ../../src/sysmem/server/protected_ranges.h
const Ranges & goal_ranges ()
largest_interior_unused_ranges() gaps plus the left-most and right-most ranges needed to cover
the rest of colesced_required_ranges()
Defined at line 546 of file ../../src/sysmem/server/protected_ranges.h
const Ranges & ranges ()
current ranges; when called within a ProtectedRangesControl method, this will reutnr the
"before" ranges.
Defined at line 550 of file ../../src/sysmem/server/protected_ranges.h
template <typename F1>
void ForUnprotectedRanges (F1 callback)
Defined at line 560 of file ../../src/sysmem/server/protected_ranges.h
template <typename F1>
void ForUnprotectedRangesOverlappingRange (const Range & range, F1 callback)
Defined at line 566 of file ../../src/sysmem/server/protected_ranges.h
bool StepTowardOptimalRanges ()
When AddRange() or DeleteRange() is called, we don't instantly try to fix the ranges to be
completely optimal immediately, because optimizing can involve some reclaiming of pages and
loaning of different pages. If we do all of that too quickly, the opportunistic borrowing
during PageQueues rotation / GC will not necessarily have enough time to soak up the
newly-loaned pages before an OOM is triggered. We basically want to incrementally step toward
optimal instead of slamming the whole set of ranges into place all at once, especially when
the optimal set of ranges is changing to a quite different configuration.
As we incrementally optimize, it's possible we'll end up triggering a PageQueues rotation / GC
sooner than would have happened otherwise, and that's fine/good, but we do want to give
PageQueues rotation enough time to borrow (or "re-borrow" if you like, from the point of view
of an offset of a pager-backed VMO) some pages before we perform another step toward optimal
ranges.
Despite the delayed optimization of ranges in steps with some delay in between steps, a
a successful call to AddRange() is guaranteed to make the added range usable for protected DMA.
Calling this "extra" times after this has returned true and before any more AddRange() or
DeleteRange() is permitted, but is also not necessary, and won't achieve any further
optimization (until called after the next AddRange() or DeleteRange() when there may be more
optimizing to do).
This method guarantees that it'll eventually return true if called repeatedly without any more
calls to AddRange() or DeleteRange(), _if_ memory pressure is low enough to allow optimizing
ranges. If memory pressure is too high to make progress for a while, this method will keep
returning true during that while.
true - known done optimizing, until the next call to AddRange() or DeleteRange().
false - call again later to try to do more optimizing.
Defined at line 711 of file ../../src/sysmem/server/protected_ranges.cc
void DebugDumpRangeForUnitTest (const Range & range, const char * info)
only for dumping the small ranges in unit tests
Defined at line 603 of file ../../src/sysmem/server/protected_ranges.cc
void DebugDumpRangesForUnitTest (const Ranges & ranges, const char * info)
Defined at line 630 of file ../../src/sysmem/server/protected_ranges.cc
void DebugDumpOffset (uint64_t offset, const char * info)
Defined at line 674 of file ../../src/sysmem/server/protected_ranges.cc
void DebugDumpBacktrace ()
Defined at line 699 of file ../../src/sysmem/server/protected_ranges.cc
void DynamicSetDlogEnabled (bool enabled)
Defined at line 705 of file ../../src/sysmem/server/protected_ranges.cc
std::pair<Ranges::iterator, Ranges::iterator> IteratorsCoveringPotentialOverlapsOfRangeWithRanges (const Range & range, const Ranges & ranges)
Requirement: Either ranges must not contain any self-overlaps (all Ranges available via this
class except required_ranges()), or ranges must be required_ranges(). The required_ranges()
can have a limited degree of self-overlap, which this method does accommodate.
The returned iterators are a begin, end pair. If there is no range in "ranges" that overlaps
"range", then both iterators will be ranges.end(). If there are any ranges in "ranges" that
overlap "range", then [begin, end) is exactly those ranges in "ranges" which overlap "range".
Defined at line 836 of file ../../src/sysmem/server/protected_ranges.cc
double GetEfficiency ()
un-covered pages / un-used pages
Defined at line 1395 of file ../../src/sysmem/server/protected_ranges.cc
double GetLoanableRatio ()
un-covered pages / total pages
Defined at line 1406 of file ../../src/sysmem/server/protected_ranges.cc
uint64_t GetLoanableBytes ()
un-covered bytes
Defined at line 1416 of file ../../src/sysmem/server/protected_ranges.cc