MALI: midgard: RK: add separate src dir of Midgard driver for RK Linux device
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / midgard_for_linux / mali_kbase_debug_mem_view.c
1 /*
2  *
3  * (C) COPYRIGHT 2013-2015 ARM Limited. All rights reserved.
4  *
5  * This program is free software and is provided to you under the terms of the
6  * GNU General Public License version 2 as published by the Free Software
7  * Foundation, and any use by you of this program is subject to the terms
8  * of such GNU licence.
9  *
10  * A copy of the licence is included with the program, and can also be obtained
11  * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12  * Boston, MA  02110-1301, USA.
13  *
14  */
15
16
17
18 /*
19  * Debugfs interface to dump the memory visible to the GPU
20  */
21
22 #include "mali_kbase_debug_mem_view.h"
23 #include "mali_kbase.h"
24
25 #include <linux/list.h>
26 #include <linux/file.h>
27
28 #ifdef CONFIG_DEBUG_FS
29
30 struct debug_mem_mapping {
31         struct list_head node;
32
33         struct kbase_mem_phy_alloc *alloc;
34         unsigned long flags;
35
36         u64 start_pfn;
37         size_t nr_pages;
38 };
39
40 struct debug_mem_data {
41         struct list_head mapping_list;
42         struct kbase_context *kctx;
43 };
44
45 struct debug_mem_seq_off {
46         struct list_head *lh;
47         size_t offset;
48 };
49
50 static void *debug_mem_start(struct seq_file *m, loff_t *_pos)
51 {
52         struct debug_mem_data *mem_data = m->private;
53         struct debug_mem_seq_off *data;
54         struct debug_mem_mapping *map;
55         loff_t pos = *_pos;
56
57         list_for_each_entry(map, &mem_data->mapping_list, node) {
58                 if (pos >= map->nr_pages) {
59                         pos -= map->nr_pages;
60                 } else {
61                         data = kmalloc(sizeof(*data), GFP_KERNEL);
62                         if (!data)
63                                 return NULL;
64                         data->lh = &map->node;
65                         data->offset = pos;
66                         return data;
67                 }
68         }
69
70         /* Beyond the end */
71         return NULL;
72 }
73
74 static void debug_mem_stop(struct seq_file *m, void *v)
75 {
76         kfree(v);
77 }
78
79 static void *debug_mem_next(struct seq_file *m, void *v, loff_t *pos)
80 {
81         struct debug_mem_data *mem_data = m->private;
82         struct debug_mem_seq_off *data = v;
83         struct debug_mem_mapping *map;
84
85         map = list_entry(data->lh, struct debug_mem_mapping, node);
86
87         if (data->offset < map->nr_pages - 1) {
88                 data->offset++;
89                 ++*pos;
90                 return data;
91         }
92
93         if (list_is_last(data->lh, &mem_data->mapping_list))
94                 return NULL;
95
96         data->lh = data->lh->next;
97         data->offset = 0;
98         ++*pos;
99
100         return data;
101 }
102
103 static int debug_mem_show(struct seq_file *m, void *v)
104 {
105         struct debug_mem_data *mem_data = m->private;
106         struct debug_mem_seq_off *data = v;
107         struct debug_mem_mapping *map;
108         int i, j;
109         struct page *page;
110         uint32_t *mapping;
111         pgprot_t prot = PAGE_KERNEL;
112
113         map = list_entry(data->lh, struct debug_mem_mapping, node);
114
115         kbase_gpu_vm_lock(mem_data->kctx);
116
117         if (data->offset >= map->alloc->nents) {
118                 seq_printf(m, "%016llx: Unbacked page\n\n", (map->start_pfn +
119                                 data->offset) << PAGE_SHIFT);
120                 goto out;
121         }
122
123         if (!(map->flags & KBASE_REG_CPU_CACHED))
124                 prot = pgprot_writecombine(prot);
125
126         page = pfn_to_page(PFN_DOWN(map->alloc->pages[data->offset]));
127         mapping = vmap(&page, 1, VM_MAP, prot);
128
129         for (i = 0; i < PAGE_SIZE; i += 4*sizeof(*mapping)) {
130                 seq_printf(m, "%016llx:", i + ((map->start_pfn +
131                                 data->offset) << PAGE_SHIFT));
132
133                 for (j = 0; j < 4*sizeof(*mapping); j += sizeof(*mapping))
134                         seq_printf(m, " %08x", mapping[(i+j)/sizeof(*mapping)]);
135                 seq_putc(m, '\n');
136         }
137
138         vunmap(mapping);
139
140         seq_putc(m, '\n');
141
142 out:
143         kbase_gpu_vm_unlock(mem_data->kctx);
144         return 0;
145 }
146
147 static const struct seq_operations ops = {
148         .start = debug_mem_start,
149         .next = debug_mem_next,
150         .stop = debug_mem_stop,
151         .show = debug_mem_show,
152 };
153
154 static int debug_mem_open(struct inode *i, struct file *file)
155 {
156         struct file *kctx_file = i->i_private;
157         struct kbase_context *kctx = kctx_file->private_data;
158         struct rb_node *p;
159         struct debug_mem_data *mem_data;
160         int ret;
161
162         ret = seq_open(file, &ops);
163
164         if (ret)
165                 return ret;
166
167         mem_data = kmalloc(sizeof(*mem_data), GFP_KERNEL);
168         mem_data->kctx = kctx;
169
170         INIT_LIST_HEAD(&mem_data->mapping_list);
171
172         get_file(kctx_file);
173
174         kbase_gpu_vm_lock(kctx);
175
176         for (p = rb_first(&kctx->reg_rbtree); p; p = rb_next(p)) {
177                 struct kbase_va_region *reg;
178                 struct debug_mem_mapping *mapping;
179
180                 reg = rb_entry(p, struct kbase_va_region, rblink);
181
182                 if (reg->gpu_alloc == NULL)
183                         /* Empty region - ignore */
184                         continue;
185
186                 mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
187
188                 mapping->alloc = kbase_mem_phy_alloc_get(reg->gpu_alloc);
189                 mapping->start_pfn = reg->start_pfn;
190                 mapping->nr_pages = reg->nr_pages;
191                 mapping->flags = reg->flags;
192                 list_add_tail(&mapping->node, &mem_data->mapping_list);
193         }
194
195         kbase_gpu_vm_unlock(kctx);
196
197         ((struct seq_file *)file->private_data)->private = mem_data;
198
199         return 0;
200 }
201
202 static int debug_mem_release(struct inode *inode, struct file *file)
203 {
204         struct file *kctx_file = inode->i_private;
205         struct seq_file *sfile = file->private_data;
206         struct debug_mem_data *mem_data = sfile->private;
207         struct debug_mem_mapping *mapping;
208
209         seq_release(inode, file);
210
211         while (!list_empty(&mem_data->mapping_list)) {
212                 mapping = list_first_entry(&mem_data->mapping_list,
213                                 struct debug_mem_mapping, node);
214                 kbase_mem_phy_alloc_put(mapping->alloc);
215                 list_del(&mapping->node);
216                 kfree(mapping);
217         }
218
219         kfree(mem_data);
220
221         fput(kctx_file);
222
223         return 0;
224 }
225
226 static const struct file_operations kbase_debug_mem_view_fops = {
227         .open = debug_mem_open,
228         .release = debug_mem_release,
229         .read = seq_read,
230         .llseek = seq_lseek
231 };
232
233 /**
234  * kbase_debug_mem_view_init - Initialise the mem_view sysfs file
235  * @kctx_file: The /dev/mali0 file instance for the context
236  *
237  * This function creates a "mem_view" file which can be used to get a view of
238  * the context's memory as the GPU sees it (i.e. using the GPU's page tables).
239  *
240  * The file is cleaned up by a call to debugfs_remove_recursive() deleting the
241  * parent directory.
242  */
243 void kbase_debug_mem_view_init(struct file *kctx_file)
244 {
245         struct kbase_context *kctx = kctx_file->private_data;
246
247         debugfs_create_file("mem_view", S_IRUGO, kctx->kctx_dentry, kctx_file,
248                         &kbase_debug_mem_view_fops);
249 }
250
251 #endif