From 0926649bece8e06dfdb19e4f819795da6ee364dd Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@gmail.com>
Date: Tue, 5 Jan 2010 08:05:32 +0000
Subject: [PATCH 1/2] [sanboot] Prevent leaking a stack reference for "keep-san" AoE
When the "keep-san" option is used, the function is exited without
unregistering the stack allocated int13h drive. To prevent a dangling
pointer to the stack, these structs should be heap allocated.
---
src/arch/i386/interface/pcbios/aoeboot.c | 54 ++++++++++++++++++-----------
1 files changed, 33 insertions(+), 21 deletions(-)
@@ -1,11 +1,11 @@
#include <stdint.h>
#include <string.h>
+#include <stdlib.h>
#include <stdio.h>
-#include <byteswap.h>
+#include <errno.h>
#include <gpxe/aoe.h>
#include <gpxe/ata.h>
#include <gpxe/netdevice.h>
-#include <gpxe/settings.h>
#include <gpxe/sanboot.h>
#include <gpxe/abft.h>
#include <int13.h>
@@ -13,50 +13,62 @@
FILE_LICENCE ( GPL2_OR_LATER );
static int aoeboot ( const char *root_path ) {
- struct ata_device ata;
- struct int13_drive drive;
+ struct ata_device *ata;
+ struct int13_drive *drive;
int rc;
- memset ( &ata, 0, sizeof ( ata ) );
- memset ( &drive, 0, sizeof ( drive ) );
+ ata = zalloc ( sizeof ( *ata ) );
+ if ( ! ata ) {
+ rc = -ENOMEM;
+ goto err_alloc_ata;
+ }
+ drive = zalloc ( sizeof ( *drive ) );
+ if ( ! drive ) {
+ rc = -ENOMEM;
+ goto err_alloc_drive;
+ }
/* FIXME: ugly, ugly hack */
struct net_device *netdev = last_opened_netdev();
- if ( ( rc = aoe_attach ( &ata, netdev, root_path ) ) != 0 ) {
+ if ( ( rc = aoe_attach ( ata, netdev, root_path ) ) != 0 ) {
printf ( "Could not attach AoE device: %s\n",
strerror ( rc ) );
- goto error_attach;
+ goto err_attach;
}
- if ( ( rc = init_atadev ( &ata ) ) != 0 ) {
+ if ( ( rc = init_atadev ( ata ) ) != 0 ) {
printf ( "Could not initialise AoE device: %s\n",
strerror ( rc ) );
- goto error_init;
+ goto err_init;
}
/* FIXME: ugly, ugly hack */
struct aoe_session *aoe =
- container_of ( ata.backend, struct aoe_session, refcnt );
+ container_of ( ata->backend, struct aoe_session, refcnt );
abft_fill_data ( aoe );
- drive.blockdev = &ata.blockdev;
+ drive->blockdev = &ata->blockdev;
- register_int13_drive ( &drive );
- printf ( "Registered as BIOS drive %#02x\n", drive.drive );
- printf ( "Booting from BIOS drive %#02x\n", drive.drive );
- rc = int13_boot ( drive.drive );
+ register_int13_drive ( drive );
+ printf ( "Registered as BIOS drive %#02x\n", drive->drive );
+ printf ( "Booting from BIOS drive %#02x\n", drive->drive );
+ rc = int13_boot ( drive->drive );
printf ( "Boot failed\n" );
/* Leave drive registered, if instructed to do so */
if ( keep_san() )
return rc;
- printf ( "Unregistering BIOS drive %#02x\n", drive.drive );
- unregister_int13_drive ( &drive );
+ printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
+ unregister_int13_drive ( drive );
- error_init:
- aoe_detach ( &ata );
- error_attach:
+ err_init:
+ aoe_detach ( ata );
+ err_attach:
+ free ( drive );
+ err_alloc_drive:
+ free ( ata );
+ err_alloc_ata:
return rc;
}
--
1.6.5
From 3dbc385d29cbdff520ed0694934c01a6808ff1dc Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@gmail.com>
Date: Tue, 5 Jan 2010 17:39:18 +0000
Subject: [PATCH 2/2] [virtio] Add virtio block device sanboot support
This patch adds virtio block device support alongside the existing
iSCSI, ATA-over-Ethernet, and ramdisk block devices. A gPXE option ROM
can boot a QEMU/KVM virtual machine directly from a virtio block device:
# Ensure SANBOOT_PROTO_VIRTIO_BLK is defined in config/defaults.h
make bin/1af41001.rom
The sanboot gPXE command is used with the virtio_blk: root path scheme.
The virtio block device instance is identified by its PCI bus, device,
and function (there could be multiple virtio block devices).
sanboot virtio_blk:PCI00:04.0
Successfully boots Debian testing i386 and FreeDOS 0.84-pre2 under QEMU.
---
src/arch/i386/interface/pcbios/virtio_blkboot.c | 70 ++++++
src/config/config.c | 3 +
src/config/general.h | 1 +
src/drivers/block/virtio-blk.c | 271 +++++++++++++++++++++++
src/drivers/block/virtio-blk.h | 30 +++
src/include/gpxe/errfile.h | 2 +
src/include/gpxe/virtblk.h | 37 +++
7 files changed, 414 insertions(+), 0 deletions(-)
create mode 100644 src/arch/i386/interface/pcbios/virtio_blkboot.c
create mode 100644 src/drivers/block/virtio-blk.c
create mode 100644 src/drivers/block/virtio-blk.h
create mode 100644 src/include/gpxe/virtblk.h
new file mode 100644
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/sanboot.h>
+#include <gpxe/virtblk.h>
+#include <int13.h>
+
+/**
+ * @file
+ *
+ * Virtio block device boot
+ *
+ */
+
+static int virtio_blkboot ( const char *root_path ) {
+ struct int13_drive *drive;
+ struct virtblk *virtblk;
+ int rc;
+
+ virtblk = find_virtblk ( root_path + sizeof ( "virtio_blk:" ) - 1 );
+ if ( ! virtblk )
+ return -ENOENT;
+
+ drive = zalloc ( sizeof ( *drive ) );
+ if ( ! drive )
+ return -ENOMEM;
+
+ drive->blockdev = &virtblk->blockdev;
+
+ register_int13_drive ( drive );
+ printf ( "Registered as BIOS drive %#02x\n", drive->drive );
+ printf ( "Booting from BIOS drive %#02x\n", drive->drive );
+ rc = int13_boot ( drive->drive );
+ printf ( "Boot failed\n" );
+
+ /* Leave drive registered, if instructed to do so */
+ if ( keep_san() )
+ return rc;
+
+ printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
+ unregister_int13_drive ( drive );
+ free ( drive );
+ return rc;
+}
+
+struct sanboot_protocol virtblk_sanboot_protocol __sanboot_protocol = {
+ .prefix = "virtio_blk:",
+ .boot = virtio_blkboot,
+};
@@ -130,6 +130,9 @@ REQUIRE_OBJECT ( aoeboot );
#ifdef SANBOOT_PROTO_IB_SRP
REQUIRE_OBJECT ( ib_srpboot );
#endif
+#ifdef SANBOOT_PROTO_VIRTIO_BLK
+REQUIRE_OBJECT ( virtio_blkboot );
+#endif
/*
* Drag in all requested resolvers
@@ -63,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
//#undef SANBOOT_PROTO_ISCSI /* iSCSI protocol */
//#undef SANBOOT_PROTO_AOE /* AoE protocol */
//#undef SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */
+//#undef SANBOOT_PROTO_VIRTIO_BLK /* Virtio block device */
/*
* Name resolution modules
new file mode 100644
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <stdlib.h>
+#include <gpxe/process.h>
+#include <gpxe/virtblk.h>
+#include "virtio-blk.h"
+
+/**
+ * @file
+ *
+ * Virtio block devices
+ *
+ */
+
+/** List of virtio block devices */
+static LIST_HEAD ( virtblk_devices );
+
+/**
+ * Register virtio block device
+ *
+ * @v virtblk Virtio block device
+ *
+ * Adds the virtio block device to the list of virtio block devices.
+ */
+static void register_virtblk ( struct virtblk *virtblk ) {
+ list_add_tail ( &virtblk->virtblk_devices, &virtblk_devices );
+}
+
+/**
+ * Unregister virtio block device
+ *
+ * @v virtblk Virtio block device
+ *
+ * Removes the virtio block device from the list of virtio block devices.
+ */
+static void unregister_virtblk ( struct virtblk *virtblk ) {
+ list_del ( &virtblk->virtblk_devices );
+}
+
+/**
+ * Find virtio block device by name
+ *
+ * @v device_name Device name
+ * @ret virtblk Virtio block device or NULL
+ *
+ * Searches the list of virtio block devices by name.
+ */
+struct virtblk *find_virtblk ( const char *device_name ) {
+ struct virtblk *virtblk;
+ DBG ( "find_virtblk \"%s\"\n", device_name );
+ list_for_each_entry ( virtblk, &virtblk_devices, virtblk_devices ) {
+ if ( ! strcmp ( device_name, virtblk->pdev->dev.name ) )
+ return virtblk;
+ }
+ return NULL;
+}
+
+/**
+ * Issue an I/O request and wait for completion
+ *
+ * @v virtblk Virtio block device
+ * @v type VIRTIO_BLK_T_IN or VIRTIO_BLK_T_OUT
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int virtblk_command ( struct virtblk *virtblk, u32 type,
+ uint64_t block, unsigned long count,
+ userptr_t buffer ) {
+ /*
+ * Virtio block requests have the following format:
+ *
+ * +--------------------------------+
+ * | struct virtio_blk_outhdr [IN] |
+ * +--------------------------------+
+ * | In/out buffer [IN/OUT] |
+ * +--------------------------------+
+ * | Status byte [OUT] |
+ * +--------------------------------+
+ *
+ * The device fills in the status byte to indicate the outcome the
+ * operation.
+ */
+ struct vring_virtqueue *vq = &virtblk->virtqueue;
+ struct virtio_blk_outhdr hdr = {
+ .type = type,
+ .ioprio = 0,
+ .sector = block,
+ };
+ uint8_t status = VIRTIO_BLK_S_UNSUPP;
+ struct vring_list list[] = {
+ {
+ .addr = ( char * ) &hdr,
+ .length = sizeof ( hdr ),
+ },
+ {
+ .addr = ( char * ) user_to_virt ( buffer, 0 ),
+ .length = virtblk->blockdev.blksize * count,
+ },
+ {
+ .addr = ( char * ) &status,
+ .length = sizeof ( status ),
+ },
+ };
+ unsigned int in, out;
+
+ DBG ( "VIRTBLK req 0x%02x LBA 0x%llx count 0x%lx\n",
+ type, block, count );
+
+ /* Number of elements readable and writable */
+ if ( type == VIRTIO_BLK_T_IN ) {
+ out = 1;
+ in = 2;
+ } else {
+ out = 2;
+ in = 1;
+ }
+
+ /* Add to virtqueue and kick host */
+ vring_add_buf ( vq, list, out, in, 0, 0 );
+ vring_kick ( virtblk->pdev->ioaddr, vq, 1 );
+
+ /* Wait for reply */
+ while ( ! vring_more_used ( vq ) ) {
+ mb();
+ step();
+ }
+
+ /* Reclaim virtqueue element */
+ vring_get_buf ( vq, NULL );
+ return status == VIRTIO_BLK_S_OK ? 0 : -EIO;
+}
+
+/**
+ * Read block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int virtblk_read ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct virtblk *virtblk =
+ container_of ( blockdev, struct virtblk, blockdev );
+ return virtblk_command ( virtblk, VIRTIO_BLK_T_IN, block,
+ count, buffer );
+}
+
+/**
+ * Write block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int virtblk_write ( struct block_device *blockdev __unused, uint64_t block __unused,
+ unsigned long count __unused, userptr_t buffer __unused ) {
+ struct virtblk *virtblk =
+ container_of ( blockdev, struct virtblk, blockdev );
+ return virtblk_command ( virtblk, VIRTIO_BLK_T_OUT, block,
+ count, buffer );
+}
+
+static struct block_device_operations virtblk_operations = {
+ .read = virtblk_read,
+ .write = virtblk_write,
+};
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int virtblk_probe ( struct pci_device *pci,
+ const struct pci_device_id *id __unused ) {
+ unsigned long ioaddr = pci->ioaddr;
+
+ /* Initialize driver state */
+ struct virtblk *virtblk = zalloc ( sizeof *virtblk );
+ if ( ! virtblk )
+ return -ENOMEM;
+ INIT_LIST_HEAD ( &virtblk->virtblk_devices );
+ virtblk->blockdev.op = &virtblk_operations;
+ virtblk->pdev = pci;
+ pci->priv = virtblk;
+
+ /* Prepare the device */
+ adjust_pci_device ( pci );
+ vp_reset ( ioaddr );
+
+ /* Indicate that the driver is starting */
+ vp_set_status ( ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+ VIRTIO_CONFIG_S_DRIVER );
+
+ /* Grab the I/O requests virtqueue */
+ if ( vp_find_vq ( ioaddr, 0, &virtblk->virtqueue ) < 0 ) {
+ free ( virtblk );
+ return -ENOENT;
+ }
+
+ /* Feature negotiation */
+ u32 features = vp_get_features ( ioaddr );
+ vp_set_features ( ioaddr, 0 ); /* no features required */
+
+ /* Find out the drive capacity */
+ virtblk->blockdev.blksize = 512; /* the default */
+ vp_get ( ioaddr, 0, &virtblk->blockdev.blocks,
+ sizeof virtblk->blockdev.blocks );
+
+ /* Indicate that the driver is ready */
+ vp_set_status ( ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+ VIRTIO_CONFIG_S_DRIVER |
+ VIRTIO_CONFIG_S_DRIVER_OK );
+
+ DBGC ( virtblk, "VIRTBLK %p virtio_blk:%s ioaddr=0x%lx irq=%d features=0x%x capacity=%lld\n",
+ virtblk, pci->dev.name, ioaddr, pci->irq,
+ features, virtblk->blockdev.blocks );
+
+ register_virtblk ( virtblk );
+ return 0;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void virtblk_remove ( struct pci_device *pci ) {
+ struct virtblk *virtblk = pci->priv;
+
+ unregister_virtblk ( virtblk );
+ vp_reset ( pci->ioaddr );
+ free ( virtblk );
+}
+
+static struct pci_device_id virtblk_ids[] = {
+ PCI_ROM(0x1af4, 0x1001, "virtio-blk", "Virtio Block Device", 0),
+};
+
+struct pci_driver virtblk_driver __pci_driver = {
+ .ids = virtblk_ids,
+ .id_count = ( sizeof ( virtblk_ids ) / sizeof ( virtblk_ids[0] ) ),
+ .probe = virtblk_probe,
+ .remove = virtblk_remove,
+};
new file mode 100644
@@ -0,0 +1,30 @@
+#ifndef _VIRTIO_BLK_H
+#define _VIRTIO_BLK_H
+/* This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers. */
+
+FILE_LICENCE ( BSD2 );
+
+/*
+ * Command types
+ */
+
+/* These two define direction. */
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+
+/* This is the first element of the read scatter-gather list. */
+struct virtio_blk_outhdr {
+ /* VIRTIO_BLK_T* */
+ u32 type;
+ /* io priority. */
+ u32 ioprio;
+ /* Sector (ie. 512 byte offset) */
+ u64 sector;
+};
+
+/* And this is the final byte of the write scatter-gather list. */
+#define VIRTIO_BLK_S_OK 0
+#define VIRTIO_BLK_S_IOERR 1
+#define VIRTIO_BLK_S_UNSUPP 2
+#endif /* _VIRTIO_BLK_H */
@@ -122,6 +122,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_linda ( ERRFILE_DRIVER | 0x00730000 )
#define ERRFILE_ata ( ERRFILE_DRIVER | 0x00740000 )
#define ERRFILE_srp ( ERRFILE_DRIVER | 0x00750000 )
+#define ERRFILE_virtio_blk ( ERRFILE_DRIVER | 0x00760000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )
@@ -191,6 +192,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_x509 ( ERRFILE_OTHER | 0x00160000 )
#define ERRFILE_login_ui ( ERRFILE_OTHER | 0x00170000 )
#define ERRFILE_ib_srpboot ( ERRFILE_OTHER | 0x00180000 )
+#define ERRFILE_virtio_blkboot ( ERRFILE_OTHER | 0x00190000 )
/** @} */
new file mode 100644
@@ -0,0 +1,37 @@
+#ifndef _GPXE_VIRTBLK_H
+#define _GPXE_VIRTBLK_H
+
+/** @file
+ *
+ * Virtio block device
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/list.h>
+#include <gpxe/pci.h>
+#include <gpxe/virtio-ring.h>
+#include <gpxe/virtio-pci.h>
+#include <gpxe/blockdev.h>
+
+/**
+ * Virtio block device
+ */
+struct virtblk {
+ /** List of virtio-blk devices */
+ struct list_head virtblk_devices;
+
+ /** Underlying PCI device */
+ struct pci_device *pdev;
+
+ /** Outgoing requests virtqueue */
+ struct vring_virtqueue virtqueue;
+
+ /** Block device */
+ struct block_device blockdev;
+};
+
+extern struct virtblk *find_virtblk ( const char *device_name );
+
+#endif /* _GPXE_VIRTBLK_H */
--
1.6.5
This patch adds virtio block device support alongside the existing iSCSI, ATA-over-Ethernet, and ramdisk block devices. The virtio block device provides storage in virtualized environments. Using this patch, a gPXE option ROM can boot a QEMU/KVM virtual machine directly from a virtio block device. Here is an example QEMU invocation: qemu -drive if=virtio,file=debian.qcow2 -option-rom gpxe/src/bin/1af41001.rom SANBOOT_PROTO_VIRTIO_BLK must be defined in config/general.h to enable this feature. The gPXE option ROM must be built for PCI ID 1af4:1001. The sanboot gPXE command is used with the virtio_blk: root path scheme. The virtio block device instance is identified by its PCI bus, device, and function (there could be multiple virtio block devices). gPXE> sanboot virtio_blk:PCI00:04.0 Perhaps the first available device should be chosen if virtio_blk: is given without PCI bus, device, and function. I am open to suggestions on how virtio block device option ROMs should work. I have successfully booted Debian testing i386 and FreeDOS 0.84-pre2 under QEMU 0.11.0. Note that QEMU/KVM can boot from a virtio block device using a separate non-virtio interface (-drive if=virtio,boot=on,[...]). This gPXE patch provides a native virtio block implementation. While developing this feature, I noticed that the AoE sanboot code leaves a pointer to a stack value around after it returns. A commit to fix this is included. Stefan Hajnoczi (2): [sanboot] Prevent leaking a stack reference for "keep-san" AoE [virtio] Add virtio block device sanboot support src/arch/i386/interface/pcbios/aoeboot.c | 54 +++-- src/arch/i386/interface/pcbios/virtio_blkboot.c | 70 ++++++ src/config/config.c | 3 + src/config/general.h | 1 + src/drivers/block/virtio-blk.c | 271 +++++++++++++++++++++++ src/drivers/block/virtio-blk.h | 30 +++ src/include/gpxe/errfile.h | 2 + src/include/gpxe/virtblk.h | 37 +++ 8 files changed, 447 insertions(+), 21 deletions(-) create mode 100644 src/arch/i386/interface/pcbios/virtio_blkboot.c create mode 100644 src/drivers/block/virtio-blk.c create mode 100644 src/drivers/block/virtio-blk.h create mode 100644 src/include/gpxe/virtblk.h From 0926649bece8e06dfdb19e4f819795da6ee364dd Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi <stefanha@gmail.com> Date: Tue, 5 Jan 2010 08:05:32 +0000 Subject: [PATCH 1/2] [sanboot] Prevent leaking a stack reference for "keep-san" AoE When the "keep-san" option is used, the function is exited without unregistering the stack allocated int13h drive. To prevent a dangling pointer to the stack, these structs should be heap allocated. --- src/arch/i386/interface/pcbios/aoeboot.c | 54 ++++++++++++++++++----------- 1 files changed, 33 insertions(+), 21 deletions(-)