diff options
Diffstat (limited to 'drivers/xen/xenfs/xensyms.c')
| -rw-r--r-- | drivers/xen/xenfs/xensyms.c | 152 | 
1 files changed, 152 insertions, 0 deletions
diff --git a/drivers/xen/xenfs/xensyms.c b/drivers/xen/xenfs/xensyms.c new file mode 100644 index 000000000000..f8b12856753f --- /dev/null +++ b/drivers/xen/xenfs/xensyms.c @@ -0,0 +1,152 @@ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/seq_file.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <xen/interface/platform.h> +#include <asm/xen/hypercall.h> +#include <xen/xen-ops.h> +#include "xenfs.h" + + +#define XEN_KSYM_NAME_LEN 127 /* Hypervisor may have different name length */ + +struct xensyms { +	struct xen_platform_op op; +	char *name; +	uint32_t namelen; +}; + +/* Grab next output page from the hypervisor */ +static int xensyms_next_sym(struct xensyms *xs) +{ +	int ret; +	struct xenpf_symdata *symdata = &xs->op.u.symdata; +	uint64_t symnum; + +	memset(xs->name, 0, xs->namelen); +	symdata->namelen = xs->namelen; + +	symnum = symdata->symnum; + +	ret = HYPERVISOR_dom0_op(&xs->op); +	if (ret < 0) +		return ret; + +	/* +	 * If hypervisor's symbol didn't fit into the buffer then allocate +	 * a larger buffer and try again. +	 */ +	if (unlikely(symdata->namelen > xs->namelen)) { +		kfree(xs->name); + +		xs->namelen = symdata->namelen; +		xs->name = kzalloc(xs->namelen, GFP_KERNEL); +		if (!xs->name) +			return -ENOMEM; + +		set_xen_guest_handle(symdata->name, xs->name); +		symdata->symnum--; /* Rewind */ + +		ret = HYPERVISOR_dom0_op(&xs->op); +		if (ret < 0) +			return ret; +	} + +	if (symdata->symnum == symnum) +		/* End of symbols */ +		return 1; + +	return 0; +} + +static void *xensyms_start(struct seq_file *m, loff_t *pos) +{ +	struct xensyms *xs = (struct xensyms *)m->private; + +	xs->op.u.symdata.symnum = *pos; + +	if (xensyms_next_sym(xs)) +		return NULL; + +	return m->private; +} + +static void *xensyms_next(struct seq_file *m, void *p, loff_t *pos) +{ +	struct xensyms *xs = (struct xensyms *)m->private; + +	xs->op.u.symdata.symnum = ++(*pos); + +	if (xensyms_next_sym(xs)) +		return NULL; + +	return p; +} + +static int xensyms_show(struct seq_file *m, void *p) +{ +	struct xensyms *xs = (struct xensyms *)m->private; +	struct xenpf_symdata *symdata = &xs->op.u.symdata; + +	seq_printf(m, "%016llx %c %s\n", symdata->address, +		   symdata->type, xs->name); + +	return 0; +} + +static void xensyms_stop(struct seq_file *m, void *p) +{ +} + +static const struct seq_operations xensyms_seq_ops = { +	.start = xensyms_start, +	.next = xensyms_next, +	.show = xensyms_show, +	.stop = xensyms_stop, +}; + +static int xensyms_open(struct inode *inode, struct file *file) +{ +	struct seq_file *m; +	struct xensyms *xs; +	int ret; + +	ret = seq_open_private(file, &xensyms_seq_ops, +			       sizeof(struct xensyms)); +	if (ret) +		return ret; + +	m = file->private_data; +	xs = (struct xensyms *)m->private; + +	xs->namelen = XEN_KSYM_NAME_LEN + 1; +	xs->name = kzalloc(xs->namelen, GFP_KERNEL); +	if (!xs->name) { +		seq_release_private(inode, file); +		return -ENOMEM; +	} +	set_xen_guest_handle(xs->op.u.symdata.name, xs->name); +	xs->op.cmd = XENPF_get_symbol; +	xs->op.u.symdata.namelen = xs->namelen; + +	return 0; +} + +static int xensyms_release(struct inode *inode, struct file *file) +{ +	struct seq_file *m = file->private_data; +	struct xensyms *xs = (struct xensyms *)m->private; + +	kfree(xs->name); +	return seq_release_private(inode, file); +} + +const struct file_operations xensyms_ops = { +	.open = xensyms_open, +	.read = seq_read, +	.llseek = seq_lseek, +	.release = xensyms_release +};  |