* [Qemu-devel] [RFC] gPXE fw_cfg file interface support
@ 2010-01-27 9:42 Stefan Hajnoczi
2010-01-27 10:15 ` Gerd Hoffmann
0 siblings, 1 reply; 4+ messages in thread
From: Stefan Hajnoczi @ 2010-01-27 9:42 UTC (permalink / raw)
To: gpxe; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1474 bytes --]
This patch makes it possible for gPXE under QEMU/KVM to fetch files from the
host using the fw_cfg file interface. This means gPXE in the guest can fetch an
exposed file from the host without using networking.
I believe this feature will be useful to management software so that
network boot
configuration can be managed alongside with the virtual machine configuration,
not on a remote TFTP server.
Any interest in this feature? I imagine libvirt could use this to set
up iSCSI or
other network boot settings.
Images are fetched from the hypervisor using the fw_cfg interface provided by
QEMU/KVM. The URI syntax is as follows:
gPXE> imgfetch fw_cfg:genroms/static.gpxe
where 'fw_cfg' is the new URI scheme this patch adds and 'genroms/static.gpxe'
is the filename exported from QEMU/KVM via the fw_cfg interface.
Note that the URI uses a single colon ':', not '://', since there is no
hostname.
Current QEMU/KVM builds can be (ab)used to expose arbitrary files
like this:
qemu -option-rom path/to/file [...]
The file will appear as 'genroms/file'. Use the 'fw_cfg:genroms/file' URI to
reference it in gPXE.
The fw_cfg file interface is a recent addition to QEMU/KVM. You may need to
build QEMU/KVM from source in order to get this feature.
Note that this patch only adds the fw_cfg file interface mechanism, it does not
automatically probe for a special file when gPXE starts. You will
need to manually
"chain fw_cfg:genroms/file".
Any comments?
Stefan
[-- Attachment #2: fw_cfg.patch --]
[-- Type: text/x-diff, Size: 11329 bytes --]
From 586b7502ebf3dcfe5ab315052446c6b10bb2637e Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@gmail.com>
Date: Thu, 14 Jan 2010 08:55:38 +0000
Subject: [PATCH] [fw_cfg] Support fetching files from QEMU fw_cfg
This patch adds support for fetching files from the QEMU fw_cfg file
interface, which exposes a set of files on the host into the guest.
Virtual machine netboot configuration can be maintained by a management
layer on the host. This feature makes virtual machine deployment via
gPXE more flexible.
URIs are in the form "fw_cfg:<filename>", where <filename> is the name
of the file exposed via fw_cfg. Note that there are no double slashes
after the "fw_cfg:".
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
---
src/arch/i386/Makefile | 1 +
src/arch/i386/firmware/qemu/fw_cfg.c | 300 ++++++++++++++++++++++++++++++++++
src/arch/i386/include/bits/errfile.h | 1 +
src/config/config.c | 3 +
src/config/general.h | 1 +
5 files changed, 306 insertions(+), 0 deletions(-)
create mode 100644 src/arch/i386/firmware/qemu/fw_cfg.c
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile
index dd8da80..6ddb86a 100644
--- a/src/arch/i386/Makefile
+++ b/src/arch/i386/Makefile
@@ -75,6 +75,7 @@ ISOLINUX_BIN = /usr/lib/syslinux/isolinux.bin
#
SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix
SRCDIRS += arch/i386/firmware/pcbios
+SRCDIRS += arch/i386/firmware/qemu
SRCDIRS += arch/i386/image
SRCDIRS += arch/i386/drivers
SRCDIRS += arch/i386/drivers/net
diff --git a/src/arch/i386/firmware/qemu/fw_cfg.c b/src/arch/i386/firmware/qemu/fw_cfg.c
new file mode 100644
index 0000000..de26ebe
--- /dev/null
+++ b/src/arch/i386/firmware/qemu/fw_cfg.c
@@ -0,0 +1,300 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/io.h>
+#include <gpxe/uri.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/process.h>
+
+/** @file
+ *
+ * QEMU firmware configuration interface
+ *
+ * The QEMU firmware configuration interface allows boot firmware access to
+ * configuration key/value pairs in the hypervisor. This can be used to pass
+ * boot commands when starting a virtual machine.
+ *
+ */
+
+#define FW_CFG_CTL 0x510 /** control I/O port */
+#define FW_CFG_DATA 0x511 /** data I/O port */
+#define FW_CFG_SIGNATURE 0x00 /** magic number */
+#define FW_CFG_ID 0x01 /** interface version */
+#define FW_CFG_FILE_DIR 0x19 /** file listing */
+#define FW_CFG_NO_KEY 0xffff /** invalid key */
+
+/**
+ * File metadata, part of a dir listing
+ */
+struct fw_cfg_file {
+ uint32_t size; /** length in bytes */
+ uint16_t select; /** key */
+ uint16_t reserved;
+ char name[56]; /** filename */
+};
+
+/**
+ * A fw_cfg request
+ *
+ * Requests could be performed synchronously in the open() function but the
+ * xfer interface chain is not fully plugged at that point. Therefore an
+ * explicit request struct needs to be passed to a step() function, which
+ * performs the request later when the callers have plugged their xfer
+ * interfaces.
+ */
+struct fw_cfg_request {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Data xfer interface */
+ struct xfer_interface xfer;
+ /** URI being fetched */
+ struct uri *uri;
+ /** Fetch process */
+ struct process process;
+};
+
+/**
+ * Read a bytestring value for a given key
+ *
+ * @v key FW_CFG_* key
+ * @v buf Buffer
+ * @v len Buffer length in bytes
+ *
+ * If key is FW_CFG_NO_KEY then bytes will be read from the current key.
+ */
+static void fw_cfg_get_bytes ( uint16_t key, void *buf, size_t len ) {
+ char *p;
+
+ /* Select key */
+ if ( key != FW_CFG_NO_KEY )
+ outw ( key, FW_CFG_CTL );
+
+ /* Read value */
+ for ( p = buf; len > 0; len--, p++ )
+ *p = inb ( FW_CFG_DATA );
+}
+
+/**
+ * Read a 32-bit integer for a given key
+ *
+ * @v key FW_CFG_* key
+ * @ret i Integer value
+ */
+static uint32_t fw_cfg_get_i32 ( uint16_t key ) {
+ uint32_t i;
+ fw_cfg_get_bytes ( key, &i, sizeof ( i ) );
+ return le32_to_cpu ( i );
+}
+
+/**
+ * Probe for the fw_cfg interface
+ *
+ * @ret present 1 if present, 0 otherwise
+ */
+static int fw_cfg_detect ( void ) {
+ /* Check for fw_cfg presence */
+ char signature[4];
+ fw_cfg_get_bytes ( FW_CFG_SIGNATURE, signature, sizeof ( signature ) );
+ if ( memcmp ( signature, "QEMU", 4 ) != 0 ) {
+ DBG ( "FW_CFG signature check failed\n" );
+ return 0;
+ }
+
+ /* Check interface version */
+ if ( fw_cfg_get_i32 ( FW_CFG_ID ) != 1 ) {
+ DBG ( "FW_CFG unsupported version\n" );
+ return 0;
+ }
+
+ DBG ( "FW_CFG support detected\n" );
+ return 1;
+}
+
+/**
+ * Find the key for a given filename
+ *
+ * @v name Filename
+ * @v file File pointer
+ * @ret rc Return status code
+ */
+static int fw_cfg_find_file ( const char *name, struct fw_cfg_file *file ) {
+ uint32_t count = be32_to_cpu ( fw_cfg_get_i32 ( FW_CFG_FILE_DIR ) );
+ DBG2 ( "FW_CFG %d files:\n", count );
+ while ( count-- > 0 ) {
+ fw_cfg_get_bytes ( FW_CFG_NO_KEY, file, sizeof ( *file ) );
+ file->size = be32_to_cpu ( file->size );
+ file->select = be16_to_cpu ( file->select );
+ DBG2 ( "FW_CFG \"%s\" %d bytes key=0x%x\n",
+ file->name, file->size, file->select );
+ if ( strcmp ( name, file->name ) == 0 )
+ return 0;
+ }
+ return -ENOENT;
+}
+
+/**
+ * Read a file into a transfer interface
+ *
+ * @v file File pointer
+ * @v xfer Transfer interface
+ * @ret rc Return status code
+ */
+static int fw_cfg_read_file ( struct fw_cfg_file *file, struct xfer_interface *xfer ) {
+ int rc = 0;
+ size_t len = file->size;
+
+ /* Use seek() to notify recipient of filesize */
+ xfer_seek ( xfer, len, SEEK_SET );
+ xfer_seek ( xfer, 0, SEEK_SET );
+
+ /* Select file key */
+ fw_cfg_get_bytes ( file->select, NULL, 0 );
+
+ while ( rc == 0 && len > 0 ) {
+ size_t read_size = len > 4096 ? 4096 : len;
+ struct io_buffer *iobuf = xfer_alloc_iob ( xfer, read_size );
+ if ( ! iobuf )
+ return -ENOMEM;
+
+ fw_cfg_get_bytes ( FW_CFG_NO_KEY, iob_put ( iobuf, read_size ),
+ read_size );
+ rc = xfer_deliver_iob ( xfer, iobuf );
+ len -= read_size;
+ }
+ return rc;
+}
+
+/**
+ * Free fw_cfg request
+ *
+ * @v refcnt Reference counter
+ */
+static void fw_cfg_free ( struct refcnt *refcnt ) {
+ struct fw_cfg_request *fw_cfg =
+ container_of ( refcnt, struct fw_cfg_request, refcnt );
+
+ DBGC2 ( fw_cfg, "FW_CFG %p freed\n", fw_cfg );
+
+ uri_put ( fw_cfg->uri );
+ free ( fw_cfg );
+}
+
+/**
+ * Mark fw_cfg request as complete
+ *
+ * @v fw_cfg fw_cfg request
+ */
+static void fw_cfg_done ( struct fw_cfg_request *fw_cfg, int rc ) {
+ /* Remove process */
+ process_del ( &fw_cfg->process );
+
+ /* Shut down xfer interface */
+ xfer_nullify ( &fw_cfg->xfer );
+ xfer_close ( &fw_cfg->xfer, rc );
+}
+
+/**
+ * Close a fw_cfg request
+ *
+ * @v xfer Transfer interface
+ * @v rc Return status code
+ */
+static void fw_cfg_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct fw_cfg_request *fw_cfg =
+ container_of ( xfer, struct fw_cfg_request, xfer );
+
+ DBGC ( fw_cfg, "FW_CFG %p closed\n", fw_cfg );
+
+ fw_cfg_done ( fw_cfg, rc );
+}
+
+/**
+ * fw_cfg process
+ *
+ * @v process Process
+ */
+static void fw_cfg_step ( struct process *process ) {
+ struct fw_cfg_request *fw_cfg =
+ container_of ( process, struct fw_cfg_request, process );
+ struct fw_cfg_file file;
+ int rc;
+
+ /* Only execute once */
+ process_del ( &fw_cfg->process );
+
+ if ( ( rc = fw_cfg_find_file ( fw_cfg->uri->opaque, &file ) ) == 0 )
+ rc = fw_cfg_read_file ( &file, &fw_cfg->xfer );
+
+ fw_cfg_done ( fw_cfg, rc );
+}
+
+/** fw_cfg data transfer interface operations */
+static struct xfer_interface_operations fw_cfg_xfer_operations = {
+ .close = fw_cfg_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+/**
+ * Open fw_cfg URI
+ *
+ * @v xfer Data transfer interface
+ * @v uri URI
+ * @ret rc Return status code
+ */
+static int fw_cfg_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ struct fw_cfg_request *fw_cfg;
+
+ /* Sanity checks */
+ if ( ! fw_cfg_detect() )
+ return -ENOSYS;
+ if ( ! uri->opaque )
+ return -EINVAL;
+
+ /* Allocate and populate structure */
+ fw_cfg = zalloc ( sizeof ( *fw_cfg ) );
+ if ( ! fw_cfg )
+ return -ENOMEM;
+ fw_cfg->refcnt.free = fw_cfg_free;
+ fw_cfg->uri = uri_get ( uri );
+ xfer_init ( &fw_cfg->xfer, &fw_cfg_xfer_operations, &fw_cfg->refcnt );
+ process_init ( &fw_cfg->process, fw_cfg_step, &fw_cfg->refcnt );
+
+ DBGC ( fw_cfg, "FW_CFG %p fetching %s\n", fw_cfg, uri->opaque );
+
+ /* Attach to parent interface, mortalise self, and return */
+ xfer_plug_plug ( &fw_cfg->xfer, xfer );
+ ref_put ( &fw_cfg->refcnt );
+ return 0;
+}
+
+/** fw_cfg URI opener */
+struct uri_opener fw_cfg_uri_opener __uri_opener = {
+ .scheme = "fw_cfg",
+ .open = fw_cfg_open,
+};
diff --git a/src/arch/i386/include/bits/errfile.h b/src/arch/i386/include/bits/errfile.h
index 32b8a08..849cdc0 100644
--- a/src/arch/i386/include/bits/errfile.h
+++ b/src/arch/i386/include/bits/errfile.h
@@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_biosint ( ERRFILE_ARCH | ERRFILE_CORE | 0x00040000 )
#define ERRFILE_int13 ( ERRFILE_ARCH | ERRFILE_CORE | 0x00050000 )
#define ERRFILE_pxeparent ( ERRFILE_ARCH | ERRFILE_CORE | 0x00060000 )
+#define ERRFILE_fw_cfg ( ERRFILE_ARCH | ERRFILE_CORE | 0x00070000 )
#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
diff --git a/src/config/config.c b/src/config/config.c
index 8252402..8023e41 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -127,6 +127,9 @@ REQUIRE_OBJECT ( tftm );
#ifdef DOWNLOAD_PROTO_SLAM
REQUIRE_OBJECT ( slam );
#endif
+#ifdef DOWNLOAD_PROTO_FW_CFG
+REQUIRE_OBJECT ( fw_cfg );
+#endif
/*
* Drag in all requested SAN boot protocols
diff --git a/src/config/general.h b/src/config/general.h
index f721f61..3fff9ef 100644
--- a/src/config/general.h
+++ b/src/config/general.h
@@ -61,6 +61,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */
#undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
#undef DOWNLOAD_PROTO_FSP /* FSP? */
+#undef DOWNLOAD_PROTO_FW_CFG /* QEMU fw_cfg file interface */
/*
* SAN boot protocols
--
1.6.5
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [Qemu-devel] [RFC] gPXE fw_cfg file interface support
2010-01-27 9:42 [Qemu-devel] [RFC] gPXE fw_cfg file interface support Stefan Hajnoczi
@ 2010-01-27 10:15 ` Gerd Hoffmann
2010-01-29 10:06 ` Stefan Hajnoczi
2010-02-08 2:23 ` [gPXE] " Shao Miller
0 siblings, 2 replies; 4+ messages in thread
From: Gerd Hoffmann @ 2010-01-27 10:15 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: gpxe, qemu-devel
> gPXE> imgfetch fw_cfg:genroms/static.gpxe
Neat idea. On the qemu side we better shouldn't abuse the -option-rom
switch though as seabios will try to load these files as option roms.
> Note that this patch only adds the fw_cfg file interface mechanism, it does not
> automatically probe for a special file when gPXE starts. You will
> need to manually
> "chain fw_cfg:genroms/file".
That should be changed too IMHO. qemu needs some kind of new switch, so
you can place the firmware file into some other virtual directory where
gpxe could look by default, i.e. something like "fw_cfg:gpxe/default".
Then you can easily have qemu guests netboot from somewhere without any
local pxe/tftp setup, like this:
qemu -fw-file gpxe/default=bko.gpxe
with bko.gpxe being:
#!gpxe
dhcp net0
set 209:string pxelinux.cfg/default
set 210:string http://boot.kernel.org/bko/
chain http://boot.kernel.org/bko/pxelinux.0
cheers,
Gerd
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [Qemu-devel] [RFC] gPXE fw_cfg file interface support
2010-01-27 10:15 ` Gerd Hoffmann
@ 2010-01-29 10:06 ` Stefan Hajnoczi
2010-02-08 2:23 ` [gPXE] " Shao Miller
1 sibling, 0 replies; 4+ messages in thread
From: Stefan Hajnoczi @ 2010-01-29 10:06 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: gpxe, qemu-devel
On Wed, Jan 27, 2010 at 10:15 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> Note that this patch only adds the fw_cfg file interface mechanism, it
>> does not
>> automatically probe for a special file when gPXE starts. You will
>> need to manually
>> "chain fw_cfg:genroms/file".
>
> That should be changed too IMHO. qemu needs some kind of new switch, so you
> can place the firmware file into some other virtual directory where gpxe
> could look by default, i.e. something like "fw_cfg:gpxe/default". Then you
> can easily have qemu guests netboot from somewhere without any local
> pxe/tftp setup, like this:
>
> qemu -fw-file gpxe/default=bko.gpxe
>
> with bko.gpxe being:
>
> #!gpxe
> dhcp net0
> set 209:string pxelinux.cfg/default
> set 210:string http://boot.kernel.org/bko/
> chain http://boot.kernel.org/bko/pxelinux.0
That sounds good. I will send out a new patch which fetches
fw_cfg:gpxe/default on startup.
Stefan
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [gPXE] [Qemu-devel] [RFC] gPXE fw_cfg file interface support
2010-01-27 10:15 ` Gerd Hoffmann
2010-01-29 10:06 ` Stefan Hajnoczi
@ 2010-02-08 2:23 ` Shao Miller
1 sibling, 0 replies; 4+ messages in thread
From: Shao Miller @ 2010-02-08 2:23 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Stefan Hajnoczi, gpxe, qemu-devel
[-- Attachment #1: Type: text/plain, Size: 477 bytes --]
Gerd Hoffmann wrote:
>
> ...Then you can easily have qemu guests netboot from somewhere without any
> local pxe/tftp setup, like this:
>
> qemu -fw-file gpxe/default=bko.gpxe
>
That looks a bit like:
# qemu -kernel gpxe.lkrn -initrd bko.gpxe -hda /dev/null
which might be useful iff the *.lkrn build target feature for using an
initrd as a gPXE startup script is incorporated into gPXE mainline. Of
course, it's different because an .lkrn is not a ROM.
- Shao Miller
[-- Attachment #2: Type: text/html, Size: 1157 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2010-02-08 2:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-27 9:42 [Qemu-devel] [RFC] gPXE fw_cfg file interface support Stefan Hajnoczi
2010-01-27 10:15 ` Gerd Hoffmann
2010-01-29 10:06 ` Stefan Hajnoczi
2010-02-08 2:23 ` [gPXE] " Shao Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).