VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevVirtioNet.cpp@ 33325

最後變更 在這個檔案從33325是 33325,由 vboxsync 提交於 14 年 前

virtio-net: large (GSO) receive packet support (#4807)

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 72.6 KB
 
1/* $Id: DevVirtioNet.cpp 33325 2010-10-21 20:34:14Z vboxsync $ */
2/** @file
3 * DevVirtioNet - Virtio Network Device
4 */
5
6/*
7 * Copyright (C) 2009-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
20#define VNET_GC_SUPPORT
21#define VNET_WITH_GSO
22#define VNET_WITH_MERGEABLE_RX_BUFS
23
24#include <VBox/pdmdev.h>
25#include <VBox/pdmnetifs.h>
26#include <iprt/asm.h>
27#include <iprt/net.h>
28#include <iprt/semaphore.h>
29#ifdef IN_RING3
30# include <iprt/mem.h>
31# include <iprt/uuid.h>
32#endif /* IN_RING3 */
33#include "../Builtins.h"
34#include "../VirtIO/Virtio.h"
35
36
37#ifndef VBOX_DEVICE_STRUCT_TESTCASE
38
39#define INSTANCE(pState) pState->VPCI.szInstance
40#define STATUS pState->config.uStatus
41
42#ifdef IN_RING3
43
44#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_NET_ID
45#define VNET_PCI_CLASS 0x0200
46#define VNET_N_QUEUES 3
47#define VNET_NAME_FMT "VNet%d"
48
49#if 0
50/* Virtio Block Device */
51#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_BLK_ID
52#define VNET_PCI_CLASS 0x0180
53#define VNET_N_QUEUES 2
54#define VNET_NAME_FMT "VBlk%d"
55#endif
56
57#endif /* IN_RING3 */
58
59/* Forward declarations ******************************************************/
60RT_C_DECLS_BEGIN
61PDMBOTHCBDECL(int) vnetIOPortIn (PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t *pu32, unsigned cb);
62PDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t u32, unsigned cb);
63RT_C_DECLS_END
64
65#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
66
67
68#define VNET_TX_DELAY 150 /* 150 microseconds */
69#define VNET_MAX_FRAME_SIZE 65536 // TODO: Is it the right limit?
70#define VNET_MAC_FILTER_LEN 32
71#define VNET_MAX_VID (1 << 12)
72
73/* Virtio net features */
74#define VNET_F_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
75#define VNET_F_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
76#define VNET_F_MAC 0x00000020 /* Host has given MAC address. */
77#define VNET_F_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
78#define VNET_F_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
79#define VNET_F_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
80#define VNET_F_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
81#define VNET_F_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
82#define VNET_F_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
83#define VNET_F_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
84#define VNET_F_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
85#define VNET_F_HOST_UFO 0x00004000 /* Host can handle UFO in. */
86#define VNET_F_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
87#define VNET_F_STATUS 0x00010000 /* virtio_net_config.status available */
88#define VNET_F_CTRL_VQ 0x00020000 /* Control channel available */
89#define VNET_F_CTRL_RX 0x00040000 /* Control channel RX mode support */
90#define VNET_F_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
91
92#define VNET_S_LINK_UP 1
93
94
95#ifdef _MSC_VER
96struct VNetPCIConfig
97#else /* !_MSC_VER */
98struct __attribute__ ((__packed__)) VNetPCIConfig
99#endif /* !_MSC_VER */
100{
101 RTMAC mac;
102 uint16_t uStatus;
103};
104AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
105
106/**
107 * Device state structure. Holds the current state of device.
108 *
109 * @extends VPCISTATE
110 * @implements PDMINETWORKDOWN
111 * @implements PDMINETWORKCONFIG
112 */
113struct VNetState_st
114{
115 /* VPCISTATE must be the first member! */
116 VPCISTATE VPCI;
117
118// PDMCRITSECT csRx; /**< Protects RX queue. */
119
120 PDMINETWORKDOWN INetworkDown;
121 PDMINETWORKCONFIG INetworkConfig;
122 R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
123 R3PTRTYPE(PPDMINETWORKUP) pDrv; /**< Connector of attached network driver. */
124
125 R3PTRTYPE(PPDMQUEUE) pCanRxQueueR3; /**< Rx wakeup signaller - R3. */
126 R0PTRTYPE(PPDMQUEUE) pCanRxQueueR0; /**< Rx wakeup signaller - R0. */
127 RCPTRTYPE(PPDMQUEUE) pCanRxQueueRC; /**< Rx wakeup signaller - RC. */
128 uint32_t padding;
129
130 /**< Link Up(/Restore) Timer. */
131 PTMTIMERR3 pLinkUpTimer;
132
133#ifdef VNET_TX_DELAY
134 /**< Transmit Delay Timer - R3. */
135 PTMTIMERR3 pTxTimerR3;
136 /**< Transmit Delay Timer - R0. */
137 PTMTIMERR0 pTxTimerR0;
138 /**< Transmit Delay Timer - GC. */
139 PTMTIMERRC pTxTimerRC;
140
141# if HC_ARCH_BITS == 64
142 uint32_t padding2;
143# endif
144
145 uint32_t u32i;
146 uint32_t u32AvgDiff;
147 uint32_t u32MinDiff;
148 uint32_t u32MaxDiff;
149 uint64_t u64NanoTS;
150#endif /* VNET_TX_DELAY */
151
152 /** Indicates transmission in progress -- only one thread is allowed. */
153 uint32_t uIsTransmitting;
154
155 /** PCI config area holding MAC address as well as TBD. */
156 struct VNetPCIConfig config;
157 /** MAC address obtained from the configuration. */
158 RTMAC macConfigured;
159 /** True if physical cable is attached in configuration. */
160 bool fCableConnected;
161
162 /** Number of packet being sent/received to show in debug log. */
163 uint32_t u32PktNo;
164
165 /** N/A: */
166 bool volatile fMaybeOutOfSpace;
167
168 /** Promiscuous mode -- RX filter accepts all packets. */
169 bool fPromiscuous;
170 /** AllMulti mode -- RX filter accepts all multicast packets. */
171 bool fAllMulti;
172 /** The number of actually used slots in aMacTable. */
173 uint32_t nMacFilterEntries;
174 /** Array of MAC addresses accepted by RX filter. */
175 RTMAC aMacFilter[VNET_MAC_FILTER_LEN];
176 /** Bit array of VLAN filter, one bit per VLAN ID. */
177 uint8_t aVlanFilter[VNET_MAX_VID / sizeof(uint8_t)];
178
179 R3PTRTYPE(PVQUEUE) pRxQueue;
180 R3PTRTYPE(PVQUEUE) pTxQueue;
181 R3PTRTYPE(PVQUEUE) pCtlQueue;
182 /* Receive-blocking-related fields ***************************************/
183
184 /** EMT: Gets signalled when more RX descriptors become available. */
185 RTSEMEVENT hEventMoreRxDescAvail;
186
187 /* Statistic fields ******************************************************/
188
189 STAMCOUNTER StatReceiveBytes;
190 STAMCOUNTER StatTransmitBytes;
191 STAMCOUNTER StatReceiveGSO;
192 STAMCOUNTER StatTransmitPackets;
193 STAMCOUNTER StatTransmitGSO;
194 STAMCOUNTER StatTransmitCSum;
195#if defined(VBOX_WITH_STATISTICS)
196 STAMPROFILE StatReceive;
197 STAMPROFILE StatReceiveStore;
198 STAMPROFILEADV StatTransmit;
199 STAMPROFILE StatTransmitSend;
200 STAMPROFILE StatRxOverflow;
201 STAMCOUNTER StatRxOverflowWakeup;
202#endif /* VBOX_WITH_STATISTICS */
203
204};
205typedef struct VNetState_st VNETSTATE;
206typedef VNETSTATE *PVNETSTATE;
207
208#ifndef VBOX_DEVICE_STRUCT_TESTCASE
209
210#define VNETHDR_F_NEEDS_CSUM 1 // Use u16CSumStart, u16CSumOffset
211
212#define VNETHDR_GSO_NONE 0 // Not a GSO frame
213#define VNETHDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
214#define VNETHDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
215#define VNETHDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
216#define VNETHDR_GSO_ECN 0x80 // TCP has ECN set
217
218struct VNetHdr
219{
220 uint8_t u8Flags;
221 uint8_t u8GSOType;
222 uint16_t u16HdrLen;
223 uint16_t u16GSOSize;
224 uint16_t u16CSumStart;
225 uint16_t u16CSumOffset;
226};
227typedef struct VNetHdr VNETHDR;
228typedef VNETHDR *PVNETHDR;
229AssertCompileSize(VNETHDR, 10);
230
231struct VNetHdrMrx
232{
233 VNETHDR Hdr;
234 uint16_t u16NumBufs;
235};
236typedef struct VNetHdrMrx VNETHDRMRX;
237typedef VNETHDRMRX *PVNETHDRMRX;
238AssertCompileSize(VNETHDRMRX, 12);
239
240AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
241
242#define VNET_OK 0
243#define VNET_ERROR 1
244typedef uint8_t VNETCTLACK;
245
246#define VNET_CTRL_CLS_RX_MODE 0
247#define VNET_CTRL_CMD_RX_MODE_PROMISC 0
248#define VNET_CTRL_CMD_RX_MODE_ALLMULTI 1
249
250#define VNET_CTRL_CLS_MAC 1
251#define VNET_CTRL_CMD_MAC_TABLE_SET 0
252
253#define VNET_CTRL_CLS_VLAN 2
254#define VNET_CTRL_CMD_VLAN_ADD 0
255#define VNET_CTRL_CMD_VLAN_DEL 1
256
257
258struct VNetCtlHdr
259{
260 uint8_t u8Class;
261 uint8_t u8Command;
262};
263typedef struct VNetCtlHdr VNETCTLHDR;
264typedef VNETCTLHDR *PVNETCTLHDR;
265AssertCompileSize(VNETCTLHDR, 2);
266
267/* Returns true if large packets are written into several RX buffers. */
268DECLINLINE(bool) vnetMergeableRxBuffers(PVNETSTATE pState)
269{
270 return !!(pState->VPCI.uGuestFeatures & VNET_F_MRG_RXBUF);
271}
272
273DECLINLINE(int) vnetCsEnter(PVNETSTATE pState, int rcBusy)
274{
275 return vpciCsEnter(&pState->VPCI, rcBusy);
276}
277
278DECLINLINE(void) vnetCsLeave(PVNETSTATE pState)
279{
280 vpciCsLeave(&pState->VPCI);
281}
282
283DECLINLINE(int) vnetCsRxEnter(PVNETSTATE pState, int rcBusy)
284{
285 // STAM_PROFILE_START(&pState->CTXSUFF(StatCsRx), a);
286 // int rc = PDMCritSectEnter(&pState->csRx, rcBusy);
287 // STAM_PROFILE_STOP(&pState->CTXSUFF(StatCsRx), a);
288 // return rc;
289 return VINF_SUCCESS;
290}
291
292DECLINLINE(void) vnetCsRxLeave(PVNETSTATE pState)
293{
294 // PDMCritSectLeave(&pState->csRx);
295}
296
297/**
298 * Dump a packet to debug log.
299 *
300 * @param pState The device state structure.
301 * @param cpPacket The packet.
302 * @param cb The size of the packet.
303 * @param cszText A string denoting direction of packet transfer.
304 */
305DECLINLINE(void) vnetPacketDump(PVNETSTATE pState, const uint8_t *cpPacket, size_t cb, const char *cszText)
306{
307#ifdef DEBUG
308 Log(("%s %s packet #%d (%d bytes):\n",
309 INSTANCE(pState), cszText, ++pState->u32PktNo, cb));
310 Log3(("%.*Rhxd\n", cb, cpPacket));
311#endif
312}
313
314
315
316PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pvState)
317{
318 /* We support:
319 * - Host-provided MAC address
320 * - Link status reporting in config space
321 * - Control queue
322 * - RX mode setting
323 * - MAC filter table
324 * - VLAN filter
325 */
326 return VNET_F_MAC
327 | VNET_F_STATUS
328 | VNET_F_CTRL_VQ
329 | VNET_F_CTRL_RX
330 | VNET_F_CTRL_VLAN
331#ifdef VNET_WITH_GSO
332 | VNET_F_CSUM
333 | VNET_F_HOST_TSO4
334 | VNET_F_HOST_TSO6
335 | VNET_F_HOST_UFO
336#endif
337#ifdef VNET_WITH_MERGEABLE_RX_BUFS
338 | VNET_F_MRG_RXBUF
339#endif
340 ;
341}
342
343PDMBOTHCBDECL(uint32_t) vnetGetHostMinimalFeatures(void *pvState)
344{
345 return VNET_F_MAC;
346}
347
348PDMBOTHCBDECL(void) vnetSetHostFeatures(void *pvState, uint32_t uFeatures)
349{
350 // TODO: Nothing to do here yet
351 VNETSTATE *pState = (VNETSTATE *)pvState;
352 LogFlow(("%s vnetSetHostFeatures: uFeatures=%x\n", INSTANCE(pState), uFeatures));
353}
354
355PDMBOTHCBDECL(int) vnetGetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
356{
357 VNETSTATE *pState = (VNETSTATE *)pvState;
358 if (port + cb > sizeof(struct VNetPCIConfig))
359 {
360 Log(("%s vnetGetConfig: Read beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
361 return VERR_IOM_IOPORT_UNUSED;
362 }
363 memcpy(data, ((uint8_t*)&pState->config) + port, cb);
364 return VINF_SUCCESS;
365}
366
367PDMBOTHCBDECL(int) vnetSetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
368{
369 VNETSTATE *pState = (VNETSTATE *)pvState;
370 if (port + cb > sizeof(struct VNetPCIConfig))
371 {
372 Log(("%s vnetGetConfig: Write beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
373 if (port < sizeof(struct VNetPCIConfig))
374 memcpy(((uint8_t*)&pState->config) + port, data,
375 sizeof(struct VNetPCIConfig) - port);
376 return VINF_SUCCESS;
377 }
378 memcpy(((uint8_t*)&pState->config) + port, data, cb);
379 return VINF_SUCCESS;
380}
381
382/**
383 * Hardware reset. Revert all registers to initial values.
384 *
385 * @param pState The device state structure.
386 */
387PDMBOTHCBDECL(int) vnetReset(void *pvState)
388{
389 VNETSTATE *pState = (VNETSTATE*)pvState;
390 Log(("%s Reset triggered\n", INSTANCE(pState)));
391
392 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
393 if (RT_UNLIKELY(rc != VINF_SUCCESS))
394 {
395 LogRel(("vnetReset failed to enter RX critical section!\n"));
396 return rc;
397 }
398 vpciReset(&pState->VPCI);
399 vnetCsRxLeave(pState);
400
401 // TODO: Implement reset
402 if (pState->fCableConnected)
403 STATUS = VNET_S_LINK_UP;
404 else
405 STATUS = 0;
406
407 /*
408 * By default we pass all packets up since the older guests cannot control
409 * virtio mode.
410 */
411 pState->fPromiscuous = true;
412 pState->fAllMulti = false;
413 pState->nMacFilterEntries = 0;
414 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
415 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
416 pState->uIsTransmitting = 0;
417#ifndef IN_RING3
418 return VINF_IOM_HC_IOPORT_WRITE;
419#else
420 if (pState->pDrv)
421 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv, true);
422 return VINF_SUCCESS;
423#endif
424}
425
426#ifdef IN_RING3
427
428/**
429 * Wakeup the RX thread.
430 */
431static void vnetWakeupReceive(PPDMDEVINS pDevIns)
432{
433 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE *);
434 if ( pState->fMaybeOutOfSpace
435 && pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
436 {
437 STAM_COUNTER_INC(&pState->StatRxOverflowWakeup);
438 Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pState)));
439 RTSemEventSignal(pState->hEventMoreRxDescAvail);
440 }
441}
442
443/**
444 * Link Up Timer handler.
445 *
446 * @param pDevIns Pointer to device instance structure.
447 * @param pTimer Pointer to the timer.
448 * @param pvUser NULL.
449 * @thread EMT
450 */
451static DECLCALLBACK(void) vnetLinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
452{
453 VNETSTATE *pState = (VNETSTATE *)pvUser;
454
455 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
456 if (RT_UNLIKELY(rc != VINF_SUCCESS))
457 return;
458 STATUS |= VNET_S_LINK_UP;
459 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
460 vnetWakeupReceive(pDevIns);
461 vnetCsLeave(pState);
462}
463
464
465
466
467/**
468 * Handler for the wakeup signaller queue.
469 */
470static DECLCALLBACK(bool) vnetCanRxQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
471{
472 vnetWakeupReceive(pDevIns);
473 return true;
474}
475
476#endif /* IN_RING3 */
477
478/**
479 * This function is called when the driver becomes ready.
480 *
481 * @param pState The device state structure.
482 */
483PDMBOTHCBDECL(void) vnetReady(void *pvState)
484{
485 VNETSTATE *pState = (VNETSTATE*)pvState;
486 Log(("%s Driver became ready, waking up RX thread...\n", INSTANCE(pState)));
487#ifdef IN_RING3
488 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
489#else
490 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pState->CTX_SUFF(pCanRxQueue));
491 if (pItem)
492 PDMQueueInsert(pState->CTX_SUFF(pCanRxQueue), pItem);
493#endif
494}
495
496/**
497 * Port I/O Handler for IN operations.
498 *
499 * @returns VBox status code.
500 *
501 * @param pDevIns The device instance.
502 * @param pvUser Pointer to the device state structure.
503 * @param port Port number used for the IN operation.
504 * @param pu32 Where to store the result.
505 * @param cb Number of bytes read.
506 * @thread EMT
507 */
508PDMBOTHCBDECL(int) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser,
509 RTIOPORT port, uint32_t *pu32, unsigned cb)
510{
511 return vpciIOPortIn(pDevIns, pvUser, port, pu32, cb,
512 vnetGetHostFeatures,
513 vnetGetConfig);
514}
515
516
517/**
518 * Port I/O Handler for OUT operations.
519 *
520 * @returns VBox status code.
521 *
522 * @param pDevIns The device instance.
523 * @param pvUser User argument.
524 * @param Port Port number used for the IN operation.
525 * @param u32 The value to output.
526 * @param cb The value size in bytes.
527 * @thread EMT
528 */
529PDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser,
530 RTIOPORT port, uint32_t u32, unsigned cb)
531{
532 return vpciIOPortOut(pDevIns, pvUser, port, u32, cb,
533 vnetGetHostMinimalFeatures,
534 vnetGetHostFeatures,
535 vnetSetHostFeatures,
536 vnetReset,
537 vnetReady,
538 vnetSetConfig);
539}
540
541
542#ifdef IN_RING3
543
544/**
545 * Check if the device can receive data now.
546 * This must be called before the pfnRecieve() method is called.
547 *
548 * @remarks As a side effect this function enables queue notification
549 * if it cannot receive because the queue is empty.
550 * It disables notification if it can receive.
551 *
552 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
553 * @param pInterface Pointer to the interface structure containing the called function pointer.
554 * @thread RX
555 */
556static int vnetCanReceive(VNETSTATE *pState)
557{
558 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
559 AssertRCReturn(rc, rc);
560
561 LogFlow(("%s vnetCanReceive\n", INSTANCE(pState)));
562 if (!(pState->VPCI.uStatus & VPCI_STATUS_DRV_OK))
563 rc = VERR_NET_NO_BUFFER_SPACE;
564 else if (!vqueueIsReady(&pState->VPCI, pState->pRxQueue))
565 rc = VERR_NET_NO_BUFFER_SPACE;
566 else if (vqueueIsEmpty(&pState->VPCI, pState->pRxQueue))
567 {
568 vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, true);
569 rc = VERR_NET_NO_BUFFER_SPACE;
570 }
571 else
572 {
573 vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, false);
574 rc = VINF_SUCCESS;
575 }
576
577 LogFlow(("%s vnetCanReceive -> %Rrc\n", INSTANCE(pState), rc));
578 vnetCsRxLeave(pState);
579 return rc;
580}
581
582/**
583 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
584 */
585static DECLCALLBACK(int) vnetNetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
586{
587 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
588 LogFlow(("%s vnetNetworkDown_WaitReceiveAvail(cMillies=%u)\n", INSTANCE(pState), cMillies));
589 int rc = vnetCanReceive(pState);
590
591 if (RT_SUCCESS(rc))
592 return VINF_SUCCESS;
593 if (RT_UNLIKELY(cMillies == 0))
594 return VERR_NET_NO_BUFFER_SPACE;
595
596 rc = VERR_INTERRUPTED;
597 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, true);
598 STAM_PROFILE_START(&pState->StatRxOverflow, a);
599
600 VMSTATE enmVMState;
601 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns))) == VMSTATE_RUNNING
602 || enmVMState == VMSTATE_RUNNING_LS))
603 {
604 int rc2 = vnetCanReceive(pState);
605 if (RT_SUCCESS(rc2))
606 {
607 rc = VINF_SUCCESS;
608 break;
609 }
610 Log(("%s vnetNetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n",
611 INSTANCE(pState), cMillies));
612 RTSemEventWait(pState->hEventMoreRxDescAvail, cMillies);
613 }
614 STAM_PROFILE_STOP(&pState->StatRxOverflow, a);
615 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, false);
616
617 LogFlow(("%s vnetNetworkDown_WaitReceiveAvail -> %d\n", INSTANCE(pState), rc));
618 return rc;
619}
620
621
622/**
623 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
624 */
625static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
626{
627 VNETSTATE *pThis = RT_FROM_MEMBER(pInterface, VNETSTATE, VPCI.IBase);
628 Assert(&pThis->VPCI.IBase == pInterface);
629
630 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
631 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
632 return vpciQueryInterface(pInterface, pszIID);
633}
634
635/**
636 * Returns true if it is a broadcast packet.
637 *
638 * @returns true if destination address indicates broadcast.
639 * @param pvBuf The ethernet packet.
640 */
641DECLINLINE(bool) vnetIsBroadcast(const void *pvBuf)
642{
643 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
644 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
645}
646
647/**
648 * Returns true if it is a multicast packet.
649 *
650 * @remarks returns true for broadcast packets as well.
651 * @returns true if destination address indicates multicast.
652 * @param pvBuf The ethernet packet.
653 */
654DECLINLINE(bool) vnetIsMulticast(const void *pvBuf)
655{
656 return (*(char*)pvBuf) & 1;
657}
658
659/**
660 * Determines if the packet is to be delivered to upper layer.
661 *
662 * @returns true if packet is intended for this node.
663 * @param pState Pointer to the state structure.
664 * @param pvBuf The ethernet packet.
665 * @param cb Number of bytes available in the packet.
666 */
667static bool vnetAddressFilter(PVNETSTATE pState, const void *pvBuf, size_t cb)
668{
669 if (pState->fPromiscuous)
670 return true;
671
672 /* Ignore everything outside of our VLANs */
673 uint16_t *u16Ptr = (uint16_t*)pvBuf;
674 /* Compare TPID with VLAN Ether Type */
675 if ( u16Ptr[6] == RT_H2BE_U16(0x8100)
676 && !ASMBitTest(pState->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
677 {
678 Log4(("%s vnetAddressFilter: not our VLAN, returning false\n", INSTANCE(pState)));
679 return false;
680 }
681
682 if (vnetIsBroadcast(pvBuf))
683 return true;
684
685 if (pState->fAllMulti && vnetIsMulticast(pvBuf))
686 return true;
687
688 if (!memcmp(pState->config.mac.au8, pvBuf, sizeof(RTMAC)))
689 return true;
690 Log4(("%s vnetAddressFilter: %RTmac (conf) != %RTmac (dest)\n",
691 INSTANCE(pState), pState->config.mac.au8, pvBuf));
692
693 for (unsigned i = 0; i < pState->nMacFilterEntries; i++)
694 if (!memcmp(&pState->aMacFilter[i], pvBuf, sizeof(RTMAC)))
695 return true;
696
697 Log2(("%s vnetAddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pState)));
698 vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
699
700 return false;
701}
702
703/**
704 * Pad and store received packet.
705 *
706 * @remarks Make sure that the packet appears to upper layer as one coming
707 * from real Ethernet: pad it and insert FCS.
708 *
709 * @returns VBox status code.
710 * @param pState The device state structure.
711 * @param pvBuf The available data.
712 * @param cb Number of bytes available in the buffer.
713 * @thread RX
714 */
715static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb,
716 PCPDMNETWORKGSO pGso)
717{
718 VNETHDRMRX Hdr;
719 PVNETHDRMRX pHdr;
720 unsigned uHdrLen;
721 RTGCPHYS addrHdrMrx = 0;
722
723 if (pGso)
724 {
725 Log2(("%s vnetHandleRxPacket: gso type=%x cbHdr=%u mss=%u"
726 " off1=0x%x off2=0x%x\n", INSTANCE(pState), pGso->u8Type,
727 pGso->cbHdrs, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
728 Hdr.Hdr.u8Flags = VNETHDR_F_NEEDS_CSUM;
729 switch (pGso->u8Type)
730 {
731 case PDMNETWORKGSOTYPE_IPV4_TCP:
732 Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV4;
733 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
734 break;
735 case PDMNETWORKGSOTYPE_IPV6_TCP:
736 Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV6;
737 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
738 break;
739 case PDMNETWORKGSOTYPE_IPV4_UDP:
740 Hdr.Hdr.u8GSOType = VNETHDR_GSO_UDP;
741 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
742 break;
743 default:
744 return VERR_INVALID_PARAMETER;
745 }
746 Hdr.Hdr.u16HdrLen = pGso->cbHdrs;
747 Hdr.Hdr.u16GSOSize = pGso->cbMaxSeg;
748 Hdr.Hdr.u16CSumStart = pGso->offHdr2;
749 STAM_REL_COUNTER_INC(&pState->StatReceiveGSO);
750 }
751 else
752 {
753 Hdr.Hdr.u8Flags = 0;
754 Hdr.Hdr.u8GSOType = VNETHDR_GSO_NONE;
755 }
756
757 if (vnetMergeableRxBuffers(pState))
758 uHdrLen = sizeof(VNETHDRMRX);
759 else
760 uHdrLen = sizeof(VNETHDR);
761
762 //vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
763
764 unsigned int uOffset = 0;
765 unsigned int nElem;
766 for (nElem = 0; uOffset < cb; nElem++)
767 {
768 VQUEUEELEM elem;
769 unsigned int nSeg = 0, uElemSize = 0, cbReserved = 0;
770
771 if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
772 {
773 /*
774 * @todo: It is possible to run out of RX buffers if only a few
775 * were added and we received a big packet.
776 */
777 Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
778 return VERR_INTERNAL_ERROR;
779 }
780
781 if (elem.nIn < 1)
782 {
783 Log(("%s vnetHandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pState)));
784 return VERR_INTERNAL_ERROR;
785 }
786
787 if (nElem == 0)
788 {
789 if (vnetMergeableRxBuffers(pState))
790 {
791 addrHdrMrx = elem.aSegsIn[nSeg].addr;
792 cbReserved = uHdrLen;
793 }
794 else
795 {
796 /* The very first segment of the very first element gets the header. */
797 if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
798 {
799 Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
800 return VERR_INTERNAL_ERROR;
801 }
802 elem.aSegsIn[nSeg++].pv = &Hdr;
803 }
804 uElemSize += uHdrLen;
805 }
806 while (nSeg < elem.nIn && uOffset < cb)
807 {
808 unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb - (nSeg?0:cbReserved),
809 cb - uOffset);
810 elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
811 uOffset += uSize;
812 uElemSize += uSize;
813 }
814 STAM_PROFILE_START(&pState->StatReceiveStore, a);
815 vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize, cbReserved);
816 STAM_PROFILE_STOP(&pState->StatReceiveStore, a);
817 if (!vnetMergeableRxBuffers(pState))
818 break;
819 cbReserved = 0;
820 }
821 if (vnetMergeableRxBuffers(pState))
822 {
823 Hdr.u16NumBufs = nElem;
824 int rc = PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns), addrHdrMrx,
825 &Hdr, sizeof(Hdr));
826 if (RT_FAILURE(rc))
827 {
828 Log(("%s vnetHandleRxPacket: Failed to write merged RX buf header: %Rrc\n",
829 INSTANCE(pState), rc));
830 return rc;
831 }
832 }
833 vqueueSync(&pState->VPCI, pState->pRxQueue);
834 if (uOffset < cb)
835 {
836 Log(("%s vnetHandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n",
837 INSTANCE(pState), cb));
838 return VERR_TOO_MUCH_DATA;
839 }
840
841 return VINF_SUCCESS;
842}
843
844/**
845 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
846 */
847static DECLCALLBACK(int) vnetNetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface,
848 const void *pvBuf, size_t cb,
849 PCPDMNETWORKGSO pGso)
850{
851 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
852
853 Log2(("%s vnetNetworkDown_ReceiveGso: pvBuf=%p cb=%u pGso=%p\n",
854 INSTANCE(pState), pvBuf, cb, pGso));
855 int rc = vnetCanReceive(pState);
856 if (RT_FAILURE(rc))
857 return rc;
858
859 /* Drop packets if VM is not running or cable is disconnected. */
860 VMSTATE enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns));
861 if (( enmVMState != VMSTATE_RUNNING
862 && enmVMState != VMSTATE_RUNNING_LS)
863 || !(STATUS & VNET_S_LINK_UP))
864 return VINF_SUCCESS;
865
866 STAM_PROFILE_START(&pState->StatReceive, a);
867 vpciSetReadLed(&pState->VPCI, true);
868 if (vnetAddressFilter(pState, pvBuf, cb))
869 {
870 rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
871 if (RT_SUCCESS(rc))
872 {
873 rc = vnetHandleRxPacket(pState, pvBuf, cb, pGso);
874 STAM_REL_COUNTER_ADD(&pState->StatReceiveBytes, cb);
875 vnetCsRxLeave(pState);
876 }
877 }
878 vpciSetReadLed(&pState->VPCI, false);
879 STAM_PROFILE_STOP(&pState->StatReceive, a);
880 return rc;
881}
882
883/**
884 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
885 */
886static DECLCALLBACK(int) vnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
887{
888 return vnetNetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
889}
890
891/**
892 * Gets the current Media Access Control (MAC) address.
893 *
894 * @returns VBox status code.
895 * @param pInterface Pointer to the interface structure containing the called function pointer.
896 * @param pMac Where to store the MAC address.
897 * @thread EMT
898 */
899static DECLCALLBACK(int) vnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
900{
901 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkConfig);
902 memcpy(pMac, pState->config.mac.au8, sizeof(RTMAC));
903 return VINF_SUCCESS;
904}
905
906/**
907 * Gets the new link state.
908 *
909 * @returns The current link state.
910 * @param pInterface Pointer to the interface structure containing the called function pointer.
911 * @thread EMT
912 */
913static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetGetLinkState(PPDMINETWORKCONFIG pInterface)
914{
915 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkConfig);
916 if (STATUS & VNET_S_LINK_UP)
917 return PDMNETWORKLINKSTATE_UP;
918 return PDMNETWORKLINKSTATE_DOWN;
919}
920
921
922/**
923 * Sets the new link state.
924 *
925 * @returns VBox status code.
926 * @param pInterface Pointer to the interface structure containing the called function pointer.
927 * @param enmState The new link state
928 */
929static DECLCALLBACK(int) vnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
930{
931 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkConfig);
932 bool fOldUp = !!(STATUS & VNET_S_LINK_UP);
933 bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
934
935 if (fNewUp != fOldUp)
936 {
937 if (fNewUp)
938 {
939 Log(("%s Link is up\n", INSTANCE(pState)));
940 STATUS |= VNET_S_LINK_UP;
941 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
942 }
943 else
944 {
945 Log(("%s Link is down\n", INSTANCE(pState)));
946 STATUS &= ~VNET_S_LINK_UP;
947 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
948 }
949 if (pState->pDrv)
950 pState->pDrv->pfnNotifyLinkChanged(pState->pDrv, enmState);
951 }
952 return VINF_SUCCESS;
953}
954
955static DECLCALLBACK(void) vnetQueueReceive(void *pvState, PVQUEUE pQueue)
956{
957 VNETSTATE *pState = (VNETSTATE*)pvState;
958 Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pState)));
959 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
960}
961
962/**
963 * Sets up the GSO context according to the Virtio header.
964 *
965 * @param pGso The GSO context to setup.
966 * @param pCtx The context descriptor.
967 */
968DECLINLINE(PPDMNETWORKGSO) vnetSetupGsoCtx(PPDMNETWORKGSO pGso, VNETHDR const *pHdr)
969{
970 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
971
972 if (pHdr->u8GSOType & VNETHDR_GSO_ECN)
973 {
974 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
975 return NULL;
976 }
977 switch (pHdr->u8GSOType & ~VNETHDR_GSO_ECN)
978 {
979 case VNETHDR_GSO_TCPV4:
980 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
981 break;
982 case VNETHDR_GSO_TCPV6:
983 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
984 break;
985 case VNETHDR_GSO_UDP:
986 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
987 break;
988 default:
989 return NULL;
990 }
991 if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
992 pGso->offHdr2 = pHdr->u16CSumStart;
993 else
994 {
995 AssertMsgFailed(("GSO without checksum offloading!\n"));
996 return NULL;
997 }
998 pGso->offHdr1 = sizeof(RTNETETHERHDR);
999 pGso->cbHdrs = pHdr->u16HdrLen;
1000 pGso->cbMaxSeg = pHdr->u16GSOSize;
1001 return pGso;
1002}
1003
1004DECLINLINE(uint16_t) vnetCSum16(const void *pvBuf, size_t cb)
1005{
1006 uint32_t csum = 0;
1007 uint16_t *pu16 = (uint16_t *)pvBuf;
1008
1009 while (cb > 1)
1010 {
1011 csum += *pu16++;
1012 cb -= 2;
1013 }
1014 if (cb)
1015 csum += *(uint8_t*)pu16;
1016 while (csum >> 16)
1017 csum = (csum >> 16) + (csum & 0xFFFF);
1018 return ~csum;
1019}
1020
1021DECLINLINE(void) vnetCompleteChecksum(uint8_t *pBuf, unsigned cbSize, uint16_t uStart, uint16_t uOffset)
1022{
1023 *(uint16_t*)(pBuf + uStart + uOffset) = vnetCSum16(pBuf + uStart, cbSize - uStart);
1024}
1025
1026static void vnetTransmitPendingPackets(PVNETSTATE pState, PVQUEUE pQueue, bool fOnWorkerThread)
1027{
1028 /*
1029 * Only one thread is allowed to transmit at a time, others should skip
1030 * transmission as the packets will be picked up by the transmitting
1031 * thread.
1032 */
1033 if (!ASMAtomicCmpXchgU32(&pState->uIsTransmitting, 1, 0))
1034 return;
1035
1036 if ((pState->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
1037 {
1038 Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n",
1039 INSTANCE(pState), pState->VPCI.uStatus));
1040 return;
1041 }
1042
1043 PPDMINETWORKUP pDrv = pState->pDrv;
1044 if (pDrv)
1045 {
1046 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
1047 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
1048 if (rc == VERR_TRY_AGAIN)
1049 {
1050 ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
1051 return;
1052 }
1053 }
1054
1055 unsigned int uHdrLen;
1056 if (vnetMergeableRxBuffers(pState))
1057 uHdrLen = sizeof(VNETHDRMRX);
1058 else
1059 uHdrLen = sizeof(VNETHDR);
1060
1061 Log3(("%s vnetTransmitPendingPackets: About to trasmit %d pending packets\n", INSTANCE(pState),
1062 vringReadAvailIndex(&pState->VPCI, &pState->pTxQueue->VRing) - pState->pTxQueue->uNextAvailIndex));
1063
1064 vpciSetWriteLed(&pState->VPCI, true);
1065
1066 VQUEUEELEM elem;
1067 while (vqueueGet(&pState->VPCI, pQueue, &elem))
1068 {
1069 unsigned int uOffset = 0;
1070 if (elem.nOut < 2 || elem.aSegsOut[0].cb != uHdrLen)
1071 {
1072 Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
1073 INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, uHdrLen));
1074 break; /* For now we simply ignore the header, but it must be there anyway! */
1075 }
1076 else
1077 {
1078 unsigned int uSize = 0;
1079 STAM_PROFILE_ADV_START(&pState->StatTransmit, a);
1080 /* Compute total frame size. */
1081 for (unsigned int i = 1; i < elem.nOut; i++)
1082 uSize += elem.aSegsOut[i].cb;
1083 Assert(uSize <= VNET_MAX_FRAME_SIZE);
1084 if (pState->pDrv)
1085 {
1086 VNETHDR Hdr;
1087 PDMNETWORKGSO Gso, *pGso;
1088
1089 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[0].addr,
1090 &Hdr, sizeof(Hdr));
1091
1092 STAM_REL_COUNTER_INC(&pState->StatTransmitPackets);
1093
1094 STAM_PROFILE_START(&pState->StatTransmitSend, a);
1095
1096 pGso = vnetSetupGsoCtx(&Gso, &Hdr);
1097 /** @todo Optimize away the extra copying! (lazy bird) */
1098 PPDMSCATTERGATHER pSgBuf;
1099 int rc = pState->pDrv->pfnAllocBuf(pState->pDrv, uSize, pGso, &pSgBuf);
1100 if (RT_SUCCESS(rc))
1101 {
1102 Assert(pSgBuf->cSegs == 1);
1103 /* Assemble a complete frame. */
1104 for (unsigned int i = 1; i < elem.nOut; i++)
1105 {
1106 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
1107 ((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
1108 elem.aSegsOut[i].cb);
1109 uOffset += elem.aSegsOut[i].cb;
1110 }
1111 pSgBuf->cbUsed = uSize;
1112 //vnetPacketDump(pState, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
1113 if (pGso)
1114 STAM_REL_COUNTER_INC(&pState->StatTransmitGSO);
1115 else if (Hdr.u8Flags & VNETHDR_F_NEEDS_CSUM)
1116 {
1117 STAM_REL_COUNTER_INC(&pState->StatTransmitCSum);
1118 /*
1119 * This is not GSO frame but checksum offloading is requested.
1120 */
1121 vnetCompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize,
1122 Hdr.u16CSumStart, Hdr.u16CSumOffset);
1123 }
1124
1125 rc = pState->pDrv->pfnSendBuf(pState->pDrv, pSgBuf, false);
1126 }
1127 else
1128 LogRel(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
1129
1130 STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
1131 STAM_REL_COUNTER_ADD(&pState->StatTransmitBytes, uOffset);
1132 }
1133 }
1134 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
1135 vqueueSync(&pState->VPCI, pQueue);
1136 STAM_PROFILE_ADV_STOP(&pState->StatTransmit, a);
1137 }
1138 vpciSetWriteLed(&pState->VPCI, false);
1139
1140 if (pDrv)
1141 pDrv->pfnEndXmit(pDrv);
1142 ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
1143}
1144
1145/**
1146 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
1147 */
1148static DECLCALLBACK(void) vnetNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
1149{
1150 VNETSTATE *pThis = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
1151 vnetTransmitPendingPackets(pThis, pThis->pTxQueue, false /*fOnWorkerThread*/);
1152}
1153
1154#ifdef VNET_TX_DELAY
1155
1156static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1157{
1158 VNETSTATE *pState = (VNETSTATE*)pvState;
1159
1160 if (TMTimerIsActive(pState->CTX_SUFF(pTxTimer)))
1161 {
1162 int rc = TMTimerStop(pState->CTX_SUFF(pTxTimer));
1163 Log3(("%s vnetQueueTransmit: Got kicked with notification disabled, "
1164 "re-enable notification and flush TX queue\n", INSTANCE(pState)));
1165 vnetTransmitPendingPackets(pState, pQueue, false /*fOnWorkerThread*/);
1166 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1167 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1168 else
1169 {
1170 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
1171 vnetCsLeave(pState);
1172 }
1173 }
1174 else
1175 {
1176 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1177 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1178 else
1179 {
1180 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, false);
1181 TMTimerSetMicro(pState->CTX_SUFF(pTxTimer), VNET_TX_DELAY);
1182 pState->u64NanoTS = RTTimeNanoTS();
1183 vnetCsLeave(pState);
1184 }
1185 }
1186}
1187
1188/**
1189 * Transmit Delay Timer handler.
1190 *
1191 * @remarks We only get here when the timer expires.
1192 *
1193 * @param pDevIns Pointer to device instance structure.
1194 * @param pTimer Pointer to the timer.
1195 * @param pvUser NULL.
1196 * @thread EMT
1197 */
1198static DECLCALLBACK(void) vnetTxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1199{
1200 VNETSTATE *pState = (VNETSTATE*)pvUser;
1201
1202 uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pState->u64NanoTS)/1000);
1203 if (u32MicroDiff < pState->u32MinDiff)
1204 pState->u32MinDiff = u32MicroDiff;
1205 if (u32MicroDiff > pState->u32MaxDiff)
1206 pState->u32MaxDiff = u32MicroDiff;
1207 pState->u32AvgDiff = (pState->u32AvgDiff * pState->u32i + u32MicroDiff) / (pState->u32i + 1);
1208 pState->u32i++;
1209 Log3(("vnetTxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
1210 u32MicroDiff, pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1211
1212// Log3(("%s vnetTxTimer: Expired\n", INSTANCE(pState)));
1213 vnetTransmitPendingPackets(pState, pState->pTxQueue, false /*fOnWorkerThread*/);
1214 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1215 {
1216 LogRel(("vnetTxTimer: Failed to enter critical section!/n"));
1217 return;
1218 }
1219 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
1220 vnetCsLeave(pState);
1221}
1222
1223#else /* !VNET_TX_DELAY */
1224
1225static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1226{
1227 VNETSTATE *pState = (VNETSTATE*)pvState;
1228
1229 vnetTransmitPendingPackets(pState, pQueue, false /*fOnWorkerThread*/);
1230}
1231
1232#endif /* !VNET_TX_DELAY */
1233
1234static uint8_t vnetControlRx(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1235{
1236 uint8_t u8Ack = VNET_OK;
1237 uint8_t fOn, fDrvWasPromisc = pState->fPromiscuous | pState->fAllMulti;
1238 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1239 pElem->aSegsOut[1].addr,
1240 &fOn, sizeof(fOn));
1241 Log(("%s vnetControlRx: uCommand=%u fOn=%u\n", INSTANCE(pState), pCtlHdr->u8Command, fOn));
1242 switch (pCtlHdr->u8Command)
1243 {
1244 case VNET_CTRL_CMD_RX_MODE_PROMISC:
1245 pState->fPromiscuous = !!fOn;
1246 break;
1247 case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
1248 pState->fAllMulti = !!fOn;
1249 break;
1250 default:
1251 u8Ack = VNET_ERROR;
1252 }
1253 if (fDrvWasPromisc != (pState->fPromiscuous | pState->fAllMulti) && pState->pDrv)
1254 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv,
1255 (pState->fPromiscuous | pState->fAllMulti));
1256
1257 return u8Ack;
1258}
1259
1260static uint8_t vnetControlMac(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1261{
1262 uint32_t nMacs = 0;
1263
1264 if (pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
1265 || pElem->nOut != 3
1266 || pElem->aSegsOut[1].cb < sizeof(nMacs)
1267 || pElem->aSegsOut[2].cb < sizeof(nMacs))
1268 {
1269 Log(("%s vnetControlMac: Segment layout is wrong "
1270 "(u8Command=%u nOut=%u cb1=%u cb2=%u)\n", INSTANCE(pState),
1271 pCtlHdr->u8Command, pElem->nOut,
1272 pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
1273 return VNET_ERROR;
1274 }
1275
1276 /* Load unicast addresses */
1277 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1278 pElem->aSegsOut[1].addr,
1279 &nMacs, sizeof(nMacs));
1280
1281 if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1282 {
1283 Log(("%s vnetControlMac: The unicast mac segment is too small "
1284 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[1].cb));
1285 return VNET_ERROR;
1286 }
1287
1288 if (nMacs > VNET_MAC_FILTER_LEN)
1289 {
1290 Log(("%s vnetControlMac: MAC table is too big, have to use promiscuous"
1291 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1292 pState->fPromiscuous = true;
1293 }
1294 else
1295 {
1296 if (nMacs)
1297 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1298 pElem->aSegsOut[1].addr + sizeof(nMacs),
1299 pState->aMacFilter, nMacs * sizeof(RTMAC));
1300 pState->nMacFilterEntries = nMacs;
1301#ifdef DEBUG
1302 Log(("%s vnetControlMac: unicast macs:\n", INSTANCE(pState)));
1303 for(unsigned i = 0; i < nMacs; i++)
1304 Log((" %RTmac\n", &pState->aMacFilter[i]));
1305#endif /* DEBUG */
1306 }
1307
1308 /* Load multicast addresses */
1309 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1310 pElem->aSegsOut[2].addr,
1311 &nMacs, sizeof(nMacs));
1312
1313 if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1314 {
1315 Log(("%s vnetControlMac: The multicast mac segment is too small "
1316 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[2].cb));
1317 return VNET_ERROR;
1318 }
1319
1320 if (nMacs > VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1321 {
1322 Log(("%s vnetControlMac: MAC table is too big, have to use allmulti"
1323 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1324 pState->fAllMulti = true;
1325 }
1326 else
1327 {
1328 if (nMacs)
1329 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1330 pElem->aSegsOut[2].addr + sizeof(nMacs),
1331 &pState->aMacFilter[pState->nMacFilterEntries],
1332 nMacs * sizeof(RTMAC));
1333#ifdef DEBUG
1334 Log(("%s vnetControlMac: multicast macs:\n", INSTANCE(pState)));
1335 for(unsigned i = 0; i < nMacs; i++)
1336 Log((" %RTmac\n",
1337 &pState->aMacFilter[i+pState->nMacFilterEntries]));
1338#endif /* DEBUG */
1339 pState->nMacFilterEntries += nMacs;
1340 }
1341
1342 return VNET_OK;
1343}
1344
1345static uint8_t vnetControlVlan(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1346{
1347 uint8_t u8Ack = VNET_OK;
1348 uint16_t u16Vid;
1349
1350 if (pElem->nOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
1351 {
1352 Log(("%s vnetControlVlan: Segment layout is wrong "
1353 "(u8Command=%u nOut=%u cb=%u)\n", INSTANCE(pState),
1354 pCtlHdr->u8Command, pElem->nOut, pElem->aSegsOut[1].cb));
1355 return VNET_ERROR;
1356 }
1357
1358 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1359 pElem->aSegsOut[1].addr,
1360 &u16Vid, sizeof(u16Vid));
1361
1362 if (u16Vid >= VNET_MAX_VID)
1363 {
1364 Log(("%s vnetControlVlan: VLAN ID is out of range "
1365 "(VID=%u)\n", INSTANCE(pState), u16Vid));
1366 return VNET_ERROR;
1367 }
1368
1369 Log(("%s vnetControlVlan: uCommand=%u VID=%u\n", INSTANCE(pState),
1370 pCtlHdr->u8Command, u16Vid));
1371
1372 switch (pCtlHdr->u8Command)
1373 {
1374 case VNET_CTRL_CMD_VLAN_ADD:
1375 ASMBitSet(pState->aVlanFilter, u16Vid);
1376 break;
1377 case VNET_CTRL_CMD_VLAN_DEL:
1378 ASMBitClear(pState->aVlanFilter, u16Vid);
1379 break;
1380 default:
1381 u8Ack = VNET_ERROR;
1382 }
1383
1384 return u8Ack;
1385}
1386
1387
1388static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
1389{
1390 VNETSTATE *pState = (VNETSTATE*)pvState;
1391 uint8_t u8Ack;
1392 VQUEUEELEM elem;
1393 while (vqueueGet(&pState->VPCI, pQueue, &elem))
1394 {
1395 unsigned int uOffset = 0;
1396 if (elem.nOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
1397 {
1398 Log(("%s vnetQueueControl: The first 'out' segment is not the "
1399 "header! (%u < 1 || %u < %u).\n", INSTANCE(pState), elem.nOut,
1400 elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
1401 break; /* Skip the element and hope the next one is good. */
1402 }
1403 else if ( elem.nIn < 1
1404 || elem.aSegsIn[elem.nIn - 1].cb < sizeof(VNETCTLACK))
1405 {
1406 Log(("%s vnetQueueControl: The last 'in' segment is too small "
1407 "to hold the acknowledge! (%u < 1 || %u < %u).\n",
1408 INSTANCE(pState), elem.nIn, elem.aSegsIn[elem.nIn - 1].cb,
1409 sizeof(VNETCTLACK)));
1410 break; /* Skip the element and hope the next one is good. */
1411 }
1412 else
1413 {
1414 VNETCTLHDR CtlHdr;
1415 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1416 elem.aSegsOut[0].addr,
1417 &CtlHdr, sizeof(CtlHdr));
1418 switch (CtlHdr.u8Class)
1419 {
1420 case VNET_CTRL_CLS_RX_MODE:
1421 u8Ack = vnetControlRx(pState, &CtlHdr, &elem);
1422 break;
1423 case VNET_CTRL_CLS_MAC:
1424 u8Ack = vnetControlMac(pState, &CtlHdr, &elem);
1425 break;
1426 case VNET_CTRL_CLS_VLAN:
1427 u8Ack = vnetControlVlan(pState, &CtlHdr, &elem);
1428 break;
1429 default:
1430 u8Ack = VNET_ERROR;
1431 }
1432 Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pState),
1433 CtlHdr.u8Class, u8Ack));
1434 PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns),
1435 elem.aSegsIn[elem.nIn - 1].addr,
1436 &u8Ack, sizeof(u8Ack));
1437 }
1438 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(u8Ack));
1439 vqueueSync(&pState->VPCI, pQueue);
1440 }
1441}
1442
1443/**
1444 * Saves the configuration.
1445 *
1446 * @param pState The VNET state.
1447 * @param pSSM The handle to the saved state.
1448 */
1449static void vnetSaveConfig(VNETSTATE *pState, PSSMHANDLE pSSM)
1450{
1451 SSMR3PutMem(pSSM, &pState->macConfigured, sizeof(pState->macConfigured));
1452}
1453
1454/**
1455 * Live save - save basic configuration.
1456 *
1457 * @returns VBox status code.
1458 * @param pDevIns The device instance.
1459 * @param pSSM The handle to the saved state.
1460 * @param uPass
1461 */
1462static DECLCALLBACK(int) vnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1463{
1464 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1465 vnetSaveConfig(pState, pSSM);
1466 return VINF_SSM_DONT_CALL_AGAIN;
1467}
1468
1469/**
1470 * Prepares for state saving.
1471 *
1472 * @returns VBox status code.
1473 * @param pDevIns The device instance.
1474 * @param pSSM The handle to the saved state.
1475 */
1476static DECLCALLBACK(int) vnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1477{
1478 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1479
1480 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1481 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1482 return rc;
1483 vnetCsRxLeave(pState);
1484 return VINF_SUCCESS;
1485}
1486
1487/**
1488 * Saves the state of device.
1489 *
1490 * @returns VBox status code.
1491 * @param pDevIns The device instance.
1492 * @param pSSM The handle to the saved state.
1493 */
1494static DECLCALLBACK(int) vnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1495{
1496 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1497
1498 /* Save config first */
1499 vnetSaveConfig(pState, pSSM);
1500
1501 /* Save the common part */
1502 int rc = vpciSaveExec(&pState->VPCI, pSSM);
1503 AssertRCReturn(rc, rc);
1504 /* Save device-specific part */
1505 rc = SSMR3PutMem( pSSM, pState->config.mac.au8, sizeof(pState->config.mac));
1506 AssertRCReturn(rc, rc);
1507 rc = SSMR3PutBool(pSSM, pState->fPromiscuous);
1508 AssertRCReturn(rc, rc);
1509 rc = SSMR3PutBool(pSSM, pState->fAllMulti);
1510 AssertRCReturn(rc, rc);
1511 rc = SSMR3PutU32( pSSM, pState->nMacFilterEntries);
1512 AssertRCReturn(rc, rc);
1513 rc = SSMR3PutMem( pSSM, pState->aMacFilter,
1514 pState->nMacFilterEntries * sizeof(RTMAC));
1515 AssertRCReturn(rc, rc);
1516 rc = SSMR3PutMem( pSSM, pState->aVlanFilter, sizeof(pState->aVlanFilter));
1517 AssertRCReturn(rc, rc);
1518 Log(("%s State has been saved\n", INSTANCE(pState)));
1519 return VINF_SUCCESS;
1520}
1521
1522
1523/**
1524 * Serializes the receive thread, it may be working inside the critsect.
1525 *
1526 * @returns VBox status code.
1527 * @param pDevIns The device instance.
1528 * @param pSSM The handle to the saved state.
1529 */
1530static DECLCALLBACK(int) vnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1531{
1532 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1533
1534 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1535 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1536 return rc;
1537 vnetCsRxLeave(pState);
1538 return VINF_SUCCESS;
1539}
1540
1541/**
1542 * Takes down the link temporarily if it's current status is up.
1543 *
1544 * This is used during restore and when replumbing the network link.
1545 *
1546 * The temporary link outage is supposed to indicate to the OS that all network
1547 * connections have been lost and that it for instance is appropriate to
1548 * renegotiate any DHCP lease.
1549 *
1550 * @param pThis The PCNet instance data.
1551 */
1552static void vnetTempLinkDown(PVNETSTATE pState)
1553{
1554 if (STATUS & VNET_S_LINK_UP)
1555 {
1556 STATUS &= ~VNET_S_LINK_UP;
1557 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1558 /* Restore the link back in 5 seconds. */
1559 int rc = TMTimerSetMillies(pState->pLinkUpTimer, 5000);
1560 AssertRC(rc);
1561 }
1562}
1563
1564
1565/**
1566 * Restore previously saved state of device.
1567 *
1568 * @returns VBox status code.
1569 * @param pDevIns The device instance.
1570 * @param pSSM The handle to the saved state.
1571 * @param uVersion The data unit version number.
1572 * @param uPass The data pass.
1573 */
1574static DECLCALLBACK(int) vnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1575{
1576 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1577 int rc;
1578
1579 /* config checks */
1580 RTMAC macConfigured;
1581 rc = SSMR3GetMem(pSSM, &macConfigured, sizeof(macConfigured));
1582 AssertRCReturn(rc, rc);
1583 if (memcmp(&macConfigured, &pState->macConfigured, sizeof(macConfigured))
1584 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1585 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pState), &pState->macConfigured, &macConfigured));
1586
1587 rc = vpciLoadExec(&pState->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
1588 AssertRCReturn(rc, rc);
1589
1590 if (uPass == SSM_PASS_FINAL)
1591 {
1592 rc = SSMR3GetMem( pSSM, pState->config.mac.au8,
1593 sizeof(pState->config.mac));
1594 AssertRCReturn(rc, rc);
1595
1596 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
1597 {
1598 rc = SSMR3GetBool(pSSM, &pState->fPromiscuous);
1599 AssertRCReturn(rc, rc);
1600 rc = SSMR3GetBool(pSSM, &pState->fAllMulti);
1601 AssertRCReturn(rc, rc);
1602 rc = SSMR3GetU32(pSSM, &pState->nMacFilterEntries);
1603 AssertRCReturn(rc, rc);
1604 rc = SSMR3GetMem(pSSM, pState->aMacFilter,
1605 pState->nMacFilterEntries * sizeof(RTMAC));
1606 AssertRCReturn(rc, rc);
1607 /* Clear the rest. */
1608 if (pState->nMacFilterEntries < VNET_MAC_FILTER_LEN)
1609 memset(&pState->aMacFilter[pState->nMacFilterEntries],
1610 0,
1611 (VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1612 * sizeof(RTMAC));
1613 rc = SSMR3GetMem(pSSM, pState->aVlanFilter,
1614 sizeof(pState->aVlanFilter));
1615 AssertRCReturn(rc, rc);
1616 }
1617 else
1618 {
1619 pState->fPromiscuous = true;
1620 pState->fAllMulti = false;
1621 pState->nMacFilterEntries = 0;
1622 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
1623 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
1624 if (pState->pDrv)
1625 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv, true);
1626 }
1627 }
1628
1629 return rc;
1630}
1631
1632/**
1633 * Link status adjustments after loading.
1634 *
1635 * @returns VBox status code.
1636 * @param pDevIns The device instance.
1637 * @param pSSM The handle to the saved state.
1638 */
1639static DECLCALLBACK(int) vnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1640{
1641 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1642
1643 if (pState->pDrv)
1644 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv,
1645 (pState->fPromiscuous | pState->fAllMulti));
1646 /*
1647 * Indicate link down to the guest OS that all network connections have
1648 * been lost, unless we've been teleported here.
1649 */
1650 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1651 vnetTempLinkDown(pState);
1652
1653 return VINF_SUCCESS;
1654}
1655
1656/**
1657 * Map PCI I/O region.
1658 *
1659 * @return VBox status code.
1660 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
1661 * @param iRegion The region number.
1662 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
1663 * I/O port, else it's a physical address.
1664 * This address is *NOT* relative to pci_mem_base like earlier!
1665 * @param cb Region size.
1666 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
1667 * @thread EMT
1668 */
1669static DECLCALLBACK(int) vnetMap(PPCIDEVICE pPciDev, int iRegion,
1670 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
1671{
1672 int rc;
1673 VNETSTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VNETSTATE*);
1674
1675 if (enmType != PCI_ADDRESS_SPACE_IO)
1676 {
1677 /* We should never get here */
1678 AssertMsgFailed(("Invalid PCI address space param in map callback"));
1679 return VERR_INTERNAL_ERROR;
1680 }
1681
1682 pState->VPCI.addrIOPort = (RTIOPORT)GCPhysAddress;
1683 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1684 cb, 0, vnetIOPortOut, vnetIOPortIn,
1685 NULL, NULL, "VirtioNet");
1686#ifdef VNET_GC_SUPPORT
1687 AssertRCReturn(rc, rc);
1688 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1689 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1690 NULL, NULL, "VirtioNet");
1691 AssertRCReturn(rc, rc);
1692 rc = PDMDevHlpIOPortRegisterRC(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1693 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1694 NULL, NULL, "VirtioNet");
1695#endif
1696 AssertRC(rc);
1697 return rc;
1698}
1699
1700/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
1701
1702#ifdef VBOX_DYNAMIC_NET_ATTACH
1703
1704/**
1705 * Detach notification.
1706 *
1707 * One port on the network card has been disconnected from the network.
1708 *
1709 * @param pDevIns The device instance.
1710 * @param iLUN The logical unit which is being detached.
1711 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1712 */
1713static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1714{
1715 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1716 Log(("%s vnetDetach:\n", INSTANCE(pState)));
1717
1718 AssertLogRelReturnVoid(iLUN == 0);
1719
1720 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1721 if (RT_FAILURE(rc))
1722 {
1723 LogRel(("vnetDetach failed to enter critical section!\n"));
1724 return;
1725 }
1726
1727 /*
1728 * Zero some important members.
1729 */
1730 pState->pDrvBase = NULL;
1731 pState->pDrv = NULL;
1732
1733 vnetCsLeave(pState);
1734}
1735
1736
1737/**
1738 * Attach the Network attachment.
1739 *
1740 * One port on the network card has been connected to a network.
1741 *
1742 * @returns VBox status code.
1743 * @param pDevIns The device instance.
1744 * @param iLUN The logical unit which is being attached.
1745 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1746 *
1747 * @remarks This code path is not used during construction.
1748 */
1749static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1750{
1751 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1752 LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
1753
1754 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1755
1756 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1757 if (RT_FAILURE(rc))
1758 {
1759 LogRel(("vnetAttach failed to enter critical section!\n"));
1760 return rc;
1761 }
1762
1763 /*
1764 * Attach the driver.
1765 */
1766 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1767 if (RT_SUCCESS(rc))
1768 {
1769 if (rc == VINF_NAT_DNS)
1770 {
1771#ifdef RT_OS_LINUX
1772 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1773 N_("A Domain Name Server (DNS) for NAT networking could not be determined. Please check your /etc/resolv.conf for <tt>nameserver</tt> entries. Either add one manually (<i>man resolv.conf</i>) or ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
1774#else
1775 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1776 N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
1777#endif
1778 }
1779 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1780 AssertMsgStmt(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1781 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
1782 }
1783 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
1784 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
1785 {
1786 /* This should never happen because this function is not called
1787 * if there is no driver to attach! */
1788 Log(("%s No attached driver!\n", INSTANCE(pState)));
1789 }
1790
1791 /*
1792 * Temporary set the link down if it was up so that the guest
1793 * will know that we have change the configuration of the
1794 * network card
1795 */
1796 if (RT_SUCCESS(rc))
1797 vnetTempLinkDown(pState);
1798
1799 vnetCsLeave(pState);
1800 return rc;
1801
1802}
1803
1804#endif /* VBOX_DYNAMIC_NET_ATTACH */
1805
1806/**
1807 * @copydoc FNPDMDEVSUSPEND
1808 */
1809static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1810{
1811 /* Poke thread waiting for buffer space. */
1812 vnetWakeupReceive(pDevIns);
1813}
1814
1815/**
1816 * @copydoc FNPDMDEVPOWEROFF
1817 */
1818static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1819{
1820 /* Poke thread waiting for buffer space. */
1821 vnetWakeupReceive(pDevIns);
1822}
1823
1824/**
1825 * Device relocation callback.
1826 *
1827 * When this callback is called the device instance data, and if the
1828 * device have a GC component, is being relocated, or/and the selectors
1829 * have been changed. The device must use the chance to perform the
1830 * necessary pointer relocations and data updates.
1831 *
1832 * Before the GC code is executed the first time, this function will be
1833 * called with a 0 delta so GC pointer calculations can be one in one place.
1834 *
1835 * @param pDevIns Pointer to the device instance.
1836 * @param offDelta The relocation delta relative to the old location.
1837 *
1838 * @remark A relocation CANNOT fail.
1839 */
1840static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1841{
1842 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1843 vpciRelocate(pDevIns, offDelta);
1844 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1845#ifdef VNET_TX_DELAY
1846 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1847#endif /* VNET_TX_DELAY */
1848 // TBD
1849}
1850
1851/**
1852 * Destruct a device instance.
1853 *
1854 * We need to free non-VM resources only.
1855 *
1856 * @returns VBox status.
1857 * @param pDevIns The device instance data.
1858 * @thread EMT
1859 */
1860static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1861{
1862 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1863 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1864
1865 LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n",
1866 pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1867 Log(("%s Destroying instance\n", INSTANCE(pState)));
1868 if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1869 {
1870 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1871 RTSemEventDestroy(pState->hEventMoreRxDescAvail);
1872 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1873 }
1874
1875 // if (PDMCritSectIsInitialized(&pState->csRx))
1876 // PDMR3CritSectDelete(&pState->csRx);
1877
1878 return vpciDestruct(&pState->VPCI);
1879}
1880
1881/**
1882 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1883 */
1884static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1885{
1886 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1887 int rc;
1888 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1889
1890 /* Initialize PCI part first. */
1891 pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1892 rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance,
1893 VNET_NAME_FMT, VNET_PCI_SUBSYSTEM_ID,
1894 VNET_PCI_CLASS, VNET_N_QUEUES);
1895 pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueReceive, "RX ");
1896 pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueTransmit, "TX ");
1897 pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetQueueControl, "CTL");
1898
1899 Log(("%s Constructing new instance\n", INSTANCE(pState)));
1900
1901 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1902
1903 /*
1904 * Validate configuration.
1905 */
1906 if (!CFGMR3AreValuesValid(pCfg, "MAC\0" "CableConnected\0" "LineSpeed\0"))
1907 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1908 N_("Invalid configuration for VirtioNet device"));
1909
1910 /* Get config params */
1911 rc = CFGMR3QueryBytes(pCfg, "MAC", pState->macConfigured.au8,
1912 sizeof(pState->macConfigured));
1913 if (RT_FAILURE(rc))
1914 return PDMDEV_SET_ERROR(pDevIns, rc,
1915 N_("Configuration error: Failed to get MAC address"));
1916 rc = CFGMR3QueryBool(pCfg, "CableConnected", &pState->fCableConnected);
1917 if (RT_FAILURE(rc))
1918 return PDMDEV_SET_ERROR(pDevIns, rc,
1919 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1920
1921 /* Initialize PCI config space */
1922 memcpy(pState->config.mac.au8, pState->macConfigured.au8, sizeof(pState->config.mac.au8));
1923 pState->config.uStatus = 0;
1924
1925 /* Initialize state structure */
1926 pState->u32PktNo = 1;
1927
1928 /* Interfaces */
1929 pState->INetworkDown.pfnWaitReceiveAvail = vnetNetworkDown_WaitReceiveAvail;
1930 pState->INetworkDown.pfnReceive = vnetNetworkDown_Receive;
1931 pState->INetworkDown.pfnReceiveGso = vnetNetworkDown_ReceiveGso;
1932 pState->INetworkDown.pfnXmitPending = vnetNetworkDown_XmitPending;
1933
1934 pState->INetworkConfig.pfnGetMac = vnetGetMac;
1935 pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1936 pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1937
1938 /* Initialize critical section. */
1939 // char szTmp[sizeof(pState->VPCI.szInstance) + 2];
1940 // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pState->VPCI.szInstance);
1941 // rc = PDMDevHlpCritSectInit(pDevIns, &pState->csRx, szTmp);
1942 // if (RT_FAILURE(rc))
1943 // return rc;
1944
1945 /* Map our ports to IO space. */
1946 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0,
1947 VPCI_CONFIG + sizeof(VNetPCIConfig),
1948 PCI_ADDRESS_SPACE_IO, vnetMap);
1949 if (RT_FAILURE(rc))
1950 return rc;
1951
1952
1953 /* Register save/restore state handlers. */
1954 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
1955 NULL, vnetLiveExec, NULL,
1956 vnetSavePrep, vnetSaveExec, NULL,
1957 vnetLoadPrep, vnetLoadExec, vnetLoadDone);
1958 if (RT_FAILURE(rc))
1959 return rc;
1960
1961 /* Create the RX notifier signaller. */
1962 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
1963 vnetCanRxQueueConsumer, true, "VNet-Rcv", &pState->pCanRxQueueR3);
1964 if (RT_FAILURE(rc))
1965 return rc;
1966 pState->pCanRxQueueR0 = PDMQueueR0Ptr(pState->pCanRxQueueR3);
1967 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1968
1969 /* Create Link Up Timer */
1970 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
1971 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1972 "VirtioNet Link Up Timer", &pState->pLinkUpTimer);
1973 if (RT_FAILURE(rc))
1974 return rc;
1975
1976#ifdef VNET_TX_DELAY
1977 /* Create Transmit Delay Timer */
1978 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetTxTimer, pState,
1979 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1980 "VirtioNet TX Delay Timer", &pState->pTxTimerR3);
1981 if (RT_FAILURE(rc))
1982 return rc;
1983 pState->pTxTimerR0 = TMTimerR0Ptr(pState->pTxTimerR3);
1984 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1985
1986 pState->u32i = pState->u32AvgDiff = pState->u32MaxDiff = 0;
1987 pState->u32MinDiff = ~0;
1988#endif /* VNET_TX_DELAY */
1989
1990 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1991 if (RT_SUCCESS(rc))
1992 {
1993 if (rc == VINF_NAT_DNS)
1994 {
1995 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1996 N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
1997 }
1998 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1999 AssertMsgReturn(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2000 VERR_PDM_MISSING_INTERFACE_BELOW);
2001 }
2002 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2003 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME )
2004 {
2005 /* No error! */
2006 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
2007 }
2008 else
2009 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
2010
2011 rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
2012 if (RT_FAILURE(rc))
2013 return rc;
2014
2015 rc = vnetReset(pState);
2016 AssertRC(rc);
2017
2018 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/Bytes/Receive", iInstance);
2019 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/Bytes/Transmit", iInstance);
2020 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of received GSO packets", "/Devices/VNet%d/Packets/ReceiveGSO", iInstance);
2021 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitPackets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent packets", "/Devices/VNet%d/Packets/Transmit", iInstance);
2022 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent GSO packets", "/Devices/VNet%d/Packets/Transmit-Gso", iInstance);
2023 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitCSum, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of completed TX checksums", "/Devices/VNet%d/Packets/Transmit-Csum", iInstance);
2024#if defined(VBOX_WITH_STATISTICS)
2025 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
2026 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);
2027 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
2028 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
2029 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
2030 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
2031#endif /* VBOX_WITH_STATISTICS */
2032
2033 return VINF_SUCCESS;
2034}
2035
2036/**
2037 * The device registration structure.
2038 */
2039const PDMDEVREG g_DeviceVirtioNet =
2040{
2041 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
2042 PDM_DEVREG_VERSION,
2043 /* Device name. */
2044 "virtio-net",
2045 /* Name of guest context module (no path).
2046 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
2047 "VBoxDDGC.gc",
2048 /* Name of ring-0 module (no path).
2049 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
2050 "VBoxDDR0.r0",
2051 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
2052 * remain unchanged from registration till VM destruction. */
2053 "Virtio Ethernet.\n",
2054
2055 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
2056#ifdef VNET_GC_SUPPORT
2057 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
2058#else
2059 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2060#endif
2061 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
2062 PDM_DEVREG_CLASS_NETWORK,
2063 /* Maximum number of instances (per VM). */
2064 8,
2065 /* Size of the instance data. */
2066 sizeof(VNETSTATE),
2067
2068 /* Construct instance - required. */
2069 vnetConstruct,
2070 /* Destruct instance - optional. */
2071 vnetDestruct,
2072 /* Relocation command - optional. */
2073 vnetRelocate,
2074 /* I/O Control interface - optional. */
2075 NULL,
2076 /* Power on notification - optional. */
2077 NULL,
2078 /* Reset notification - optional. */
2079 NULL,
2080 /* Suspend notification - optional. */
2081 vnetSuspend,
2082 /* Resume notification - optional. */
2083 NULL,
2084#ifdef VBOX_DYNAMIC_NET_ATTACH
2085 /* Attach command - optional. */
2086 vnetAttach,
2087 /* Detach notification - optional. */
2088 vnetDetach,
2089#else /* !VBOX_DYNAMIC_NET_ATTACH */
2090 /* Attach command - optional. */
2091 NULL,
2092 /* Detach notification - optional. */
2093 NULL,
2094#endif /* !VBOX_DYNAMIC_NET_ATTACH */
2095 /* Query a LUN base interface - optional. */
2096 NULL,
2097 /* Init complete notification - optional. */
2098 NULL,
2099 /* Power off notification - optional. */
2100 vnetPowerOff,
2101 /* pfnSoftReset */
2102 NULL,
2103 /* u32VersionEnd */
2104 PDM_DEVREG_VERSION
2105};
2106
2107#endif /* IN_RING3 */
2108#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette