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