win-pvdrivers

annotate xennet/xennet_common.c @ 1033:cb767700f91c

Correctly initialise pi values and set header size based on lookahead
author James Harper <james.harper@bendigoit.com.au>
date Sun Mar 03 13:49:54 2013 +1100 (2013-03-03)
parents 1ce315b193d1
children fba0ce4d9e54
rev   line source
james@1007 1 /*
james@1007 2 PV Net Driver for Windows Xen HVM Domains
james@1007 3 Copyright (C) 2007 James Harper
james@1007 4 Copyright (C) 2007 Andrew Grover <andy.grover@oracle.com>
james@1007 5
james@1007 6 This program is free software; you can redistribute it and/or
james@1007 7 modify it under the terms of the GNU General Public License
james@1007 8 as published by the Free Software Foundation; either version 2
james@1007 9 of the License, or (at your option) any later version.
james@1007 10
james@1007 11 This program is distributed in the hope that it will be useful,
james@1007 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
james@1007 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
james@1007 14 GNU General Public License for more details.
james@1007 15
james@1007 16 You should have received a copy of the GNU General Public License
james@1007 17 along with this program; if not, write to the Free Software
james@1007 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
james@1007 19 */
james@1007 20
james@1007 21 #include "xennet.h"
james@1007 22
james@1007 23 /* Increase the header to a certain size */
james@1007 24 BOOLEAN
james@1007 25 XenNet_BuildHeader(packet_info_t *pi, PUCHAR header, ULONG new_header_size)
james@1007 26 {
james@1007 27 ULONG bytes_remaining;
james@1007 28
james@1007 29 //FUNCTION_ENTER();
james@1007 30
james@1007 31 if (!header)
james@1007 32 header = pi->header;
james@1007 33
james@1007 34 if (new_header_size > pi->total_length) {
james@1007 35 new_header_size = pi->total_length;
james@1007 36 }
james@1007 37
james@1007 38 if (new_header_size <= pi->header_length) {
james@1007 39 //FUNCTION_EXIT();
james@1007 40 return TRUE; /* header is already at least the required size */
james@1007 41 }
james@1007 42
james@1007 43 if (header == pi->first_mdl_virtual) {
james@1007 44 /* still working in the first buffer */
james@1007 45 if (new_header_size <= pi->first_mdl_length) {
james@1007 46 //KdPrint((__DRIVER_NAME " new_header_size <= pi->first_mdl_length\n"));
james@1007 47 pi->header_length = new_header_size;
james@1007 48 if (pi->header_length == pi->first_mdl_length) {
james@1007 49 #if NTDDI_VERSION < NTDDI_VISTA
james@1007 50 NdisGetNextBuffer(pi->curr_mdl, &pi->curr_mdl);
james@1007 51 #else
james@1007 52 NdisGetNextMdl(pi->curr_mdl, &pi->curr_mdl);
james@1007 53 #endif
james@1007 54 pi->curr_mdl_offset = 0;
james@1007 55 if (pi->curr_pb)
james@1007 56 pi->curr_pb = pi->curr_pb->next;
james@1007 57 } else {
james@1007 58 pi->curr_mdl_offset = (USHORT)new_header_size;
james@1007 59 }
james@1007 60 //FUNCTION_EXIT();
james@1007 61 return TRUE;
james@1007 62 } else {
james@1007 63 //KdPrint((__DRIVER_NAME " Switching to header_data\n"));
james@1007 64 memcpy(pi->header_data, header, pi->header_length);
james@1007 65 header = pi->header = pi->header_data;
james@1007 66 }
james@1007 67 }
james@1007 68
james@1007 69 bytes_remaining = new_header_size - pi->header_length;
james@1007 70 // TODO: if there are only a small number of bytes left in the current buffer then increase to consume that too... it would have to be no more than the size of header+mss though
james@1007 71
james@1007 72 //KdPrint((__DRIVER_NAME " A bytes_remaining = %d, pi->curr_mdl = %p\n", bytes_remaining, pi->curr_mdl));
james@1007 73 while (bytes_remaining && pi->curr_mdl) {
james@1007 74 ULONG copy_size;
james@1007 75
james@1023 76 XN_ASSERT(pi->curr_mdl);
james@1007 77 //KdPrint((__DRIVER_NAME " B bytes_remaining = %d, pi->curr_mdl = %p\n", bytes_remaining, pi->curr_mdl));
james@1007 78 if (MmGetMdlByteCount(pi->curr_mdl)) {
james@1007 79 PUCHAR src_addr;
james@1007 80 src_addr = MmGetSystemAddressForMdlSafe(pi->curr_mdl, NormalPagePriority);
james@1007 81 if (!src_addr) {
james@1007 82 //FUNCTION_EXIT();
james@1007 83 return FALSE;
james@1007 84 }
james@1007 85 copy_size = min(bytes_remaining, MmGetMdlByteCount(pi->curr_mdl) - pi->curr_mdl_offset);
james@1007 86 //KdPrint((__DRIVER_NAME " B copy_size = %d\n", copy_size));
james@1007 87 memcpy(header + pi->header_length,
james@1007 88 src_addr + pi->curr_mdl_offset, copy_size);
james@1007 89 pi->curr_mdl_offset = (USHORT)(pi->curr_mdl_offset + copy_size);
james@1007 90 pi->header_length += copy_size;
james@1007 91 bytes_remaining -= copy_size;
james@1007 92 }
james@1007 93 if (pi->curr_mdl_offset == MmGetMdlByteCount(pi->curr_mdl)) {
james@1007 94 #if NTDDI_VERSION < NTDDI_VISTA
james@1007 95 NdisGetNextBuffer(pi->curr_mdl, &pi->curr_mdl);
james@1007 96 #else
james@1007 97 NdisGetNextMdl(pi->curr_mdl, &pi->curr_mdl);
james@1007 98 #endif
james@1007 99 if (pi->curr_pb)
james@1007 100 pi->curr_pb = pi->curr_pb->next;
james@1007 101 pi->curr_mdl_offset = 0;
james@1007 102 }
james@1007 103 }
james@1007 104 //KdPrint((__DRIVER_NAME " C bytes_remaining = %d, pi->curr_mdl = %p\n", bytes_remaining, pi->curr_mdl));
james@1007 105 if (bytes_remaining) {
james@1007 106 //KdPrint((__DRIVER_NAME " bytes_remaining\n"));
james@1007 107 //FUNCTION_EXIT();
james@1007 108 return FALSE;
james@1007 109 }
james@1007 110 //FUNCTION_EXIT();
james@1007 111 return TRUE;
james@1007 112 }
james@1007 113
james@1007 114 VOID
james@1007 115 XenNet_ParsePacketHeader(packet_info_t *pi, PUCHAR alt_buffer, ULONG min_header_size)
james@1007 116 {
james@1007 117 //FUNCTION_ENTER();
james@1007 118
james@1023 119 XN_ASSERT(pi->first_mdl);
james@1007 120
james@1007 121 #if NTDDI_VERSION < NTDDI_VISTA
james@1007 122 NdisQueryBufferSafe(pi->first_mdl, (PVOID)&pi->first_mdl_virtual, &pi->first_mdl_length, NormalPagePriority);
james@1007 123 #else
james@1007 124 NdisQueryMdl(pi->first_mdl, (PVOID)&pi->first_mdl_virtual, &pi->first_mdl_length, NormalPagePriority);
james@1007 125 #endif
james@1007 126 pi->curr_mdl = pi->first_mdl;
james@1007 127 if (alt_buffer)
james@1007 128 pi->header = alt_buffer;
james@1007 129 else
james@1007 130 pi->header = pi->first_mdl_virtual;
james@1007 131
james@1007 132 pi->header_length = 0;
james@1007 133 pi->curr_mdl_offset = pi->first_mdl_offset;
james@1033 134
james@1033 135 pi->ip_proto = 0;
james@1033 136 pi->ip_version = 0;
james@1033 137 pi->ip4_header_length = 0;
james@1033 138 pi->ip4_length = 0;
james@1033 139 pi->tcp_header_length = 0;
james@1033 140 pi->tcp_length = 0;
james@1033 141 pi->split_required = 0;
james@1007 142
james@1007 143 XenNet_BuildHeader(pi, NULL, min_header_size);
james@1007 144
james@1033 145 if (!XenNet_BuildHeader(pi, NULL, (ULONG)XN_HDR_SIZE)) {
james@1007 146 //KdPrint((__DRIVER_NAME " packet too small (Ethernet Header)\n"));
james@1007 147 pi->parse_result = PARSE_TOO_SMALL;
james@1007 148 return;
james@1007 149 }
james@1007 150
james@1007 151 if (pi->header[0] == 0xFF && pi->header[1] == 0xFF
james@1007 152 && pi->header[2] == 0xFF && pi->header[3] == 0xFF
james@1033 153 && pi->header[4] == 0xFF && pi->header[5] == 0xFF) {
james@1007 154 pi->is_broadcast = TRUE;
james@1033 155 } else if (pi->header[0] & 0x01) {
james@1007 156 pi->is_multicast = TRUE;
james@1007 157 }
james@1007 158
james@1033 159 switch (GET_NET_PUSHORT(&pi->header[12])) { // L2 protocol field
james@1007 160 case 0x0800: /* IPv4 */
james@1007 161 //KdPrint((__DRIVER_NAME " IP\n"));
james@1033 162 if (pi->header_length < (ULONG)(XN_HDR_SIZE + 20)) {
james@1033 163 if (!XenNet_BuildHeader(pi, NULL, (ULONG)(XN_HDR_SIZE + 20))) {
james@1007 164 KdPrint((__DRIVER_NAME " packet too small (IP Header)\n"));
james@1007 165 pi->parse_result = PARSE_TOO_SMALL;
james@1007 166 return;
james@1007 167 }
james@1007 168 }
james@1007 169 pi->ip_version = (pi->header[XN_HDR_SIZE + 0] & 0xF0) >> 4;
james@1033 170 if (pi->ip_version != 4) {
james@1007 171 //KdPrint((__DRIVER_NAME " ip_version = %d\n", pi->ip_version));
james@1007 172 pi->parse_result = PARSE_UNKNOWN_TYPE;
james@1007 173 return;
james@1007 174 }
james@1007 175 pi->ip4_header_length = (pi->header[XN_HDR_SIZE + 0] & 0x0F) << 2;
james@1033 176 if (pi->header_length < (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + 20)) {
james@1033 177 if (!XenNet_BuildHeader(pi, NULL, (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + 20))) {
james@1007 178 //KdPrint((__DRIVER_NAME " packet too small (IP Header + IP Options + TCP Header)\n"));
james@1007 179 pi->parse_result = PARSE_TOO_SMALL;
james@1007 180 return;
james@1007 181 }
james@1007 182 }
james@1007 183 break;
james@1007 184 case 0x86DD: /* IPv6 */
james@1007 185 //KdPrint((__DRIVER_NAME " IPv6\n"));
james@1007 186 //KdPrint((__DRIVER_NAME " (not currently used)\n"));
james@1007 187 pi->parse_result = PARSE_UNKNOWN_TYPE;
james@1007 188 return;
james@1007 189 default:
james@1007 190 //KdPrint((__DRIVER_NAME " Not IP (%04x)\n", GET_NET_PUSHORT(&pi->header[12])));
james@1007 191 pi->parse_result = PARSE_UNKNOWN_TYPE;
james@1007 192 return;
james@1007 193 }
james@1007 194 pi->ip_proto = pi->header[XN_HDR_SIZE + 9];
james@1007 195 pi->ip4_length = GET_NET_PUSHORT(&pi->header[XN_HDR_SIZE + 2]);
james@1007 196 pi->ip_has_options = (BOOLEAN)(pi->ip4_header_length > 20);
james@1033 197 switch (pi->ip_proto) {
james@1007 198 case 6: // TCP
james@1007 199 case 17: // UDP
james@1007 200 break;
james@1007 201 default:
james@1007 202 //KdPrint((__DRIVER_NAME " Not TCP/UDP (%d)\n", pi->ip_proto));
james@1007 203 pi->parse_result = PARSE_UNKNOWN_TYPE;
james@1007 204 return;
james@1007 205 }
james@1007 206 pi->tcp_header_length = (pi->header[XN_HDR_SIZE + pi->ip4_header_length + 12] & 0xf0) >> 2;
james@1007 207
james@1033 208 if (pi->header_length < (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + pi->tcp_header_length)) {
james@1007 209 /* we don't actually need the tcp options to analyse the header */
james@1033 210 if (!XenNet_BuildHeader(pi, NULL, (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + MIN_TCP_HEADER_LENGTH))) {
james@1007 211 //KdPrint((__DRIVER_NAME " packet too small (IP Header + IP Options + TCP Header (not including TCP Options))\n"));
james@1007 212 pi->parse_result = PARSE_TOO_SMALL;
james@1007 213 return;
james@1007 214 }
james@1007 215 }
james@1007 216
james@1033 217 if ((ULONG)XN_HDR_SIZE + pi->ip4_length > pi->total_length) {
james@1007 218 //KdPrint((__DRIVER_NAME " XN_HDR_SIZE + ip4_length (%d) > total_length (%d)\n", XN_HDR_SIZE + pi->ip4_length, pi->total_length));
james@1007 219 pi->parse_result = PARSE_UNKNOWN_TYPE;
james@1007 220 return;
james@1007 221 }
james@1007 222
james@1007 223 pi->tcp_length = pi->ip4_length - pi->ip4_header_length - pi->tcp_header_length;
james@1007 224 pi->tcp_remaining = pi->tcp_length;
james@1007 225 pi->tcp_seq = GET_NET_PULONG(&pi->header[XN_HDR_SIZE + pi->ip4_header_length + 4]);
james@1007 226 pi->tcp_has_options = (BOOLEAN)(pi->tcp_header_length > 20);
james@1007 227 if (pi->mss > 0 && pi->tcp_length > pi->mss)
james@1007 228 pi->split_required = TRUE;
james@1007 229
james@1007 230 //KdPrint((__DRIVER_NAME " ip4_length = %d\n", pi->ip4_length));
james@1007 231 //KdPrint((__DRIVER_NAME " tcp_length = %d\n", pi->tcp_length));
james@1007 232 //FUNCTION_EXIT();
james@1007 233
james@1007 234 pi->parse_result = PARSE_OK;
james@1007 235 }
james@1007 236
james@1007 237 BOOLEAN
james@1007 238 XenNet_CheckIpHeaderSum(PUCHAR header, USHORT ip4_header_length) {
james@1007 239 ULONG csum = 0;
james@1007 240 USHORT i;
james@1007 241
james@1023 242 XN_ASSERT(ip4_header_length > 12);
james@1023 243 XN_ASSERT(!(ip4_header_length & 1));
james@1007 244
james@1007 245 for (i = 0; i < ip4_header_length; i += 2) {
james@1007 246 csum += GET_NET_PUSHORT(&header[XN_HDR_SIZE + i]);
james@1007 247 }
james@1007 248 while (csum & 0xFFFF0000)
james@1007 249 csum = (csum & 0xFFFF) + (csum >> 16);
james@1007 250 return (BOOLEAN)(csum == 0xFFFF);
james@1007 251 }
james@1007 252
james@1007 253 VOID
james@1007 254 XenNet_SumIpHeader(PUCHAR header, USHORT ip4_header_length) {
james@1007 255 ULONG csum = 0;
james@1007 256 USHORT i;
james@1007 257
james@1023 258 XN_ASSERT(ip4_header_length > 12);
james@1023 259 XN_ASSERT(!(ip4_header_length & 1));
james@1007 260
james@1007 261 header[XN_HDR_SIZE + 10] = 0;
james@1007 262 header[XN_HDR_SIZE + 11] = 0;
james@1007 263 for (i = 0; i < ip4_header_length; i += 2) {
james@1007 264 csum += GET_NET_PUSHORT(&header[XN_HDR_SIZE + i]);
james@1007 265 }
james@1007 266 while (csum & 0xFFFF0000)
james@1007 267 csum = (csum & 0xFFFF) + (csum >> 16);
james@1007 268 csum = ~csum;
james@1007 269 SET_NET_USHORT(&header[XN_HDR_SIZE + 10], (USHORT)csum);
james@1007 270 }
james@1007 271
james@1007 272 BOOLEAN
james@1007 273 XenNet_FilterAcceptPacket(struct xennet_info *xi,packet_info_t *pi)
james@1007 274 {
james@1007 275 ULONG i;
james@1007 276 BOOLEAN is_my_multicast = FALSE;
james@1007 277 BOOLEAN is_directed = FALSE;
james@1007 278
james@1007 279 if (memcmp(xi->curr_mac_addr, pi->header, ETH_ALEN) == 0)
james@1007 280 {
james@1007 281 is_directed = TRUE;
james@1007 282 }
james@1007 283 else if (pi->is_multicast)
james@1007 284 {
james@1007 285 for (i = 0; i < xi->multicast_list_size; i++)
james@1007 286 {
james@1007 287 if (memcmp(xi->multicast_list[i], pi->header, 6) == 0)
james@1007 288 break;
james@1007 289 }
james@1007 290 if (i < xi->multicast_list_size)
james@1007 291 {
james@1007 292 is_my_multicast = TRUE;
james@1007 293 }
james@1007 294 }
james@1007 295 if (is_directed && (xi->packet_filter & NDIS_PACKET_TYPE_DIRECTED))
james@1007 296 {
james@1007 297 return TRUE;
james@1007 298 }
james@1007 299 if (is_my_multicast && (xi->packet_filter & NDIS_PACKET_TYPE_MULTICAST))
james@1007 300 {
james@1007 301 return TRUE;
james@1007 302 }
james@1007 303 if (pi->is_multicast && (xi->packet_filter & NDIS_PACKET_TYPE_ALL_MULTICAST))
james@1007 304 {
james@1007 305 return TRUE;
james@1007 306 }
james@1007 307 if (pi->is_broadcast && (xi->packet_filter & NDIS_PACKET_TYPE_BROADCAST))
james@1007 308 {
james@1007 309 return TRUE;
james@1007 310 }
james@1007 311 if (xi->packet_filter & NDIS_PACKET_TYPE_PROMISCUOUS)
james@1007 312 {
james@1007 313 return TRUE;
james@1007 314 }
james@1007 315 //return TRUE;
james@1007 316 return FALSE;
james@1007 317 }
james@1007 318
james@1007 319 static VOID
james@1007 320 XenNet_RxTxDpc(PKDPC dpc, PVOID context, PVOID arg1, PVOID arg2)
james@1007 321 {
james@1007 322 struct xennet_info *xi = context;
james@1007 323 BOOLEAN dont_set_event;
james@1007 324
james@1007 325 UNREFERENCED_PARAMETER(dpc);
james@1007 326 UNREFERENCED_PARAMETER(arg1);
james@1007 327 UNREFERENCED_PARAMETER(arg2);
james@1007 328
james@1007 329 //FUNCTION_ENTER();
james@1007 330 /* if Rx goes over its per-dpc quota then make sure TxBufferGC doesn't set an event as we are already guaranteed to be called again */
james@1007 331 dont_set_event = XenNet_RxBufferCheck(xi);
james@1007 332 XenNet_TxBufferGC(xi, dont_set_event);
james@1007 333 //FUNCTION_EXIT();
james@1007 334 }
james@1007 335
james@1007 336 static BOOLEAN
james@1007 337 XenNet_HandleEvent_DIRQL(PVOID context)
james@1007 338 {
james@1007 339 struct xennet_info *xi = context;
james@1007 340 //ULONG suspend_resume_state_pdo;
james@1007 341
james@1007 342 //FUNCTION_ENTER();
james@1007 343 if (xi->device_state == DEVICE_STATE_ACTIVE || xi->device_state == DEVICE_STATE_DISCONNECTING) {
james@1007 344 KeInsertQueueDpc(&xi->rxtx_dpc, NULL, NULL);
james@1007 345 }
james@1007 346 //FUNCTION_EXIT();
james@1007 347 return TRUE;
james@1007 348 }
james@1007 349
james@1007 350 NTSTATUS
james@1007 351 XenNet_Connect(PVOID context, BOOLEAN suspend) {
james@1007 352 NTSTATUS status;
james@1007 353 struct xennet_info *xi = context;
james@1007 354 PFN_NUMBER pfn;
james@1007 355 ULONG qemu_hide_filter;
james@1007 356 ULONG qemu_hide_flags_value;
james@1007 357 int i;
james@1007 358 ULONG state;
james@1007 359 ULONG octet;
james@1007 360 PCHAR tmp_string;
james@1007 361 ULONG tmp_ulong;
james@1007 362
james@1007 363 if (!suspend) {
james@1007 364 xi->handle = XnOpenDevice(xi->pdo, XenNet_DeviceCallback, xi);
james@1007 365 }
james@1007 366 if (!xi->handle) {
james@1007 367 FUNCTION_MSG("Cannot open Xen device\n");
james@1007 368 return STATUS_UNSUCCESSFUL;
james@1007 369 }
james@1007 370 XnGetValue(xi->handle, XN_VALUE_TYPE_QEMU_HIDE_FLAGS, &qemu_hide_flags_value);
james@1007 371 XnGetValue(xi->handle, XN_VALUE_TYPE_QEMU_FILTER, &qemu_hide_filter);
james@1007 372 if (!(qemu_hide_flags_value & QEMU_UNPLUG_ALL_NICS) || qemu_hide_filter) {
james@1007 373 FUNCTION_MSG("inactive\n");
james@1007 374 xi->device_state = DEVICE_STATE_INACTIVE;
james@1007 375 /* continue with setup so all the flags and capabilities are correct */
james@1007 376 }
james@1007 377
james@1017 378 if (xi->device_state != DEVICE_STATE_INACTIVE) {
james@1017 379 for (i = 0; i <= 5 && xi->backend_state != XenbusStateInitialising && xi->backend_state != XenbusStateInitWait && xi->backend_state != XenbusStateInitialised; i++) {
james@1017 380 FUNCTION_MSG("Waiting for XenbusStateInitXxx\n");
james@1017 381 if (xi->backend_state == XenbusStateClosed) {
james@1017 382 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "state", XenbusStateInitialising);
james@1017 383 }
james@1017 384 KeWaitForSingleObject(&xi->backend_event, Executive, KernelMode, FALSE, NULL);
james@1017 385 }
james@1017 386 if (xi->backend_state != XenbusStateInitialising && xi->backend_state != XenbusStateInitWait && xi->backend_state != XenbusStateInitialised) {
james@1017 387 FUNCTION_MSG("Backend state timeout\n");
james@1017 388 return STATUS_UNSUCCESSFUL;
james@1017 389 }
james@1017 390 if (!NT_SUCCESS(status = XnBindEvent(xi->handle, &xi->event_channel, XenNet_HandleEvent_DIRQL, xi))) {
james@1017 391 FUNCTION_MSG("Cannot allocate event channel\n");
james@1017 392 return STATUS_UNSUCCESSFUL;
james@1017 393 }
james@1017 394 FUNCTION_MSG("event_channel = %d\n", xi->event_channel);
james@1017 395 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "event-channel", xi->event_channel);
james@1017 396 xi->tx_sring = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, XENNET_POOL_TAG);
james@1017 397 if (!xi->tx_sring) {
james@1017 398 FUNCTION_MSG("Cannot allocate tx_sring\n");
james@1017 399 return STATUS_UNSUCCESSFUL;
james@1017 400 }
james@1017 401 SHARED_RING_INIT(xi->tx_sring);
james@1017 402 FRONT_RING_INIT(&xi->tx_ring, xi->tx_sring, PAGE_SIZE);
james@1017 403 pfn = (PFN_NUMBER)(MmGetPhysicalAddress(xi->tx_sring).QuadPart >> PAGE_SHIFT);
james@1017 404 FUNCTION_MSG("tx sring pfn = %d\n", (ULONG)pfn);
james@1017 405 xi->tx_sring_gref = XnGrantAccess(xi->handle, (ULONG)pfn, FALSE, INVALID_GRANT_REF, XENNET_POOL_TAG);
james@1017 406 FUNCTION_MSG("tx sring_gref = %d\n", xi->tx_sring_gref);
james@1017 407 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "tx-ring-ref", xi->tx_sring_gref);
james@1017 408 xi->rx_sring = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, XENNET_POOL_TAG);
james@1017 409 if (!xi->rx_sring) {
james@1017 410 FUNCTION_MSG("Cannot allocate rx_sring\n");
james@1017 411 return STATUS_UNSUCCESSFUL;
james@1017 412 }
james@1017 413 SHARED_RING_INIT(xi->rx_sring);
james@1017 414 FRONT_RING_INIT(&xi->rx_ring, xi->rx_sring, PAGE_SIZE);
james@1017 415 pfn = (PFN_NUMBER)(MmGetPhysicalAddress(xi->rx_sring).QuadPart >> PAGE_SHIFT);
james@1017 416 FUNCTION_MSG("rx sring pfn = %d\n", (ULONG)pfn);
james@1017 417 xi->rx_sring_gref = XnGrantAccess(xi->handle, (ULONG)pfn, FALSE, INVALID_GRANT_REF, XENNET_POOL_TAG);
james@1017 418 FUNCTION_MSG("rx sring_gref = %d\n", xi->rx_sring_gref);
james@1017 419 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "rx-ring-ref", xi->rx_sring_gref);
james@1017 420
james@1017 421 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "request-rx-copy", 1);
james@1017 422 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "request-rx-notify", 1);
james@1017 423 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "feature-no-csum-offload", !xi->frontend_csum_supported);
james@1017 424 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "feature-sg", (int)xi->frontend_sg_supported);
james@1017 425 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "feature-gso-tcpv4", !!xi->frontend_gso_value);
james@1007 426 }
james@1007 427 status = XnReadInt32(xi->handle, XN_BASE_BACKEND, "feature-sg", &tmp_ulong);
james@1007 428 if (tmp_ulong) {
james@1007 429 xi->backend_sg_supported = TRUE;
james@1007 430 }
james@1007 431 status = XnReadInt32(xi->handle, XN_BASE_BACKEND, "feature-gso-tcpv4", &tmp_ulong);
james@1007 432 if (tmp_ulong) {
james@1007 433 xi->backend_gso_value = xi->frontend_gso_value;
james@1007 434 }
james@1007 435
james@1007 436 status = XnReadString(xi->handle, XN_BASE_BACKEND, "mac", &tmp_string);
james@1007 437 state = 0;
james@1007 438 octet = 0;
james@1007 439 for (i = 0; state != 3 && i < (int)strlen(tmp_string); i++) {
james@1007 440 if (octet == 6) {
james@1007 441 state = 3;
james@1007 442 break;
james@1007 443 }
james@1007 444 switch(state) {
james@1007 445 case 0:
james@1007 446 case 1:
james@1007 447 if (tmp_string[i] >= '0' && tmp_string[i] <= '9') {
james@1007 448 xi->perm_mac_addr[octet] |= (tmp_string[i] - '0') << ((1 - state) * 4);
james@1007 449 state++;
james@1007 450 } else if (tmp_string[i] >= 'A' && tmp_string[i] <= 'F') {
james@1007 451 xi->perm_mac_addr[octet] |= (tmp_string[i] - 'A' + 10) << ((1 - state) * 4);
james@1007 452 state++;
james@1007 453 } else if (tmp_string[i] >= 'a' && tmp_string[i] <= 'f') {
james@1007 454 xi->perm_mac_addr[octet] |= (tmp_string[i] - 'a' + 10) << ((1 - state) * 4);
james@1007 455 state++;
james@1007 456 } else {
james@1007 457 state = 3;
james@1007 458 }
james@1007 459 break;
james@1007 460 case 2:
james@1007 461 if (tmp_string[i] == ':') {
james@1007 462 octet++;
james@1007 463 state = 0;
james@1007 464 } else {
james@1007 465 state = 3;
james@1007 466 }
james@1007 467 break;
james@1007 468 }
james@1007 469 }
james@1007 470 if (octet != 5 || state != 2) {
james@1007 471 FUNCTION_MSG("Failed to parse backend MAC address %s\n", tmp_string);
james@1007 472 XnFreeMem(xi->handle, tmp_string);
james@1007 473 return STATUS_UNSUCCESSFUL;
james@1007 474 } else if ((xi->curr_mac_addr[0] & 0x03) != 0x02) {
james@1007 475 /* only copy if curr_mac_addr is not a LUA */
james@1007 476 memcpy(xi->curr_mac_addr, xi->perm_mac_addr, ETH_ALEN);
james@1007 477 }
james@1007 478 XnFreeMem(xi->handle, tmp_string);
james@1007 479 FUNCTION_MSG("MAC address is %02X:%02X:%02X:%02X:%02X:%02X\n",
james@1007 480 xi->curr_mac_addr[0], xi->curr_mac_addr[1], xi->curr_mac_addr[2],
james@1007 481 xi->curr_mac_addr[3], xi->curr_mac_addr[4], xi->curr_mac_addr[5]);
james@1007 482
james@1017 483 if (xi->device_state != DEVICE_STATE_INACTIVE) {
james@1017 484 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "state", XenbusStateConnected);
james@1007 485
james@1017 486 for (i = 0; i <= 5 && xi->backend_state != XenbusStateConnected; i++) {
james@1017 487 FUNCTION_MSG("Waiting for XenbusStateConnected\n");
james@1017 488 KeWaitForSingleObject(&xi->backend_event, Executive, KernelMode, FALSE, NULL);
james@1017 489 }
james@1017 490 if (xi->backend_state != XenbusStateConnected) {
james@1017 491 FUNCTION_MSG("Backend state timeout\n");
james@1017 492 return STATUS_UNSUCCESSFUL;
james@1017 493 }
james@1017 494 XenNet_TxInit(xi);
james@1017 495 XenNet_RxInit(xi);
james@1007 496 }
james@1007 497
james@1007 498 /* we don't set device_state = DEVICE_STATE_ACTIVE here - has to be done during init once ndis is ready */
james@1007 499
james@1007 500 return STATUS_SUCCESS;
james@1007 501 }
james@1007 502
james@1007 503 NTSTATUS
james@1007 504 XenNet_Disconnect(PVOID context, BOOLEAN suspend) {
james@1007 505 struct xennet_info *xi = (struct xennet_info *)context;
james@1007 506 //PFN_NUMBER pfn;
james@1007 507 NTSTATUS status;
james@1007 508
james@1017 509 if (xi->device_state != DEVICE_STATE_ACTIVE && xi->device_state != DEVICE_STATE_INACTIVE) {
james@1017 510 FUNCTION_MSG("state not DEVICE_STATE_(IN)ACTIVE, is %d instead\n", xi->device_state);
james@1007 511 FUNCTION_EXIT();
james@1007 512 return STATUS_SUCCESS;
james@1007 513 }
james@1017 514 if (xi->device_state != DEVICE_STATE_INACTIVE) {
james@1017 515 xi->device_state = DEVICE_STATE_DISCONNECTING;
james@1017 516 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "state", XenbusStateClosing);
james@1017 517 while (xi->backend_state != XenbusStateClosing && xi->backend_state != XenbusStateClosed) {
james@1017 518 FUNCTION_MSG("Waiting for XenbusStateClosing/Closed\n");
james@1017 519 KeWaitForSingleObject(&xi->backend_event, Executive, KernelMode, FALSE, NULL);
james@1017 520 }
james@1017 521 status = XnWriteInt32(xi->handle, XN_BASE_FRONTEND, "state", XenbusStateClosed);
james@1017 522 while (xi->backend_state != XenbusStateClosed) {
james@1017 523 FUNCTION_MSG("Waiting for XenbusStateClosed\n");
james@1017 524 KeWaitForSingleObject(&xi->backend_event, Executive, KernelMode, FALSE, NULL);
james@1017 525 }
james@1017 526 XnUnbindEvent(xi->handle, xi->event_channel);
james@1017 527
james@1017 528 #if NTDDI_VERSION < WINXP
james@1017 529 KeFlushQueuedDpcs();
james@1017 530 #endif
james@1017 531 XenNet_TxShutdown(xi);
james@1017 532 XenNet_RxShutdown(xi);
james@1017 533 XnEndAccess(xi->handle, xi->rx_sring_gref, FALSE, XENNET_POOL_TAG);
james@1017 534 ExFreePoolWithTag(xi->rx_sring, XENNET_POOL_TAG);
james@1017 535 XnEndAccess(xi->handle, xi->tx_sring_gref, FALSE, XENNET_POOL_TAG);
james@1017 536 ExFreePoolWithTag(xi->tx_sring, XENNET_POOL_TAG);
james@1007 537 }
james@1007 538 if (!suspend) {
james@1007 539 XnCloseDevice(xi->handle);
james@1007 540 }
james@1007 541 xi->device_state = DEVICE_STATE_DISCONNECTED;
james@1007 542 return STATUS_SUCCESS;
james@1011 543 }
james@1007 544
james@1007 545 VOID
james@1007 546 XenNet_DeviceCallback(PVOID context, ULONG callback_type, PVOID value) {
james@1007 547 struct xennet_info *xi = (struct xennet_info *)context;
james@1007 548 ULONG state;
james@1007 549
james@1007 550 FUNCTION_ENTER();
james@1007 551 switch (callback_type) {
james@1007 552 case XN_DEVICE_CALLBACK_BACKEND_STATE:
james@1007 553 state = (ULONG)(ULONG_PTR)value;
james@1007 554 if (state == xi->backend_state) {
james@1007 555 FUNCTION_MSG("same state %d\n", state);
james@1007 556 FUNCTION_EXIT();
james@1007 557 }
james@1007 558 FUNCTION_MSG("XenBusState = %d -> %d\n", xi->backend_state, state);
james@1007 559 xi->backend_state = state;
james@1007 560 KeSetEvent(&xi->backend_event, 0, FALSE);
james@1007 561 break;
james@1007 562 case XN_DEVICE_CALLBACK_SUSPEND:
james@1007 563 FUNCTION_MSG("XN_DEVICE_CALLBACK_SUSPEND");
james@1007 564 XenNet_Disconnect(xi, TRUE);
james@1007 565 break;
james@1007 566 case XN_DEVICE_CALLBACK_RESUME:
james@1007 567 FUNCTION_MSG("XN_DEVICE_CALLBACK_RESUME");
james@1017 568 xi->device_state = DEVICE_STATE_INITIALISING;
james@1007 569 XenNet_Connect(xi, TRUE);
james@1017 570 if (xi->device_state != DEVICE_STATE_INACTIVE) {
james@1017 571 xi->device_state = DEVICE_STATE_ACTIVE;
james@1017 572 }
james@1007 573 KeInsertQueueDpc(&xi->rxtx_dpc, NULL, NULL);
james@1007 574 break;
james@1007 575 }
james@1007 576 FUNCTION_EXIT();
james@1007 577 }
james@1007 578