[ipxe-devel] [PATCH 2/2] virtio-net: reset virtio NICs when booting under UEFI
Laszlo Ersek
lersek at redhat.com
Fri Apr 10 19:53:22 UTC 2015
In efi_init() [src/interface/efi/efi_init.c], iPXE registers the
efi_shutdown_hook() function as a callback for the
EVT_SIGNAL_EXIT_BOOT_SERVICES event. This event is emitted under UEFI when
the OS loader (including the Linux EFI stub) calls ExitBootServices().
Currently, that event results in the following call chain:
efi_shutdown_hook() [src/interface/efi/efi_init.c]
shutdown_boot() [src/include/ipxe/init.h]
shutdown(1) [src/core/init.c]
/* Call registered shutdown functions (in reverse order) */
forall startup_fn:
startup_fn->shutdown(1)
This infrastructure is fine. However, the virtio-net driver does not
register such a shutdown function at the moment.
Consequently, virtio-net devices remain configured (active) after
ExitBootServices(). If the runtime OS then overwrites the virtio ring area
for whatever purpose, before it loads its own virtio-net driver (which
usually starts by resetting the device), then QEMU tries to interpret
whatever garbage ends up in the ring as virtio communication, and kills
the guest with a message like:
qemu-system-x86_64: Guest moved used index from 14 to 62857
Add global startup and shutdown hooks for the virtio-net driver. When the
shutdown hook is called outside of the ExitBootServices() callback
context, it does nothing (because the list of open virtio-net NICs is
expected to be empty, due to earlier, controlled teardowns). Otherwise,
all open virtio-net NICs are reset, which causes QEMU to deconfigure (ie.
forget about) the virtio rings.
Cc: Michael Brown <mcb30 at ipxe.org>
Cc: Stefan Hajnoczi <stefanha at redhat.com>
Cc: Gerd Hoffmann <kraxel at redhat.com>
Cc: BALATON Zoltan <balaton at eik.bme.hu>
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
src/drivers/net/virtio-net.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c
index 533ccb0..63f2196 100644
--- a/src/drivers/net/virtio-net.c
+++ b/src/drivers/net/virtio-net.c
@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/ethernet.h>
#include <ipxe/virtio-ring.h>
#include <ipxe/virtio-pci.h>
+#include <ipxe/init.h>
#include "virtio-net.h"
/*
@@ -84,7 +85,18 @@ enum {
RX_BUF_SIZE = 1522,
};
+/*
+ * We must reset all virtio-net NICs when exiting. This is particularly
+ * important when running under UEFI, because then we don't get a controlled
+ * teardown sequence, just a callback on ExitBootServices(), and then we must
+ * not even free memory.
+ */
+static struct list_head open_nics;
+
struct virtnet_nic {
+ /** Entry in open_nics */
+ struct list_head list;
+
/** Base pio register address */
unsigned long ioaddr;
@@ -207,6 +219,9 @@ static int virtnet_open ( struct net_device *netdev ) {
features = vp_get_features ( ioaddr );
vp_set_features ( ioaddr, features & ( 1 << VIRTIO_NET_F_MAC ) );
vp_set_status ( ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK );
+
+ /* Add ourselves to the list of open virtnet NICs */
+ list_add ( &virtnet->list, &open_nics );
return 0;
}
@@ -231,6 +246,9 @@ static void virtnet_close ( struct net_device *netdev ) {
}
INIT_LIST_HEAD ( &virtnet->rx_iobufs );
virtnet->rx_num_iobufs = 0;
+
+ /* Remove ourselves from the list of open virtnet NICs */
+ list_del ( &virtnet->list );
}
/** Transmit packet
@@ -417,3 +435,27 @@ struct pci_driver virtnet_driver __pci_driver = {
.probe = virtnet_probe,
.remove = virtnet_remove,
};
+
+static void virtnet_startup ( void ) {
+ INIT_LIST_HEAD ( &open_nics );
+}
+
+static void virtnet_shutdown ( int booting ) {
+ struct virtnet_nic *virtnet;
+
+ if ( ! booting ) {
+ assert ( list_empty ( &open_nics ) );
+ return;
+ }
+
+ list_for_each_entry ( virtnet, &open_nics, list ) {
+ DBGC ( virtnet, "VIRTIO-NET %p ioaddr=%#lx "
+ "resetting for boot\n", virtnet, virtnet->ioaddr);
+ vp_reset ( virtnet->ioaddr );
+ }
+}
+
+struct startup_fn virtnet_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+ .startup = virtnet_startup,
+ .shutdown = virtnet_shutdown,
+};
--
1.8.3.1
More information about the ipxe-devel
mailing list