3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/Metrics.h"
6#include "pedigree/kernel/machine/IrqManager.h"
7#include "pedigree/kernel/machine/Machine.h"
8#include "pedigree/kernel/panic.h"
9#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
10#include "pedigree/kernel/processor/VirtualAddressSpace.h"
11#include "pedigree/kernel/utilities/utility.h"
13#include "modules/system/network-stack/NetworkStack.h"
16constexpr uint64_t NetMac = 1ULL << 5;
17constexpr uint64_t NetStatus = 1ULL << 16;
18constexpr uint16_t LinkUp = 1;
19constexpr size_t EthernetHeaderSize = 14;
20constexpr size_t EthernetFrameMax = 1518;
21constexpr size_t EthernetFrameMin = 60;
23struct VirtioNetHeader {
26 uint16_t headerLength;
28 uint16_t checksumStart;
29 uint16_t checksumOffset;
32static_assert(
sizeof(VirtioNetHeader) == 12,
"unexpected virtio-net header size");
34size_t pagesFor(
size_t bytes) {
35 return (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
39VirtioNet::VirtioNet(
Device* pciDevice)
41 m_PciDevice(pciDevice),
42 m_Transport(pciDevice),
45 m_RxRegion(
"virtio-net-rx"),
46 m_TxRegion(
"virtio-net-tx"),
55 m_NetworkRegistered(false),
58 setSpecificType(
String(
"virtio-net-card"));
61VirtioNet::~VirtioNet() {
65 m_Initialised =
false;
66 if (!m_Transport.reset()) {
67 panic(
"virtio-net could not stop DMA");
71 if (m_IrqId && !Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
this)) {
72 panic(
"virtio-net could not synchronously unregister its IRQ");
74 if (m_NetworkRegistered) {
81bool VirtioNet::initialise() {
82 if (!m_Transport.initialise() || !m_Transport.negotiate(NetMac | NetStatus, NetMac) ||
83 !m_Transport.setupQueue(0, m_RxQueue) || !m_Transport.setupQueue(1, m_TxQueue)) {
84 ERROR(
"virtio-net: PCI transport or queues unavailable");
88 m_RxCount = m_RxQueue.depth() < MaxRxSlots ? m_RxQueue.depth() : MaxRxSlots;
89 m_TxCount = m_TxQueue.depth() < MaxTxSlots ? m_TxQueue.depth() : MaxTxSlots;
90 if (!m_RxCount || !m_TxCount) {
91 ERROR(
"virtio-net: no receive or transmit descriptors");
98 if (!memory.allocateRegion(m_RxRegion, pagesFor(m_RxCount * BufferSize), dmaFlags, mapFlags,
100 !memory.allocateRegion(m_TxRegion, pagesFor(m_TxCount * BufferSize), dmaFlags, mapFlags,
102 ERROR(
"virtio-net: could not allocate DMA buffers");
106 for (
size_t i = 0; i < m_RxCount; ++i) {
107 m_RxSlots[i] = {
static_cast<uint8_t*
>(m_RxRegion.
virtualAddress()) + i * BufferSize,
110 for (
size_t i = 0; i < m_TxCount; ++i) {
111 m_TxSlots[i] = {
static_cast<uint8_t*
>(m_TxRegion.
virtualAddress()) + i * BufferSize,
115 for (
size_t i = 0; i < 6; ++i) {
117 if (!m_Transport.readDeviceConfig8(i,
byte)) {
118 ERROR(
"virtio-net: could not read MAC address");
121 m_StationInfo.mac.setMac(
byte, i);
123 m_HasStatus = (m_Transport.features() & NetStatus) != 0;
125 for (
size_t i = 0; i < m_RxCount; ++i) {
126 if (!postReceive(m_RxSlots[i])) {
127 ERROR(
"virtio-net: could not populate receive queue");
132 m_IrqId = m_Transport.registerInterrupt(
this);
134 ERROR(
"virtio-net: could not register PCI interrupt");
139 m_NetworkRegistered =
true;
140 m_Initialised =
true;
141 if (!m_Transport.ready()) {
142 m_Initialised =
false;
143 ERROR(
"virtio-net: device did not accept DRIVER_OK");
146 m_Transport.notify(0);
147 NOTICE(
"virtio-net: MAC " << m_StationInfo.mac[0] <<
":" << m_StationInfo.mac[1] <<
":"
148 << m_StationInfo.mac[2] <<
":" << m_StationInfo.mac[3] <<
":"
149 << m_StationInfo.mac[4] <<
":" << m_StationInfo.mac[5]);
154 name.assign(
"virtio-net", 10);
157bool VirtioNet::postReceive(Slot& slot) {
159 return m_RxQueue.submit(&buffer, 1, &slot);
162void VirtioNet::reclaimTransmit() {
164 while (m_TxQueue.pop(completion)) {
165 auto* slot =
static_cast<Slot*
>(completion.cookie);
171 if (!buffer || length < EthernetHeaderSize || length > EthernetFrameMax) {
176 if (!m_Initialised || m_Stopping) {
181 for (
size_t n = 0; n < m_TxCount; ++n) {
182 const size_t index = (m_NextTx + n) % m_TxCount;
183 Slot& slot = m_TxSlots[index];
188 const size_t frameLength = length < EthernetFrameMin ? EthernetFrameMin : length;
189 ByteSet(slot.data, 0,
sizeof(VirtioNetHeader));
190 MemoryCopy(slot.data +
sizeof(VirtioNetHeader),
reinterpret_cast<void*
>(buffer), length);
191 if (frameLength > length) {
192 ByteSet(slot.data +
sizeof(VirtioNetHeader) + length, 0, frameLength - length);
196 slot.physical,
static_cast<uint32_t
>(
sizeof(VirtioNetHeader) + frameLength),
false};
198 if (!m_TxQueue.submit(&descriptor, 1, &slot)) {
202 m_NextTx = (index + 1) % m_TxCount;
203 m_Transport.notify(1);
211 if (m_StationInfo.dnsServers != info.dnsServers) {
212 delete[] m_StationInfo.dnsServers;
214 m_StationInfo.ipv4 = info.ipv4;
215 m_StationInfo.ipv6 = info.ipv6;
216 m_StationInfo.nIpv6Addresses = info.nIpv6Addresses;
217 m_StationInfo.subnetMask = info.subnetMask;
218 m_StationInfo.broadcast = info.broadcast;
219 m_StationInfo.
gateway = info.gateway;
220 m_StationInfo.gatewayIpv6 = info.gatewayIpv6;
221 m_StationInfo.dnsServers = info.dnsServers;
227 return m_StationInfo;
232 if (!m_Initialised || m_Stopping) {
239 return m_Transport.readDeviceConfig16(6, status) && (status & LinkUp);
245 size_t receiveCount = 0;
248 if (m_Stopping || !m_Initialised) {
251 if (!m_Transport.readIsr()) {
252 return IrqDisposition::NotHandled;
255 while (receiveCount < m_RxCount && m_RxQueue.pop(received[receiveCount])) {
262 const bool admitted = receiveCount && stack.
acquireDevice(
this, device);
263 for (
size_t i = 0; i < receiveCount; ++i) {
264 const auto& completion = received[i];
265 auto* slot =
static_cast<Slot*
>(completion.cookie);
266 const auto* header =
reinterpret_cast<const VirtioNetHeader*
>(slot->data);
267 const size_t frameLength = completion.length >=
sizeof(VirtioNetHeader)
268 ? completion.length -
sizeof(VirtioNetHeader)
271 const bool valid = completion.length >=
sizeof(VirtioNetHeader) + EthernetHeaderSize &&
272 completion.length <= BufferSize && frameLength <= EthernetFrameMax &&
273 !header->flags && !header->gsoType &&
274 LITTLE_TO_HOST16(header->numBuffers) <= 1;
279 reinterpret_cast<uintptr_t
>(slot->data +
sizeof(VirtioNetHeader)), device, 0);
281 Metrics::increment(Metrics::NetworkRxNoDevice);
288 if (!m_Stopping && m_Initialised) {
289 if (postReceive(*slot)) {
290 m_Transport.notify(0);
292 ERROR(
"virtio-net: could not replace receive buffer");
296 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)
MUST_USE_RESULT bool acquireDevice(size_t n, DeviceLease &lease)
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