| // SPDX-License-Identifier: GPL-2.0 | 
 | #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_platform_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_platform_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 | 
 | }; |