@@ -105,6 +105,128 @@ u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata)
105105 return mask ;
106106}
107107
108+ static bool is_tmc_crashdata_valid (struct tmc_drvdata * drvdata )
109+ {
110+ struct tmc_crash_metadata * mdata ;
111+
112+ if (!tmc_has_reserved_buffer (drvdata ) ||
113+ !tmc_has_crash_mdata_buffer (drvdata ))
114+ return false;
115+
116+ mdata = drvdata -> crash_mdata .vaddr ;
117+
118+ /* Check version match */
119+ if (mdata -> version != CS_CRASHDATA_VERSION )
120+ return false;
121+
122+ /* Check for valid metadata */
123+ if (!mdata -> valid ) {
124+ dev_dbg (& drvdata -> csdev -> dev ,
125+ "Data invalid in tmc crash metadata\n" );
126+ return false;
127+ }
128+
129+ /*
130+ * Buffer address given by metadata for retrieval of trace data
131+ * from previous boot is expected to be same as the reserved
132+ * trace buffer memory region provided through DTS
133+ */
134+ if (drvdata -> resrv_buf .paddr != mdata -> trace_paddr ) {
135+ dev_dbg (& drvdata -> csdev -> dev ,
136+ "Trace buffer address of previous boot invalid\n" );
137+ return false;
138+ }
139+
140+ /* Check data integrity of metadata */
141+ if (mdata -> crc32_mdata != find_crash_metadata_crc (mdata )) {
142+ dev_err (& drvdata -> csdev -> dev ,
143+ "CRC mismatch in tmc crash metadata\n" );
144+ return false;
145+ }
146+ /* Check data integrity of tracedata */
147+ if (mdata -> crc32_tdata != find_crash_tracedata_crc (drvdata , mdata )) {
148+ dev_err (& drvdata -> csdev -> dev ,
149+ "CRC mismatch in tmc crash tracedata\n" );
150+ return false;
151+ }
152+
153+ return true;
154+ }
155+
156+ static inline ssize_t tmc_get_resvbuf_trace (struct tmc_drvdata * drvdata ,
157+ loff_t pos , size_t len , char * * bufpp )
158+ {
159+ s64 offset ;
160+ ssize_t actual = len ;
161+ struct tmc_resrv_buf * rbuf = & drvdata -> resrv_buf ;
162+
163+ if (pos + actual > rbuf -> len )
164+ actual = rbuf -> len - pos ;
165+ if (actual <= 0 )
166+ return 0 ;
167+
168+ /* Compute the offset from which we read the data */
169+ offset = rbuf -> offset + pos ;
170+ if (offset >= rbuf -> size )
171+ offset -= rbuf -> size ;
172+
173+ /* Adjust the length to limit this transaction to end of buffer */
174+ actual = (actual < (rbuf -> size - offset )) ?
175+ actual : rbuf -> size - offset ;
176+
177+ * bufpp = (char * )rbuf -> vaddr + offset ;
178+
179+ return actual ;
180+ }
181+
182+ static int tmc_prepare_crashdata (struct tmc_drvdata * drvdata )
183+ {
184+ char * bufp ;
185+ ssize_t len ;
186+ u32 status , size ;
187+ u64 rrp , rwp , dba ;
188+ struct tmc_resrv_buf * rbuf ;
189+ struct tmc_crash_metadata * mdata ;
190+
191+ mdata = drvdata -> crash_mdata .vaddr ;
192+ rbuf = & drvdata -> resrv_buf ;
193+
194+ rrp = mdata -> tmc_rrp ;
195+ rwp = mdata -> tmc_rwp ;
196+ dba = mdata -> tmc_dba ;
197+ status = mdata -> tmc_sts ;
198+ size = mdata -> tmc_ram_size << 2 ;
199+
200+ /* Sync the buffer pointers */
201+ rbuf -> offset = rrp - dba ;
202+ if (status & TMC_STS_FULL )
203+ rbuf -> len = size ;
204+ else
205+ rbuf -> len = rwp - rrp ;
206+
207+ /* Additional sanity checks for validating metadata */
208+ if ((rbuf -> offset > size ) ||
209+ (rbuf -> len > size )) {
210+ dev_dbg (& drvdata -> csdev -> dev ,
211+ "Offset and length invalid in tmc crash metadata\n" );
212+ return - EINVAL ;
213+ }
214+
215+ if (status & TMC_STS_FULL ) {
216+ len = tmc_get_resvbuf_trace (drvdata , 0x0 ,
217+ CORESIGHT_BARRIER_PKT_SIZE , & bufp );
218+ if (len >= CORESIGHT_BARRIER_PKT_SIZE ) {
219+ coresight_insert_barrier_packet (bufp );
220+ /* Recalculate crc */
221+ mdata -> crc32_tdata = find_crash_tracedata_crc (drvdata ,
222+ mdata );
223+ mdata -> crc32_mdata = find_crash_metadata_crc (mdata );
224+ }
225+ }
226+
227+ return 0 ;
228+ }
229+
108230static int tmc_read_prepare (struct tmc_drvdata * drvdata )
109231{
110232 int ret = 0 ;
@@ -223,6 +345,84 @@ static const struct file_operations tmc_fops = {
223345 .release = tmc_release ,
224346};
225347
348+ static int tmc_crashdata_open (struct inode * inode , struct file * file )
349+ {
350+ int err = 0 ;
351+ unsigned long flags ;
352+ struct tmc_resrv_buf * rbuf ;
353+ struct tmc_crash_metadata * mdata ;
354+ struct tmc_drvdata * drvdata = container_of (file -> private_data ,
355+ struct tmc_drvdata ,
356+ crashdev );
357+
358+ mdata = drvdata -> crash_mdata .vaddr ;
359+ rbuf = & drvdata -> resrv_buf ;
360+
361+ spin_lock_irqsave (& drvdata -> spinlock , flags );
362+ if (mdata -> valid )
363+ rbuf -> reading = true;
364+ else
365+ err = - ENOENT ;
366+ spin_unlock_irqrestore (& drvdata -> spinlock , flags );
367+ if (err )
368+ goto exit ;
369+
370+ nonseekable_open (inode , file );
371+ dev_dbg (& drvdata -> csdev -> dev , "%s: successfully opened\n" , __func__ );
372+ exit :
373+ return err ;
374+ }
375+
376+ static ssize_t tmc_crashdata_read (struct file * file , char __user * data ,
377+ size_t len , loff_t * ppos )
378+ {
379+ char * bufp ;
380+ ssize_t actual ;
381+ struct tmc_drvdata * drvdata = container_of (file -> private_data ,
382+ struct tmc_drvdata ,
383+ crashdev );
384+
385+ actual = tmc_get_resvbuf_trace (drvdata , * ppos , len , & bufp );
386+ if (actual <= 0 )
387+ return 0 ;
388+
389+ if (copy_to_user (data , bufp , actual )) {
390+ dev_dbg (& drvdata -> csdev -> dev ,
391+ "%s: copy_to_user failed\n" , __func__ );
392+ return - EFAULT ;
393+ }
394+
395+ * ppos += actual ;
396+ dev_dbg (& drvdata -> csdev -> dev , "%zu bytes copied\n" , actual );
397+
398+ return actual ;
399+ }
400+
401+ static int tmc_crashdata_release (struct inode * inode , struct file * file )
402+ {
403+ int ret = 0 ;
404+ unsigned long flags ;
405+ struct tmc_resrv_buf * rbuf ;
406+ struct tmc_drvdata * drvdata = container_of (file -> private_data ,
407+ struct tmc_drvdata ,
408+ crashdev );
409+
410+ rbuf = & drvdata -> resrv_buf ;
411+ spin_lock_irqsave (& drvdata -> spinlock , flags );
412+ rbuf -> reading = false;
413+ spin_unlock_irqrestore (& drvdata -> spinlock , flags );
414+
415+ dev_dbg (& drvdata -> csdev -> dev , "%s: released\n" , __func__ );
416+ return ret ;
417+ }
418+
419+ static const struct file_operations tmc_crashdata_fops = {
420+ .owner = THIS_MODULE ,
421+ .open = tmc_crashdata_open ,
422+ .read = tmc_crashdata_read ,
423+ .release = tmc_crashdata_release ,
424+ };
425+
226426static enum tmc_mem_intf_width tmc_get_memwidth (u32 devid )
227427{
228428 enum tmc_mem_intf_width memwidth ;
@@ -532,6 +732,22 @@ static u32 tmc_etr_get_max_burst_size(struct device *dev)
532732 return burst_size ;
533733}
534734
735+ static void register_crash_dev_interface (struct tmc_drvdata * drvdata ,
736+ const char * name )
737+ {
738+ drvdata -> crashdev .name =
739+ devm_kasprintf (& drvdata -> csdev -> dev , GFP_KERNEL , "%s_%s" , "crash" , name );
740+ drvdata -> crashdev .minor = MISC_DYNAMIC_MINOR ;
741+ drvdata -> crashdev .fops = & tmc_crashdata_fops ;
742+ if (misc_register (& drvdata -> crashdev )) {
743+ dev_dbg (& drvdata -> csdev -> dev ,
744+ "Failed to setup user interface for crashdata\n" );
745+ drvdata -> crashdev .fops = NULL ;
746+ } else
747+ dev_info (& drvdata -> csdev -> dev ,
748+ "Valid crash tracedata found\n" );
749+ }
750+
535751static int __tmc_probe (struct device * dev , struct resource * res )
536752{
537753 int ret = 0 ;
@@ -632,9 +848,15 @@ static int __tmc_probe(struct device *dev, struct resource *res)
632848 drvdata -> miscdev .minor = MISC_DYNAMIC_MINOR ;
633849 drvdata -> miscdev .fops = & tmc_fops ;
634850 ret = misc_register (& drvdata -> miscdev );
635- if (ret )
851+ if (ret ) {
636852 coresight_unregister (drvdata -> csdev );
853+ goto out ;
854+ }
855+
637856out :
857+ if (is_tmc_crashdata_valid (drvdata ) &&
858+ !tmc_prepare_crashdata (drvdata ))
859+ register_crash_dev_interface (drvdata , desc .name );
638860 return ret ;
639861}
640862
@@ -687,6 +909,8 @@ static void __tmc_remove(struct device *dev)
687909 * handler to this device is closed.
688910 */
689911 misc_deregister (& drvdata -> miscdev );
912+ if (drvdata -> crashdev .fops )
913+ misc_deregister (& drvdata -> crashdev );
690914 coresight_unregister (drvdata -> csdev );
691915}
692916
0 commit comments