blob: ee41557a6c584178768a1bd7aef5b97d018d0ba0 [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.
#include "buffer.h"
#include <lib/syslog/global.h>
#include <zircon/status.h>
namespace network {
namespace tun {
zx::result<cpp20::span<uint8_t>> VmoStore::GetMappedVmo(uint8_t id) {
auto* stored_vmo = store_.GetVmo(id);
if (!stored_vmo) {
return zx::error(ZX_ERR_NOT_FOUND);
}
return zx::ok(stored_vmo->data());
}
zx_status_t VmoStore::RegisterVmo(uint8_t id, zx::vmo vmo) {
// Lazily reserve storage space.
// Reserve will be a no-op if we already have `MAX_VMOS` capacity.
zx_status_t status = store_.Reserve(MAX_VMOS);
if (status != ZX_OK) {
return status;
}
return store_.RegisterWithKey(id, std::move(vmo));
}
zx_status_t VmoStore::UnregisterVmo(uint8_t id) { return store_.Unregister(id).status_value(); }
zx_status_t VmoStore::Copy(VmoStore& src_store, uint8_t src_id, size_t src_offset,
VmoStore& dst_store, uint8_t dst_id, size_t dst_offset, size_t len) {
zx::result src = src_store.GetMappedVmo(src_id);
if (src.is_error()) {
return src.error_value();
}
zx::result dst = dst_store.GetMappedVmo(dst_id);
if (dst.is_error()) {
return dst.error_value();
}
if (src_offset + len > src->size()) {
return ZX_ERR_OUT_OF_RANGE;
}
if (dst_offset + len > dst->size()) {
return ZX_ERR_OUT_OF_RANGE;
}
std::copy_n(src->begin() + src_offset, len, dst->begin() + dst_offset);
return ZX_OK;
}
TxBuffer VmoStore::MakeTxBuffer(const tx_buffer_t& tx, bool get_meta) {
return TxBuffer(tx, get_meta, this);
}
RxBuffer VmoStore::MakeRxSpaceBuffer(const rx_space_buffer_t& space) {
RxBuffer b(this);
b.PushRxSpace(space);
return b;
}
RxBuffer VmoStore::MakeEmptyRxBuffer() { return RxBuffer(this); }
void Buffer::PushPart(const BufferPart& part) {
ZX_DEBUG_ASSERT(parts_count_ < parts_.size());
parts_[parts_count_++] = part;
total_length_ += part.region.length;
}
zx_status_t Buffer::Read(std::vector<uint8_t>& vec) {
auto inserter = std::back_inserter(vec);
for (const BufferPart& part : parts()) {
zx_status_t status =
vmo_store_->Read(part.region.vmo, part.region.offset, part.region.length, inserter);
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
zx_status_t Buffer::Write(const uint8_t* data, size_t count) {
for (const BufferPart& part : parts()) {
if (count == 0) {
break;
}
size_t len = std::min(count, part.region.length);
zx_status_t status = vmo_store_->Write(part.region.vmo, part.region.offset, len, data);
if (status != ZX_OK) {
return status;
}
data += len;
count -= len;
}
if (count == 0) {
return ZX_OK;
}
return ZX_ERR_OUT_OF_RANGE;
}
zx_status_t Buffer::Write(const fidl::VectorView<uint8_t>& data) {
return Write(data.data(), data.count());
}
zx_status_t Buffer::Write(const std::vector<uint8_t>& data) {
return Write(data.data(), data.size());
}
zx::result<size_t> Buffer::CopyFrom(Buffer& other) {
size_t copied = 0;
uint64_t offset_me = 0;
uint64_t offset_other = 0;
cpp20::span parts_other = other.parts();
cpp20::span parts_me = parts();
auto part_me = parts_me.begin();
for (auto part_o = parts_other.begin(); part_o != parts_other.end();) {
if (part_me == parts_me.end()) {
FX_LOG(ERROR, "tun", "Buffer: not enough space on rx buffer");
return zx::error(ZX_ERR_INTERNAL);
}
uint64_t len_o = part_o->region.length - offset_other;
uint64_t len_me = part_me->region.length - offset_me;
uint64_t wr = len_o > len_me ? len_me : len_o;
zx_status_t status =
VmoStore::Copy(*other.vmo_store_, part_o->region.vmo, part_o->region.offset + offset_other,
*vmo_store_, part_me->region.vmo, part_me->region.offset + offset_me, wr);
if (status != ZX_OK) {
FX_LOGF(ERROR, "tun", "Buffer: failed to copy between buffers: %s",
zx_status_get_string(status));
return zx::error(status);
}
offset_me += wr;
offset_other += wr;
copied += wr;
if (offset_me >= part_me->region.length) {
part_me++;
offset_me = 0;
}
if (offset_other >= part_o->region.length) {
part_o++;
offset_other = 0;
}
}
return zx::ok(copied);
}
TxBuffer::TxBuffer(const tx_buffer_t& tx, bool get_meta, VmoStore* vmo_store)
: Buffer(vmo_store),
port_id_(tx.meta.port),
frame_type_(static_cast<fuchsia_hardware_network::wire::FrameType>(tx.meta.frame_type)) {
// Enforce the banjo contract.
ZX_ASSERT(tx.data_count <= MAX_BUFFER_PARTS);
for (const buffer_region_t& region : cpp20::span(tx.data_list, tx.data_count)) {
PushPart(BufferPart{
.buffer_id = tx.id,
.region = region,
});
}
if (get_meta) {
auto info_type = static_cast<fuchsia_hardware_network::wire::InfoType>(tx.meta.info_type);
if (info_type != fuchsia_hardware_network::wire::InfoType::kNoInfo) {
FX_LOGF(WARNING, "tun", "Unrecognized InfoType %d", tx.meta.info_type);
}
meta_ = fuchsia_net_tun::wire::FrameMetadata{
.info_type = info_type,
.flags = tx.meta.flags,
};
}
}
void RxBuffer::PushRxSpace(const rx_space_buffer_t& space) {
PushPart(BufferPart{
.buffer_id = space.id,
.region = space.region,
});
}
} // namespace tun
} // namespace network