|  | // 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 | 
|  | }; |