5
5
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
6
6
*/
7
7
8
+ #include <linux/completion.h>
8
9
#include <linux/devcoredump.h>
9
10
#include <linux/device.h>
10
11
#include <linux/kernel.h>
11
12
#include <linux/remoteproc.h>
12
13
#include "remoteproc_internal.h"
13
14
#include "remoteproc_elf_helpers.h"
14
15
16
+ struct rproc_coredump_state {
17
+ struct rproc * rproc ;
18
+ void * header ;
19
+ struct completion dump_done ;
20
+ };
21
+
15
22
/**
16
23
* rproc_coredump_cleanup() - clean up dump_segments list
17
24
* @rproc: the remote processor handle
@@ -115,12 +122,110 @@ int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine)
115
122
}
116
123
EXPORT_SYMBOL (rproc_coredump_set_elf_info );
117
124
125
+ static void rproc_coredump_free (void * data )
126
+ {
127
+ struct rproc_coredump_state * dump_state = data ;
128
+
129
+ vfree (dump_state -> header );
130
+ complete (& dump_state -> dump_done );
131
+ }
132
+
133
+ static void * rproc_coredump_find_segment (loff_t user_offset ,
134
+ struct list_head * segments ,
135
+ size_t * data_left )
136
+ {
137
+ struct rproc_dump_segment * segment ;
138
+
139
+ list_for_each_entry (segment , segments , node ) {
140
+ if (user_offset < segment -> size ) {
141
+ * data_left = segment -> size - user_offset ;
142
+ return segment ;
143
+ }
144
+ user_offset -= segment -> size ;
145
+ }
146
+
147
+ * data_left = 0 ;
148
+ return NULL ;
149
+ }
150
+
151
+ static void rproc_copy_segment (struct rproc * rproc , void * dest ,
152
+ struct rproc_dump_segment * segment ,
153
+ size_t offset , size_t size )
154
+ {
155
+ void * ptr ;
156
+
157
+ if (segment -> dump ) {
158
+ segment -> dump (rproc , segment , dest , offset , size );
159
+ } else {
160
+ ptr = rproc_da_to_va (rproc , segment -> da + offset , size );
161
+ if (!ptr ) {
162
+ dev_err (& rproc -> dev ,
163
+ "invalid copy request for segment %pad with offset %zu and size %zu)\n" ,
164
+ & segment -> da , offset , size );
165
+ memset (dest , 0xff , size );
166
+ } else {
167
+ memcpy (dest , ptr , size );
168
+ }
169
+ }
170
+ }
171
+
172
+ static ssize_t rproc_coredump_read (char * buffer , loff_t offset , size_t count ,
173
+ void * data , size_t header_sz )
174
+ {
175
+ size_t seg_data , bytes_left = count ;
176
+ ssize_t copy_sz ;
177
+ struct rproc_dump_segment * seg ;
178
+ struct rproc_coredump_state * dump_state = data ;
179
+ struct rproc * rproc = dump_state -> rproc ;
180
+ void * elfcore = dump_state -> header ;
181
+
182
+ /* Copy the vmalloc'ed header first. */
183
+ if (offset < header_sz ) {
184
+ copy_sz = memory_read_from_buffer (buffer , count , & offset ,
185
+ elfcore , header_sz );
186
+
187
+ return copy_sz ;
188
+ }
189
+
190
+ /*
191
+ * Find out the segment memory chunk to be copied based on offset.
192
+ * Keep copying data until count bytes are read.
193
+ */
194
+ while (bytes_left ) {
195
+ seg = rproc_coredump_find_segment (offset - header_sz ,
196
+ & rproc -> dump_segments ,
197
+ & seg_data );
198
+ /* EOF check */
199
+ if (!seg ) {
200
+ dev_info (& rproc -> dev , "Ramdump done, %lld bytes read" ,
201
+ offset );
202
+ break ;
203
+ }
204
+
205
+ copy_sz = min_t (size_t , bytes_left , seg_data );
206
+
207
+ rproc_copy_segment (rproc , buffer , seg , seg -> size - seg_data ,
208
+ copy_sz );
209
+
210
+ offset += copy_sz ;
211
+ buffer += copy_sz ;
212
+ bytes_left -= copy_sz ;
213
+ }
214
+
215
+ return count - bytes_left ;
216
+ }
217
+
118
218
/**
119
219
* rproc_coredump() - perform coredump
120
220
* @rproc: rproc handle
121
221
*
122
222
* This function will generate an ELF header for the registered segments
123
- * and create a devcoredump device associated with rproc.
223
+ * and create a devcoredump device associated with rproc. Based on the
224
+ * coredump configuration this function will directly copy the segments
225
+ * from device memory to userspace or copy segments from device memory to
226
+ * a separate buffer, which can then be read by userspace.
227
+ * The first approach avoids using extra vmalloc memory. But it will stall
228
+ * recovery flow until dump is read by userspace.
124
229
*/
125
230
void rproc_coredump (struct rproc * rproc )
126
231
{
@@ -130,11 +235,13 @@ void rproc_coredump(struct rproc *rproc)
130
235
size_t data_size ;
131
236
size_t offset ;
132
237
void * data ;
133
- void * ptr ;
134
238
u8 class = rproc -> elf_class ;
135
239
int phnum = 0 ;
240
+ struct rproc_coredump_state dump_state ;
241
+ enum rproc_dump_mechanism dump_conf = rproc -> dump_conf ;
136
242
137
- if (list_empty (& rproc -> dump_segments ))
243
+ if (list_empty (& rproc -> dump_segments ) ||
244
+ dump_conf == RPROC_COREDUMP_DISABLED )
138
245
return ;
139
246
140
247
if (class == ELFCLASSNONE ) {
@@ -144,7 +251,14 @@ void rproc_coredump(struct rproc *rproc)
144
251
145
252
data_size = elf_size_of_hdr (class );
146
253
list_for_each_entry (segment , & rproc -> dump_segments , node ) {
147
- data_size += elf_size_of_phdr (class ) + segment -> size ;
254
+ /*
255
+ * For default configuration buffer includes headers & segments.
256
+ * For inline dump buffer just includes headers as segments are
257
+ * directly read from device memory.
258
+ */
259
+ data_size += elf_size_of_phdr (class );
260
+ if (dump_conf == RPROC_COREDUMP_DEFAULT )
261
+ data_size += segment -> size ;
148
262
149
263
phnum ++ ;
150
264
}
@@ -183,23 +297,29 @@ void rproc_coredump(struct rproc *rproc)
183
297
elf_phdr_set_p_flags (class , phdr , PF_R | PF_W | PF_X );
184
298
elf_phdr_set_p_align (class , phdr , 0 );
185
299
186
- if (segment -> dump ) {
187
- segment -> dump (rproc , segment , data + offset , 0 , segment -> size );
188
- } else {
189
- ptr = rproc_da_to_va (rproc , segment -> da , segment -> size );
190
- if (!ptr ) {
191
- dev_err (& rproc -> dev ,
192
- "invalid coredump segment (%pad, %zu)\n" ,
193
- & segment -> da , segment -> size );
194
- memset (data + offset , 0xff , segment -> size );
195
- } else {
196
- memcpy (data + offset , ptr , segment -> size );
197
- }
198
- }
300
+ if (dump_conf == RPROC_COREDUMP_DEFAULT )
301
+ rproc_copy_segment (rproc , data + offset , segment , 0 ,
302
+ segment -> size );
199
303
200
304
offset += elf_phdr_get_p_filesz (class , phdr );
201
305
phdr += elf_size_of_phdr (class );
202
306
}
307
+ if (dump_conf == RPROC_COREDUMP_DEFAULT ) {
308
+ dev_coredumpv (& rproc -> dev , data , data_size , GFP_KERNEL );
309
+ return ;
310
+ }
311
+
312
+ /* Initialize the dump state struct to be used by rproc_coredump_read */
313
+ dump_state .rproc = rproc ;
314
+ dump_state .header = data ;
315
+ init_completion (& dump_state .dump_done );
316
+
317
+ dev_coredumpm (& rproc -> dev , NULL , & dump_state , data_size , GFP_KERNEL ,
318
+ rproc_coredump_read , rproc_coredump_free );
203
319
204
- dev_coredumpv (& rproc -> dev , data , data_size , GFP_KERNEL );
320
+ /*
321
+ * Wait until the dump is read and free is called. Data is freed
322
+ * by devcoredump framework automatically after 5 minutes.
323
+ */
324
+ wait_for_completion (& dump_state .dump_done );
205
325
}
0 commit comments