1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/init.h> 3 #include <linux/seq_file.h> 4 #include <linux/fs.h> 5 #include <linux/mm.h> 6 #include <linux/proc_fs.h> 7 #include <linux/slab.h> 8 #include <xen/interface/platform.h> 9 #include <asm/xen/hypercall.h> 10 #include <xen/xen-ops.h> 11 #include "xenfs.h" 12 13 14 #define XEN_KSYM_NAME_LEN 127 /* Hypervisor may have different name length */ 15 16 struct xensyms { 17 struct xen_platform_op op; 18 char *name; 19 uint32_t namelen; 20 }; 21 22 /* Grab next output page from the hypervisor */ 23 static int xensyms_next_sym(struct xensyms *xs) 24 { 25 int ret; 26 struct xenpf_symdata *symdata = &xs->op.u.symdata; 27 uint64_t symnum; 28 29 memset(xs->name, 0, xs->namelen); 30 symdata->namelen = xs->namelen; 31 32 symnum = symdata->symnum; 33 34 ret = HYPERVISOR_platform_op(&xs->op); 35 if (ret < 0) 36 return ret; 37 38 /* 39 * If hypervisor's symbol didn't fit into the buffer then allocate 40 * a larger buffer and try again. 41 */ 42 if (unlikely(symdata->namelen > xs->namelen)) { 43 kfree(xs->name); 44 45 xs->namelen = symdata->namelen; 46 xs->name = kzalloc(xs->namelen, GFP_KERNEL); 47 if (!xs->name) 48 return -ENOMEM; 49 50 set_xen_guest_handle(symdata->name, xs->name); 51 symdata->symnum--; /* Rewind */ 52 53 ret = HYPERVISOR_platform_op(&xs->op); 54 if (ret < 0) 55 return ret; 56 } 57 58 if (symdata->symnum == symnum) 59 /* End of symbols */ 60 return 1; 61 62 return 0; 63 } 64 65 static void *xensyms_start(struct seq_file *m, loff_t *pos) 66 { 67 struct xensyms *xs = (struct xensyms *)m->private; 68 69 xs->op.u.symdata.symnum = *pos; 70 71 if (xensyms_next_sym(xs)) 72 return NULL; 73 74 return m->private; 75 } 76 77 static void *xensyms_next(struct seq_file *m, void *p, loff_t *pos) 78 { 79 struct xensyms *xs = (struct xensyms *)m->private; 80 81 xs->op.u.symdata.symnum = ++(*pos); 82 83 if (xensyms_next_sym(xs)) 84 return NULL; 85 86 return p; 87 } 88 89 static int xensyms_show(struct seq_file *m, void *p) 90 { 91 struct xensyms *xs = (struct xensyms *)m->private; 92 struct xenpf_symdata *symdata = &xs->op.u.symdata; 93 94 seq_printf(m, "%016llx %c %s\n", symdata->address, 95 symdata->type, xs->name); 96 97 return 0; 98 } 99 100 static void xensyms_stop(struct seq_file *m, void *p) 101 { 102 } 103 104 static const struct seq_operations xensyms_seq_ops = { 105 .start = xensyms_start, 106 .next = xensyms_next, 107 .show = xensyms_show, 108 .stop = xensyms_stop, 109 }; 110 111 static int xensyms_open(struct inode *inode, struct file *file) 112 { 113 struct seq_file *m; 114 struct xensyms *xs; 115 int ret; 116 117 ret = seq_open_private(file, &xensyms_seq_ops, 118 sizeof(struct xensyms)); 119 if (ret) 120 return ret; 121 122 m = file->private_data; 123 xs = (struct xensyms *)m->private; 124 125 xs->namelen = XEN_KSYM_NAME_LEN + 1; 126 xs->name = kzalloc(xs->namelen, GFP_KERNEL); 127 if (!xs->name) { 128 seq_release_private(inode, file); 129 return -ENOMEM; 130 } 131 set_xen_guest_handle(xs->op.u.symdata.name, xs->name); 132 xs->op.cmd = XENPF_get_symbol; 133 xs->op.u.symdata.namelen = xs->namelen; 134 135 return 0; 136 } 137 138 static int xensyms_release(struct inode *inode, struct file *file) 139 { 140 struct seq_file *m = file->private_data; 141 struct xensyms *xs = (struct xensyms *)m->private; 142 143 kfree(xs->name); 144 return seq_release_private(inode, file); 145 } 146 147 const struct file_operations xensyms_ops = { 148 .open = xensyms_open, 149 .read = seq_read, 150 .llseek = seq_lseek, 151 .release = xensyms_release 152 }; 153