blob: 03587381d3dbfca69d8d7b9eed8ea4f1bff7d9c2 [file] [log] [blame]
// Copyright 2020 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 LIB_ZBITL_VMO_H_
#define LIB_ZBITL_VMO_H_
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <zircon/status.h>
#include <optional>
#include <utility>
#include "storage_traits.h"
namespace zbitl {
// zbitl::MapUnownedVmo is handled as a storage type that works like
// zx::unowned_vmo. The difference is that payload access (for CRC32 et al)
// works by mapping a range of the VMO containing the payload into the process
// using the provided VMAR, rather than by zx::vmo::read into an allocated
// buffer of fixed size. Note that access to the headers is still done via
// zx::vmo::read (and zx::vmo::write for mutation). You can also map the
// entire image into memory at once and then use an in-memory storage type like
// zbitl::ByteView instead.
class MapUnownedVmo {
public:
explicit MapUnownedVmo(zx::unowned_vmo vmo, zx::unowned_vmar vmar = zx::vmar::root_self())
: vmo_(std::move(vmo)), vmar_(std::move(vmar)) {}
MapUnownedVmo() = default;
MapUnownedVmo(MapUnownedVmo&&) = default;
MapUnownedVmo& operator=(MapUnownedVmo&&) = default;
// The default copy constructor and copy assignment operator are implicitly
// deleted because the zx::unowned_* types are not copy-constructible, but
// they are safe to copy here since it's clear from this type's purpose that
// it always holds non-owning references.
MapUnownedVmo(const MapUnownedVmo& other)
: vmo_{zx::unowned_vmo{other.vmo_}}, vmar_{zx::unowned_vmar{other.vmar_}} {}
MapUnownedVmo& operator=(const MapUnownedVmo& other) {
vmo_ = zx::unowned_vmo{other.vmo_};
vmar_ = zx::unowned_vmar{other.vmar_};
return *this;
}
~MapUnownedVmo();
const zx::vmo& vmo() const { return *vmo_; }
const zx::vmar& vmar() const { return *vmar_; }
private:
friend StorageTraits<MapUnownedVmo>;
struct Mapping {
Mapping() = default;
// These are almost the default move constructor and assignment operator,
// but they ensure the values are never copied.
Mapping(Mapping&& other) { *this = std::move(other); }
Mapping& operator=(Mapping&& other) {
std::swap(offset_, other.offset_);
std::swap(address_, other.address_);
std::swap(size_, other.size_);
return *this;
}
std::byte* data() const { return reinterpret_cast<std::byte*>(address_); }
ByteView bytes() const { return {data(), size_}; }
uint64_t offset_ = 0;
uintptr_t address_ = 0;
size_t size_ = 0;
bool write_ = false;
};
zx::unowned_vmo vmo_;
zx::unowned_vmar vmar_;
Mapping mapping_;
};
// zbitl::MapOwnedVmo is like zbitl::MapUnownedVmo, but it owns the VMO handle.
// zbitl::View<zbitl::MapUnownedVmo>::Copy creates a zbitl::MapOwnedVmo.
class MapOwnedVmo : public MapUnownedVmo {
public:
explicit MapOwnedVmo(zx::vmo vmo, zx::unowned_vmar vmar = zx::vmar::root_self())
: MapUnownedVmo(zx::unowned_vmo{vmo}, zx::unowned_vmar{vmar}), owned_vmo_(std::move(vmo)) {}
MapOwnedVmo() = default;
MapOwnedVmo(const MapOwnedVmo&) = delete;
MapOwnedVmo(MapOwnedVmo&&) = default;
MapOwnedVmo& operator=(const MapOwnedVmo&) = delete;
MapOwnedVmo& operator=(MapOwnedVmo&& other) = default;
zx::vmo release() { return std::move(owned_vmo_); }
private:
zx::vmo owned_vmo_;
};
template <>
struct StorageTraits<zx::vmo> {
public:
/// Errors from zx::vmo calls.
using error_type = zx_status_t;
/// Offset into the VMO where the ZBI item payload begins.
using payload_type = uint64_t;
static std::string_view error_string(error_type error) { return zx_status_get_string(error); }
// Returns ZX_PROP_VMO_CONTENT_SIZE, if set - or else the page-rounded VMO
// size.
static fitx::result<error_type, uint32_t> Capacity(const zx::vmo&);
// Will enlarge the underlying VMO size if needed, updating
// ZX_PROP_VMO_CONTENT_SIZE to the new capacity value if so.
static fitx::result<error_type> EnsureCapacity(const zx::vmo&, uint32_t capacity_bytes);
static fitx::result<error_type, zbi_header_t> Header(const zx::vmo&, uint32_t offset);
static fitx::result<error_type, payload_type> Payload(const zx::vmo&, uint32_t offset,
uint32_t length) {
return fitx::ok(offset);
}
static fitx::result<error_type> Read(const zx::vmo& zbi, payload_type payload, void* buffer,
uint32_t length);
template <typename Callback>
static auto Read(const zx::vmo& zbi, payload_type payload, uint32_t length, Callback&& callback)
-> fitx::result<error_type, decltype(callback(ByteView{}))> {
std::optional<decltype(callback(ByteView{}))> result;
auto cb = [&](ByteView chunk) -> bool {
result = callback(chunk);
return result->is_ok();
};
using CbType = decltype(cb);
if (auto read_error = DoRead(
zbi, payload, length,
[](void* cb, ByteView chunk) { return (*static_cast<CbType*>(cb))(chunk); }, &cb);
read_error.is_error()) {
return fitx::error{read_error.error_value()};
} else {
ZX_DEBUG_ASSERT(result);
return fitx::ok(*result);
}
}
static fitx::result<error_type> Write(const zx::vmo&, uint32_t offset, ByteView);
static fitx::result<error_type, zx::vmo> Create(const zx::vmo&, uint32_t size,
uint32_t initial_zero_size);
template <typename SlopCheck>
static fitx::result<error_type, std::optional<std::pair<zx::vmo, uint32_t>>> Clone(
const zx::vmo& zbi, uint32_t offset, uint32_t length, uint32_t to_offset,
SlopCheck&& slopcheck) {
if (slopcheck(offset % ZX_PAGE_SIZE)) {
return DoClone(zbi, offset, length);
}
return fitx::ok(std::nullopt);
}
private:
static fitx::result<error_type> DoRead(const zx::vmo& zbi, uint64_t offset, uint32_t length,
bool (*)(void*, ByteView), void*);
static fitx::result<error_type, std::optional<std::pair<zx::vmo, uint32_t>>> DoClone(
const zx::vmo& zbi, uint32_t offset, uint32_t length);
};
template <>
struct StorageTraits<zx::unowned_vmo> {
using Owned = StorageTraits<zx::vmo>;
using error_type = Owned::error_type;
using payload_type = Owned::payload_type;
static auto error_string(error_type error) { return Owned::error_string(error); }
static auto Capacity(const zx::unowned_vmo& vmo) { return Owned::Capacity(*vmo); }
static auto EnsureCapacity(const zx::unowned_vmo& vmo, uint32_t capacity_bytes) {
return Owned::EnsureCapacity(*vmo, capacity_bytes);
}
static auto Header(const zx::unowned_vmo& vmo, uint32_t offset) {
return Owned::Header(*vmo, offset);
}
static auto Payload(const zx::unowned_vmo& vmo, uint32_t offset, uint32_t length) {
return Owned::Payload(*vmo, offset, length);
}
static fitx::result<error_type> Read(const zx::unowned_vmo& vmo, payload_type payload,
void* buffer, uint32_t length) {
return Owned::Read(*vmo, payload, buffer, length);
}
template <typename Callback>
static auto Read(const zx::unowned_vmo& vmo, payload_type payload, uint32_t length,
Callback&& callback) {
return Owned::Read(*vmo, payload, length, std::forward<Callback>(callback));
}
static auto Write(const zx::unowned_vmo& vmo, uint32_t offset, ByteView data) {
return Owned::Write(*vmo, offset, data);
}
static auto Create(const zx::unowned_vmo& vmo, uint32_t size, uint32_t initial_zero_size) {
return Owned::Create(*vmo, size, initial_zero_size);
}
template <typename SlopCheck>
static auto Clone(const zx::unowned_vmo& zbi, uint32_t offset, uint32_t length,
uint32_t to_offset, SlopCheck&& slopcheck) {
return Owned::Clone(*zbi, offset, length, to_offset, std::forward<SlopCheck>(slopcheck));
}
};
template <>
class StorageTraits<MapUnownedVmo> {
public:
using Owned = StorageTraits<zx::vmo>;
using error_type = Owned::error_type;
using payload_type = Owned::payload_type;
static auto error_string(error_type error) { return Owned::error_string(error); }
static auto Capacity(const MapUnownedVmo& zbi) { return Owned::Capacity(zbi.vmo()); }
static fitx::result<error_type> EnsureCapacity(const MapUnownedVmo& zbi,
uint32_t capacity_bytes) {
return Owned::EnsureCapacity(zbi.vmo(), capacity_bytes);
}
static auto Header(const MapUnownedVmo& zbi, uint32_t offset) {
return Owned::Header(zbi.vmo(), offset);
}
static auto Payload(const MapUnownedVmo& zbi, uint32_t offset, uint32_t length) {
return Owned::Payload(zbi.vmo(), offset, length);
}
static fitx::result<error_type, ByteView> Read(MapUnownedVmo& zbi, payload_type payload,
uint32_t length) {
auto result = Map(zbi, payload, length, false);
if (result.is_error()) {
return result.take_error();
}
return fitx::ok(ByteView{static_cast<std::byte*>(result.value()), length});
}
static auto Write(const MapUnownedVmo& zbi, uint32_t offset, ByteView data) {
return Owned::Write(zbi.vmo(), offset, data);
}
static fitx::result<error_type, void*> Write(MapUnownedVmo& zbi, uint32_t offset,
uint32_t length) {
return Map(zbi, offset, length, true);
}
static fitx::result<error_type, MapOwnedVmo> Create(const MapUnownedVmo& proto, uint32_t size,
uint32_t initial_zero_size) {
auto result = Owned::Create(proto.vmo(), size, initial_zero_size);
if (result.is_error()) {
return result.take_error();
}
return fitx::ok(MapOwnedVmo{std::move(result).value(), zx::unowned_vmar{proto.vmar()}});
}
template <typename SlopCheck>
static fitx::result<error_type, std::optional<std::pair<MapOwnedVmo, uint32_t>>> Clone(
const MapUnownedVmo& zbi, uint32_t offset, uint32_t length, uint32_t to_offset,
SlopCheck&& slopcheck) {
auto result =
Owned::Clone(zbi.vmo(), offset, length, to_offset, std::forward<SlopCheck>(slopcheck));
if (result.is_error()) {
return result.take_error();
}
if (result.value()) {
auto [vmo, slop] = std::move(*std::move(result).value());
return fitx::ok(
std::make_pair(MapOwnedVmo{std::move(vmo), zx::unowned_vmar{zbi.vmar()}}, slop));
}
return fitx::ok(std::nullopt);
}
private:
static fitx::result<error_type, void*> Map(MapUnownedVmo& zbi, uint64_t offset, uint32_t length,
bool write);
};
template <>
struct StorageTraits<MapOwnedVmo> : public StorageTraits<MapUnownedVmo> {};
} // namespace zbitl
#endif // LIB_ZBITL_VMO_H_