qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: "Gabriel L. Somlo" <gsomlo@gmail.com>
Cc: Matt Fleming <matt.fleming@intel.com>,
	mdroth@linux.vnet.ibm.com, rjones@redhat.com,
	jordan.l.justen@intel.com, qemu-devel@nongnu.org,
	gleb@cloudius-systems.com, kraxel@redhat.com,
	pbonzini@redhat.com
Subject: Re: [Qemu-devel] RFC: guest-side retrieval of fw_cfg file
Date: Sun, 26 Jul 2015 11:37:41 +0200	[thread overview]
Message-ID: <55B4AA65.808@redhat.com> (raw)
In-Reply-To: <20150725232152.GA14615@HEDWIG.INI.CMU.EDU>

On 07/26/15 01:21, Gabriel L. Somlo wrote:
> On Tue, Jul 21, 2015 at 12:07:08AM +0200, Laszlo Ersek wrote:
>> On 07/20/15 23:19, Gabriel L. Somlo wrote:
>>
>> ... I just love "find", sorry. :) Anyway, I don't envision myself as a
>> user of this feature any time soon, so please feel free to ignore me.
> 
> How about this: I create /sys/firmware/fw_cfg/by_select with *all*
> entries, named after their selector key, in a flat folder.

I think this is nice. The fw_cfg directory blob itself is available at a
fixed key (FW_CFG_FILE_DIR, 0x0019, and it is even documented :)), so
for a fool-proof, non-interactive lookup, a C language program can
search the 0x0019 blob, grab the relevant key value, and then open
/sys/firmware/fw_cfg/by_select/<THAT_KEY>.

For interactive use or scripting, "by_name" can be used with "find" etc.
I think this is really nice.

Thanks
Laszlo

> Then, I also create /sys/firmware/fw_cfg/by_name/... where I try to
> build the folder hierarchy given by whatever the file names end up
> being, *on a best effort basis*, i.e. I may fail to create *some* of
> them, if they have brain-damaged names :)
> 
> The kernel will issue big scary warnings if a symlink already exists
> where I'm trying to add a new subdirectory of the same name, or the
> other way around.
> 
> The whole thing is below, tested and working on x86 and arm (so one
> example of IOPort and one example of MMIO).
> 
> I'm out all of next week, but will submit this to the kernel (and cc
> Greg-K-H as advised by Matt) once I get back.
> 
> In the mean time, any final words of wisdom, advice, testing, etc.
> much much appreciated !
> 
> Cheers,
> --Gabriel
> 
> 
> /*
>  * drivers/firmware/fw_cfg.c
>  *
>  * Expose entries from QEMU's firmware configuration (fw_cfg) device in
>  * sysfs (read-only, under "/sys/firmware/fw_cfg/...").
>  *
>  * Copyright 2015 Gabriel L. Somlo <somlo@cmu.edu>
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License v2 as published
>  * by the Free Software Foundation.
>  *
>  * 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.,
>  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
>  */
> 
> #include <linux/module.h>
> #include <linux/capability.h>
> #include <linux/slab.h>
> #include <linux/io.h>
> #include <linux/ioport.h>
> #include <linux/ctype.h>
> 
> /* selector values for "well-known" fw_cfg entries */
> #define FW_CFG_SIGNATURE  0x00
> #define FW_CFG_FILE_DIR   0x19
> 
> /* size in bytes of fw_cfg signature */
> #define FW_CFG_SIG_SIZE 4
> 
> /* fw_cfg "file name" is up to 56 characters (including terminating nul) */
> #define FW_CFG_MAX_FILE_PATH 56
> 
> /* fw_cfg file directory entry type */
> struct fw_cfg_file {
> 	uint32_t size;
> 	uint16_t select;
> 	uint16_t reserved;
> 	char name[FW_CFG_MAX_FILE_PATH];
> };
> 
> /* fw_cfg device i/o access options type */
> struct fw_cfg_access {
> 	phys_addr_t start;
> 	uint8_t size;
> 	uint8_t ctrl_offset;
> 	uint8_t data_offset;
> 	bool is_mmio;
> 	const char *name;
> };
> 
> /* fw_cfg device i/o access available options for known architectures */
> static struct fw_cfg_access fw_cfg_modes[] = {
> 	{ 0x510,       2, 0, 1, false, "fw_cfg on i386, sun4u" },
> 	{ 0x9020000,  10, 8, 0,  true, "fw_cfg on arm" },
> 	{ 0xd00000510, 3, 0, 2,  true, "fw_cfg on sun4m" },
> 	{ 0xf0000510,  3, 0, 2,  true, "fw_cfg on ppc/mac" },
> 	{ }
> };
> 
> /* fw_cfg device i/o currently selected option set */
> static struct fw_cfg_access *fw_cfg_mode;
> 
> /* fw_cfg device i/o register addresses */
> static void __iomem *fw_cfg_dev_base;
> static void __iomem *fw_cfg_dev_ctrl;
> static void __iomem *fw_cfg_dev_data;
> 
> /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
> static DEFINE_MUTEX(fw_cfg_dev_lock);
> 
> /* pick apropriate endianness for selector key */
> static inline uint16_t fw_cfg_sel_endianness(uint16_t select)
> {
> 	return fw_cfg_mode->is_mmio ? cpu_to_be16(select) : cpu_to_le16(select);
> }
> 
> /* type for fw_cfg "directory scan" visitor/callback function */
> typedef int (*fw_cfg_file_callback)(const struct fw_cfg_file *f);
> 
> /* run a given callback on each fw_cfg directory entry */
> static int fw_cfg_scan_dir(fw_cfg_file_callback callback)
> {
> 	int ret = 0;
> 	uint32_t count, i;
> 	struct fw_cfg_file f;
> 
> 	mutex_lock(&fw_cfg_dev_lock);
> 	iowrite16(fw_cfg_sel_endianness(FW_CFG_FILE_DIR), fw_cfg_dev_ctrl);
> 	ioread8_rep(fw_cfg_dev_data, &count, sizeof(count));
> 	for (i = 0; i < be32_to_cpu(count); i++) {
> 		ioread8_rep(fw_cfg_dev_data, &f, sizeof(f));
> 		ret = callback(&f);
> 		if (ret)
> 			break;
> 	}
> 	mutex_unlock(&fw_cfg_dev_lock);
> 	return ret;
> }
> 
> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */
> static inline void fw_cfg_read_blob(uint16_t select,
> 				     void *buf, loff_t pos, size_t count)
> {
> 	mutex_lock(&fw_cfg_dev_lock);
> 	iowrite16(fw_cfg_sel_endianness(select), fw_cfg_dev_ctrl);
> 	while (pos-- > 0)
> 		ioread8(fw_cfg_dev_data);
> 	ioread8_rep(fw_cfg_dev_data, buf, count);
> 	mutex_unlock(&fw_cfg_dev_lock);
> }
> 
> /* clean up fw_cfg device i/o setup */
> static void fw_cfg_io_cleanup(void)
> {
> 	if (fw_cfg_mode->is_mmio) {
> 		iounmap(fw_cfg_dev_base);
> 		release_mem_region(fw_cfg_mode->start, fw_cfg_mode->size);
> 	} else {
> 		ioport_unmap(fw_cfg_dev_base);
> 		release_region(fw_cfg_mode->start, fw_cfg_mode->size);
> 	}
> }
> 
> /* probe and map fw_cfg device */
> static int __init fw_cfg_io_probe(void)
> {
> 	char sig[FW_CFG_SIG_SIZE];
> 
> 	for (fw_cfg_mode = &fw_cfg_modes[0];
> 	     fw_cfg_mode->start; fw_cfg_mode++) {
> 
> 		phys_addr_t start = fw_cfg_mode->start;
> 		uint8_t size = fw_cfg_mode->size;
> 
> 		/* reserve and map mmio or ioport region */
> 		if (fw_cfg_mode->is_mmio) {
> 			if (!request_mem_region(start, size, fw_cfg_mode->name))
> 				continue;
> 			fw_cfg_dev_base = ioremap(start, size);
> 			if (!fw_cfg_dev_base) {
> 				release_mem_region(start, size);
> 				continue;
> 			}
> 		} else {
> 			if (!request_region(start, size, fw_cfg_mode->name))
> 				continue;
> 			fw_cfg_dev_base = ioport_map(start, size);
> 			if (!fw_cfg_dev_base) {
> 				release_region(start, size);
> 				continue;
> 			}
> 		}
> 
> 		/* set control and data register addresses */
> 		fw_cfg_dev_ctrl = fw_cfg_dev_base + fw_cfg_mode->ctrl_offset;
> 		fw_cfg_dev_data = fw_cfg_dev_base + fw_cfg_mode->data_offset;
> 
> 		/* verify fw_cfg device signature */
> 		fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE);
> 		if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) == 0)
> 			/* success, we're done */
> 			return 0;
> 
> 		/* clean up before probing next access mode */
> 		fw_cfg_io_cleanup();
> 	}
> 
> 	return -ENODEV;
> }
> 
> /* fw_cfg_sysfs_entry type */
> struct fw_cfg_sysfs_entry {
> 	struct kobject kobj;
> 	struct fw_cfg_file f;
> 	struct list_head list;
> };
> 
> /* get fw_cfg_sysfs_entry from kobject member */
> static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj)
> {
> 	return container_of(kobj, struct fw_cfg_sysfs_entry, kobj);
> }
> 
> /* fw_cfg_sysfs_attribute type */
> struct fw_cfg_sysfs_attribute {
> 	struct attribute attr;
> 	ssize_t (*show)(struct fw_cfg_sysfs_entry *entry, char *buf);
> };
> 
> /* get fw_cfg_sysfs_attribute from attribute member */
> static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr)
> {
> 	return container_of(attr, struct fw_cfg_sysfs_attribute, attr);
> }
> 
> /* global cache of fw_cfg_sysfs_entry objects */
> static LIST_HEAD(fw_cfg_entry_cache);
> 
> /* kobjects removed lazily by kernel, mutual exclusion needed */
> static DEFINE_SPINLOCK(fw_cfg_cache_lock);
> 
> static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry)
> {
> 	spin_lock(&fw_cfg_cache_lock);
> 	list_add_tail(&entry->list, &fw_cfg_entry_cache);
> 	spin_unlock(&fw_cfg_cache_lock);
> }
> 
> static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry)
> {
> 	spin_lock(&fw_cfg_cache_lock);
> 	list_del(&entry->list);
> 	spin_unlock(&fw_cfg_cache_lock);
> }
> 
> static void fw_cfg_sysfs_cache_cleanup(void)
> {
> 	struct fw_cfg_sysfs_entry *entry, *next;
> 
> 	list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) {
> 		/* will end up invoking fw_cfg_sysfs_cache_delist()
> 	         * via each object's release() method (i.e. destructor) */
> 		kobject_put(&entry->kobj);
> 	}
> }
> 
> /* default_attrs: per-entry attributes and show methods */
> 
> #define FW_CFG_SYSFS_ATTR(_attr) \
> struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \
> 	.attr = { .name = __stringify(_attr), .mode = 0400 }, \
> 	.show = fw_cfg_sysfs_show_##_attr, \
> }
> 
> static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf)
> {
> 	return sprintf(buf, "%d\n", e->f.size);
> }
> 
> static ssize_t fw_cfg_sysfs_show_select(struct fw_cfg_sysfs_entry *e, char *buf)
> {
> 	return sprintf(buf, "%d\n", e->f.select);
> }
> 
> static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf)
> {
> 	return sprintf(buf, "%s\n", e->f.name);
> }
> 
> static FW_CFG_SYSFS_ATTR(size);
> static FW_CFG_SYSFS_ATTR(select);
> static FW_CFG_SYSFS_ATTR(name);
> 
> static struct attribute *fw_cfg_sysfs_entry_attrs[] = {
> 	&fw_cfg_sysfs_attr_size.attr,
> 	&fw_cfg_sysfs_attr_select.attr,
> 	&fw_cfg_sysfs_attr_name.attr,
> 	NULL,
> };
> 
> /* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */
> static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a,
> 				      char *buf)
> {
> 	struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
> 	struct fw_cfg_sysfs_attribute *attr = to_attr(a);
> 
> 	if (!capable(CAP_SYS_ADMIN))
> 		return -EACCES;
> 
> 	return attr->show(entry, buf);
> }
> 
> static const struct sysfs_ops fw_cfg_sysfs_attr_ops = {
> 	.show = fw_cfg_sysfs_attr_show,
> };
> 
> /* release: destructor, to be called via kobject_put() */
> static void fw_cfg_sysfs_release_entry(struct kobject *kobj)
> {
> 	struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
> 
> 	fw_cfg_sysfs_cache_delist(entry);
> 	kfree(entry);
> }
> 
> /* kobj_type: ties together all properties required to register an entry */
> static struct kobj_type fw_cfg_sysfs_entry_ktype = {
> 	.default_attrs = fw_cfg_sysfs_entry_attrs,
> 	.sysfs_ops = &fw_cfg_sysfs_attr_ops,
> 	.release = fw_cfg_sysfs_release_entry,
> };
> 
> /* raw-read method and attribute */
> static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
> 				     struct bin_attribute *bin_attr,
> 				     char *buf, loff_t pos, size_t count)
> {
> 	struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
> 
> 	if (!capable(CAP_SYS_ADMIN))
> 		return -EACCES;
> 
> 	if (pos > entry->f.size)
> 		return -EINVAL;
> 
> 	if (count > entry->f.size - pos)
> 		count = entry->f.size - pos;
> 
> 	fw_cfg_read_blob(entry->f.select, buf, pos, count);
> 	return count;
> }
> 
> static struct bin_attribute fw_cfg_sysfs_attr_raw = {
> 	.attr = { .name = "raw", .mode = 0400 },
> 	.read = fw_cfg_sysfs_read_raw,
> };
> 
> /*
>  * kset_find_obj() is not EXPORTed from lib/kobject.c, clone it here for now.
>  * FIXME: patch adding EXPORT in lib/kobject.c tested working, but then
>  * 	  building this module for older kernels wouldn't work anymore...
>  */
> struct kobject *my_kset_find_obj(struct kset *kset, const char *name)
> {
>         struct kobject *k;
>         struct kobject *ret = NULL;
> 
>         spin_lock(&kset->list_lock);
> 
>         list_for_each_entry(k, &kset->list, entry) {
>                 if (kobject_name(k) && !strcmp(kobject_name(k), name)) {
>                         ret = kobject_get(k);
>                         break;
>                 }
>         }
> 
>         spin_unlock(&kset->list_lock);
>         return ret;
> }
> 
> /*
>  * Create a kset subdirectory matching each '/' delimited dirname token
>  * in 'name', starting with sysfs kset/folder 'dir'; At the end, create
>  * a symlink directed at the given 'target'.
>  * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed
>  * to be a well-behaved path name. Whenever a symlink vs. kset directory
>  * name collision occurs, the kernel will issue big scary warnings while
>  * refusing to add the offending link or directory. We follow up with our
>  * own, slightly less scary error messages explaining the situation :)
>  */
> static void __init fw_cfg_build_symlink(struct kset *dir,
> 					struct kobject *target,
> 					const char *name)
> {
> 	struct kset *subdir;
> 	struct kobject *ko;
> 	char *name_copy, *p, *tok;
> 
> 	if (!dir || !target || !name || !*name) {
> 		pr_err("fw_cfg: invalid argument to fw_cfg_build_symlink\n");
> 		return;
> 	}
> 
> 	/* clone a copy of name for parsing */
> 	name_copy = p = kstrdup(name, GFP_KERNEL);
> 	if (!name_copy) {
> 		pr_err("fw_cfg: kstrdup failed while "
> 		       "creating symlink %s\n", name);
> 		return;
> 	}
> 
> 	/* create folders for each dirname token, then symlink for basename */
> 	while ((tok = strsep(&p, "/")) && *tok) {
> 
> 		/* last (basename) token? If so, add symlink here */
> 		if (!p || !*p) {
> 			if (sysfs_create_link(&dir->kobj, target, tok)) {
> 				pr_err("fw_cfg: can't create symlink %s "
> 				       "of %s\n", tok, name);
> 			}
> 			break;
> 		}
> 
> 		/* does the current dir contain an item named after tok ? */
> 		ko = my_kset_find_obj(dir, tok);
> 		if (ko) {
> 			/* drop reference added by kset_find_obj */
> 			kobject_put(ko);
> 
> 			/* ko MUST be a kset - we're about to use it as one ! */
> 			if (ko->ktype != dir->kobj.ktype) {
> 				pr_err("fw_cfg: non-folder token %s already "
> 				       "exists while creating %s\n", tok, name);
> 				break;
> 			}
> 
> 			/* descend into already existing subdirectory */
> 			dir = to_kset(ko);
> 		} else {
> 			/* create new subdirectory kset */
> 			subdir = kzalloc(sizeof(struct kset), GFP_KERNEL);
> 			if (!subdir) {
> 				pr_err("fw_cfg: alloc error creating subdir %s "
> 				       "of %s\n", tok, name);
> 				break;
> 			}
> 			subdir->kobj.kset = dir;
> 			subdir->kobj.ktype = dir->kobj.ktype;
> 			if (kobject_set_name(&subdir->kobj, "%s", tok)) {
> 				kfree(subdir);
> 				pr_err("fw_cfg: set_name failed on subdir %s "
> 				       "of %s\n", tok, name);
> 				break;
> 			}
> 			if (kset_register(subdir)) {
> 				kfree(subdir);
> 				pr_err("fw_cfg: register failed on subdir %s "
> 				       "of %s\n", tok, name);
> 				break;
> 			}
> 
> 			/* descend into newly created subdirectory */
> 			dir = subdir;
> 		}
> 	}
> 
> 	/* we're done with cloned copy of name */
> 	kfree(name_copy);
> }
> 
> /* recursively unregister fw_cfg/by_name/ kset directory tree */
> static void fw_cfg_kset_unregister_recursive(struct kset *kset)
> {
> 	struct kobject *k, *next;
> 
> 	list_for_each_entry_safe(k, next, &kset->list, entry)
> 		/* all set members are ksets too, but check just in case... */
> 		if (k->ktype == kset->kobj.ktype)
> 			fw_cfg_kset_unregister_recursive(to_kset(k));
> 
> 	/* symlinks are cleanly and automatically removed with the directory */
> 	kset_unregister(kset);
> }
> 
> /* kobjects & kset representing top-level, by_select, and by_name folders */
> static struct kobject *fw_cfg_top_ko;
> static struct kobject *fw_cfg_sel_ko;
> static struct kset *fw_cfg_fname_kset;
> 
> /* callback function to register an individual fw_cfg file */
> static int __init fw_cfg_register_file(const struct fw_cfg_file *f)
> {
> 	int err;
> 	struct fw_cfg_sysfs_entry *entry;
> 
> 	/* allocate new entry */
> 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> 	if (!entry)
> 		return -ENOMEM;
> 
> 	/* set file entry information */
> 	entry->f.size = be32_to_cpu(f->size);
> 	entry->f.select = be16_to_cpu(f->select);
> 	strcpy(entry->f.name, f->name);
> 
> 	/* register entry under "/sys/firmware/fw_cfg/by_select/" */
> 	err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype,
> 				   fw_cfg_sel_ko, "%d", entry->f.select);
> 	if (err)
> 		goto err_register;
> 
> 	/* add raw binary content access */
> 	err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw);
> 	if (err)
> 		goto err_add_raw;
> 
> 	/* try adding "/sys/firmware/fw_cfg/by_name/" symlink (best-effort) */
> 	fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->f.name);
> 
> 	/* success, add entry to global cache */
> 	fw_cfg_sysfs_cache_enlist(entry);
> 	return 0;
> 
> err_add_raw:
> 	kobject_del(&entry->kobj);
> err_register:
> 	kfree(entry);
> 	return err;
> }
> 
> /* unregister top-level or by_select folder */
> static inline void fw_cfg_kobj_cleanup(struct kobject *kobj)
> {
> 	kobject_del(kobj);
> 	kobject_put(kobj);
> }
> 
> static int __init fw_cfg_sysfs_init(void)
> {
> 	int err;
> 
> 	/* probe for the fw_cfg "hardware" */
> 	err = fw_cfg_io_probe();
> 	if (err)
> 		return err;
> 
> 	/* create sysfs toplevel, by_select, and by_name folders */
> 	err = -ENOMEM;
> 	fw_cfg_top_ko = kobject_create_and_add("fw_cfg", firmware_kobj);
> 	if (!fw_cfg_top_ko)
> 		goto err_sysfs_top;
> 	fw_cfg_sel_ko = kobject_create_and_add("by_select", fw_cfg_top_ko);
> 	if (!fw_cfg_sel_ko)
> 		goto err_sysfs_sel;
> 	fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko);
> 	if (!fw_cfg_fname_kset)
> 		goto err_sysfs_name;
> 
> 	/* process fw_cfg file directory entry, registering each file */
> 	err = fw_cfg_scan_dir(fw_cfg_register_file);
> 	if (err)
> 		goto err_scan;
> 
> 	/* success */
> 	pr_debug("fw_cfg: loaded.\n");
> 	return 0;
> 
> err_scan:
> 	fw_cfg_sysfs_cache_cleanup();
> 	fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset);
> err_sysfs_name:
> 	fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
> err_sysfs_sel:
> 	fw_cfg_kobj_cleanup(fw_cfg_top_ko);
> err_sysfs_top:
> 	fw_cfg_io_cleanup();
> 	return err;
> }
> 
> static void __exit fw_cfg_sysfs_exit(void)
> {
> 	pr_debug("fw_cfg: unloading.\n");
> 	fw_cfg_sysfs_cache_cleanup();
> 	fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset);
> 	fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
> 	fw_cfg_kobj_cleanup(fw_cfg_top_ko);
> 	fw_cfg_io_cleanup();
> }
> 
> module_init(fw_cfg_sysfs_init);
> module_exit(fw_cfg_sysfs_exit);
> MODULE_AUTHOR("Gabriel L. Somlo <somlo@cmu.edu>");
> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support");
> MODULE_LICENSE("GPL");
> 

  reply	other threads:[~2015-07-26  9:37 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-13 20:09 [Qemu-devel] RFC: guest-side retrieval of fw_cfg file Gabriel L. Somlo
2015-07-13 23:03 ` Eric Blake
2015-07-14 17:00   ` Gabriel L. Somlo
2015-07-15 11:06     ` Matt Fleming
2015-07-15 11:15       ` Laszlo Ersek
2015-07-14  9:43 ` Richard W.M. Jones
2015-07-14 18:23   ` Gabriel L. Somlo
2015-07-14 18:31     ` Gabriel L. Somlo
2015-07-14 18:48     ` Richard W.M. Jones
2015-07-14 18:51       ` Richard W.M. Jones
2015-07-14 19:16       ` Peter Maydell
2015-07-14 19:24       ` Gabriel L. Somlo
2015-07-14 19:24     ` Gerd Hoffmann
2015-07-16  0:43   ` Gabriel L. Somlo
2015-07-16 19:27     ` Eric Blake
2015-07-16 20:42       ` Gabriel L. Somlo
2015-07-14 11:38 ` Laszlo Ersek
2015-07-15 12:00 ` Matt Fleming
2015-07-20 21:19   ` Gabriel L. Somlo
2015-07-20 22:07     ` Laszlo Ersek
2015-07-25 23:21       ` Gabriel L. Somlo
2015-07-26  9:37         ` Laszlo Ersek [this message]
2015-07-15 14:08 ` Michael S. Tsirkin
2015-07-15 16:01   ` Igor Mammedov
2015-07-15 16:24     ` Michael S. Tsirkin
2015-07-16  1:21       ` Gabriel L. Somlo
2015-07-16  6:57       ` Igor Mammedov
2015-07-16  7:30         ` Michael S. Tsirkin
2015-07-16  9:50           ` Igor Mammedov
2015-07-16 10:25             ` Michael S. Tsirkin
2015-07-16 11:15               ` Igor Mammedov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=55B4AA65.808@redhat.com \
    --to=lersek@redhat.com \
    --cc=gleb@cloudius-systems.com \
    --cc=gsomlo@gmail.com \
    --cc=jordan.l.justen@intel.com \
    --cc=kraxel@redhat.com \
    --cc=matt.fleming@intel.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rjones@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).