Browse Source

[hyperv] Cope with Windows Server 2016 enlightenments

An "enlightened" external bootloader (such as Windows Server 2016's
winload.exe) may take ownership of the Hyper-V connection before all
INT 13 operations have been completed.  When this happens, all VMBus
devices are implicitly closed and we are left with a non-functional
network connection.

Detect when our Hyper-V connection has been lost (by checking the
SynIC message page MSR).  Reclaim ownership of the Hyper-V connection
and reestablish any VMBus devices, without disrupting any existing
iPXE state (such as IPv4 settings attached to the network device).

Windows Server 2016 will not cleanly take ownership of an active
Hyper-V connection.  Experimentation shows that we can quiesce by
resetting only the SynIC message page MSR; this results in a
successful SAN boot (on a Windows 2012 R2 physical host).  Choose to
quiesce by resetting (almost) all MSRs, in the hope that this will be
more robust against corner cases such as a stray synthetic interrupt
occurring during the handover.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 7 years ago
parent
commit
b91cc983da

+ 125
- 10
src/arch/x86/drivers/hyperv/hyperv.c View File

@@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
40 40
 #include <ipxe/malloc.h>
41 41
 #include <ipxe/device.h>
42 42
 #include <ipxe/timer.h>
43
+#include <ipxe/quiesce.h>
43 44
 #include <ipxe/cpuid.h>
44 45
 #include <ipxe/msr.h>
45 46
 #include <ipxe/hyperv.h>
@@ -299,6 +300,10 @@ static void hv_map_synic ( struct hv_hypervisor *hv ) {
299 300
 	uint64_t siefp;
300 301
 	uint64_t scontrol;
301 302
 
303
+	/* Zero SynIC message and event pages */
304
+	memset ( hv->synic.message, 0, PAGE_SIZE );
305
+	memset ( hv->synic.event, 0, PAGE_SIZE );
306
+
302 307
 	/* Map SynIC message page */
303 308
 	simp = rdmsr ( HV_X64_MSR_SIMP );
304 309
 	simp &= ( PAGE_SIZE - 1 );
@@ -321,21 +326,14 @@ static void hv_map_synic ( struct hv_hypervisor *hv ) {
321 326
 }
322 327
 
323 328
 /**
324
- * Unmap synthetic interrupt controller
329
+ * Unmap synthetic interrupt controller, leaving SCONTROL untouched
325 330
  *
326 331
  * @v hv		Hyper-V hypervisor
327 332
  */
328
-static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
329
-	uint64_t scontrol;
333
+static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor *hv ) {
330 334
 	uint64_t siefp;
331 335
 	uint64_t simp;
332 336
 
333
-	/* Disable SynIC */
334
-	scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
335
-	scontrol &= ~HV_SCONTROL_ENABLE;
336
-	DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
337
-	wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
338
-
339 337
 	/* Unmap SynIC event page */
340 338
 	siefp = rdmsr ( HV_X64_MSR_SIEFP );
341 339
 	siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
@@ -349,6 +347,24 @@ static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
349 347
 	wrmsr ( HV_X64_MSR_SIMP, simp );
350 348
 }
351 349
 
350
+/**
351
+ * Unmap synthetic interrupt controller
352
+ *
353
+ * @v hv		Hyper-V hypervisor
354
+ */
355
+static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
356
+	uint64_t scontrol;
357
+
358
+	/* Disable SynIC */
359
+	scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
360
+	scontrol &= ~HV_SCONTROL_ENABLE;
361
+	DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
362
+	wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
363
+
364
+	/* Unmap SynIC event and message pages */
365
+	hv_unmap_synic_no_scontrol ( hv );
366
+}
367
+
352 368
 /**
353 369
  * Enable synthetic interrupt
354 370
  *
@@ -385,8 +401,12 @@ void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
385 401
 	unsigned long msr = HV_X64_MSR_SINT ( sintx );
386 402
 	uint64_t sint;
387 403
 
388
-	/* Disable synthetic interrupt */
404
+	/* Do nothing if interrupt is already disabled */
389 405
 	sint = rdmsr ( msr );
406
+	if ( sint & HV_SINT_MASKED )
407
+		return;
408
+
409
+	/* Disable synthetic interrupt */
390 410
 	sint &= ~HV_SINT_AUTO_EOI;
391 411
 	sint |= HV_SINT_MASKED;
392 412
 	DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
@@ -589,6 +609,7 @@ static void hv_remove ( struct root_device *rootdev ) {
589 609
 	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
590 610
 			NULL );
591 611
 	free ( hv );
612
+	rootdev_set_drvdata ( rootdev, NULL );
592 613
 }
593 614
 
594 615
 /** Hyper-V root device driver */
@@ -603,6 +624,100 @@ struct root_device hv_root_device __root_device = {
603 624
 	.driver = &hv_root_driver,
604 625
 };
605 626
 
627
+/**
628
+ * Quiesce system
629
+ *
630
+ */
631
+static void hv_quiesce ( void ) {
632
+	struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
633
+	unsigned int i;
634
+
635
+	/* Do nothing if we are not running in Hyper-V */
636
+	if ( ! hv )
637
+		return;
638
+
639
+	/* The "enlightened" portions of the Windows Server 2016 boot
640
+	 * process will not cleanly take ownership of an active
641
+	 * Hyper-V connection.  Experimentation shows that the minimum
642
+	 * requirement is that we disable the SynIC message page
643
+	 * (i.e. zero the SIMP MSR).
644
+	 *
645
+	 * We cannot perform a full shutdown of the Hyper-V
646
+	 * connection.  Experimentation shows that if we disable the
647
+	 * SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016
648
+	 * will enter an indefinite wait loop.
649
+	 *
650
+	 * Attempt to create a safe handover environment by resetting
651
+	 * all MSRs except for SCONTROL.
652
+	 *
653
+	 * Note that we do not shut down our VMBus devices, since we
654
+	 * may need to unquiesce the system and continue operation.
655
+	 */
656
+
657
+	/* Disable all synthetic interrupts */
658
+	for ( i = 0 ; i <= HV_SINT_MAX ; i++ )
659
+		hv_disable_sint ( hv, i );
660
+
661
+	/* Unmap synthetic interrupt controller, leaving SCONTROL
662
+	 * enabled (see above).
663
+	 */
664
+	hv_unmap_synic_no_scontrol ( hv );
665
+
666
+	/* Unmap hypercall page */
667
+	hv_unmap_hypercall ( hv );
668
+
669
+	DBGC ( hv, "HV %p quiesced\n", hv );
670
+}
671
+
672
+/**
673
+ * Unquiesce system
674
+ *
675
+ */
676
+static void hv_unquiesce ( void ) {
677
+	struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
678
+	uint64_t simp;
679
+	int rc;
680
+
681
+	/* Do nothing if we are not running in Hyper-V */
682
+	if ( ! hv )
683
+		return;
684
+
685
+	/* Experimentation shows that the "enlightened" portions of
686
+	 * Windows Server 2016 will break our Hyper-V connection at
687
+	 * some point during a SAN boot.  Surprisingly it does not
688
+	 * change the guest OS ID MSR, but it does leave the SynIC
689
+	 * message page disabled.
690
+	 *
691
+	 * Our own explicit quiescing procedure will also disable the
692
+	 * SynIC message page.  We can therefore use the SynIC message
693
+	 * page enable bit as a heuristic to determine when we need to
694
+	 * reestablish our Hyper-V connection.
695
+	 */
696
+	simp = rdmsr ( HV_X64_MSR_SIMP );
697
+	if ( simp & HV_SIMP_ENABLE )
698
+		return;
699
+
700
+	/* Remap hypercall page */
701
+	hv_map_hypercall ( hv );
702
+
703
+	/* Remap synthetic interrupt controller */
704
+	hv_map_synic ( hv );
705
+
706
+	/* Reset Hyper-V devices */
707
+	if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) {
708
+		DBGC ( hv, "HV %p could not unquiesce: %s\n",
709
+		       hv, strerror ( rc ) );
710
+		/* Nothing we can do */
711
+		return;
712
+	}
713
+}
714
+
715
+/** Hyper-V quiescer */
716
+struct quiescer hv_quiescer __quiescer = {
717
+	.quiesce = hv_quiesce,
718
+	.unquiesce = hv_unquiesce,
719
+};
720
+
606 721
 /**
607 722
  * Probe timer
608 723
  *

+ 47
- 0
src/drivers/net/netvsc.c View File

@@ -259,6 +259,15 @@ static int netvsc_revoke_buffer ( struct netvsc_device *netvsc,
259 259
 	struct netvsc_revoke_buffer_message msg;
260 260
 	int rc;
261 261
 
262
+	/* If the buffer's GPADL is obsolete (i.e. was created before
263
+	 * the most recent Hyper-V reset), then we will never receive
264
+	 * a response to the revoke message.  Since the GPADL is
265
+	 * already destroyed as far as the hypervisor is concerned, no
266
+	 * further action is required.
267
+	 */
268
+	if ( netvsc_is_obsolete ( netvsc ) )
269
+		return 0;
270
+
262 271
 	/* Construct message */
263 272
 	memset ( &msg, 0, sizeof ( msg ) );
264 273
 	msg.header.type = cpu_to_le32 ( buffer->revoke_type );
@@ -474,6 +483,14 @@ static int netvsc_transmit ( struct rndis_device *rndis,
474 483
 	uint64_t xid;
475 484
 	int rc;
476 485
 
486
+	/* If the device is obsolete (i.e. was opened before the most
487
+	 * recent Hyper-V reset), then we will never receive transmit
488
+	 * completions.  Fail transmissions immediately to minimise
489
+	 * the delay in closing and reopening the device.
490
+	 */
491
+	if ( netvsc_is_obsolete ( netvsc ) )
492
+		return -EPIPE;
493
+
477 494
 	/* Sanity check */
478 495
 	assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
479 496
 	assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
@@ -823,6 +840,35 @@ static int netvsc_probe ( struct vmbus_device *vmdev ) {
823 840
 	return rc;
824 841
 }
825 842
 
843
+/**
844
+ * Reset device
845
+ *
846
+ * @v vmdev		VMBus device
847
+ * @ret rc		Return status code
848
+ */
849
+static int netvsc_reset ( struct vmbus_device *vmdev ) {
850
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
851
+	struct netvsc_device *netvsc = rndis->priv;
852
+	struct net_device *netdev = rndis->netdev;
853
+	int rc;
854
+
855
+	/* A closed device holds no NetVSC (or RNDIS) state, so there
856
+	 * is nothing to reset.
857
+	 */
858
+	if ( ! netdev_is_open ( netdev ) )
859
+		return 0;
860
+
861
+	/* Close and reopen device to reset any stale state */
862
+	netdev_close ( netdev );
863
+	if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
864
+		DBGC ( netvsc, "NETVSC %s could not reopen: %s\n",
865
+		       netvsc->name, strerror ( rc ) );
866
+		return rc;
867
+	}
868
+
869
+	return 0;
870
+}
871
+
826 872
 /**
827 873
  * Remove device
828 874
  *
@@ -844,5 +890,6 @@ struct vmbus_driver netvsc_driver __vmbus_driver = {
844 890
 	.type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
845 891
 			     0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
846 892
 	.probe = netvsc_probe,
893
+	.reset = netvsc_reset,
847 894
 	.remove = netvsc_remove,
848 895
 };

+ 15
- 0
src/drivers/net/netvsc.h View File

@@ -362,4 +362,19 @@ struct netvsc_device {
362 362
 	int wait_rc;
363 363
 };
364 364
 
365
+/**
366
+ * Check if NetVSC device is obsolete
367
+ *
368
+ * @v netvsc		NetVSC device
369
+ * @v is_obsolete	NetVSC device is obsolete
370
+ *
371
+ * Check if NetVSC device is obsolete (i.e. was opened before the most
372
+ * recent Hyper-V reset).
373
+ */
374
+static inline __attribute__ (( always_inline )) int
375
+netvsc_is_obsolete ( struct netvsc_device *netvsc ) {
376
+
377
+	return vmbus_gpadl_is_obsolete ( netvsc->rx.gpadl );
378
+}
379
+
365 380
 #endif /* _NETVSC_H */

+ 3
- 0
src/include/ipxe/hyperv.h View File

@@ -61,6 +61,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
61 61
 /** Synthetic interrupt vector mask */
62 62
 #define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff )
63 63
 
64
+/** Maximum synthetic interrupt number */
65
+#define HV_SINT_MAX 15
66
+
64 67
 /** Post message */
65 68
 #define HV_POST_MESSAGE 0x005c
66 69
 

+ 26
- 0
src/include/ipxe/vmbus.h View File

@@ -479,6 +479,8 @@ struct vmbus_device {
479 479
 	/** Hyper-V hypervisor */
480 480
 	struct hv_hypervisor *hv;
481 481
 
482
+	/** Channel instance */
483
+	union uuid instance;
482 484
 	/** Channel ID */
483 485
 	unsigned int channel;
484 486
 	/** Monitor ID */
@@ -527,6 +529,12 @@ struct vmbus_driver {
527 529
 	 * @ret rc		Return status code
528 530
 	 */
529 531
 	int ( * probe ) ( struct vmbus_device *vmdev );
532
+	/** Reset device
533
+	 *
534
+	 * @v vmdev		VMBus device
535
+	 * @ret rc		Return status code
536
+	 */
537
+	int ( * reset ) ( struct vmbus_device *vmdev );
530 538
 	/** Remove device
531 539
 	 *
532 540
 	 * @v vmdev		VMBus device
@@ -609,6 +617,23 @@ vmbus_unregister_pages ( struct vmbus_device *vmdev,
609 617
 	list_del ( &pages->list );
610 618
 }
611 619
 
620
+extern unsigned int vmbus_obsolete_gpadl;
621
+
622
+/**
623
+ * Check if GPADL is obsolete
624
+ *
625
+ * @v gpadl		GPADL ID
626
+ * @v is_obsolete	GPADL ID is obsolete
627
+ *
628
+ * Check if GPADL is obsolete (i.e. was created before the most recent
629
+ * Hyper-V reset).
630
+ */
631
+static inline __attribute__ (( always_inline )) int
632
+vmbus_gpadl_is_obsolete ( unsigned int gpadl ) {
633
+
634
+	return ( gpadl <= vmbus_obsolete_gpadl );
635
+}
636
+
612 637
 extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
613 638
 				   size_t len );
614 639
 extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev,
@@ -629,6 +654,7 @@ extern int vmbus_poll ( struct vmbus_device *vmdev );
629 654
 extern void vmbus_dump_channel ( struct vmbus_device *vmdev );
630 655
 
631 656
 extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent );
657
+extern int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent );
632 658
 extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent );
633 659
 
634 660
 #endif /* _IPXE_VMBUS_H */

+ 129
- 4
src/interface/hyperv/vmbus.c View File

@@ -50,6 +50,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
50 50
  */
51 51
 #define VMBUS_GPADL_MAGIC 0x18ae0000
52 52
 
53
+/** Current (i.e. most recently issued) GPADL ID */
54
+static unsigned int vmbus_gpadl = VMBUS_GPADL_MAGIC;
55
+
56
+/** Obsolete GPADL ID threshold
57
+ *
58
+ * When the Hyper-V connection is reset, any previous GPADLs are
59
+ * automatically rendered obsolete.
60
+ */
61
+unsigned int vmbus_obsolete_gpadl;
62
+
53 63
 /**
54 64
  * Post message
55 65
  *
@@ -281,12 +291,12 @@ int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
281 291
 		uint64_t pfn[pfn_count];
282 292
 	} __attribute__ (( packed )) gpadlhdr;
283 293
 	const struct vmbus_gpadl_created *created = &vmbus->message->created;
284
-	static unsigned int gpadl = VMBUS_GPADL_MAGIC;
294
+	unsigned int gpadl;
285 295
 	unsigned int i;
286 296
 	int rc;
287 297
 
288 298
 	/* Allocate GPADL ID */
289
-	gpadl++;
299
+	gpadl = ++vmbus_gpadl;
290 300
 
291 301
 	/* Construct message */
292 302
 	memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) );
@@ -347,6 +357,15 @@ int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) {
347 357
 	const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown;
348 358
 	int rc;
349 359
 
360
+	/* If GPADL is obsolete (i.e. was created before the most
361
+	 * recent Hyper-V reset), then we will never receive a
362
+	 * response to the teardown message.  Since the GPADL is
363
+	 * already destroyed as far as the hypervisor is concerned, no
364
+	 * further action is required.
365
+	 */
366
+	if ( vmbus_gpadl_is_obsolete ( gpadl ) )
367
+		return 0;
368
+
350 369
 	/* Construct message */
351 370
 	memset ( &teardown, 0, sizeof ( teardown ) );
352 371
 	teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN );
@@ -530,8 +549,7 @@ void vmbus_close ( struct vmbus_device *vmdev ) {
530 549
 	}
531 550
 
532 551
 	/* Tear down GPADL */
533
-	if ( ( rc = vmbus_gpadl_teardown ( vmdev,
534
-					   vmdev->gpadl ) ) != 0 ) {
552
+	if ( ( rc = vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ) ) != 0 ) {
535 553
 		DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: "
536 554
 		       "%s\n", vmdev->dev.name, strerror ( rc ) );
537 555
 		/* We can't prevent the remote VM from continuing to
@@ -1187,6 +1205,8 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv,
1187 1205
 					&parent->children );
1188 1206
 			vmdev->dev.parent = parent;
1189 1207
 			vmdev->hv = hv;
1208
+			memcpy ( &vmdev->instance, &offer->instance,
1209
+				 sizeof ( vmdev->instance ) );
1190 1210
 			vmdev->channel = channel;
1191 1211
 			vmdev->monitor = offer->monitor;
1192 1212
 			vmdev->signal = ( offer->monitored ?
@@ -1201,6 +1221,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv,
1201 1221
 		} else if ( header->type ==
1202 1222
 			    cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
1203 1223
 
1224
+			/* End of offer list */
1204 1225
 			break;
1205 1226
 
1206 1227
 		} else {
@@ -1244,6 +1265,77 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv,
1244 1265
 	return rc;
1245 1266
 }
1246 1267
 
1268
+
1269
+/**
1270
+ * Reset channels
1271
+ *
1272
+ * @v hv		Hyper-V hypervisor
1273
+ * @v parent		Parent device
1274
+ * @ret rc		Return status code
1275
+ */
1276
+static int vmbus_reset_channels ( struct hv_hypervisor *hv,
1277
+				  struct device *parent ) {
1278
+	struct vmbus *vmbus = hv->vmbus;
1279
+	const struct vmbus_message_header *header = &vmbus->message->header;
1280
+	const struct vmbus_offer_channel *offer = &vmbus->message->offer;
1281
+	const union uuid *type;
1282
+	struct vmbus_device *vmdev;
1283
+	unsigned int channel;
1284
+	int rc;
1285
+
1286
+	/* Post message */
1287
+	if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0)
1288
+		return rc;
1289
+
1290
+	/* Collect responses */
1291
+	while ( 1 ) {
1292
+
1293
+		/* Wait for response */
1294
+		if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 )
1295
+			return rc;
1296
+
1297
+		/* Handle response */
1298
+		if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) {
1299
+
1300
+			/* Parse offer */
1301
+			type = &offer->type;
1302
+			channel = le32_to_cpu ( offer->channel );
1303
+			DBGC2 ( vmbus, "VMBUS %p offer %d type %s",
1304
+				vmbus, channel, uuid_ntoa ( type ) );
1305
+			if ( offer->monitored )
1306
+				DBGC2 ( vmbus, " monitor %d", offer->monitor );
1307
+			DBGC2 ( vmbus, "\n" );
1308
+
1309
+			/* Do nothing with the offer; we already have all
1310
+			 * of the relevant state from the initial probe.
1311
+			 */
1312
+
1313
+		} else if ( header->type ==
1314
+			    cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
1315
+
1316
+			/* End of offer list */
1317
+			break;
1318
+
1319
+		} else {
1320
+			DBGC ( vmbus, "VMBUS %p unexpected offer response type "
1321
+			       "%d\n", vmbus, le32_to_cpu ( header->type ) );
1322
+			return -EPROTO;
1323
+		}
1324
+	}
1325
+
1326
+	/* Reset all devices */
1327
+	list_for_each_entry ( vmdev, &parent->children, dev.siblings ) {
1328
+		if ( ( rc = vmdev->driver->reset ( vmdev ) ) != 0 ) {
1329
+			DBGC ( vmdev, "VMBUS %s could not reset: %s\n",
1330
+			       vmdev->dev.name, strerror ( rc ) );
1331
+			/* Continue attempting to reset other devices */
1332
+			continue;
1333
+		}
1334
+	}
1335
+
1336
+	return 0;
1337
+}
1338
+
1247 1339
 /**
1248 1340
  * Remove channels
1249 1341
  *
@@ -1330,6 +1422,39 @@ int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) {
1330 1422
 	return rc;
1331 1423
 }
1332 1424
 
1425
+/**
1426
+ * Reset Hyper-V virtual machine bus
1427
+ *
1428
+ * @v hv		Hyper-V hypervisor
1429
+ * @v parent		Parent device
1430
+ * @ret rc		Return status code
1431
+ */
1432
+int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ) {
1433
+	struct vmbus *vmbus = hv->vmbus;
1434
+	int rc;
1435
+
1436
+	/* Mark all existent GPADLs as obsolete */
1437
+	vmbus_obsolete_gpadl = vmbus_gpadl;
1438
+
1439
+	/* Clear interrupt and monitor pages */
1440
+	memset ( vmbus->intr, 0, PAGE_SIZE );
1441
+	memset ( vmbus->monitor_in, 0, PAGE_SIZE );
1442
+	memset ( vmbus->monitor_out, 0, PAGE_SIZE );
1443
+
1444
+	/* Enable message interrupt */
1445
+	hv_enable_sint ( hv, VMBUS_MESSAGE_SINT );
1446
+
1447
+	/* Renegotiate protocol version */
1448
+	if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 )
1449
+		return rc;
1450
+
1451
+	/* Reenumerate channels */
1452
+	if ( ( rc = vmbus_reset_channels ( hv, parent ) ) != 0 )
1453
+		return rc;
1454
+
1455
+	return 0;
1456
+}
1457
+
1333 1458
 /**
1334 1459
  * Remove Hyper-V virtual machine bus
1335 1460
  *

Loading…
Cancel
Save