]>
Commit | Line | Data |
---|---|---|
a8c21a54 T |
1 | /* |
2 | * Copyright (C) 2015 Etnaviv Project | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License version 2 as published by | |
6 | * the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include <linux/devcoredump.h> | |
18 | #include "etnaviv_dump.h" | |
19 | #include "etnaviv_gem.h" | |
20 | #include "etnaviv_gpu.h" | |
21 | #include "etnaviv_mmu.h" | |
22 | #include "state.xml.h" | |
23 | #include "state_hi.xml.h" | |
24 | ||
25 | struct core_dump_iterator { | |
26 | void *start; | |
27 | struct etnaviv_dump_object_header *hdr; | |
28 | void *data; | |
29 | }; | |
30 | ||
31 | static const unsigned short etnaviv_dump_registers[] = { | |
32 | VIVS_HI_AXI_STATUS, | |
33 | VIVS_HI_CLOCK_CONTROL, | |
34 | VIVS_HI_IDLE_STATE, | |
35 | VIVS_HI_AXI_CONFIG, | |
36 | VIVS_HI_INTR_ENBL, | |
37 | VIVS_HI_CHIP_IDENTITY, | |
38 | VIVS_HI_CHIP_FEATURE, | |
39 | VIVS_HI_CHIP_MODEL, | |
40 | VIVS_HI_CHIP_REV, | |
41 | VIVS_HI_CHIP_DATE, | |
42 | VIVS_HI_CHIP_TIME, | |
43 | VIVS_HI_CHIP_MINOR_FEATURE_0, | |
44 | VIVS_HI_CACHE_CONTROL, | |
45 | VIVS_HI_AXI_CONTROL, | |
46 | VIVS_PM_POWER_CONTROLS, | |
47 | VIVS_PM_MODULE_CONTROLS, | |
48 | VIVS_PM_MODULE_STATUS, | |
49 | VIVS_PM_PULSE_EATER, | |
50 | VIVS_MC_MMU_FE_PAGE_TABLE, | |
51 | VIVS_MC_MMU_TX_PAGE_TABLE, | |
52 | VIVS_MC_MMU_PE_PAGE_TABLE, | |
53 | VIVS_MC_MMU_PEZ_PAGE_TABLE, | |
54 | VIVS_MC_MMU_RA_PAGE_TABLE, | |
55 | VIVS_MC_DEBUG_MEMORY, | |
56 | VIVS_MC_MEMORY_BASE_ADDR_RA, | |
57 | VIVS_MC_MEMORY_BASE_ADDR_FE, | |
58 | VIVS_MC_MEMORY_BASE_ADDR_TX, | |
59 | VIVS_MC_MEMORY_BASE_ADDR_PEZ, | |
60 | VIVS_MC_MEMORY_BASE_ADDR_PE, | |
61 | VIVS_MC_MEMORY_TIMING_CONTROL, | |
62 | VIVS_MC_BUS_CONFIG, | |
63 | VIVS_FE_DMA_STATUS, | |
64 | VIVS_FE_DMA_DEBUG_STATE, | |
65 | VIVS_FE_DMA_ADDRESS, | |
66 | VIVS_FE_DMA_LOW, | |
67 | VIVS_FE_DMA_HIGH, | |
68 | VIVS_FE_AUTO_FLUSH, | |
69 | }; | |
70 | ||
71 | static void etnaviv_core_dump_header(struct core_dump_iterator *iter, | |
72 | u32 type, void *data_end) | |
73 | { | |
74 | struct etnaviv_dump_object_header *hdr = iter->hdr; | |
75 | ||
76 | hdr->magic = cpu_to_le32(ETDUMP_MAGIC); | |
77 | hdr->type = cpu_to_le32(type); | |
78 | hdr->file_offset = cpu_to_le32(iter->data - iter->start); | |
79 | hdr->file_size = cpu_to_le32(data_end - iter->data); | |
80 | ||
81 | iter->hdr++; | |
82 | iter->data += hdr->file_size; | |
83 | } | |
84 | ||
85 | static void etnaviv_core_dump_registers(struct core_dump_iterator *iter, | |
86 | struct etnaviv_gpu *gpu) | |
87 | { | |
88 | struct etnaviv_dump_registers *reg = iter->data; | |
89 | unsigned int i; | |
90 | ||
91 | for (i = 0; i < ARRAY_SIZE(etnaviv_dump_registers); i++, reg++) { | |
92 | reg->reg = etnaviv_dump_registers[i]; | |
93 | reg->value = gpu_read(gpu, etnaviv_dump_registers[i]); | |
94 | } | |
95 | ||
96 | etnaviv_core_dump_header(iter, ETDUMP_BUF_REG, reg); | |
97 | } | |
98 | ||
99 | static void etnaviv_core_dump_mmu(struct core_dump_iterator *iter, | |
100 | struct etnaviv_gpu *gpu, size_t mmu_size) | |
101 | { | |
102 | etnaviv_iommu_dump(gpu->mmu, iter->data); | |
103 | ||
104 | etnaviv_core_dump_header(iter, ETDUMP_BUF_MMU, iter->data + mmu_size); | |
105 | } | |
106 | ||
107 | static void etnaviv_core_dump_mem(struct core_dump_iterator *iter, u32 type, | |
108 | void *ptr, size_t size, u64 iova) | |
109 | { | |
110 | memcpy(iter->data, ptr, size); | |
111 | ||
112 | iter->hdr->iova = cpu_to_le64(iova); | |
113 | ||
114 | etnaviv_core_dump_header(iter, type, iter->data + size); | |
115 | } | |
116 | ||
117 | void etnaviv_core_dump(struct etnaviv_gpu *gpu) | |
118 | { | |
119 | struct core_dump_iterator iter; | |
120 | struct etnaviv_vram_mapping *vram; | |
121 | struct etnaviv_gem_object *obj; | |
122 | struct etnaviv_cmdbuf *cmd; | |
123 | unsigned int n_obj, n_bomap_pages; | |
124 | size_t file_size, mmu_size; | |
125 | __le64 *bomap, *bomap_start; | |
126 | ||
127 | mmu_size = etnaviv_iommu_dump_size(gpu->mmu); | |
128 | ||
129 | /* We always dump registers, mmu, ring and end marker */ | |
130 | n_obj = 4; | |
131 | n_bomap_pages = 0; | |
132 | file_size = ARRAY_SIZE(etnaviv_dump_registers) * | |
133 | sizeof(struct etnaviv_dump_registers) + | |
134 | mmu_size + gpu->buffer->size; | |
135 | ||
136 | /* Add in the active command buffers */ | |
137 | list_for_each_entry(cmd, &gpu->active_cmd_list, node) { | |
138 | file_size += cmd->size; | |
139 | n_obj++; | |
140 | } | |
141 | ||
142 | /* Add in the active buffer objects */ | |
143 | list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { | |
144 | if (!vram->use) | |
145 | continue; | |
146 | ||
147 | obj = vram->object; | |
148 | file_size += obj->base.size; | |
149 | n_bomap_pages += obj->base.size >> PAGE_SHIFT; | |
150 | n_obj++; | |
151 | } | |
152 | ||
153 | /* If we have any buffer objects, add a bomap object */ | |
154 | if (n_bomap_pages) { | |
155 | file_size += n_bomap_pages * sizeof(__le64); | |
156 | n_obj++; | |
157 | } | |
158 | ||
159 | /* Add the size of the headers */ | |
160 | file_size += sizeof(*iter.hdr) * n_obj; | |
161 | ||
162 | /* Allocate the file in vmalloc memory, it's likely to be big */ | |
163 | iter.start = vmalloc(file_size); | |
164 | if (!iter.start) { | |
165 | dev_warn(gpu->dev, "failed to allocate devcoredump file\n"); | |
166 | return; | |
167 | } | |
168 | ||
169 | /* Point the data member after the headers */ | |
170 | iter.hdr = iter.start; | |
171 | iter.data = &iter.hdr[n_obj]; | |
172 | ||
173 | memset(iter.hdr, 0, iter.data - iter.start); | |
174 | ||
175 | etnaviv_core_dump_registers(&iter, gpu); | |
176 | etnaviv_core_dump_mmu(&iter, gpu, mmu_size); | |
177 | etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr, | |
178 | gpu->buffer->size, gpu->buffer->paddr); | |
179 | ||
180 | list_for_each_entry(cmd, &gpu->active_cmd_list, node) | |
181 | etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr, | |
182 | cmd->size, cmd->paddr); | |
183 | ||
184 | /* Reserve space for the bomap */ | |
185 | if (n_bomap_pages) { | |
186 | bomap_start = bomap = iter.data; | |
187 | memset(bomap, 0, sizeof(*bomap) * n_bomap_pages); | |
188 | etnaviv_core_dump_header(&iter, ETDUMP_BUF_BOMAP, | |
189 | bomap + n_bomap_pages); | |
190 | } else { | |
191 | /* Silence warning */ | |
192 | bomap_start = bomap = NULL; | |
193 | } | |
194 | ||
195 | list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { | |
196 | struct page **pages; | |
197 | void *vaddr; | |
198 | ||
199 | if (vram->use == 0) | |
200 | continue; | |
201 | ||
202 | obj = vram->object; | |
203 | ||
339073ef | 204 | mutex_lock(&obj->lock); |
a8c21a54 | 205 | pages = etnaviv_gem_get_pages(obj); |
339073ef | 206 | mutex_unlock(&obj->lock); |
a8c21a54 T |
207 | if (pages) { |
208 | int j; | |
209 | ||
210 | iter.hdr->data[0] = bomap - bomap_start; | |
211 | ||
212 | for (j = 0; j < obj->base.size >> PAGE_SHIFT; j++) | |
213 | *bomap++ = cpu_to_le64(page_to_phys(*pages++)); | |
214 | } | |
215 | ||
216 | iter.hdr->iova = cpu_to_le64(vram->iova); | |
217 | ||
ce3088fd | 218 | vaddr = etnaviv_gem_vmap(&obj->base); |
9f07bb0d | 219 | if (vaddr) |
a8c21a54 T |
220 | memcpy(iter.data, vaddr, obj->base.size); |
221 | ||
222 | etnaviv_core_dump_header(&iter, ETDUMP_BUF_BO, iter.data + | |
223 | obj->base.size); | |
224 | } | |
225 | ||
226 | etnaviv_core_dump_header(&iter, ETDUMP_BUF_END, iter.data); | |
227 | ||
228 | dev_coredumpv(gpu->dev, iter.start, iter.data - iter.start, GFP_KERNEL); | |
229 | } |