blob: 9b5ca1bf6d95c29b527db23f3787840402e15eca [file] [log] [blame]
// Copyright 2017 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.
#ifndef SRC_VIRTUALIZATION_BIN_VMM_DEV_MEM_H_
#define SRC_VIRTUALIZATION_BIN_VMM_DEV_MEM_H_
#include <lib/syslog/cpp/macros.h>
#include <zircon/types.h>
#include <algorithm>
#include <set>
#include "src/virtualization/bin/vmm/guest.h"
class DevMem {
public:
struct Range {
zx_gpaddr_t addr;
size_t size;
bool operator<(const Range& r) const { return addr + size <= r.addr; }
bool contains(const Range& r) const { return !(r < *this) && !(*this < r); }
};
using RangeSet = std::set<Range>;
[[nodiscard]] bool AddRange(zx_gpaddr_t addr, size_t size) {
if (finalized_) {
FX_LOGS(ERROR) << "Cannot add device memory ranges after finalizing the set";
return false;
}
if (size == 0) {
FX_LOGS(WARNING) << "Cannot add zero length ranges";
return false;
}
return ranges_.emplace(Range{addr, size}).second;
}
bool HasGuestMemoryOverlap(const std::vector<GuestMemoryRegion>& guest_memory_regions) const {
zx_gpaddr_t addr = 0;
size_t size = 0;
auto yield = [&addr, &size](zx_gpaddr_t addr_in, size_t size_in) {
addr = addr_in;
size = size_in;
};
// If yielding the inverse range does not exactly match a given guest memory region, it means
// that there is at least one region of device memory that intersected the provided region
// of guest memory.
for (const GuestMemoryRegion& guest_mem : guest_memory_regions) {
YieldInverseRange(guest_mem.base, guest_mem.size, yield);
if (addr != guest_mem.base || size != guest_mem.size) {
FX_LOGS(ERROR) << "Guest memory range " << guest_mem.base << " - "
<< guest_mem.base + guest_mem.size << " overlaps with device memory";
return true;
}
}
return false;
}
// Called to prevent adding additional device memory ranges. This allows the Guest to validate
// that there is no overlap between guest memory and device memory before starting the VM.
void Finalize() { finalized_ = true; }
const RangeSet::const_iterator begin() const { return ranges_.begin(); }
const RangeSet::const_iterator end() const { return ranges_.end(); }
// Generates, by calling the provided functor, all Ranges that are in the
// provided range, that do not overlap with any internal ranges. This means
// the generated set is precisely the inverse of our contained ranges, unioned
// with the provided range.
template <typename F>
void YieldInverseRange(zx_gpaddr_t base, size_t size, F yield) const {
zx_gpaddr_t prev = base;
for (const auto& range : ranges_) {
if ((range.addr + range.size <= base) || (range.addr >= base + size)) {
// Ignore any device memory ranges which have zero overlap with the provided range.
continue;
}
zx_gpaddr_t next_top = std::min(range.addr, base + size);
if (next_top > prev) {
yield(prev, next_top - prev);
}
prev = range.addr + range.size;
}
zx_gpaddr_t next_top = std::max(prev, base + size);
if (next_top > prev) {
yield(prev, next_top - prev);
}
}
private:
RangeSet ranges_;
bool finalized_ = false;
};
#endif // SRC_VIRTUALIZATION_BIN_VMM_DEV_MEM_H_