2 #include <l4/re/error_helper>
3 #include <l4/re/util/cap_alloc>
4 #include <l4/re/util/object_registry>
7 #include <l4/sys/thread>
8 #include <l4/sys/factory>
9 #include <l4/sys/compiler.h>
11 #include <l4/cxx/bitfield>
12 #include <l4/cxx/ipc_stream>
13 #include <l4/cxx/ipc_server>
15 #include <l4/re/dataspace>
16 #include <l4/re/util/meta>
26 //#define CONFIG_STATS 1
27 //#define CONFIG_BENCHMARK 1
29 static L4::Cap<L4::Kobject> rcv_cap[2];
32 class Mmio_remote : public L4::Kobject_t<Mmio_remote, L4::Kobject>
34 L4_KOBJECT(Mmio_remote)
38 template< typename T >
39 class Irq_object : public L4::Server_object
42 int dispatch(l4_umword_t, L4::Ipc::Iostream &)
44 static_cast<T*>(this)->kick();
49 L4::Cap<L4::Irq> irq_obj_cap() const
50 { return L4::cap_cast<L4::Irq>(obj_cap()); }
54 class Mmio_device_t : public L4::Server_object
57 int dispatch(l4_umword_t, L4::Ipc::Iostream &ios)
64 case L4::Meta::Protocol:
65 return L4Re::Util::handle_meta_request<Virtio::Mmio_remote>(ios);
83 l4_uint32_t val = static_cast<T*>(this)->read(reg, size);
93 ios >> reg >> size >> val;
94 static_cast<T*>(this)->write(reg, size, val);
100 L4::Ipc::Snd_fpage irq_cap_fp, ds_cap_fp;
106 ios >> ds_base >> irq_cap_fp >> ds_cap_fp;
108 if (!irq_cap_fp.cap_received())
111 kick_guest_irq.get().move(L4::cap_cast<L4::Irq>(rcv_cap[0]));
112 static_cast<T*>(this)->check_n_init_shm(ds_cap_fp, rcv_cap[1],
115 ios << static_cast<T*>(this)->irq_obj_cap()
116 << static_cast<T*>(this)->device_config_ds.get();
125 L4Re::Util::Auto_cap<L4::Irq>::Cap kick_guest_irq;
131 class Dev : public Mmio_device_t<Dev>, public Irq_object<Dev>
134 typedef Irq_object<Dev> Irq;
135 typedef Mmio_device_t<Dev> Mmio;
137 template<typename REG>
138 void register_obj(REG *registry, char const *service = 0)
140 L4Re::chkcap(registry->register_irq_obj(static_cast<Irq*>(this)));
142 L4Re::chkcap(registry->register_obj(static_cast<Mmio*>(this), service));
144 L4Re::chkcap(registry->register_obj(static_cast<Mmio*>(this)));
146 kick_guest_irq = L4Re::Util::cap_alloc.alloc<L4::Irq>();
147 guest_shm = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
153 unsigned _num_queues;
155 L4Re::Rm::Auto_region<l4_addr_t> shm;
157 l4_mword_t _shm_offset;
158 L4Re::Util::Auto_cap<L4Re::Dataspace>::Cap guest_shm;
159 L4Re::Rm::Auto_region<Virtio::Dev_config*> device_config;
161 L4Re::Util::Auto_cap<L4Re::Dataspace>::Cap device_config_ds;
164 Dev(l4_uint32_t vendor, l4_uint32_t device)
165 : _current_q(0), _queues(0), _num_queues(0)
167 L4Re::Util::Auto_cap<L4Re::Dataspace>::Cap cfg
168 = L4Re::chkcap(L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>());
169 L4Re::chksys(L4Re::Env::env()->mem_alloc()->alloc(L4_PAGESIZE, cfg.get()));
170 L4Re::chksys(L4Re::Env::env()->rm()->attach(&device_config, L4_PAGESIZE,
171 L4Re::Rm::Search_addr,
173 device_config_ds = cfg;
175 device_config->vendor = vendor;
176 device_config->device = device;
177 device_config->irq_status = 0;
178 device_config->status = Dev_config::Status(0);
179 device_config->host_features = Dev_config::Features(0);
180 device_config->page_size = L4_PAGESIZE;
184 T *access(Virtio::Ptr<T> p)
185 { return (T*)(p.get() + _shm_offset); }
187 void check_n_init_shm(L4::Ipc::Snd_fpage shm_cap_fp, L4::Cap<void> rcv_cap,
190 if (!shm_cap_fp.cap_received())
191 L4Re::chksys(-L4_EINVAL);
193 guest_shm.get().move(L4::cap_cast<L4Re::Dataspace>(rcv_cap));
194 L4Re::chksys(L4Re::Env::env()->rm()->attach(&shm, guest_shm->size(),
195 L4Re::Rm::Search_addr, guest_shm.get()));
197 printf("PORT[%p]: shm @ %lx\n", this, shm.get());
200 _shm_offset = shm.get() - base;
203 virtual l4_uint32_t read_config(unsigned /*reg*/) { return 0; }
204 virtual void write_config(unsigned /*reg*/, l4_uint32_t /*value*/) {}
205 virtual void kick() = 0;
207 Ring *queue(unsigned idx)
209 if (idx < _num_queues)
210 return &_queues[idx];
214 virtual void reset() {}
216 l4_uint32_t read(unsigned reg, char /*size*/)
219 return read_config(reg - 0x100);
223 case 0: return *reinterpret_cast<l4_uint32_t const *>("virt");
225 case 2: return device_config->device;
226 case 3: return device_config->vendor;
227 case 4: return device_config->host_features.raw;
230 return _current_q->max_num;
236 return (unsigned long)_current_q->desc / device_config->page_size;
243 l4_uint32_t tmp = device_config->irq_status;
244 device_config->irq_status = 0;
249 case 28: return device_config->status.raw;
254 void write(unsigned reg, char /*size*/, l4_uint32_t value)
258 write_config(reg - 0x100, value);
265 device_config->guest_features.raw = value;
269 device_config->page_size = value;
273 _current_q = queue(value);
278 _current_q->num = _current_q->max_num >= value
280 : _current_q->max_num;
285 _current_q->align = value;
290 _current_q->setup((void *)(shm.get()
291 + value * device_config->page_size
296 // TODO: Kick must be implemented in VMM
300 device_config->status.raw = value;
311 static void *stats_thread_loop(void *);
322 CXX_BITFIELD_MEMBER( 0, 0, need_csum, raw);
323 CXX_BITFIELD_MEMBER( 1, 1, data_valid, raw);
331 l4_uint16_t gso_size;
332 l4_uint16_t csum_start;
333 l4_uint16_t csum_offset;
334 l4_uint16_t num_buffers;
337 virtual void tx(Hdr const *, Virtio::Ring::Desc *descs, unsigned pkt,
342 class Virtio_net : public Virtio::Dev
345 struct Features : Virtio::Dev_config::Features
347 CXX_BITFIELD_MEMBER( 0, 0, csum, raw); // host handles partial csum
348 CXX_BITFIELD_MEMBER( 1, 1, guest_csum, raw); // guest handles partial csum
349 CXX_BITFIELD_MEMBER( 5, 5, mac, raw); // host has given mac
350 CXX_BITFIELD_MEMBER( 6, 6, gso, raw); // host handles packets /w any GSO
351 CXX_BITFIELD_MEMBER( 7, 7, guest_tso4, raw); // guest handles TSOv4 in
352 CXX_BITFIELD_MEMBER( 8, 8, guest_tso6, raw); // guest handles TSOv6 in
353 CXX_BITFIELD_MEMBER( 9, 9, guest_ecn, raw); // guest handles TSO[6] with ECN in
354 CXX_BITFIELD_MEMBER(10, 10, guest_ufo, raw); // guest handles UFO in
355 CXX_BITFIELD_MEMBER(11, 11, host_tso4, raw); // host handles TSOv4 in
356 CXX_BITFIELD_MEMBER(12, 12, host_tso6, raw); // host handles TSOv6 in
357 CXX_BITFIELD_MEMBER(13, 13, host_ecn, raw); // host handles TSO[6] with ECN in
358 CXX_BITFIELD_MEMBER(14, 14, host_ufo, raw); // host handles UFO
359 CXX_BITFIELD_MEMBER(15, 15, mrg_rxbuf, raw); // host can merge receive buffers
360 CXX_BITFIELD_MEMBER(16, 16, status, raw); // virtio_net_config.status available
361 CXX_BITFIELD_MEMBER(17, 17, ctrl_vq, raw); // Control channel available
362 CXX_BITFIELD_MEMBER(18, 18, ctrl_rx, raw); // Control channel RX mode support
363 CXX_BITFIELD_MEMBER(19, 19, ctrl_vlan, raw); // Control channel VLAN filtering
364 CXX_BITFIELD_MEMBER(20, 20, ctrl_rx_extra, raw); // Extra RX mode control support
365 CXX_BITFIELD_MEMBER(21, 21, guest_announce, raw); // Guest can announce device on the network
366 CXX_BITFIELD_MEMBER(22, 22, mq, raw); // Device supports Receive Flow Steering
367 CXX_BITFIELD_MEMBER(23, 23, ctrl_mac_addr, raw); // Set MAC address
370 typedef Switch::Hdr Hdr;
372 Features &host_features()
373 { return static_cast<Features &>(device_config->host_features); }
374 Features host_features() const
375 { return static_cast<Features const &>(device_config->host_features); }
384 unsigned long num_tx;
385 unsigned long num_rx;
386 unsigned long num_dropped;
387 unsigned long num_irqs;
391 : Virtio::Dev(0x44, 1)
393 , num_tx(0), num_rx(0), num_dropped(0), num_irqs(0)
397 device_config->num_queues = _num_queues = 2;
398 _q[Rx].max_num = 0x1000;
399 _q[Tx].max_num = 0x1000;
401 host_features().csum() = true;
402 host_features().host_tso4() = true;
403 host_features().host_tso6() = true;
404 host_features().host_ufo() = true;
405 host_features().host_ecn() = true;
407 host_features().guest_csum() = true;
408 host_features().guest_tso4() = true;
409 host_features().guest_tso6() = true;
410 host_features().guest_ufo() = true;
411 host_features().guest_ecn() = true;
423 printf("%s: process tx queue\n", name);
424 Virtio::Ring *q = &_q[Tx];
426 l4_uint32_t irqs = 0;
428 Virtio::Ring::Desc *d;
431 q->used->flags.no_notify() = 1;
433 printf("q->avail.idx=%d\n", q->avail->idx);
434 while ((d = q->next_avail()))
439 #ifndef CONFIG_BENCHMARK
440 if (L4_UNLIKELY(d->flags.write()))
442 printf("PORT[%p]: input buffer in TX queue (skip)\n", this);
447 if (L4_UNLIKELY(!d->flags.next()))
449 printf("PORT[%p]: input w/o data\n", this);
455 Hdr const *hdr = access(d->buf<Hdr const>());
457 Virtio::Ring::Desc *pkt = &q->desc[d->next];
459 #ifndef CONFIG_BENSCHMARK
460 if (L4_UNLIKELY(pkt->flags.write()))
462 printf("PORT[%p]: input buffer in TX queue (skip)\n", this);
471 printf("TX[%s]\n", name ? name : "<unk>");
472 net_switch->tx(hdr, q->desc, d->next, this);
478 q->used->flags.no_notify() = 0;
480 while (q->desc_avail());
482 printf("%s: wait\n", name);
486 void notify_guest(Virtio::Ring *queue)
488 if (queue->avail->flags.no_irq())
491 // we do not care about this anywhere, so skip
493 device_config->irq_status |= 1;
495 kick_guest_irq->trigger();
504 notify_guest(&_q[Tx]);
507 bool rx(Hdr const *tx_hdr, Virtio::Ring::Desc *tx_descs, unsigned tx_idx,
510 if (L4_UNLIKELY(!device_config->status.running()))
514 printf("%s: copy packet\n", name);
516 Virtio::Ring *q = &_q[Rx];
519 printf("q->avail.idx=%d\n", q->avail->idx);
520 Virtio::Ring::Desc *d = q->next_avail();
525 #ifndef CONFIG_BENCHMARK
526 if (L4_UNLIKELY(!d->flags.write()))
528 printf("PORT[%p]: invalid buffer in RX queue\n", this);
534 if (L4_UNLIKELY(!d->flags.next()))
536 printf("PORT[%p]: invalid buffer in RX queue\n", this);
543 Virtio::Ring::Desc *rxb = q->desc + d->next;
544 Virtio::Ring::Desc *txb = tx_descs + tx_idx;
545 char *rx_addr = access(rxb->buf<char>());
546 char const *tx_addr = tx_port->access(txb->buf<char const>());
548 Hdr *rx_hdr = access(d->buf<Hdr>());
550 rx_hdr->flags.raw = 0;
551 rx_hdr->gso_type = 0;
557 if (tx_hdr->flags.need_csum())
558 rx_hdr->flags.data_valid() = 1;
560 unsigned long tx_space = txb->len;
561 unsigned long rx_space = rxb->len;
562 unsigned long rx_bytes = d->len;
566 unsigned long cpy = std::min(tx_space, rx_space);
568 memcpy(rx_addr, tx_addr, cpy);
580 if (!txb->flags.next())
583 txb = tx_descs + txb->next;
584 tx_addr = tx_port->access(txb->buf<char const>());
590 if (!rxb->flags.next())
593 rxb = q->desc + rxb->next;
594 rx_addr = access(rxb->buf<char>());
599 printf("more to copy rx=%s\n", name);
602 #ifndef CONFIG_BENCHMARK
603 if (L4_UNLIKELY(tx_space))
604 printf("PORT[%p]: truncate packet: %lx bytes left\n", this, tx_space);
607 q->consumed(d, rx_bytes);
618 friend void *stats_thread_loop(void *);
622 template<unsigned PORTS>
623 class Switch_t : public Switch
631 Virtio_net port[PORTS];
635 for (unsigned i = 0; i < PORTS; ++i)
636 port[i].net_switch = this;
639 void tx(Hdr const *tx_hdr, Virtio::Ring::Desc *descs, unsigned pkt,
642 for (unsigned i = 0; i < PORTS; ++i)
645 if (L4_UNLIKELY(!port[i].rx(tx_hdr, descs, pkt, p)))
648 ++port[i].num_dropped;
657 : L4::Ipc_svr::Ignore_errors,
658 L4::Ipc_svr::Default_timeout,
659 L4::Ipc_svr::Compound_reply
661 void setup_wait(L4::Ipc::Istream &istr, L4::Ipc_svr::Reply_mode)
664 istr << L4::Ipc::Small_buf(rcv_cap[0].cap(), L4_RCV_ITEM_LOCAL_ID);
665 istr << L4::Ipc::Small_buf(rcv_cap[1].cap(), L4_RCV_ITEM_LOCAL_ID);
666 l4_utcb_br_u(istr.utcb())->bdr = 0;
670 static L4Re::Util::Object_registry registry;
671 static L4::Server<Loop_hooks> server(l4_utcb());
673 static Switch_t<2> net;
676 static void *stats_thread_loop(void *)
681 for (unsigned i = 0; i < net.Num_ports; ++i)
683 Virtio_net *p = &net.port[i];
684 printf("%s: tx:%ld rx:%ld drp:%ld irqs:%ld ri:%d:%d ",
685 p->name, p->num_tx, p->num_rx, p->num_dropped, p->num_irqs,
686 p->device_config->status.running()
687 ? (unsigned)p->_q[0].avail->idx : -1,
688 (unsigned)p->_q[0].current_avail);
698 printf("Hello from virtio server\n");
699 rcv_cap[0] = L4Re::chkcap(L4Re::Util::cap_alloc.alloc<L4::Kobject>());
700 rcv_cap[1] = L4Re::chkcap(L4Re::Util::cap_alloc.alloc<L4::Kobject>());
703 pthread_t stats_thread;
704 pthread_create(&stats_thread, NULL, stats_thread_loop, NULL);
707 net.port[0].register_obj(®istry, "net0");
708 net.port[0].name = "net0";
709 net.port[1].register_obj(®istry, "net1");
710 net.port[1].name = "net1";
712 server.loop(registry);