1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ethernet.h"
6
7 #include <assert.h>
8 #include <limits.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12
13 #include <ddk/debug.h>
14 #include <ddk/io-buffer.h>
15 #include <ddk/protocol/ethernet.h>
16 #include <fbl/algorithm.h>
17 #include <fbl/alloc_checker.h>
18 #include <fbl/auto_call.h>
19 #include <fbl/auto_lock.h>
20 #include <fbl/unique_ptr.h>
21 #include <pretty/hexdump.h>
22 #include <virtio/net.h>
23 #include <virtio/virtio.h>
24 #include <zircon/assert.h>
25 #include <zircon/status.h>
26 #include <zircon/types.h>
27
28 #include <utility>
29
30 #include "ring.h"
31 #include "trace.h"
32
33 // Enables/disables debugging info
34 #define LOCAL_TRACE 0
35
36 namespace virtio {
37
38 namespace {
39
40 // Specifies how many packets can fit in each of the receive and transmit
41 // backlogs.
42 const size_t kBacklog = 32;
43
44 // Specifies the maximum transfer unit we support and the maximum layer 1
45 // Ethernet packet header length.
46 const size_t kVirtioMtu = 1500;
47 const size_t kEthHeaderSizeBytes = 14;
48 const size_t kEthFrameSize = kVirtioMtu + kEthHeaderSizeBytes;
49
50 // Other constants determined by the values above and the memory architecture.
51 // The goal here is to allocate single-page I/O buffers.
52 const size_t kFrameSize = sizeof(virtio_net_hdr_t) + kEthFrameSize;
53 const size_t kFramesInBuf = PAGE_SIZE / kFrameSize;
54 const size_t kNumIoBufs = fbl::round_up(kBacklog * 2, kFramesInBuf) / kFramesInBuf;
55
56 const uint16_t kRxId = 0u;
57 const uint16_t kTxId = 1u;
58
59 // Strictly for convenience...
60 typedef struct vring_desc desc_t;
61
62 // Device bridge helpers
virtio_net_unbind(void * ctx)63 void virtio_net_unbind(void* ctx) {
64 virtio::EthernetDevice* eth = static_cast<virtio::EthernetDevice*>(ctx);
65 eth->Unbind();
66 }
67
virtio_net_release(void * ctx)68 void virtio_net_release(void* ctx) {
69 fbl::unique_ptr<virtio::EthernetDevice> eth(static_cast<virtio::EthernetDevice*>(ctx));
70 eth->Release();
71 }
72
73 zx_protocol_device_t kDeviceOps = {
74 DEVICE_OPS_VERSION,
75 nullptr, // get_protocol
76 nullptr, // open
77 nullptr, // openat
78 nullptr, // close
79 virtio_net_unbind,
80 virtio_net_release,
81 nullptr, // read
82 nullptr, // write
83 nullptr, // get_size
84 nullptr, // ioctl
85 nullptr, // suspend
86 nullptr, // resume
87 nullptr, // rxrpc
88 nullptr, // rxmsg
89 };
90
91 // Protocol bridge helpers
virtio_net_query(void * ctx,uint32_t options,ethmac_info_t * info)92 zx_status_t virtio_net_query(void* ctx, uint32_t options, ethmac_info_t* info) {
93 virtio::EthernetDevice* eth = static_cast<virtio::EthernetDevice*>(ctx);
94 return eth->Query(options, info);
95 }
96
virtio_net_stop(void * ctx)97 void virtio_net_stop(void* ctx) {
98 virtio::EthernetDevice* eth = static_cast<virtio::EthernetDevice*>(ctx);
99 eth->Stop();
100 }
101
virtio_net_start(void * ctx,const ethmac_ifc_t * ifc)102 zx_status_t virtio_net_start(void* ctx, const ethmac_ifc_t* ifc) {
103 virtio::EthernetDevice* eth = static_cast<virtio::EthernetDevice*>(ctx);
104 return eth->Start(ifc);
105 }
106
virtio_net_queue_tx(void * ctx,uint32_t options,ethmac_netbuf_t * netbuf)107 zx_status_t virtio_net_queue_tx(void* ctx, uint32_t options, ethmac_netbuf_t* netbuf) {
108 virtio::EthernetDevice* eth = static_cast<virtio::EthernetDevice*>(ctx);
109 return eth->QueueTx(options, netbuf);
110 }
111
virtio_set_param(void * ctx,uint32_t param,int32_t value,const void * data,size_t data_size)112 static zx_status_t virtio_set_param(void* ctx, uint32_t param, int32_t value, const void* data,
113 size_t data_size) {
114 return ZX_ERR_NOT_SUPPORTED;
115 }
116
117 ethmac_protocol_ops_t kProtoOps = {
118 virtio_net_query,
119 virtio_net_stop,
120 virtio_net_start,
121 virtio_net_queue_tx,
122 virtio_set_param,
123 NULL, // get_bti not implemented because we don't have FEATURE_DMA
124 };
125
126 // I/O buffer helpers
InitBuffers(const zx::bti & bti,fbl::unique_ptr<io_buffer_t[]> * out)127 zx_status_t InitBuffers(const zx::bti& bti, fbl::unique_ptr<io_buffer_t[]>* out) {
128 zx_status_t rc;
129 fbl::AllocChecker ac;
130 fbl::unique_ptr<io_buffer_t[]> bufs(new (&ac) io_buffer_t[kNumIoBufs]);
131 if (!ac.check()) {
132 zxlogf(ERROR, "out of memory!\n");
133 return ZX_ERR_NO_MEMORY;
134 }
135 memset(bufs.get(), 0, sizeof(io_buffer_t) * kNumIoBufs);
136 size_t buf_size = kFrameSize * kFramesInBuf;
137 for (uint16_t id = 0; id < kNumIoBufs; ++id) {
138 if ((rc = io_buffer_init(&bufs[id], bti.get(), buf_size,
139 IO_BUFFER_RW | IO_BUFFER_CONTIG)) != ZX_OK) {
140 zxlogf(ERROR, "failed to allocate I/O buffers: %s\n", zx_status_get_string(rc));
141 return rc;
142 }
143 }
144 *out = std::move(bufs);
145 return ZX_OK;
146 }
147
ReleaseBuffers(fbl::unique_ptr<io_buffer_t[]> bufs)148 void ReleaseBuffers(fbl::unique_ptr<io_buffer_t[]> bufs) {
149 if (!bufs) {
150 return;
151 }
152 for (size_t i = 0; i < kNumIoBufs; ++i) {
153 if (io_buffer_is_valid(&bufs[i])) {
154 io_buffer_release(&bufs[i]);
155 }
156 }
157 }
158
159 // Frame access helpers
GetFrame(io_buffer_t ** bufs,uint16_t ring_id,uint16_t desc_id)160 zx_off_t GetFrame(io_buffer_t** bufs, uint16_t ring_id, uint16_t desc_id) {
161 uint16_t i = static_cast<uint16_t>(desc_id + ring_id * kBacklog);
162 *bufs = &((*bufs)[i / kFramesInBuf]);
163 return (i % kFramesInBuf) * kFrameSize;
164 }
165
GetFrameVirt(io_buffer_t * bufs,uint16_t ring_id,uint16_t desc_id)166 void* GetFrameVirt(io_buffer_t* bufs, uint16_t ring_id, uint16_t desc_id) {
167 zx_off_t offset = GetFrame(&bufs, ring_id, desc_id);
168 uintptr_t vaddr = reinterpret_cast<uintptr_t>(io_buffer_virt(bufs));
169 return reinterpret_cast<void*>(vaddr + offset);
170 }
171
GetFramePhys(io_buffer_t * bufs,uint16_t ring_id,uint16_t desc_id)172 zx_paddr_t GetFramePhys(io_buffer_t* bufs, uint16_t ring_id, uint16_t desc_id) {
173 zx_off_t offset = GetFrame(&bufs, ring_id, desc_id);
174 return io_buffer_phys(bufs) + offset;
175 }
176
GetFrameHdr(io_buffer_t * bufs,uint16_t ring_id,uint16_t desc_id)177 virtio_net_hdr_t* GetFrameHdr(io_buffer_t* bufs, uint16_t ring_id, uint16_t desc_id) {
178 return reinterpret_cast<virtio_net_hdr_t*>(GetFrameVirt(bufs, ring_id, desc_id));
179 }
180
GetFrameData(io_buffer_t * bufs,uint16_t ring_id,uint16_t desc_id,size_t hdr_size)181 uint8_t* GetFrameData(io_buffer_t* bufs, uint16_t ring_id, uint16_t desc_id, size_t hdr_size) {
182 uintptr_t vaddr = reinterpret_cast<uintptr_t>(GetFrameHdr(bufs, ring_id, desc_id));
183 return reinterpret_cast<uint8_t*>(vaddr + hdr_size);
184 }
185
186 } // namespace
187
EthernetDevice(zx_device_t * bus_device,zx::bti bti,fbl::unique_ptr<Backend> backend)188 EthernetDevice::EthernetDevice(zx_device_t* bus_device, zx::bti bti, fbl::unique_ptr<Backend> backend)
189 : Device(bus_device, std::move(bti), std::move(backend)), rx_(this), tx_(this), bufs_(nullptr),
190 unkicked_(0), ifc_({nullptr, nullptr}) {
191 }
192
~EthernetDevice()193 EthernetDevice::~EthernetDevice() {
194 LTRACE_ENTRY;
195 }
196
Init()197 zx_status_t EthernetDevice::Init() {
198 LTRACE_ENTRY;
199 zx_status_t rc;
200 if (mtx_init(&state_lock_, mtx_plain) != thrd_success ||
201 mtx_init(&tx_lock_, mtx_plain) != thrd_success) {
202 return ZX_ERR_NO_RESOURCES;
203 }
204 fbl::AutoLock lock(&state_lock_);
205
206 // Reset the device and read our configuration
207 DeviceReset();
208 CopyDeviceConfig(&config_, sizeof(config_));
209 LTRACEF("mac %02x:%02x:%02x:%02x:%02x:%02x\n", config_.mac[0], config_.mac[1], config_.mac[2],
210 config_.mac[3], config_.mac[4], config_.mac[5]);
211 LTRACEF("status %u\n", config_.status);
212 LTRACEF("max_virtqueue_pairs %u\n", config_.max_virtqueue_pairs);
213
214 // Ack and set the driver status bit
215 DriverStatusAck();
216
217 virtio_hdr_len_ = sizeof(virtio_net_hdr_t);
218 if (DeviceFeatureSupported(VIRTIO_F_VERSION_1)) {
219 DriverFeatureAck(VIRTIO_F_VERSION_1);
220 } else {
221 // 5.1.6.1 Legacy Interface: Device Operation
222 //
223 // The legacy driver only presented num_buffers in the struct
224 // virtio_net_hdr when VIRTIO_NET_F_MRG_RXBUF was negotiated; without
225 // that feature the structure was 2 bytes shorter.
226 virtio_hdr_len_ -= 2;
227 }
228
229 // TODO(aarongreen): Check additional features bits and ack/nak them
230 rc = DeviceStatusFeaturesOk();
231 if (rc != ZX_OK) {
232 zxlogf(ERROR, "%s: Feature negotiation failed (%d)\n", tag(), rc);
233 return rc;
234 }
235
236 // Plan to clean up unless everything goes right.
237 auto cleanup = fbl::MakeAutoCall([this]() { Release(); });
238
239 // Allocate I/O buffers and virtqueues.
240 uint16_t num_descs = static_cast<uint16_t>(kBacklog & 0xffff);
241 if ((rc = InitBuffers(bti_, &bufs_)) != ZX_OK || (rc = rx_.Init(kRxId, num_descs)) != ZX_OK ||
242 (rc = tx_.Init(kTxId, num_descs)) != ZX_OK) {
243 zxlogf(ERROR, "failed to allocate virtqueue: %s\n", zx_status_get_string(rc));
244 return rc;
245 }
246
247 // Associate the I/O buffers with the virtqueue descriptors
248 desc_t* desc = nullptr;
249 uint16_t id;
250
251 // For rx buffers, we queue a bunch of "reads" from the network that
252 // complete when packets arrive.
253 for (uint16_t i = 0; i < num_descs; ++i) {
254 desc = rx_.AllocDescChain(1, &id);
255 desc->addr = GetFramePhys(bufs_.get(), kRxId, id);
256 desc->len = kFrameSize;
257 desc->flags |= VRING_DESC_F_WRITE;
258 LTRACE_DO(virtio_dump_desc(desc));
259 rx_.SubmitChain(id);
260 }
261
262 // For tx buffers, we hold onto them until we need to send a packet.
263 for (uint16_t id = 0; id < num_descs; ++id) {
264 desc = tx_.DescFromIndex(id);
265 desc->addr = GetFramePhys(bufs_.get(), kTxId, id);
266 desc->len = 0;
267 desc->flags &= static_cast<uint16_t>(~VRING_DESC_F_WRITE);
268 LTRACE_DO(virtio_dump_desc(desc));
269 }
270
271 // Start the interrupt thread and set the driver OK status
272 StartIrqThread();
273
274 // Initialize the zx_device and publish us
275 device_add_args_t args;
276 memset(&args, 0, sizeof(args));
277 args.version = DEVICE_ADD_ARGS_VERSION;
278 args.name = "virtio-net";
279 args.ctx = this;
280 args.ops = &kDeviceOps;
281 args.proto_id = ZX_PROTOCOL_ETHMAC;
282 args.proto_ops = &kProtoOps;
283 if ((rc = device_add(bus_device_, &args, &device_)) != ZX_OK) {
284 zxlogf(ERROR, "failed to add device: %s\n", zx_status_get_string(rc));
285 return rc;
286 }
287 // Give the rx buffers to the host
288 rx_.Kick();
289
290 // Woohoo! Driver should be ready.
291 cleanup.cancel();
292 DriverStatusOk();
293 return ZX_OK;
294 }
295
Release()296 void EthernetDevice::Release() {
297 LTRACE_ENTRY;
298 fbl::AutoLock lock(&state_lock_);
299 ReleaseLocked();
300 }
301
ReleaseLocked()302 void EthernetDevice::ReleaseLocked() {
303 ifc_.ops = nullptr;
304 ReleaseBuffers(std::move(bufs_));
305 Device::Release();
306 }
307
IrqRingUpdate()308 void EthernetDevice::IrqRingUpdate() {
309 LTRACE_ENTRY;
310 // Lock to prevent changes to ifc_.
311 {
312 fbl::AutoLock lock(&state_lock_);
313 if (!ifc_.ops) {
314 return;
315 }
316 // Ring::IrqRingUpdate will call this lambda on each rx buffer filled by
317 // the underlying device since the last IRQ.
318 // Thread safety analysis is explicitly disabled as clang isn't able to determine that the
319 // state_lock_ is held when the lambda invoked.
320 rx_.IrqRingUpdate([this](vring_used_elem* used_elem) TA_NO_THREAD_SAFETY_ANALYSIS {
321 uint16_t id = static_cast<uint16_t>(used_elem->id & 0xffff);
322 desc_t* desc = rx_.DescFromIndex(id);
323
324 // Transitional driver does not merge rx buffers.
325 if ((desc->flags & VRING_DESC_F_NEXT) != 0) {
326 zxlogf(ERROR, "dropping rx packet; do not support descriptor chaining");
327 while((desc->flags & VRING_DESC_F_NEXT)) {
328 uint16_t next_id = desc->next;
329 rx_.FreeDesc(id);
330 id = next_id;
331 desc = rx_.DescFromIndex(id);
332 }
333 return;
334 }
335 assert(used_elem->len <= desc->len);
336 uint8_t* data = GetFrameData(bufs_.get(), kRxId, id, virtio_hdr_len_);
337 size_t len = used_elem->len - virtio_hdr_len_;
338 LTRACEF("Receiving %zu bytes:\n", len);
339 LTRACE_DO(hexdump8_ex(data, len, 0));
340
341 // Pass the data up the stack to the generic Ethernet driver
342 ethmac_ifc_recv(&ifc_, data, len, 0);
343 LTRACE_DO(virtio_dump_desc(desc));
344 rx_.FreeDesc(id);
345 });
346 }
347
348 // Now recycle the rx buffers. As in Init(), this means queuing a bunch of
349 // "reads" from the network that will complete when packets arrive.
350 desc_t* desc = nullptr;
351 uint16_t id;
352 bool need_kick = false;
353 while ((desc = rx_.AllocDescChain(1, &id))) {
354 desc->len = kFrameSize;
355 rx_.SubmitChain(id);
356 need_kick = true;
357 }
358
359 // If we have re-queued any rx buffers, poke the virtqueue to pick them up.
360 if (need_kick) {
361 rx_.Kick();
362 }
363 }
364
IrqConfigChange()365 void EthernetDevice::IrqConfigChange() {
366 LTRACE_ENTRY;
367 fbl::AutoLock lock(&state_lock_);
368 if (!ifc_.ops) {
369 return;
370 }
371
372 // Re-read our configuration
373 CopyDeviceConfig(&config_, sizeof(config_));
374 ethmac_ifc_status(&ifc_, (config_.status & VIRTIO_NET_S_LINK_UP) ? ETHMAC_STATUS_ONLINE : 0);
375 }
376
Query(uint32_t options,ethmac_info_t * info)377 zx_status_t EthernetDevice::Query(uint32_t options, ethmac_info_t* info) {
378 LTRACE_ENTRY;
379 if (options) {
380 return ZX_ERR_INVALID_ARGS;
381 }
382 fbl::AutoLock lock(&state_lock_);
383 if (info) {
384 // TODO(aarongreen): Add info->features = GetFeatures();
385 info->mtu = kVirtioMtu;
386 info->netbuf_size = sizeof(ethmac_netbuf_t);
387 memcpy(info->mac, config_.mac, sizeof(info->mac));
388 }
389 return ZX_OK;
390 }
391
Stop()392 void EthernetDevice::Stop() {
393 LTRACE_ENTRY;
394 fbl::AutoLock lock(&state_lock_);
395 ifc_.ops = nullptr;
396 }
397
Start(const ethmac_ifc_t * ifc)398 zx_status_t EthernetDevice::Start(const ethmac_ifc_t* ifc) {
399 LTRACE_ENTRY;
400 if (!ifc) {
401 return ZX_ERR_INVALID_ARGS;
402 }
403 fbl::AutoLock lock(&state_lock_);
404 if (!bufs_ || ifc_.ops) {
405 return ZX_ERR_BAD_STATE;
406 }
407 ifc_ = *ifc;
408 ethmac_ifc_status(&ifc_, (config_.status & VIRTIO_NET_S_LINK_UP) ? ETHMAC_STATUS_ONLINE : 0);
409 return ZX_OK;
410 }
411
QueueTx(uint32_t options,ethmac_netbuf_t * netbuf)412 zx_status_t EthernetDevice::QueueTx(uint32_t options, ethmac_netbuf_t* netbuf) {
413 LTRACE_ENTRY;
414 const void* data = netbuf->data_buffer;
415 size_t length = netbuf->data_size;
416 // First, validate the packet
417 if (!data || length > kEthFrameSize) {
418 zxlogf(ERROR, "dropping packet; invalid packet\n");
419 return ZX_ERR_INVALID_ARGS;
420 }
421
422 fbl::AutoLock lock(&tx_lock_);
423
424 // Flush outstanding descriptors. Ring::IrqRingUpdate will call this lambda
425 // on each sent tx_buffer, allowing us to reclaim them.
426 auto flush = [this](vring_used_elem* used_elem) {
427 uint16_t id = static_cast<uint16_t>(used_elem->id & 0xffff);
428 desc_t* desc = tx_.DescFromIndex(id);
429 assert((desc->flags & VRING_DESC_F_NEXT) == 0);
430 LTRACE_DO(virtio_dump_desc(desc));
431 tx_.FreeDesc(id);
432 };
433
434 // Grab a free descriptor
435 uint16_t id;
436 desc_t* desc = tx_.AllocDescChain(1, &id);
437 if (!desc) {
438 tx_.IrqRingUpdate(flush);
439 desc = tx_.AllocDescChain(1, &id);
440 }
441 if (!desc) {
442 zxlogf(ERROR, "dropping packet; out of descriptors\n");
443 return ZX_ERR_NO_RESOURCES;
444 }
445
446 // Add the data to be sent
447 virtio_net_hdr_t* tx_hdr = GetFrameHdr(bufs_.get(), kTxId, id);
448 memset(tx_hdr, 0, virtio_hdr_len_);
449
450 // 5.1.6.2.1 Driver Requirements: Packet Transmission
451 //
452 // The driver MUST set num_buffers to zero.
453 //
454 // Implementation note: This field doesn't exist if neither
455 // |VIRTIO_F_VERSION_1| or |VIRTIO_F_MRG_RXBUF| have been negotiated. Since
456 // this field will be part of the payload without these features we elide
457 // the check as we know the memory is valid and will soon be overwritten
458 // with packet data.
459 tx_hdr->num_buffers = 0;
460
461 // If VIRTIO_NET_F_CSUM is not negotiated, the driver MUST set flags to
462 // zero and SHOULD supply a fully checksummed packet to the device.
463 tx_hdr->flags = 0;
464
465 // If none of the VIRTIO_NET_F_HOST_TSO4, TSO6 or UFO options have been
466 // negotiated, the driver MUST set gso_type to VIRTIO_NET_HDR_GSO_NONE.
467 tx_hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
468
469 void* tx_buf = GetFrameData(bufs_.get(), kTxId, id, virtio_hdr_len_);
470 memcpy(tx_buf, data, length);
471 desc->len = static_cast<uint32_t>(virtio_hdr_len_ + length);
472
473 // Submit the descriptor and notify the back-end.
474 LTRACE_DO(virtio_dump_desc(desc));
475 LTRACEF("Sending %zu bytes:\n", length);
476 LTRACE_DO(hexdump8_ex(tx_buf, length, 0));
477 tx_.SubmitChain(id);
478 ++unkicked_;
479 if ((options & ETHMAC_TX_OPT_MORE) == 0 || unkicked_ > kBacklog / 2) {
480 tx_.Kick();
481 unkicked_ = 0;
482 }
483 return ZX_OK;
484 }
485
486 } // namespace virtio
487