blob: 13dfd5edd676d4b6559dbe81c109eb460f9ccd42 [file] [log] [blame]
// Copyright 2018 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 <lib/async/default.h>
#include <lib/fxl/logging.h>
#include <zx/fifo.h>
#include "guest_ethernet.h"
// This is a locally administered MAC address (first byte 0x02) mixed with the
// Google Organizationally Unique Identifier (00:1a:11). The host gets ff:ff:ff
// and the guest gets 00:00:00 for the last three octets.
static constexpr uint8_t kHostMacAddress[6] = {0x02, 0x1a, 0x11,
0xff, 0xff, 0xff};
static constexpr uint32_t kMtu = 1500;
zx_status_t GuestEthernet::Send(void* data, size_t length) {
if (!io_vmo_) {
FXL_LOG(ERROR) << "Send called before IO buffer was set up";
return ZX_ERR_BAD_STATE;
}
if (rx_entries_count_ == 0) {
size_t count;
zx_status_t status =
rx_fifo_.read(sizeof(fuchsia::hardware::ethernet::FifoEntry),
rx_entries_.data(), rx_entries_.size(), &count);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to read from rx fifo: " << status;
return status;
}
rx_entries_count_ = count;
}
rx_entries_count_--;
fuchsia::hardware::ethernet::FifoEntry entry = rx_entries_[rx_entries_count_];
if (entry.offset >= io_size_ || entry.length > (io_size_ - entry.offset) ||
length > entry.length) {
FXL_LOG(ERROR) << "Invalid fifo entry for packet";
entry.length = 0;
entry.flags = fuchsia::hardware::ethernet::FIFO_INVALID;
} else {
memcpy(reinterpret_cast<void*>(io_addr_ + entry.offset), data, length);
entry.length = length;
entry.flags = fuchsia::hardware::ethernet::FIFO_RX_OK;
}
zx_status_t status =
rx_fifo_.write(sizeof(fuchsia::hardware::ethernet::FifoEntry), &entry, 1,
nullptr /* actual count */);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to write to rx fifo";
return status;
}
return ZX_OK;
}
void GuestEthernet::OnTxFifoReadable(async_dispatcher_t* dispatcher,
async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
FXL_CHECK(status == ZX_OK) << "Wait for tx fifo readable failed " << status;
std::vector<fuchsia::hardware::ethernet::FifoEntry> entries(
kVirtioNetQueueSize / 2);
size_t count;
while (true) {
status = tx_fifo_.read(sizeof(fuchsia::hardware::ethernet::FifoEntry),
entries.data(), entries.size(), &count);
if (status == ZX_ERR_SHOULD_WAIT) {
status = tx_fifo_wait_.Begin(async_get_default_dispatcher());
FXL_CHECK(status == ZX_OK) << "Failed to wait on tx fifo";
return;
}
FXL_CHECK(status == ZX_OK) << "Failed to read tx fifo";
for (size_t i = 0; i != count; ++i) {
receiver_->Receive(io_addr_ + entries[i].offset, entries[i].length,
entries[i]);
}
}
}
void GuestEthernet::Complete(
const fuchsia::hardware::ethernet::FifoEntry& entry) {
size_t count;
zx_status_t status = tx_fifo_.write(
sizeof(fuchsia::hardware::ethernet::FifoEntry), &entry, 1, &count);
FXL_CHECK(status == ZX_OK);
FXL_CHECK(count == 1);
}
void GuestEthernet::GetInfo(GetInfoCallback callback) {
fuchsia::hardware::ethernet::Info info;
info.features = fuchsia::hardware::ethernet::INFO_FEATURE_SYNTH;
info.mtu = kMtu;
memcpy(&info.mac, kHostMacAddress, sizeof(info.mac));
callback(info);
}
void GuestEthernet::GetFifos(GetFifosCallback callback) {
auto fifos = std::make_unique<fuchsia::hardware::ethernet::Fifos>();
zx_status_t status = zx::fifo::create(
kVirtioNetQueueSize, sizeof(fuchsia::hardware::ethernet::FifoEntry),
/* options */ 0u, &fifos->rx, &rx_fifo_);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to create fifo";
callback(status, nullptr);
return;
}
status = zx::fifo::create(kVirtioNetQueueSize,
sizeof(fuchsia::hardware::ethernet::FifoEntry),
/* options */ 0u, &fifos->tx, &tx_fifo_);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to create fifo";
FXL_CHECK(rx_fifo_.release() == ZX_OK) << "Failed to release fifo";
callback(status, nullptr);
return;
}
fifos->rx_depth = kVirtioNetQueueSize;
fifos->tx_depth = kVirtioNetQueueSize;
callback(ZX_OK, std::move(fifos));
}
void GuestEthernet::SetIOBuffer(zx::vmo vmo, SetIOBufferCallback callback) {
if (io_vmo_) {
callback(ZX_ERR_ALREADY_BOUND);
return;
}
uint64_t vmo_size;
zx_status_t status = vmo.get_size(&vmo_size);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to get vmo size";
callback(status);
return;
}
status = zx::vmar::root_self()->map(
0, vmo, 0, vmo_size,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_REQUIRE_NON_RESIZABLE,
&io_addr_);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to map io buffer";
callback(status);
return;
}
io_vmo_ = std::move(vmo);
io_size_ = vmo_size;
callback(ZX_OK);
}
void GuestEthernet::Start(StartCallback callback) {
if (!io_vmo_) {
FXL_LOG(ERROR) << "Start called before IO buffer was set up";
callback(ZX_ERR_BAD_STATE);
return;
}
tx_fifo_wait_.set_object(tx_fifo_.get());
tx_fifo_wait_.set_trigger(ZX_SOCKET_READABLE);
zx_status_t status = tx_fifo_wait_.Begin(async_get_default_dispatcher());
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to wait on tx fifo";
}
callback(status);
}
void GuestEthernet::Stop(StopCallback callback) { callback(); }
void GuestEthernet::ListenStart(ListenStartCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}
void GuestEthernet::ListenStop(ListenStopCallback callback) { callback(); }
void GuestEthernet::SetClientName(std::string name,
SetClientNameCallback callback) {
FXL_LOG(INFO) << "Guest ethernet client set to " << name;
callback(ZX_OK);
}
void GuestEthernet::GetStatus(GetStatusCallback callback) { callback(0); }
void GuestEthernet::SetPromiscuousMode(bool enabled,
SetPromiscuousModeCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}
void GuestEthernet::ConfigMulticastAddMac(
fuchsia::hardware::ethernet::MacAddress addr,
ConfigMulticastAddMacCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}
void GuestEthernet::ConfigMulticastDeleteMac(
fuchsia::hardware::ethernet::MacAddress addr,
ConfigMulticastDeleteMacCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}
void GuestEthernet::ConfigMulticastSetPromiscuousMode(
bool enabled, ConfigMulticastSetPromiscuousModeCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}
void GuestEthernet::ConfigMulticastTestFilter(
ConfigMulticastTestFilterCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}
void GuestEthernet::DumpRegisters(DumpRegistersCallback callback) {
callback(ZX_ERR_NOT_SUPPORTED);
}