CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-ethernet.hh
1#pragma once
2#include <allocator.h>
3#include <array>
4#include <cheri.hh>
5#include <cstddef>
6#include <cstdint>
7#include <debug.hh>
8#include <futex.h>
9#include <interrupt.h>
10#include <optional>
11#include <platform/concepts/ethernet.hh>
12#include <thread.h>
13#include <type_traits>
14
16 EthernetReceiveInterrupt,
17 true,
18 true);
19
20/**
21 * The driver for Kunyan Liu's custom Ethernet MAC for the Arty A7. This is
22 * intended to run at 10Mb/s. It provides two send and two receive buffers in
23 * shared SRAM.
24 *
25 * WARNING: This is currently evolving and is not yet stable.
26 */
28{
29 /**
30 * Flag set when we're debugging this driver.
31 */
32 static constexpr bool DebugEthernet = false;
33
34 /**
35 * Flag set to log messages when frames are dropped.
36 */
37 static constexpr bool DebugDroppedFrames = false;
38
39 /**
40 * Helper for conditional debug logs and assertions.
41 */
42 using Debug = ConditionalDebug<DebugEthernet, "Ethernet driver">;
43
44 /**
45 * Import the Capability helper from the CHERI namespace.
46 */
47 template<typename T>
48 using Capability = CHERI::Capability<T>;
49
50 /**
51 * The location of registers
52 */
53 enum class RegisterOffset : size_t
54 {
55 /**
56 * MAC control register. Higher bits control filtering modes:
57 *
58 * 12: Drop frames with invalid CRC.
59 * 11: Allow incoming IPv6 multicast filtering
60 * 10: Allow incoming IPv4 multicast filtering
61 * 9: Allow incoming broadcast filtering
62 * 8: Allow incoming unicast filtering
63 *
64 *
65 * Write a 1 to reset the PHY. This takes 167 ms.
66 */
67 MACControl = 0,
68 /**
69 * High 4 bytes of the MAC address. Byte 2 is in bits 31:24, byte 5 is
70 * in 7:0.
71 */
72 MACAddressHigh = 4,
73 /**
74 * Low 2 bytes of the MAC address. Byte 1 in bits 15:8, byte 0 in 7:0.
75 */
76 MACAddressLow = 8,
77 /**
78 * Scratch register, unused.
79 */
80 Scratch = 12,
81 /**
82 * MDIO address, controls which PHY register will be read or written.
83 */
84 MDIOAddress = 0x10,
85 /**
86 * MDIO Write, written to set the value to write to a PHY register.
87 */
88 MDIODataWrite = 0x14,
89 /**
90 * MDIO Read, set by the device to the value of a PHY register in
91 * response to an MDIO read.
92 */
93 MDIODataRead = 0x18,
94 /**
95 * MDIO control. Used to control and report status of MDIO
96 * transactions.
97 */
98 MDIOControl = 0x1c,
99 /**
100 * Length of the frame written to the ping buffer for sending.
101 */
102 TransmitFrameLengthPing = 0x20,
103 /**
104 * Transmit control for the ping buffer.
105 */
106 TransmitControlPing = 0x24,
107 /**
108 * Length of the frame written to the pong buffer for sending.
109 */
110 TransmitFrameLengthPong = 0x28,
111 /**
112 * Transmit control for the pong buffer.
113 */
114 TransmitControlPong = 0x2c,
115 /**
116 * Receive control for the ping buffer.
117 */
118 ReceiveControlPing = 0x30,
119 /**
120 * Receive control for the pong buffer.
121 */
122 ReceiveControlPong = 0x34,
123 /**
124 * Interrupt enable. Write 1 to enable, 0 to disable interrupts for
125 * each direction. Bit 0 is transmit, bit 1 is receive.
126 */
127 GlobalInterruptEnable = 0x38,
128 /**
129 * Current interrupt status. Bit 0 is transmit, bit 1 is receive.
130 *
131 * Write a 1 to each bit to clear. The device will keep the interrupt
132 * asserted until it is explicitly cleared.
133 */
134 InterruptStatus = 0x3c,
135 /**
136 * Length of the frames in the ping and pong buffer, in the low and
137 * high 16 bits respectively.
138 */
139 ReceiveFrameLength = 0x40,
140 /**
141 * Saturating counter of the number of frames received.
142 */
143 ReceivedFramesCount = 0x44,
144 /**
145 * Saturating counter of the number of frames passed to software.
146 */
147 ReceivedFramesPassedToSoftware = 0x48,
148 /**
149 * Saturating counter of the number of frames dropped because the
150 * receive buffers were both full.
151 */
152 ReceiveDroppedFramesBuffersFull = 0x4c,
153 /**
154 * Saturating counter of the number of frames dropped because they had
155 * failed FCS checks.
156 */
157 ReceiveDroppedFramesFCSFailed = 0x50,
158 /**
159 * Saturating counter of the number of frames dropped because the
160 * target address was invalid.
161 */
162 ReceiveDroppedFramesInvalidAddress = 0x54,
163 };
164
165 using MACAddress = std::array<uint8_t, 6>;
166
167 /**
168 * MAC filtering modes.
169 */
170 enum class FilterMode : uint32_t
171 {
172 /**
173 * Allow incoming frames without doing any address filtering.
174 */
175 EnableAddressFiltering = 1 << 13,
176 /**
177 * Drop frames with invalid CRC.
178 */
179 DropInvalidCRC = 1 << 12,
180 /**
181 * Allow incoming IPv6 multicast filtering
182 */
183 AllowIPv6Multicast = 1 << 11,
184 /**
185 * Allow incoming IPv4 multicast filtering
186 */
187 AllowIPv4Multicast = 1 << 10,
188 /**
189 * Allow incoming broadcast filtering
190 */
191 AllowBroadcast = 1 << 9,
192 /**
193 * Allow incoming unicast filtering
194 */
195 AllowUnicast = 1 << 8,
196 };
197
198 /**
199 * The futex used to wait for interrupts when packets are available to
200 * receive.
201 */
202 const uint32_t *receiveInterruptFutex;
203
204 /**
205 * Counter for dropped frames, intended to be extensible to support
206 * different debugging registers.
207 */
208 struct DroppedFrameCount
209 {
210 /**
211 * The old value of the counter.
212 */
213 uint32_t droppedFrameCount[3];
214 /**
215 * Get a reference to one of the recorded counters.
216 */
217 template<RegisterOffset Reg>
218 uint32_t &get();
219 template<>
220 uint32_t &get<RegisterOffset::ReceiveDroppedFramesBuffersFull>()
221 {
222 return droppedFrameCount[0];
223 }
224
225 template<>
226 uint32_t &get<RegisterOffset::ReceiveDroppedFramesFCSFailed>()
227 {
228 return droppedFrameCount[1];
229 }
230
231 template<>
232 uint32_t &get<RegisterOffset::ReceiveDroppedFramesInvalidAddress>()
233 {
234 return droppedFrameCount[2];
235 }
236 };
237
238 /**
239 * Empty class, for use with `std::conditional_t` to conditionally add a
240 * field.
241 */
242 struct Empty
243 {
244 };
245
246 /**
247 * If we're building with debugging support for dropped-frame counters,
248 * reserve space for them.
249 */
250 [[no_unique_address]] std::
251 conditional_t<DebugDroppedFrames, DroppedFrameCount, Empty> droppedFrames;
252
253 /**
254 * Log a message if the dropped-frame counter identified by `Reg` has
255 * changed.
256 */
257 template<RegisterOffset Reg, typename T = decltype(droppedFrames)>
258 void dropped_frames_log_if_changed(T &droppedFrames)
259 {
260 if constexpr (std::is_same_v<T, DroppedFrameCount>)
261 {
262 auto &lastCount = droppedFrames.template get<Reg>();
263 auto count = mmio_register<Reg>();
264 if (count != lastCount)
265 {
266 ConditionalDebug<true, "Ethernet driver">::log(
267 "Dropped frames in counter {}: {}", Reg, count);
268 lastCount = count;
269 }
270 }
271 }
272
273 /**
274 * Set the state of one of the MAC filtering modes. Returns the previous
275 * value of the mode.
276 */
277 bool filter_mode_update(FilterMode mode, bool enable)
278 {
279 auto &macControl = mmio_register<RegisterOffset::MACControl>();
280 uint32_t modeBit = static_cast<uint32_t>(mode);
281 uint32_t oldMode = macControl;
282 Debug::log("Old filter mode {}: {}", mode, oldMode);
283 if (enable)
284 {
285 macControl = oldMode | modeBit;
286 }
287 else
288 {
289 macControl = oldMode & ~modeBit;
290 }
291 return oldMode | modeBit;
292 }
293
294 /**
295 * The PHY registers are accessed through the MDIO interface.
296 */
297 enum class PHYRegister
298 {
299 BasicControlRegister = 0,
300 BasicStatusRegister = 1,
301 Identifier1 = 2,
302 Identifier2 = 3,
303 AutoNegotiationAdvertisement = 4,
304 AutoNegotiationLinkPartnerAbility = 5,
305 };
306
307 enum BufferID
308 {
309 Ping = 0,
310 Pong = 1,
311 };
312
313 bool receiveBufferInUse[2] = {false, false};
314 bool sendBufferInUse[2] = {false, false};
315
316 BufferID nextReceiveBuffer = BufferID::Ping;
317
318 constexpr static BufferID next_buffer_id(BufferID current)
319 {
320 return current == BufferID::Ping ? BufferID::Pong : BufferID::Ping;
321 }
322
323 /**
324 * Reset the PHY. This must be done on initialisation.
325 */
326 void phy_reset()
327 {
328 auto &macControl = mmio_register<RegisterOffset::MACControl>();
329 macControl = 1;
330 // Wait for the PHY to reset.
332 // Initialise MDIO again after the PHY has been reset.
333 mmio_register<RegisterOffset::MDIOControl>() = 0x10;
334 // Wait to make sure MDIO initialisation has completed.
336 }
337
338 /**
339 * Helper. Returns a pointer to the device.
340 */
341 [[nodiscard]] __always_inline Capability<volatile uint32_t>
342 mmio_region() const
343 {
344 return MMIO_CAPABILITY(uint32_t, kunyan_ethernet);
345 }
346
347 /**
348 * Helper. Returns a reference to a register in this device's MMIO region.
349 * The register is identified by a `RegisterOffset` value.
350 */
351 template<RegisterOffset Offset>
352 [[nodiscard]] volatile uint32_t &mmio_register() const
353 {
354 auto reg = mmio_region();
355 reg.address() += static_cast<size_t>(Offset);
356 reg.bounds() = sizeof(uint32_t);
357 return *reg;
358 }
359
360 /**
361 * Poll the MDIO control register until a transaction is done.
362 *
363 * NOTE: This does no error handling. It can infinite loop if the device
364 * is broken. Normally, MDIO operations complete in fewer cycles than it
365 * takes to call this method and so it will return almost instantly.
366 */
367 void mdio_wait_for_ready()
368 {
369 auto &mdioControl = mmio_register<RegisterOffset::MDIOControl>();
370 while (mdioControl & 1)
371 {
372 }
373 }
374
375 /**
376 * Start an MDIO transaction. This assumes that the MDIO control register
377 * and, for write operations, the MDIO data write register have been set up.
378 * A call to `mdio_wait_for_ready` is required for read transactions to
379 * ensure that the operation has completed before reading the MIDO data read
380 * register.
381 */
382 void mdio_start_transaction()
383 {
384 auto &mdioControl = mmio_register<RegisterOffset::MDIOControl>();
385 Debug::Assert(((mdioControl & ((1 << 3) | 1)) == 0), "MDIO is busy");
386 // Write the MDIO enable bit and the start bit.
387 mdioControl = (1 << 3) | 1;
388 }
389
390 /**
391 * Returns a pointer to one of the receive buffers, identified by the buffer
392 * identifier (ping or pong).
393 */
394 uint8_t *receive_buffer_pointer(BufferID index = BufferID::Ping)
395 {
396 auto buffer = mmio_region();
397 buffer.address() +=
398 static_cast<size_t>(index == BufferID::Ping ? 0x2000 : 0x2800);
399 buffer.bounds() = 0x800;
400 return const_cast<uint8_t *>(
401 reinterpret_cast<volatile uint8_t *>((buffer.get())));
402 }
403
404 /**
405 * Returns a pointer to one of the receive buffers, identified by the buffer
406 * identifier (ping or pong).
407 */
408 uint8_t *transmit_buffer_pointer(BufferID index = BufferID::Ping)
409 {
410 auto buffer = mmio_region();
411 buffer.address() +=
412 static_cast<size_t>(index == BufferID::Ping ? 0x1000 : 0x1800);
413 buffer.bounds() = 0x800;
414 return const_cast<uint8_t *>(
415 reinterpret_cast<volatile uint8_t *>((buffer.get())));
416 }
417
418 /**
419 * Write a value to a PHY register via MDIO.
420 */
421 void
422 mdio_write(uint8_t phyAddress, PHYRegister registerAddress, uint16_t data)
423 {
424 mdio_wait_for_ready();
425 auto &mdioAddress = mmio_register<RegisterOffset::MDIOAddress>();
426 auto &mdioWrite = mmio_register<RegisterOffset::MDIODataWrite>();
427 uint32_t writeCommand = (0 << 10) | (phyAddress << 5) |
428 static_cast<uint32_t>(registerAddress);
429 mdioAddress = writeCommand;
430 mdioWrite = data;
431 mdio_start_transaction();
432 }
433
434 /**
435 * Read a value from a PHY register via MDIO.
436 */
437 uint16_t mdio_read(uint8_t phyAddress, PHYRegister registerAddress)
438 {
439 mdio_wait_for_ready();
440 auto &mdioAddress = mmio_register<RegisterOffset::MDIOAddress>();
441 uint32_t readCommand =
442 (1 << 10) | (phyAddress << 5) | static_cast<uint8_t>(registerAddress);
443 mdioAddress = readCommand;
444 mdio_start_transaction();
445 mdio_wait_for_ready();
446 return mmio_register<RegisterOffset::MDIODataRead>();
447 }
448
449 public:
450 /**
451 * Initialise a reference to the Ethernet device. This will check the ID
452 * registers to make sure that this is the kind of device that we're
453 * expecting.
454 */
456 {
457 phy_reset();
458 // Check that this is the device that we're looking for
459 Debug::Assert(
460 [&]() { return mdio_read(1, PHYRegister::Identifier1) == 0x2000; },
461 "PHY identifier 1 is not 0x2000");
462 Debug::Assert(
463 [&]() {
464 return (mdio_read(1, PHYRegister::Identifier2) & 0xFFF0) ==
465 0x5c90;
466 },
467 "PHY identifier 1 is not 0x5c9x");
469 filter_mode_update(FilterMode::DropInvalidCRC, true);
470 filter_mode_update(FilterMode::EnableAddressFiltering, false);
471 filter_mode_update(FilterMode::AllowUnicast, false);
472 filter_mode_update(FilterMode::AllowIPv4Multicast, false);
473 filter_mode_update(FilterMode::AllowIPv6Multicast, false);
474 receiveInterruptFutex =
476 // Enable receive interrupts
477 mmio_register<RegisterOffset::GlobalInterruptEnable>() = 0b10;
478 // Clear pending receive interrupts.
479 mmio_register<RegisterOffset::InterruptStatus>() = 0b10;
480 }
481
482 KunyanEthernet(const KunyanEthernet &) = delete;
483 KunyanEthernet(KunyanEthernet &&) = delete;
484
485 /**
486 * This device does not have a unique MAC address and so users must provide
487 * a locally administered MAC address if more than one device is present on
488 * the same network.
489 */
490 static constexpr bool has_unique_mac_address()
491 {
492 return false;
493 }
494
495 static constexpr MACAddress mac_address_default()
496 {
497 return {0x60, 0x6A, 0x6A, 0x37, 0x47, 0x88};
498 }
499
500 void mac_address_set(MACAddress address = mac_address_default())
501 {
502 auto &macAddressHigh = mmio_register<RegisterOffset::MACAddressHigh>();
503 auto &macAddressLow = mmio_register<RegisterOffset::MACAddressLow>();
504 macAddressHigh = (address[2] << 24) | (address[3] << 16) |
505 (address[4] << 8) | address[5];
506 macAddressLow = (address[1] << 8) | address[0];
507 }
508
509 uint32_t receive_interrupt_value()
510 {
511 return *receiveInterruptFutex;
512 }
513
514 int receive_interrupt_complete(Timeout *timeout,
515 uint32_t lastInterruptValue)
516 {
517 // Clear the interrupt on the device.
518 auto &interruptStatus =
519 mmio_register<RegisterOffset::InterruptStatus>();
520 interruptStatus = 0b10;
521 // There's a small window in between finishing checking the receive
522 // buffers and clearing the interrupt where we could miss the
523 // interrupt. Check that the receive buffers are empty *after*
524 // reenabling the interrupt to ensure that we don't sleep in this
525 // period.
526 if (check_frame(BufferID::Ping) || check_frame(BufferID::Pong))
527 {
528 Debug::log("Packets already ready, not waiting for interrupt");
529 return 0;
530 }
531 // Acknowledge the interrupt in the scheduler.
532 interrupt_complete(STATIC_SEALED_VALUE(ethernetReceive));
533 if (*receiveInterruptFutex == lastInterruptValue)
534 {
535 Debug::log("Acknowledged interrupt, sleeping on futex {}",
536 receiveInterruptFutex);
537 return futex_timed_wait(
538 timeout, receiveInterruptFutex, lastInterruptValue);
539 }
540 Debug::log("Scheduler announces interrupt has fired");
541 return 0;
542 }
543
544 /**
545 * Enable autonegotiation on the PHY.
546 *
547 * FIXME: This blocks forever if the cable is disconnected!
548 */
549 void autonegotiation_enable(uint8_t phyAddress = 1)
550 {
551 Debug::log("Starting autonegotiation");
552 // Advertise 802.3, 10Base-T full and half duplex
553 mdio_write(phyAddress,
554 PHYRegister::AutoNegotiationAdvertisement,
555 (1 << 5) | (1 << 6) | 1);
556 // Enable and restart autonegitiation
557 mdio_write(
558 phyAddress, PHYRegister::BasicControlRegister, (1 << 12) | (1 << 9));
559 Debug::log("Waiting for autonegotiation to complete");
560 uint32_t loops = 0;
561 while ((mdio_read(phyAddress, PHYRegister::BasicStatusRegister) &
562 (1 << 5)) == 0)
563 {
564 if ((loops++ & 0xfffff) == 0)
565 {
566 Debug::log(
567 "Waiting for autonegotiation to complete ({} loops): status: "
568 "{}",
569 loops,
570 mdio_read(phyAddress, PHYRegister::BasicStatusRegister));
571 }
572 yield();
573 }
574 Debug::Assert(
575 [&]() {
576 return (mdio_read(phyAddress, PHYRegister::BasicStatusRegister) &
577 (1 << 2)) != 0;
578 },
579 "Autonegotiation completed but link is down");
580 Debug::log("Autonegotiation complete, status: {}",
581 mdio_read(phyAddress, PHYRegister::BasicStatusRegister));
582 // Write 1 to clear the receive status.
583 mmio_register<RegisterOffset::ReceiveControlPing>() = 1;
584 mmio_register<RegisterOffset::ReceiveControlPong>() = 1;
585 }
586
587 /**
588 * Check the link status of the PHY.
589 */
591 {
592 return (mdio_read(1, PHYRegister::BasicStatusRegister) & (1 << 2)) != 0;
593 }
594
595 std::optional<uint16_t> check_frame(BufferID index = BufferID::Ping)
596 {
597 // Debug::log("Checking for frame in buffer {}", index);
598 auto receiveControl =
599 index == BufferID::Ping
600 ? mmio_register<RegisterOffset::ReceiveControlPing>()
601 : mmio_register<RegisterOffset::ReceiveControlPong>();
602 if ((receiveControl & 1) == 0)
603 {
604 return std::nullopt;
605 }
606 Debug::log("Buffer has a frame. Error? {}", receiveControl & 2);
607 Debug::log("Buffer length: {}", receiveControl >> 16);
608 return {receiveControl >> 16};
609 }
610
611 void complete_receive(BufferID index = BufferID::Ping)
612 {
613 Debug::log("Completing receive in buffer {}", index);
614 // This buffer is now free to receive another frame.
615 receiveBufferInUse[index] = false;
616 if (index == BufferID::Ping)
617 {
618 mmio_register<RegisterOffset::ReceiveControlPing>() = 1;
619 }
620 else
621 {
622 mmio_register<RegisterOffset::ReceiveControlPong>() = 1;
623 }
624 }
625
626 /**
627 * Class encapsulating a frame that has been received. This holds a
628 * reference to the internal state of the buffer and will mark the buffer
629 * as free when it is destroyed.
630 */
631 class BufferedFrame
632 {
633 friend class KunyanEthernet;
634 KunyanEthernet &ethernetAdaptor;
635 BufferID owningBuffer;
636
637 private:
638 BufferedFrame(KunyanEthernet &ethernetAdaptor,
639 BufferID owningBuffer,
640 Capability<uint8_t> buffer,
641 uint16_t length)
642
643 : ethernetAdaptor(ethernetAdaptor),
644 owningBuffer(owningBuffer),
645 buffer(buffer),
646 length(length)
647 {
648 // Mark this buffer as in use and try to advance the next buffer
649 ethernetAdaptor.receiveBufferInUse[owningBuffer] = true;
650 if (!ethernetAdaptor.receiveBufferInUse[next_buffer_id(
651 ethernetAdaptor.nextReceiveBuffer)])
652 {
653 ethernetAdaptor.nextReceiveBuffer =
654 next_buffer_id(ethernetAdaptor.nextReceiveBuffer);
655 }
656 }
657
658 public:
659 uint16_t length;
660 Capability<uint8_t> buffer;
661 BufferedFrame(const BufferedFrame &) = delete;
662 BufferedFrame(BufferedFrame &&other)
663 : ethernetAdaptor(other.ethernetAdaptor),
664 owningBuffer(other.owningBuffer),
665 buffer(other.buffer),
666 length(other.length)
667 {
668 other.buffer = nullptr;
669 other.length = 0;
670 }
671 ~BufferedFrame()
672 {
673 if (buffer != nullptr)
674 {
675 ethernetAdaptor.complete_receive(owningBuffer);
676 }
677 }
678 };
679
680 std::optional<BufferedFrame> receive_frame()
681 {
682 // To avoid processing the same packet twice, we must not use
683 // the current receive buffer if it is already in use. In that
684 // case, check the next receive buffer. If it is not in use,
685 // rotate the buffers. Otherwise, abandon.
686 if (receiveBufferInUse[nextReceiveBuffer])
687 {
688 if (!receiveBufferInUse[next_buffer_id(nextReceiveBuffer)])
689 {
690 // The next receive buffer is not in use. Rotate.
691 nextReceiveBuffer = next_buffer_id(nextReceiveBuffer);
692 }
693 else
694 {
695 // The next receive buffer is in use too. Abandon.
696 return std::nullopt;
697 }
698 }
699
700 auto maybeLength = check_frame(nextReceiveBuffer);
701
702 if (!maybeLength)
703 {
704 // The current receive buffer is empty. However, the next
705 // receive buffer, if not in use, may have a packet ready for
706 // us. Check that one as well.
707
708 if (!receiveBufferInUse[next_buffer_id(nextReceiveBuffer)])
709 {
710 // The next receive buffer is not in use. Check
711 // for packets there as well.
712 nextReceiveBuffer = next_buffer_id(nextReceiveBuffer);
713 maybeLength = check_frame(nextReceiveBuffer);
714 }
715
716 if (!maybeLength)
717 {
718 // None of the buffers has packets that we can
719 // process. Abandon.
720 return std::nullopt;
721 }
722 }
723
724 auto length = *maybeLength;
725 // Strip the FCS from the length.
726 length -= 4;
727 auto buffer = receive_buffer_pointer(nextReceiveBuffer);
728 Capability<uint8_t> boundedBuffer{buffer};
729 boundedBuffer.bounds() = length;
730 // Remove all permissions except load. This also removes global, so
731 // that this cannot be captured.
732 boundedBuffer.permissions() &=
733 CHERI::PermissionSet{CHERI::Permission::Load};
734 Debug::log("Received frame from buffer {}", nextReceiveBuffer);
735 return {{*this, nextReceiveBuffer, buffer, length}};
736 }
737
738 /**
739 * Send a packet. This is synchronous and will block until the packet has
740 * been sent. A better version would use the next available ping or pong
741 * buffer and return as soon as the send operation had been enqueued.
742 *
743 * The third argument is a callback that allows the caller to check the
744 * frame before it's sent but after it's copied into memory that isn't
745 * shared with other compartments.
746 */
747 bool send_frame(const uint8_t *buffer, uint16_t length, auto &&check)
748 {
749 auto &transmitControl =
750 mmio_register<RegisterOffset::TransmitControlPing>();
751 // Spin waiting for the transmit buffer to be free.
752 while (transmitControl & 1)
753 {
754 }
755 // Write the frame to the transmit buffer.
756 auto transmitBuffer = transmit_buffer_pointer();
757 // We must check the frame pointer and its length. Although it
758 // is supplied by the firewall which is trusted, the firewall
759 // does not check the pointer which is coming from external
760 // untrusted components.
761 Timeout t{10};
762 if ((heap_claim_ephemeral(&t, buffer) < 0) ||
764 CHERI::Permission::Load}>(buffer, length)))
765 {
766 return false;
767 }
768 memcpy(transmitBuffer, buffer, length);
769 if (!check(transmitBuffer, length))
770 {
771 return false;
772 }
773 // The Ethernet standard requires frames to be at least 60 bytes long.
774 // If we're asked to send anything shorter, pad it with zeroes.
775 // (It would be nice if the MAC did this automatically).
776 if (length < 60)
777 {
778 memset(transmitBuffer + length, 0, 60 - length);
779 length = 60;
780 }
781 // Write the length of the frame to the transmit length register.
782 mmio_register<RegisterOffset::TransmitFrameLengthPing>() = length;
783 // Start the transmit.
784 transmitControl = 1;
785 Debug::log("Sent frame, waiting for completion");
786 while (transmitControl & 1)
787 {
788 }
789 // Return if the frame was sent successfully.
790 Debug::log("Transmit control register: {}", transmitControl);
791 if ((transmitControl & 2) != 0)
792 {
793 Debug::log("Error sending frame");
794 }
795 return (transmitControl & 2) == 0;
796 }
797
798 /**
799 * If debugging dropped frames is enabled, log any counter values that have
800 * changed since the last call to this function.
801 */
803 {
804 if constexpr (DebugDroppedFrames)
805 {
806 dropped_frames_log_if_changed<
807 RegisterOffset::ReceiveDroppedFramesBuffersFull>(droppedFrames);
808 dropped_frames_log_if_changed<
809 RegisterOffset::ReceiveDroppedFramesFCSFailed>(droppedFrames);
810 dropped_frames_log_if_changed<
811 RegisterOffset::ReceiveDroppedFramesInvalidAddress>(
812 droppedFrames);
813 }
814 }
815
816 void received_frames_log()
817 {
818 auto count = mmio_register<RegisterOffset::ReceivedFramesCount>();
819 ConditionalDebug<true, "Ethernet driver">::log("Received frames: {}",
820 count);
821 }
822};
823
824using EthernetDevice = KunyanEthernet;
825
CHERIoT RTOS heap allocator interface.
int __cheri_libcall heap_claim_ephemeral(TimeoutArgument timeout, const void *ptr, const void *ptr2)
Interface to the ephemeral claims mechanism.
C++ helpers for operating on capabilities.
bool check_pointer(auto &ptr, size_t space=sizeof(std::remove_pointer< decltype(ptr)>))
Checks that ptr is valid, unsealed, has at least Permissions, and has at least space bytes after the ...
Definition cheri.hh:1314
@ Load
This capability can be used to load.
Definition cheri.hh:52
Helper class for accessing capability properties on pointers.
Definition cheri.hh:482
Class encapsulating a set of permissions.
Definition cheri.hh:90
The driver for Kunyan Liu's custom Ethernet MAC for the Arty A7.
bool phy_link_status()
Check the link status of the PHY.
bool send_frame(const uint8_t *buffer, uint16_t length, auto &&check)
Send a packet.
void dropped_frames_log_all_if_changed()
If debugging dropped frames is enabled, log any counter values that have changed since the last call ...
void autonegotiation_enable(uint8_t phyAddress=1)
Enable autonegotiation on the PHY.
KunyanEthernet()
Initialise a reference to the Ethernet device.
static constexpr bool has_unique_mac_address()
This device does not have a unique MAC address and so users must provide a locally administered MAC a...
#define STATIC_SEALED_VALUE(name)
Returns a sealed capability to the named object created with DECLARE_STATIC_SEALED_VALUE.
#define MMIO_CAPABILITY(type, name)
Provide a capability of the type volatile type * referring to the MMIO region exported in the linker ...
Concept for an Ethernet adaptor.
Definition ethernet.hh:24
C++ APIs for assertions, invariants, and writing formatted debug messages to a UART.
Futex (compare-and-wait) APIs.
int futex_timed_wait(TimeoutArgument timeout, const volatile uint32_t *address, uint32_t expected, uint32_t flags)
Compare the value at address to expected and, if they match, sleep the thread until a wake event is s...
This file describes the interfaces for compartments to wait for interrupts.
int interrupt_complete(InterruptCapability interruptcapability)
Acknowledge the end of handling an interrupt.
#define DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY( name, number, mayWait, mayComplete)
Helper macro to define an interrupt capability without a separate declaration.
Definition interrupt.h:109
const uint32_t * interrupt_futex_get(InterruptCapability interruptcapability)
Request the futex associated with an interrupt.
Structure representing a timeout.
Definition timeout.h:47
Functions and types used for thread management.
static uint64_t thread_microsecond_spin(uint32_t microseconds)
Wait for the specified number of microseconds.
Definition thread.h:122
static uint64_t thread_millisecond_wait(uint32_t milliseconds)
Wait for the specified number of milliseconds.
Definition thread.h:152
struct Timeout Timeout
Structure representing a timeout.