[17800] | 1 | /* $Id: DevINIP.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * DevINIP - Internal Network IP stack device/service.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2007-2023 Oracle and/or its affiliates.
|
---|
[17800] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[17800] | 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
[57358] | 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[17800] | 32 | #define LOG_GROUP LOG_GROUP_DEV_INIP
|
---|
[20374] | 33 | #include <iprt/cdefs.h> /* include early to allow RT_C_DECLS_BEGIN hack */
|
---|
[17800] | 34 | #include <iprt/mem.h> /* include anything of ours that the lwip headers use. */
|
---|
| 35 | #include <iprt/semaphore.h>
|
---|
| 36 | #include <iprt/thread.h>
|
---|
| 37 | #include <iprt/alloca.h>
|
---|
| 38 | /* All lwip header files are not C++ safe. So hack around this. */
|
---|
[20374] | 39 | RT_C_DECLS_BEGIN
|
---|
[17800] | 40 | #include "lwip/sys.h"
|
---|
| 41 | #include "lwip/stats.h"
|
---|
| 42 | #include "lwip/mem.h"
|
---|
| 43 | #include "lwip/memp.h"
|
---|
| 44 | #include "lwip/pbuf.h"
|
---|
| 45 | #include "lwip/netif.h"
|
---|
[55432] | 46 | #include "lwip/api.h"
|
---|
| 47 | #include "lwip/tcp_impl.h"
|
---|
| 48 | # if LWIP_IPV6
|
---|
| 49 | # include "ipv6/lwip/ethip6.h"
|
---|
| 50 | # endif
|
---|
[17800] | 51 | #include "lwip/udp.h"
|
---|
| 52 | #include "lwip/tcp.h"
|
---|
| 53 | #include "lwip/tcpip.h"
|
---|
| 54 | #include "lwip/sockets.h"
|
---|
| 55 | #include "netif/etharp.h"
|
---|
[20374] | 56 | RT_C_DECLS_END
|
---|
[35346] | 57 | #include <VBox/vmm/pdmdev.h>
|
---|
| 58 | #include <VBox/vmm/pdmnetifs.h>
|
---|
| 59 | #include <VBox/vmm/tm.h>
|
---|
[25966] | 60 | #include <iprt/assert.h>
|
---|
[17800] | 61 | #include <iprt/string.h>
|
---|
[25966] | 62 | #include <iprt/uuid.h>
|
---|
[17800] | 63 |
|
---|
[35353] | 64 | #include "VBoxDD.h"
|
---|
[55432] | 65 | #include "VBoxLwipCore.h"
|
---|
[17800] | 66 |
|
---|
| 67 |
|
---|
[57358] | 68 | /*********************************************************************************************************************************
|
---|
| 69 | * Macros and Defines *
|
---|
| 70 | *********************************************************************************************************************************/
|
---|
[17800] | 71 | /** Maximum frame size this device can handle. */
|
---|
| 72 | #define DEVINIP_MAX_FRAME 1514
|
---|
| 73 |
|
---|
| 74 |
|
---|
[57358] | 75 | /*********************************************************************************************************************************
|
---|
| 76 | * Structures and Typedefs *
|
---|
| 77 | *********************************************************************************************************************************/
|
---|
[17800] | 78 | /**
|
---|
| 79 | * Internal Network IP stack device instance data.
|
---|
[25966] | 80 | *
|
---|
| 81 | * @implements PDMIBASE
|
---|
[26305] | 82 | * @implements PDMINETWORKDOWN
|
---|
[17800] | 83 | */
|
---|
| 84 | typedef struct DEVINTNETIP
|
---|
| 85 | {
|
---|
[25966] | 86 | /** The base interface for LUN\#0. */
|
---|
[17800] | 87 | PDMIBASE IBase;
|
---|
[25966] | 88 | /** The network port this device provides (LUN\#0). */
|
---|
[26305] | 89 | PDMINETWORKDOWN INetworkDown;
|
---|
[36044] | 90 | /** The network configuration port this device provides (LUN\#0). */
|
---|
[36023] | 91 | PDMINETWORKCONFIG INetworkConfig;
|
---|
[17800] | 92 | /** The base interface of the network driver below us. */
|
---|
| 93 | PPDMIBASE pDrvBase;
|
---|
| 94 | /** The connector of the network driver below us. */
|
---|
[44670] | 95 | PPDMINETWORKUP pDrv;
|
---|
[17800] | 96 | /** Pointer to the device instance. */
|
---|
| 97 | PPDMDEVINSR3 pDevIns;
|
---|
[33540] | 98 | /** MAC address. */
|
---|
[17800] | 99 | RTMAC MAC;
|
---|
| 100 | /** Static IP address of the interface. */
|
---|
| 101 | char *pszIP;
|
---|
| 102 | /** Netmask of the interface. */
|
---|
| 103 | char *pszNetmask;
|
---|
| 104 | /** Gateway for the interface. */
|
---|
| 105 | char *pszGateway;
|
---|
| 106 | /** lwIP network interface description. */
|
---|
| 107 | struct netif IntNetIF;
|
---|
| 108 | /** lwIP ARP timer. */
|
---|
| 109 | PTMTIMERR3 ARPTimer;
|
---|
| 110 | /** lwIP TCP fast timer. */
|
---|
| 111 | PTMTIMERR3 TCPFastTimer;
|
---|
| 112 | /** lwIP TCP slow timer. */
|
---|
| 113 | PTMTIMERR3 TCPSlowTimer;
|
---|
| 114 | /** lwIP semaphore to coordinate TCPIP init/terminate. */
|
---|
| 115 | sys_sem_t LWIPTcpInitSem;
|
---|
| 116 | /** hack: get linking right. remove this eventually, once the device
|
---|
| 117 | * provides a proper interface to all IP stack functions. */
|
---|
| 118 | const void *pLinkHack;
|
---|
[36023] | 119 | /** Flag whether the link is up. */
|
---|
| 120 | bool fLnkUp;
|
---|
[48947] | 121 | /**
|
---|
[46573] | 122 | * In callback we're getting status of interface adding operation (TCPIP thread),
|
---|
| 123 | * but we need inform constructing routine whether it was success or not(EMT thread).
|
---|
[48947] | 124 | */
|
---|
[47933] | 125 | int rcInitialization;
|
---|
[82455] | 126 | } DEVINTNETIP;
|
---|
| 127 | /** Pointer to the instance data for an Internal Network IP stack. */
|
---|
| 128 | typedef DEVINTNETIP *PDEVINTNETIP;
|
---|
[17800] | 129 |
|
---|
| 130 |
|
---|
[57358] | 131 | /*********************************************************************************************************************************
|
---|
| 132 | * Global Variables *
|
---|
| 133 | *********************************************************************************************************************************/
|
---|
[17800] | 134 | /**
|
---|
| 135 | * Pointer to the (only) instance data in this device.
|
---|
| 136 | */
|
---|
| 137 | static PDEVINTNETIP g_pDevINIPData = NULL;
|
---|
| 138 |
|
---|
| 139 | /*
|
---|
| 140 | * really ugly hack to avoid linking problems on unix style platforms
|
---|
| 141 | * using .a libraries for now.
|
---|
| 142 | */
|
---|
[85193] | 143 | static const struct CLANG11WEIRDNESS { PFNRT pfn; } g_pDevINILinkHack[] =
|
---|
[17800] | 144 | {
|
---|
[85193] | 145 | { (PFNRT)lwip_socket },
|
---|
| 146 | { (PFNRT)lwip_close },
|
---|
| 147 | { (PFNRT)lwip_setsockopt },
|
---|
| 148 | { (PFNRT)lwip_recv },
|
---|
| 149 | { (PFNRT)lwip_send },
|
---|
| 150 | { (PFNRT)lwip_select },
|
---|
[17800] | 151 | };
|
---|
| 152 |
|
---|
| 153 |
|
---|
[63211] | 154 | #if 0 /* unused */
|
---|
[17800] | 155 | /**
|
---|
| 156 | * Output a TCP/IP packet on the interface. Uses the generic lwIP ARP
|
---|
| 157 | * code to resolve the address and call the link-level packet function.
|
---|
| 158 | *
|
---|
| 159 | * @returns lwIP error code
|
---|
| 160 | * @param netif Interface on which to send IP packet.
|
---|
| 161 | * @param p Packet data.
|
---|
| 162 | * @param ipaddr Destination IP address.
|
---|
| 163 | */
|
---|
[57393] | 164 | static err_t devINIPOutput(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)
|
---|
[17800] | 165 | {
|
---|
| 166 | err_t lrc;
|
---|
| 167 | LogFlow(("%s: netif=%p p=%p ipaddr=%#04x\n", __FUNCTION__, netif, p,
|
---|
| 168 | ipaddr->addr));
|
---|
[55432] | 169 |
|
---|
[43636] | 170 | lrc = lwip_etharp_output(netif, p, ipaddr);
|
---|
[55432] | 171 |
|
---|
[17800] | 172 | LogFlow(("%s: return %d\n", __FUNCTION__, lrc));
|
---|
| 173 | return lrc;
|
---|
| 174 | }
|
---|
[63211] | 175 | #endif
|
---|
[17800] | 176 |
|
---|
| 177 | /**
|
---|
| 178 | * Output a raw packet on the interface.
|
---|
| 179 | *
|
---|
| 180 | * @returns lwIP error code
|
---|
| 181 | * @param netif Interface on which to send frame.
|
---|
| 182 | * @param p Frame data.
|
---|
| 183 | */
|
---|
[57393] | 184 | static err_t devINIPOutputRaw(struct netif *netif, struct pbuf *p)
|
---|
[17800] | 185 | {
|
---|
[62961] | 186 | NOREF(netif);
|
---|
[17800] | 187 | int rc = VINF_SUCCESS;
|
---|
| 188 |
|
---|
| 189 | LogFlow(("%s: netif=%p p=%p\n", __FUNCTION__, netif, p));
|
---|
| 190 | Assert(g_pDevINIPData);
|
---|
| 191 | Assert(g_pDevINIPData->pDrv);
|
---|
| 192 |
|
---|
| 193 | /* Silently ignore packets being sent while lwIP isn't set up. */
|
---|
[28213] | 194 | if (g_pDevINIPData)
|
---|
| 195 | {
|
---|
| 196 | PPDMSCATTERGATHER pSgBuf;
|
---|
[36023] | 197 |
|
---|
| 198 | rc = g_pDevINIPData->pDrv->pfnBeginXmit(g_pDevINIPData->pDrv, true /* fOnWorkerThread */);
|
---|
| 199 | if (RT_FAILURE(rc))
|
---|
| 200 | return ERR_IF;
|
---|
| 201 |
|
---|
[28213] | 202 | rc = g_pDevINIPData->pDrv->pfnAllocBuf(g_pDevINIPData->pDrv, DEVINIP_MAX_FRAME, NULL /*pGso*/, &pSgBuf);
|
---|
| 203 | if (RT_SUCCESS(rc))
|
---|
| 204 | {
|
---|
[17800] | 205 | #if ETH_PAD_SIZE
|
---|
[28213] | 206 | lwip_pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
---|
[17800] | 207 | #endif
|
---|
| 208 |
|
---|
[28213] | 209 | uint8_t *pbBuf = pSgBuf ? (uint8_t *)pSgBuf->aSegs[0].pvSeg : NULL;
|
---|
| 210 | size_t cbBuf = 0;
|
---|
| 211 | for (struct pbuf *q = p; q != NULL; q = q->next)
|
---|
| 212 | {
|
---|
| 213 | if (cbBuf + q->len <= DEVINIP_MAX_FRAME)
|
---|
| 214 | {
|
---|
| 215 | if (RT_LIKELY(pbBuf))
|
---|
| 216 | {
|
---|
| 217 | memcpy(pbBuf, q->payload, q->len);
|
---|
| 218 | pbBuf += q->len;
|
---|
| 219 | }
|
---|
| 220 | cbBuf += q->len;
|
---|
| 221 | }
|
---|
| 222 | else
|
---|
| 223 | {
|
---|
| 224 | LogRel(("INIP: exceeded frame size\n"));
|
---|
| 225 | break;
|
---|
| 226 | }
|
---|
| 227 | }
|
---|
| 228 | if (cbBuf)
|
---|
[36023] | 229 | {
|
---|
| 230 | pSgBuf->cbUsed = cbBuf;
|
---|
| 231 | rc = g_pDevINIPData->pDrv->pfnSendBuf(g_pDevINIPData->pDrv, pSgBuf, true /* fOnWorkerThread */);
|
---|
| 232 | }
|
---|
[28213] | 233 | else
|
---|
| 234 | rc = g_pDevINIPData->pDrv->pfnFreeBuf(g_pDevINIPData->pDrv, pSgBuf);
|
---|
[17800] | 235 |
|
---|
| 236 | #if ETH_PAD_SIZE
|
---|
[28213] | 237 | lwip_pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
|
---|
[17800] | 238 | #endif
|
---|
[28213] | 239 | }
|
---|
[36023] | 240 |
|
---|
| 241 | g_pDevINIPData->pDrv->pfnEndXmit(g_pDevINIPData->pDrv);
|
---|
[28213] | 242 | }
|
---|
[17800] | 243 |
|
---|
| 244 | err_t lrc = ERR_OK;
|
---|
| 245 | if (RT_FAILURE(rc))
|
---|
| 246 | lrc = ERR_IF;
|
---|
| 247 | LogFlow(("%s: return %d (vbox: %Rrc)\n", __FUNCTION__, rc, lrc));
|
---|
| 248 | return lrc;
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 | /**
|
---|
| 252 | * Implements the ethernet interface backend initialization for lwIP.
|
---|
| 253 | *
|
---|
| 254 | * @returns lwIP error code
|
---|
| 255 | * @param netif Interface to configure.
|
---|
| 256 | */
|
---|
[85121] | 257 | static err_t devINIPInterface(struct netif *netif) RT_NOTHROW_DEF
|
---|
[17800] | 258 | {
|
---|
| 259 | LogFlow(("%s: netif=%p\n", __FUNCTION__, netif));
|
---|
| 260 | Assert(g_pDevINIPData != NULL);
|
---|
| 261 | netif->state = g_pDevINIPData;
|
---|
| 262 | netif->hwaddr_len = sizeof(g_pDevINIPData->MAC);
|
---|
| 263 | memcpy(netif->hwaddr, &g_pDevINIPData->MAC, sizeof(g_pDevINIPData->MAC));
|
---|
| 264 | netif->mtu = DEVINIP_MAX_FRAME;
|
---|
| 265 | netif->flags = NETIF_FLAG_BROADCAST;
|
---|
[43636] | 266 | netif->flags |= NETIF_FLAG_ETHARP;
|
---|
| 267 | netif->flags |= NETIF_FLAG_ETHERNET;
|
---|
[55432] | 268 |
|
---|
| 269 | #if LWIP_IPV6
|
---|
[44208] | 270 | netif_create_ip6_linklocal_address(netif, 0);
|
---|
| 271 | netif_ip6_addr_set_state(netif, 0, IP6_ADDR_VALID);
|
---|
| 272 | netif->output_ip6 = ethip6_output;
|
---|
| 273 | netif->ip6_autoconfig_enabled=1;
|
---|
| 274 | LogFunc(("netif: ipv6:%RTnaipv6\n", &netif->ip6_addr[0].addr[0]));
|
---|
[55432] | 275 | #endif
|
---|
| 276 |
|
---|
[44208] | 277 | netif->output = lwip_etharp_output;
|
---|
[17800] | 278 | netif->linkoutput = devINIPOutputRaw;
|
---|
| 279 |
|
---|
| 280 | LogFlow(("%s: success\n", __FUNCTION__));
|
---|
| 281 | return ERR_OK;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | /**
|
---|
[43924] | 285 | * Parses CFGM parameters related to network connection
|
---|
| 286 | */
|
---|
| 287 | static DECLCALLBACK(int) devINIPNetworkConfiguration(PPDMDEVINS pDevIns, PDEVINTNETIP pThis, PCFGMNODE pCfg)
|
---|
| 288 | {
|
---|
[82455] | 289 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
| 290 |
|
---|
| 291 | int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "IP", &pThis->pszIP);
|
---|
[43924] | 292 | if (RT_FAILURE(rc))
|
---|
[63562] | 293 | /** @todo perhaps we should panic if IPv4 address isn't specify, with assumtion that
|
---|
[82455] | 294 | * ISCSI target specified in IPv6 form. */
|
---|
| 295 | return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IP\" value"));
|
---|
[44208] | 296 |
|
---|
[82455] | 297 | rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Netmask", &pThis->pszNetmask);
|
---|
[43924] | 298 | if (RT_FAILURE(rc))
|
---|
[82455] | 299 | return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Netmask\" value"));
|
---|
| 300 |
|
---|
| 301 | rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Gateway", &pThis->pszGateway);
|
---|
[43924] | 302 | if ( RT_FAILURE(rc)
|
---|
| 303 | && rc != VERR_CFGM_VALUE_NOT_FOUND)
|
---|
[82455] | 304 | return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Gateway\" value"));
|
---|
| 305 |
|
---|
[43924] | 306 | return VINF_SUCCESS;
|
---|
| 307 | }
|
---|
| 308 |
|
---|
| 309 | /**
|
---|
[17800] | 310 | * Wait until data can be received.
|
---|
| 311 | *
|
---|
| 312 | * @returns VBox status code. VINF_SUCCESS means there is at least one receive descriptor available.
|
---|
| 313 | * @param pInterface PDM network port interface pointer.
|
---|
| 314 | * @param cMillies Number of milliseconds to wait. 0 means return immediately.
|
---|
| 315 | */
|
---|
[28258] | 316 | static DECLCALLBACK(int) devINIPNetworkDown_WaitInputAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
|
---|
[17800] | 317 | {
|
---|
[62961] | 318 | RT_NOREF(pInterface, cMillies);
|
---|
[17800] | 319 | LogFlow(("%s: pInterface=%p\n", __FUNCTION__, pInterface));
|
---|
| 320 | LogFlow(("%s: return VINF_SUCCESS\n", __FUNCTION__));
|
---|
| 321 | return VINF_SUCCESS;
|
---|
| 322 | }
|
---|
| 323 |
|
---|
| 324 | /**
|
---|
| 325 | * Receive data and pass it to lwIP for processing.
|
---|
| 326 | *
|
---|
| 327 | * @returns VBox status code
|
---|
| 328 | * @param pInterface PDM network port interface pointer.
|
---|
| 329 | * @param pvBuf Pointer to frame data.
|
---|
| 330 | * @param cb Frame size.
|
---|
| 331 | */
|
---|
[44670] | 332 | static DECLCALLBACK(int) devINIPNetworkDown_Input(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
|
---|
[17800] | 333 | {
|
---|
[62961] | 334 | RT_NOREF(pInterface);
|
---|
[17800] | 335 | const uint8_t *pbBuf = (const uint8_t *)pvBuf;
|
---|
| 336 | size_t len = cb;
|
---|
| 337 | const struct eth_hdr *ethhdr;
|
---|
| 338 | struct pbuf *p, *q;
|
---|
| 339 |
|
---|
[44670] | 340 | LogFlow(("%s: pInterface=%p pvBuf=%p cb=%lu\n", __FUNCTION__, pInterface, pvBuf, cb));
|
---|
[17800] | 341 | Assert(g_pDevINIPData);
|
---|
| 342 | Assert(g_pDevINIPData->pDrv);
|
---|
| 343 |
|
---|
| 344 | /* Silently ignore packets being received while lwIP isn't set up. */
|
---|
| 345 | if (!g_pDevINIPData)
|
---|
[44670] | 346 | {
|
---|
[62961] | 347 | LogFlow(("%s: return %Rrc (no global)\n", __FUNCTION__, VINF_SUCCESS));
|
---|
[44670] | 348 | return VINF_SUCCESS;
|
---|
| 349 | }
|
---|
[17800] | 350 |
|
---|
| 351 | #if ETH_PAD_SIZE
|
---|
| 352 | len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
|
---|
| 353 | #endif
|
---|
| 354 |
|
---|
| 355 | /* We allocate a pbuf chain of pbufs from the pool. */
|
---|
[62961] | 356 | Assert((u16_t)len == len);
|
---|
| 357 | p = lwip_pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
|
---|
[17800] | 358 | if (p != NULL)
|
---|
| 359 | {
|
---|
| 360 | #if ETH_PAD_SIZE
|
---|
| 361 | lwip_pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
---|
| 362 | #endif
|
---|
| 363 |
|
---|
| 364 | for (q = p; q != NULL; q = q->next)
|
---|
| 365 | {
|
---|
| 366 | /* Fill the buffers, and clean out unused buffer space. */
|
---|
| 367 | memcpy(q->payload, pbBuf, RT_MIN(cb, q->len));
|
---|
| 368 | pbBuf += RT_MIN(cb, q->len);
|
---|
| 369 | if (q->len > cb)
|
---|
| 370 | memset(((uint8_t *)q->payload) + cb, '\0', q->len - cb);
|
---|
| 371 | cb -= RT_MIN(cb, q->len);
|
---|
| 372 | }
|
---|
| 373 |
|
---|
| 374 | ethhdr = (const struct eth_hdr *)p->payload;
|
---|
| 375 | struct netif *iface = &g_pDevINIPData->IntNetIF;
|
---|
[55432] | 376 |
|
---|
[45356] | 377 | /* We've setup flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET
|
---|
| 378 | so this should be thread-safe. */
|
---|
| 379 | tcpip_input(p,iface);
|
---|
[17800] | 380 | }
|
---|
| 381 |
|
---|
[62961] | 382 | LogFlow(("%s: return %Rrc\n", __FUNCTION__, VINF_SUCCESS));
|
---|
| 383 | return VINF_SUCCESS;
|
---|
[17800] | 384 | }
|
---|
| 385 |
|
---|
| 386 | /**
|
---|
[28277] | 387 | * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
|
---|
[28258] | 388 | */
|
---|
[28277] | 389 | static DECLCALLBACK(void) devINIPNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
|
---|
[28258] | 390 | {
|
---|
| 391 | NOREF(pInterface);
|
---|
| 392 | }
|
---|
| 393 |
|
---|
[46573] | 394 |
|
---|
[28258] | 395 | /**
|
---|
[17800] | 396 | * Signals the end of lwIP TCPIP initialization.
|
---|
[48947] | 397 | *
|
---|
[46573] | 398 | * @param arg opaque argument, here the pointer to the PDEVINTNETIP.
|
---|
[82455] | 399 | * @note TCPIP thread, corresponding EMT waiting on semaphore.
|
---|
[17800] | 400 | */
|
---|
| 401 | static DECLCALLBACK(void) devINIPTcpipInitDone(void *arg)
|
---|
| 402 | {
|
---|
[46573] | 403 | PDEVINTNETIP pThis = (PDEVINTNETIP)arg;
|
---|
| 404 | AssertPtrReturnVoid(arg);
|
---|
[46425] | 405 |
|
---|
[46573] | 406 | pThis->rcInitialization = VINF_SUCCESS;
|
---|
[82455] | 407 | struct in_addr ip;
|
---|
| 408 | if (!inet_aton(pThis->pszIP, &ip))
|
---|
[46573] | 409 | {
|
---|
[82455] | 410 | pThis->rcInitialization = VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
|
---|
| 411 | PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("Configuration error: Invalid \"IP\" value"));
|
---|
| 412 | return;
|
---|
| 413 | }
|
---|
| 414 | struct ip_addr ipaddr;
|
---|
| 415 | memcpy(&ipaddr, &ip, sizeof(ipaddr));
|
---|
[46573] | 416 |
|
---|
[82455] | 417 | if (!inet_aton(pThis->pszNetmask, &ip))
|
---|
| 418 | {
|
---|
| 419 | pThis->rcInitialization = VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
|
---|
| 420 | PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("Configuration error: Invalid \"Netmask\" value"));
|
---|
| 421 | return;
|
---|
| 422 | }
|
---|
| 423 | struct ip_addr netmask;
|
---|
| 424 | memcpy(&netmask, &ip, sizeof(netmask));
|
---|
[48947] | 425 |
|
---|
[82455] | 426 | if (pThis->pszGateway)
|
---|
| 427 | {
|
---|
| 428 | if (!inet_aton(pThis->pszGateway, &ip))
|
---|
[46573] | 429 | {
|
---|
| 430 | pThis->rcInitialization = VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
|
---|
[82455] | 431 | PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("Configuration error: Invalid \"Gateway\" value"));
|
---|
| 432 | return;
|
---|
[46573] | 433 | }
|
---|
[82455] | 434 | }
|
---|
| 435 | else
|
---|
| 436 | inet_aton(pThis->pszIP, &ip);
|
---|
| 437 | struct ip_addr gw;
|
---|
| 438 | memcpy(&gw, &ip, sizeof(gw));
|
---|
[46573] | 439 |
|
---|
[82455] | 440 | pThis->IntNetIF.name[0] = 'I';
|
---|
| 441 | pThis->IntNetIF.name[1] = 'N';
|
---|
[46573] | 442 |
|
---|
[82455] | 443 | struct netif *ret = netif_add(&pThis->IntNetIF, &ipaddr, &netmask, &gw, NULL, devINIPInterface, lwip_tcpip_input);
|
---|
| 444 | if (!ret)
|
---|
| 445 | {
|
---|
[46573] | 446 |
|
---|
[82455] | 447 | pThis->rcInitialization = VERR_NET_NO_NETWORK;
|
---|
| 448 | PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("netif_add failed"));
|
---|
| 449 | return;
|
---|
| 450 | }
|
---|
[48947] | 451 |
|
---|
[82455] | 452 | lwip_netif_set_default(&pThis->IntNetIF);
|
---|
| 453 | lwip_netif_set_up(&pThis->IntNetIF);
|
---|
[46425] | 454 | }
|
---|
[46573] | 455 |
|
---|
[55432] | 456 |
|
---|
[46573] | 457 | /**
|
---|
| 458 | * This callback is for finitializing our activity on TCPIP thread.
|
---|
[82455] | 459 | * @todo XXX: We do it only for new LWIP, old LWIP will stay broken for now.
|
---|
[46573] | 460 | */
|
---|
[48947] | 461 | static DECLCALLBACK(void) devINIPTcpipFiniDone(void *arg)
|
---|
[46573] | 462 | {
|
---|
| 463 | PDEVINTNETIP pThis = (PDEVINTNETIP)arg;
|
---|
| 464 | AssertPtrReturnVoid(arg);
|
---|
| 465 |
|
---|
| 466 | netif_set_link_down(&pThis->IntNetIF);
|
---|
| 467 | netif_set_down(&pThis->IntNetIF);
|
---|
| 468 | netif_remove(&pThis->IntNetIF);
|
---|
| 469 | }
|
---|
[17800] | 470 |
|
---|
[36023] | 471 |
|
---|
| 472 | /**
|
---|
| 473 | * Gets the current Media Access Control (MAC) address.
|
---|
| 474 | *
|
---|
| 475 | * @returns VBox status code.
|
---|
| 476 | * @param pInterface Pointer to the interface structure containing the called function pointer.
|
---|
| 477 | * @param pMac Where to store the MAC address.
|
---|
| 478 | * @thread EMT
|
---|
| 479 | */
|
---|
| 480 | static DECLCALLBACK(int) devINIPGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
|
---|
| 481 | {
|
---|
| 482 | PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, INetworkConfig);
|
---|
| 483 | memcpy(pMac, pThis->MAC.au8, sizeof(RTMAC));
|
---|
| 484 | return VINF_SUCCESS;
|
---|
| 485 | }
|
---|
| 486 |
|
---|
| 487 | /**
|
---|
| 488 | * Gets the new link state.
|
---|
| 489 | *
|
---|
| 490 | * @returns The current link state.
|
---|
| 491 | * @param pInterface Pointer to the interface structure containing the called function pointer.
|
---|
| 492 | * @thread EMT
|
---|
| 493 | */
|
---|
| 494 | static DECLCALLBACK(PDMNETWORKLINKSTATE) devINIPGetLinkState(PPDMINETWORKCONFIG pInterface)
|
---|
| 495 | {
|
---|
| 496 | PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, INetworkConfig);
|
---|
| 497 | if (pThis->fLnkUp)
|
---|
| 498 | return PDMNETWORKLINKSTATE_UP;
|
---|
| 499 | return PDMNETWORKLINKSTATE_DOWN;
|
---|
| 500 | }
|
---|
| 501 |
|
---|
| 502 |
|
---|
| 503 | /**
|
---|
| 504 | * Sets the new link state.
|
---|
| 505 | *
|
---|
| 506 | * @returns VBox status code.
|
---|
| 507 | * @param pInterface Pointer to the interface structure containing the called function pointer.
|
---|
| 508 | * @param enmState The new link state
|
---|
| 509 | */
|
---|
| 510 | static DECLCALLBACK(int) devINIPSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
|
---|
| 511 | {
|
---|
| 512 | PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, INetworkConfig);
|
---|
| 513 | bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
|
---|
| 514 |
|
---|
| 515 | if (fNewUp != pThis->fLnkUp)
|
---|
| 516 | {
|
---|
| 517 | if (fNewUp)
|
---|
| 518 | {
|
---|
| 519 | LogFlowFunc(("Link is up\n"));
|
---|
| 520 | pThis->fLnkUp = true;
|
---|
| 521 | }
|
---|
| 522 | else
|
---|
| 523 | {
|
---|
| 524 | LogFlowFunc(("Link is down\n"));
|
---|
| 525 | pThis->fLnkUp = false;
|
---|
| 526 | }
|
---|
| 527 | if (pThis->pDrv)
|
---|
| 528 | pThis->pDrv->pfnNotifyLinkChanged(pThis->pDrv, enmState);
|
---|
| 529 | }
|
---|
| 530 | return VINF_SUCCESS;
|
---|
| 531 | }
|
---|
| 532 |
|
---|
[26001] | 533 | /* -=-=-=-=- PDMIBASE -=-=-=-=- */
|
---|
[17800] | 534 |
|
---|
| 535 | /**
|
---|
[25966] | 536 | * @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
---|
[17800] | 537 | */
|
---|
| 538 | static DECLCALLBACK(void *) devINIPQueryInterface(PPDMIBASE pInterface,
|
---|
[25966] | 539 | const char *pszIID)
|
---|
[17800] | 540 | {
|
---|
[25966] | 541 | PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, IBase);
|
---|
[25985] | 542 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
|
---|
[26305] | 543 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
|
---|
[36023] | 544 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
|
---|
[25966] | 545 | return NULL;
|
---|
[17800] | 546 | }
|
---|
| 547 |
|
---|
[26001] | 548 | /* -=-=-=-=- PDMDEVREG -=-=-=-=- */
|
---|
[17800] | 549 |
|
---|
| 550 | /**
|
---|
[26001] | 551 | * Destruct a device instance.
|
---|
| 552 | *
|
---|
| 553 | * Most VM resources are freed by the VM. This callback is provided so that any non-VM
|
---|
| 554 | * resources can be freed correctly.
|
---|
| 555 | *
|
---|
[58170] | 556 | * @returns VBox status code.
|
---|
[26001] | 557 | * @param pDevIns The device instance data.
|
---|
| 558 | */
|
---|
| 559 | static DECLCALLBACK(int) devINIPDestruct(PPDMDEVINS pDevIns)
|
---|
| 560 | {
|
---|
[82455] | 561 | PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
|
---|
| 562 | LogFlow(("devINIPDestruct: pDevIns=%p\n", pDevIns));
|
---|
[81591] | 563 | PDEVINTNETIP pThis = PDMDEVINS_2_DATA(pDevIns, PDEVINTNETIP);
|
---|
[26001] | 564 |
|
---|
| 565 | if (g_pDevINIPData != NULL)
|
---|
[46573] | 566 | vboxLwipCoreFinalize(devINIPTcpipFiniDone, pThis);
|
---|
[26001] | 567 |
|
---|
[91897] | 568 | PDMDevHlpMMHeapFree(pDevIns, pThis->pszIP);
|
---|
[44670] | 569 | pThis->pszIP = NULL;
|
---|
[91897] | 570 | PDMDevHlpMMHeapFree(pDevIns, pThis->pszNetmask);
|
---|
[44670] | 571 | pThis->pszNetmask = NULL;
|
---|
[91897] | 572 | PDMDevHlpMMHeapFree(pDevIns, pThis->pszGateway);
|
---|
[44670] | 573 | pThis->pszGateway = NULL;
|
---|
[26001] | 574 |
|
---|
| 575 | LogFlow(("%s: success\n", __FUNCTION__));
|
---|
| 576 | return VINF_SUCCESS;
|
---|
| 577 | }
|
---|
| 578 |
|
---|
| 579 |
|
---|
| 580 | /**
|
---|
[26160] | 581 | * @interface_method_impl{PDMDEVREG,pfnConstruct}
|
---|
[17800] | 582 | */
|
---|
[44670] | 583 | static DECLCALLBACK(int) devINIPConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
|
---|
[17800] | 584 | {
|
---|
[82455] | 585 | PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
|
---|
| 586 | PDEVINTNETIP pThis = PDMDEVINS_2_DATA(pDevIns, PDEVINTNETIP);
|
---|
| 587 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
| 588 | LogFlow(("devINIPConstruct: pDevIns=%p iInstance=%d pCfg=%p\n", pDevIns, iInstance, pCfg));
|
---|
[62961] | 589 | RT_NOREF(iInstance);
|
---|
[55432] | 590 |
|
---|
[17800] | 591 | Assert(iInstance == 0);
|
---|
| 592 |
|
---|
| 593 | /*
|
---|
| 594 | * Init the static parts.
|
---|
| 595 | */
|
---|
[82455] | 596 | //pThis->pszIP = NULL;
|
---|
| 597 | //pThis->pszNetmask = NULL;
|
---|
| 598 | //pThis->pszGateway = NULL;
|
---|
[17800] | 599 | /* Pointer to device instance */
|
---|
| 600 | pThis->pDevIns = pDevIns;
|
---|
| 601 | /* IBase */
|
---|
| 602 | pThis->IBase.pfnQueryInterface = devINIPQueryInterface;
|
---|
[26305] | 603 | /* INetworkDown */
|
---|
[28258] | 604 | pThis->INetworkDown.pfnWaitReceiveAvail = devINIPNetworkDown_WaitInputAvail;
|
---|
| 605 | pThis->INetworkDown.pfnReceive = devINIPNetworkDown_Input;
|
---|
[28277] | 606 | pThis->INetworkDown.pfnXmitPending = devINIPNetworkDown_XmitPending;
|
---|
[36023] | 607 | /* INetworkConfig */
|
---|
| 608 | pThis->INetworkConfig.pfnGetMac = devINIPGetMac;
|
---|
| 609 | pThis->INetworkConfig.pfnGetLinkState = devINIPGetLinkState;
|
---|
| 610 | pThis->INetworkConfig.pfnSetLinkState = devINIPSetLinkState;
|
---|
[17800] | 611 |
|
---|
[82455] | 612 |
|
---|
[17800] | 613 | /*
|
---|
[82455] | 614 | * Validate the config.
|
---|
| 615 | */
|
---|
| 616 | PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|IP|IPv6|Netmask|Gateway", "");
|
---|
| 617 |
|
---|
| 618 | /*
|
---|
[17800] | 619 | * Get the configuration settings.
|
---|
| 620 | */
|
---|
[82455] | 621 | int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MAC, sizeof(pThis->MAC));
|
---|
[17800] | 622 | if (rc == VERR_CFGM_NOT_BYTES)
|
---|
| 623 | {
|
---|
| 624 | char szMAC[64];
|
---|
[82455] | 625 | rc = pHlp->pfnCFGMQueryString(pCfg, "MAC", &szMAC[0], sizeof(szMAC));
|
---|
[17800] | 626 | if (RT_SUCCESS(rc))
|
---|
| 627 | {
|
---|
| 628 | char *macStr = &szMAC[0];
|
---|
| 629 | char *pMac = (char *)&pThis->MAC;
|
---|
| 630 | for (uint32_t i = 0; i < 6; i++)
|
---|
| 631 | {
|
---|
[82455] | 632 | if (!*macStr || !macStr[1] || *macStr == ':' || macStr[1] == ':')
|
---|
| 633 | return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
|
---|
[44670] | 634 | N_("Configuration error: Invalid \"MAC\" value"));
|
---|
[17800] | 635 | char c1 = *macStr++ - '0';
|
---|
| 636 | if (c1 > 9)
|
---|
| 637 | c1 -= 7;
|
---|
| 638 | char c2 = *macStr++ - '0';
|
---|
| 639 | if (c2 > 9)
|
---|
| 640 | c2 -= 7;
|
---|
| 641 | *pMac++ = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
|
---|
| 642 | if (i != 5 && *macStr == ':')
|
---|
| 643 | macStr++;
|
---|
| 644 | }
|
---|
| 645 | }
|
---|
| 646 | }
|
---|
| 647 | if (RT_FAILURE(rc))
|
---|
[82455] | 648 | return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"MAC\" value"));
|
---|
[43924] | 649 | rc = devINIPNetworkConfiguration(pDevIns, pThis, pCfg);
|
---|
[44670] | 650 | AssertLogRelRCReturn(rc, rc);
|
---|
[17800] | 651 |
|
---|
| 652 | /*
|
---|
| 653 | * Attach driver and query the network connector interface.
|
---|
| 654 | */
|
---|
[44670] | 655 | rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Network Port");
|
---|
[17800] | 656 | if (RT_FAILURE(rc))
|
---|
| 657 | {
|
---|
| 658 | pThis->pDrvBase = NULL;
|
---|
| 659 | pThis->pDrv = NULL;
|
---|
[44670] | 660 | return PDMDEV_SET_ERROR(pDevIns, rc, N_("Error attaching device below us"));
|
---|
[17800] | 661 | }
|
---|
[44670] | 662 | pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
|
---|
| 663 | AssertMsgReturn(pThis->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"), VERR_PDM_MISSING_INTERFACE_BELOW);
|
---|
[17800] | 664 |
|
---|
| 665 |
|
---|
[46573] | 666 | /*
|
---|
| 667 | * Set up global pointer to interface data.
|
---|
| 668 | */
|
---|
| 669 | g_pDevINIPData = pThis;
|
---|
[44670] | 670 |
|
---|
[46573] | 671 |
|
---|
| 672 | /* link hack */
|
---|
| 673 | pThis->pLinkHack = g_pDevINILinkHack;
|
---|
| 674 |
|
---|
[17800] | 675 | /*
|
---|
| 676 | * Initialize lwIP.
|
---|
| 677 | */
|
---|
[46573] | 678 | vboxLwipCoreInitialize(devINIPTcpipInitDone, pThis);
|
---|
[17800] | 679 |
|
---|
[46573] | 680 | /* this rc could be updated in devINIPTcpInitDone thread */
|
---|
| 681 | AssertRCReturn(pThis->rcInitialization, pThis->rcInitialization);
|
---|
[17800] | 682 |
|
---|
| 683 |
|
---|
[82455] | 684 | LogFlow(("devINIPConstruct: return %Rrc\n", rc));
|
---|
[17800] | 685 | return rc;
|
---|
| 686 | }
|
---|
| 687 |
|
---|
| 688 |
|
---|
| 689 | /**
|
---|
| 690 | * Query whether lwIP is initialized or not. Since there is only a single
|
---|
| 691 | * instance of this device ever for a VM, it can be a global function.
|
---|
| 692 | *
|
---|
| 693 | * @returns True if lwIP is initialized.
|
---|
| 694 | */
|
---|
| 695 | bool DevINIPConfigured(void)
|
---|
| 696 | {
|
---|
| 697 | return g_pDevINIPData != NULL;
|
---|
| 698 | }
|
---|
| 699 |
|
---|
| 700 |
|
---|
| 701 | /**
|
---|
| 702 | * Internal network IP stack device registration record.
|
---|
| 703 | */
|
---|
| 704 | const PDMDEVREG g_DeviceINIP =
|
---|
| 705 | {
|
---|
[80531] | 706 | /* .u32Version = */ PDM_DEVREG_VERSION,
|
---|
| 707 | /* .uReserved0 = */ 0,
|
---|
| 708 | /* .szName = */ "IntNetIP",
|
---|
[82455] | 709 | /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
|
---|
[80531] | 710 | /* .fClass = */ PDM_DEVREG_CLASS_VMM_DEV, /* As this is used by the storage devices, it must come earlier. */
|
---|
| 711 | /* .cMaxInstances = */ 1,
|
---|
| 712 | /* .uSharedVersion = */ 42,
|
---|
| 713 | /* .cbInstanceShared = */ sizeof(DEVINTNETIP),
|
---|
| 714 | /* .cbInstanceCC = */ 0,
|
---|
| 715 | /* .cbInstanceRC = */ 0,
|
---|
[80703] | 716 | /* .cMaxPciDevices = */ 0,
|
---|
[80704] | 717 | /* .cMaxMsixVectors = */ 0,
|
---|
[80531] | 718 | /* .pszDescription = */ "Internal Network IP stack device",
|
---|
| 719 | #if defined(IN_RING3)
|
---|
| 720 | /* .pszRCMod = */ "",
|
---|
| 721 | /* .pszR0Mod = */ "",
|
---|
| 722 | /* .pfnConstruct = */ devINIPConstruct,
|
---|
| 723 | /* .pfnDestruct = */ devINIPDestruct,
|
---|
| 724 | /* .pfnRelocate = */ NULL,
|
---|
| 725 | /* .pfnMemSetup = */ NULL,
|
---|
| 726 | /* .pfnPowerOn = */ NULL,
|
---|
| 727 | /* .pfnReset = */ NULL,
|
---|
| 728 | /* .pfnSuspend = */ NULL,
|
---|
| 729 | /* .pfnResume = */ NULL,
|
---|
| 730 | /* .pfnAttach = */ NULL,
|
---|
| 731 | /* .pfnDetach = */ NULL,
|
---|
| 732 | /* .pfnQueryInterface = */ NULL,
|
---|
| 733 | /* .pfnInitComplete = */ NULL,
|
---|
| 734 | /* .pfnPowerOff = */ NULL,
|
---|
| 735 | /* .pfnSoftReset = */ NULL,
|
---|
| 736 | /* .pfnReserved0 = */ NULL,
|
---|
| 737 | /* .pfnReserved1 = */ NULL,
|
---|
| 738 | /* .pfnReserved2 = */ NULL,
|
---|
| 739 | /* .pfnReserved3 = */ NULL,
|
---|
| 740 | /* .pfnReserved4 = */ NULL,
|
---|
| 741 | /* .pfnReserved5 = */ NULL,
|
---|
| 742 | /* .pfnReserved6 = */ NULL,
|
---|
| 743 | /* .pfnReserved7 = */ NULL,
|
---|
| 744 | #elif defined(IN_RING0)
|
---|
| 745 | /* .pfnEarlyConstruct = */ NULL,
|
---|
| 746 | /* .pfnConstruct = */ NULL,
|
---|
| 747 | /* .pfnDestruct = */ NULL,
|
---|
| 748 | /* .pfnFinalDestruct = */ NULL,
|
---|
| 749 | /* .pfnRequest = */ NULL,
|
---|
| 750 | /* .pfnReserved0 = */ NULL,
|
---|
| 751 | /* .pfnReserved1 = */ NULL,
|
---|
| 752 | /* .pfnReserved2 = */ NULL,
|
---|
| 753 | /* .pfnReserved3 = */ NULL,
|
---|
| 754 | /* .pfnReserved4 = */ NULL,
|
---|
| 755 | /* .pfnReserved5 = */ NULL,
|
---|
| 756 | /* .pfnReserved6 = */ NULL,
|
---|
| 757 | /* .pfnReserved7 = */ NULL,
|
---|
| 758 | #elif defined(IN_RC)
|
---|
| 759 | /* .pfnConstruct = */ NULL,
|
---|
| 760 | /* .pfnReserved0 = */ NULL,
|
---|
| 761 | /* .pfnReserved1 = */ NULL,
|
---|
| 762 | /* .pfnReserved2 = */ NULL,
|
---|
| 763 | /* .pfnReserved3 = */ NULL,
|
---|
| 764 | /* .pfnReserved4 = */ NULL,
|
---|
| 765 | /* .pfnReserved5 = */ NULL,
|
---|
| 766 | /* .pfnReserved6 = */ NULL,
|
---|
| 767 | /* .pfnReserved7 = */ NULL,
|
---|
| 768 | #else
|
---|
| 769 | # error "Not in IN_RING3, IN_RING0 or IN_RC!"
|
---|
| 770 | #endif
|
---|
| 771 | /* .u32VersionEnd = */ PDM_DEVREG_VERSION
|
---|
[17800] | 772 | };
|
---|
[80531] | 773 |
|
---|