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