3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/machine/IrqManager.h"
6#include "pedigree/kernel/machine/Machine.h"
7#include "pedigree/kernel/panic.h"
8#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
9#include "pedigree/kernel/processor/VirtualAddressSpace.h"
10#include "pedigree/kernel/utilities/utility.h"
12#include "modules/system/network-stack/NetworkStack.h"
15constexpr uint64_t NetMac = 1ULL << 5;
16constexpr uint64_t NetStatus = 1ULL << 16;
17constexpr uint16_t LinkUp = 1;
18constexpr size_t EthernetHeaderSize = 14;
19constexpr size_t EthernetFrameMax = 1518;
20constexpr size_t EthernetFrameMin = 60;
22struct VirtioNetHeader {
25 uint16_t headerLength;
27 uint16_t checksumStart;
28 uint16_t checksumOffset;
31static_assert(
sizeof(VirtioNetHeader) == 12,
"unexpected virtio-net header size");
33size_t pagesFor(
size_t bytes) {
34 return (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
38VirtioNet::VirtioNet(
Device* pciDevice)
40 m_PciDevice(pciDevice),
41 m_Transport(pciDevice),
44 m_RxRegion(
"virtio-net-rx"),
45 m_TxRegion(
"virtio-net-tx"),
54 m_NetworkRegistered(false),
57 setSpecificType(
String(
"virtio-net-card"));
60VirtioNet::~VirtioNet() {
64 m_Initialised =
false;
65 if (!m_Transport.reset()) {
66 panic(
"virtio-net could not stop DMA");
70 if (m_IrqId && !Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
this)) {
71 panic(
"virtio-net could not synchronously unregister its IRQ");
73 if (m_NetworkRegistered) {
80bool VirtioNet::initialise() {
81 if (!m_Transport.initialise() || !m_Transport.negotiate(NetMac | NetStatus, NetMac) ||
82 !m_Transport.setupQueue(0, m_RxQueue) || !m_Transport.setupQueue(1, m_TxQueue)) {
83 ERROR(
"virtio-net: PCI transport or queues unavailable");
87 m_RxCount = m_RxQueue.depth() < MaxRxSlots ? m_RxQueue.depth() : MaxRxSlots;
88 m_TxCount = m_TxQueue.depth() < MaxTxSlots ? m_TxQueue.depth() : MaxTxSlots;
89 if (!m_RxCount || !m_TxCount) {
90 ERROR(
"virtio-net: no receive or transmit descriptors");
97 if (!memory.allocateRegion(m_RxRegion, pagesFor(m_RxCount * BufferSize), dmaFlags, mapFlags,
99 !memory.allocateRegion(m_TxRegion, pagesFor(m_TxCount * BufferSize), dmaFlags, mapFlags,
101 ERROR(
"virtio-net: could not allocate DMA buffers");
105 for (
size_t i = 0; i < m_RxCount; ++i) {
106 m_RxSlots[i] = {
static_cast<uint8_t*
>(m_RxRegion.
virtualAddress()) + i * BufferSize,
109 for (
size_t i = 0; i < m_TxCount; ++i) {
110 m_TxSlots[i] = {
static_cast<uint8_t*
>(m_TxRegion.
virtualAddress()) + i * BufferSize,
114 for (
size_t i = 0; i < 6; ++i) {
116 if (!m_Transport.readDeviceConfig8(i,
byte)) {
117 ERROR(
"virtio-net: could not read MAC address");
120 m_StationInfo.mac.setMac(
byte, i);
122 m_HasStatus = (m_Transport.features() & NetStatus) != 0;
124 for (
size_t i = 0; i < m_RxCount; ++i) {
125 if (!postReceive(m_RxSlots[i])) {
126 ERROR(
"virtio-net: could not populate receive queue");
131 m_IrqId = m_Transport.registerInterrupt(
this);
133 ERROR(
"virtio-net: could not register PCI interrupt");
138 m_NetworkRegistered =
true;
139 m_Initialised =
true;
140 if (!m_Transport.ready()) {
141 m_Initialised =
false;
142 ERROR(
"virtio-net: device did not accept DRIVER_OK");
145 m_Transport.notify(0);
146 NOTICE(
"virtio-net: MAC " << m_StationInfo.mac[0] <<
":" << m_StationInfo.mac[1] <<
":"
147 << m_StationInfo.mac[2] <<
":" << m_StationInfo.mac[3] <<
":"
148 << m_StationInfo.mac[4] <<
":" << m_StationInfo.mac[5]);
153 name.assign(
"virtio-net", 10);
156bool VirtioNet::postReceive(Slot& slot) {
158 return m_RxQueue.submit(&buffer, 1, &slot);
161void VirtioNet::reclaimTransmit() {
163 while (m_TxQueue.pop(completion)) {
164 auto* slot =
static_cast<Slot*
>(completion.cookie);
170 if (!buffer || length < EthernetHeaderSize || length > EthernetFrameMax) {
175 if (!m_Initialised || m_Stopping) {
180 for (
size_t n = 0; n < m_TxCount; ++n) {
181 const size_t index = (m_NextTx + n) % m_TxCount;
182 Slot& slot = m_TxSlots[index];
187 const size_t frameLength = length < EthernetFrameMin ? EthernetFrameMin : length;
188 ByteSet(slot.data, 0,
sizeof(VirtioNetHeader));
189 MemoryCopy(slot.data +
sizeof(VirtioNetHeader),
reinterpret_cast<void*
>(buffer), length);
190 if (frameLength > length) {
191 ByteSet(slot.data +
sizeof(VirtioNetHeader) + length, 0, frameLength - length);
195 slot.physical,
static_cast<uint32_t
>(
sizeof(VirtioNetHeader) + frameLength),
false};
197 if (!m_TxQueue.submit(&descriptor, 1, &slot)) {
201 m_NextTx = (index + 1) % m_TxCount;
202 m_Transport.notify(1);
210 if (m_StationInfo.dnsServers != info.dnsServers) {
211 delete[] m_StationInfo.dnsServers;
213 m_StationInfo.ipv4 = info.ipv4;
214 m_StationInfo.ipv6 = info.ipv6;
215 m_StationInfo.nIpv6Addresses = info.nIpv6Addresses;
216 m_StationInfo.subnetMask = info.subnetMask;
217 m_StationInfo.broadcast = info.broadcast;
218 m_StationInfo.
gateway = info.gateway;
219 m_StationInfo.gatewayIpv6 = info.gatewayIpv6;
220 m_StationInfo.dnsServers = info.dnsServers;
226 return m_StationInfo;
231 if (!m_Initialised || m_Stopping) {
238 return m_Transport.readDeviceConfig16(6, status) && (status & LinkUp);
244 size_t receiveCount = 0;
247 if (m_Stopping || !m_Initialised) {
250 if (!m_Transport.readIsr()) {
251 return IrqDisposition::NotHandled;
254 while (receiveCount < m_RxCount && m_RxQueue.pop(received[receiveCount])) {
259 for (
size_t i = 0; i < receiveCount; ++i) {
260 const auto& completion = received[i];
261 auto* slot =
static_cast<Slot*
>(completion.cookie);
262 const auto* header =
reinterpret_cast<const VirtioNetHeader*
>(slot->data);
263 const size_t frameLength = completion.length >=
sizeof(VirtioNetHeader)
264 ? completion.length -
sizeof(VirtioNetHeader)
267 const bool valid = completion.length >=
sizeof(VirtioNetHeader) + EthernetHeaderSize &&
268 completion.length <= BufferSize && frameLength <= EthernetFrameMax &&
269 !header->flags && !header->gsoType &&
270 LITTLE_TO_HOST16(header->numBuffers) <= 1;
274 frameLength,
reinterpret_cast<uintptr_t
>(slot->data +
sizeof(VirtioNetHeader)),
this, 0);
280 if (!m_Stopping && m_Initialised) {
281 if (postReceive(*slot)) {
282 m_Transport.notify(0);
284 ERROR(
"virtio-net: could not replace receive buffer");
288 return IrqDisposition::Handled;
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
void deRegisterDevice(Network *pDevice)
static NetworkStack & instance()
void receive(size_t nBytes, uintptr_t packet, Network *pCard, uint32_t offset)
void registerDevice(Network *pDevice)
static const size_t continuous
static PhysicalMemoryManager & instance()
size_t nDnsServers
Can contain IPv6 addresses.
IpAddress gateway
Automatically calculated?
IrqDisposition irq(irq_id_t number) override
const StationInfo & getStationInfo() override
bool setStationInfo(const StationInfo &info) override
bool isConnected() override
void getName(String &name) override
bool send(size_t length, uintptr_t buffer) override
static const size_t KernelMode
static const size_t Write
void EXPORTED_PUBLIC panic(const char *msg) NORETURN