LOCAL / GPU: ARM: add MALI R12P0_04REL0 drivers
[platform/kernel/linux-exynos.git] / drivers / gpu / arm / midgard / r12p0_04rel0 / mali_kbase_debug_mem_view.c
1 /*
2  *
3  * (C) COPYRIGHT 2013-2016 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         if (!mapping)
129                 goto out;
130
131         for (i = 0; i < PAGE_SIZE; i += 4*sizeof(*mapping)) {
132                 seq_printf(m, "%016llx:", i + ((map->start_pfn +
133                                 data->offset) << PAGE_SHIFT));
134
135                 for (j = 0; j < 4*sizeof(*mapping); j += sizeof(*mapping))
136                         seq_printf(m, " %08x", mapping[(i+j)/sizeof(*mapping)]);
137                 seq_putc(m, '\n');
138         }
139
140         vunmap(mapping);
141
142         seq_putc(m, '\n');
143
144 out:
145         kbase_gpu_vm_unlock(mem_data->kctx);
146         return 0;
147 }
148
149 static const struct seq_operations ops = {
150         .start = debug_mem_start,
151         .next = debug_mem_next,
152         .stop = debug_mem_stop,
153         .show = debug_mem_show,
154 };
155
156 static int debug_mem_open(struct inode *i, struct file *file)
157 {
158         struct file *kctx_file = i->i_private;
159         struct kbase_context *kctx = kctx_file->private_data;
160         struct rb_node *p;
161         struct debug_mem_data *mem_data;
162         int ret;
163
164         ret = seq_open(file, &ops);
165         if (ret)
166                 return ret;
167
168         mem_data = kmalloc(sizeof(*mem_data), GFP_KERNEL);
169         if (!mem_data) {
170                 ret = -ENOMEM;
171                 goto out;
172         }
173
174         mem_data->kctx = kctx;
175
176         INIT_LIST_HEAD(&mem_data->mapping_list);
177
178         get_file(kctx_file);
179
180         kbase_gpu_vm_lock(kctx);
181
182         for (p = rb_first(&kctx->reg_rbtree); p; p = rb_next(p)) {
183                 struct kbase_va_region *reg;
184                 struct debug_mem_mapping *mapping;
185
186                 reg = rb_entry(p, struct kbase_va_region, rblink);
187
188                 if (reg->gpu_alloc == NULL)
189                         /* Empty region - ignore */
190                         continue;
191
192                 mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
193                 if (!mapping) {
194                         ret = -ENOMEM;
195                         kbase_gpu_vm_unlock(kctx);
196                         goto out;
197                 }
198
199                 mapping->alloc = kbase_mem_phy_alloc_get(reg->gpu_alloc);
200                 mapping->start_pfn = reg->start_pfn;
201                 mapping->nr_pages = reg->nr_pages;
202                 mapping->flags = reg->flags;
203                 list_add_tail(&mapping->node, &mem_data->mapping_list);
204         }
205
206         kbase_gpu_vm_unlock(kctx);
207
208         ((struct seq_file *)file->private_data)->private = mem_data;
209
210         return 0;
211
212 out:
213         if (mem_data) {
214                 while (!list_empty(&mem_data->mapping_list)) {
215                         struct debug_mem_mapping *mapping;
216
217                         mapping = list_first_entry(&mem_data->mapping_list,
218                                         struct debug_mem_mapping, node);
219                         kbase_mem_phy_alloc_put(mapping->alloc);
220                         list_del(&mapping->node);
221                         kfree(mapping);
222                 }
223                 fput(kctx_file);
224         }
225         seq_release(i, file);
226         return ret;
227 }
228
229 static int debug_mem_release(struct inode *inode, struct file *file)
230 {
231         struct file *kctx_file = inode->i_private;
232         struct seq_file *sfile = file->private_data;
233         struct debug_mem_data *mem_data = sfile->private;
234         struct debug_mem_mapping *mapping;
235
236         seq_release(inode, file);
237
238         while (!list_empty(&mem_data->mapping_list)) {
239                 mapping = list_first_entry(&mem_data->mapping_list,
240                                 struct debug_mem_mapping, node);
241                 kbase_mem_phy_alloc_put(mapping->alloc);
242                 list_del(&mapping->node);
243                 kfree(mapping);
244         }
245
246         kfree(mem_data);
247
248         fput(kctx_file);
249
250         return 0;
251 }
252
253 static const struct file_operations kbase_debug_mem_view_fops = {
254         .open = debug_mem_open,
255         .release = debug_mem_release,
256         .read = seq_read,
257         .llseek = seq_lseek
258 };
259
260 /**
261  * kbase_debug_mem_view_init - Initialise the mem_view sysfs file
262  * @kctx_file: The /dev/mali0 file instance for the context
263  *
264  * This function creates a "mem_view" file which can be used to get a view of
265  * the context's memory as the GPU sees it (i.e. using the GPU's page tables).
266  *
267  * The file is cleaned up by a call to debugfs_remove_recursive() deleting the
268  * parent directory.
269  */
270 void kbase_debug_mem_view_init(struct file *kctx_file)
271 {
272         struct kbase_context *kctx = kctx_file->private_data;
273
274         debugfs_create_file("mem_view", S_IRUGO, kctx->kctx_dentry, kctx_file,
275                         &kbase_debug_mem_view_fops);
276 }
277
278 #endif