44 */
55
66#include <linux/bitops.h>
7+ #include <linux/ctype.h>
78#include <linux/configfs.h>
89#include <linux/cleanup.h>
910#include <linux/find.h>
1213#include <linux/pci.h>
1314#include <linux/string.h>
1415
16+ #include "instructions/xe_mi_commands.h"
1517#include "xe_configfs.h"
1618#include "xe_hw_engine_types.h"
1719#include "xe_module.h"
115117 *
116118 * This attribute can only be set before binding to the device.
117119 *
120+ * Context restore BB
121+ * ------------------
122+ *
123+ * Allow to execute a batch buffer during any context switches. When the
124+ * GPU is restoring the context, it executes additional commands. It's useful
125+ * for testing additional workarounds and validating certain HW behaviors: it's
126+ * not intended for normal execution and will taint the kernel with TAINT_TEST
127+ * when used.
128+ *
129+ * Currently this is implemented only for post context restore. Examples:
130+ *
131+ * #. Execute a LRI command to write 0xDEADBEEF to register 0x4f10::
132+ *
133+ * # echo 'rcs cmd 11000001 4F100 DEADBEEF' \
134+ * > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb
135+ *
136+ * #. Load certain values in a couple of registers (it can be used as a simpler
137+ * alternative to the `cmd`) action::
138+ *
139+ * # cat > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb <<EOF
140+ * rcs reg 4F100 DEADBEEF
141+ * rcs reg 4F104 FFFFFFFF
142+ * EOF
143+ *
144+ * .. note::
145+ *
146+ * When using multiple lines, make sure to use a command that is
147+ * implemented with a single write syscall, like HEREDOC.
148+ *
149+ * This attribute can only be set before binding to the device.
150+ *
118151 * Remove devices
119152 * ==============
120153 *
123156 * # rmdir /sys/kernel/config/xe/0000:03:00.0/
124157 */
125158
159+ /* Similar to struct xe_bb, but not tied to HW (yet) */
160+ struct wa_bb {
161+ u32 * cs ;
162+ u32 len ; /* in dwords */
163+ };
164+
126165struct xe_config_group_device {
127166 struct config_group group ;
128167
129168 struct xe_config_device {
130169 u64 engines_allowed ;
170+ struct wa_bb ctx_restore_post_bb [XE_ENGINE_CLASS_MAX ];
131171 bool survivability_mode ;
132172 bool enable_psmi ;
133173 } config ;
@@ -371,11 +411,233 @@ static ssize_t enable_psmi_store(struct config_item *item, const char *page, siz
371411 return len ;
372412}
373413
414+ static bool wa_bb_read_advance (bool dereference , char * * p ,
415+ const char * append , size_t len ,
416+ size_t * max_size )
417+ {
418+ if (dereference ) {
419+ if (len >= * max_size )
420+ return false;
421+ * max_size -= len ;
422+ if (append )
423+ memcpy (* p , append , len );
424+ }
425+
426+ * p += len ;
427+
428+ return true;
429+ }
430+
431+ static ssize_t wa_bb_show (struct xe_config_group_device * dev ,
432+ struct wa_bb wa_bb [static XE_ENGINE_CLASS_MAX ],
433+ char * data , size_t sz )
434+ {
435+ char * p = data ;
436+
437+ guard (mutex )(& dev -> lock );
438+
439+ for (size_t i = 0 ; i < ARRAY_SIZE (engine_info ); i ++ ) {
440+ enum xe_engine_class ec = engine_info [i ].engine_class ;
441+ size_t len ;
442+
443+ if (!wa_bb [ec ].len )
444+ continue ;
445+
446+ len = snprintf (p , sz , "%s:" , engine_info [i ].cls );
447+ if (!wa_bb_read_advance (data , & p , NULL , len , & sz ))
448+ return - ENOBUFS ;
449+
450+ for (size_t j = 0 ; j < wa_bb [ec ].len ; j ++ ) {
451+ len = snprintf (p , sz , " %08x" , wa_bb [ec ].cs [j ]);
452+ if (!wa_bb_read_advance (data , & p , NULL , len , & sz ))
453+ return - ENOBUFS ;
454+ }
455+
456+ if (!wa_bb_read_advance (data , & p , "\n" , 1 , & sz ))
457+ return - ENOBUFS ;
458+ }
459+
460+ if (!wa_bb_read_advance (data , & p , "" , 1 , & sz ))
461+ return - ENOBUFS ;
462+
463+ /* Reserve one more to match check for '\0' */
464+ if (!data )
465+ p ++ ;
466+
467+ return p - data ;
468+ }
469+
470+ static ssize_t ctx_restore_post_bb_show (struct config_item * item , char * page )
471+ {
472+ struct xe_config_group_device * dev = to_xe_config_group_device (item );
473+
474+ return wa_bb_show (dev , dev -> config .ctx_restore_post_bb , page , SZ_4K );
475+ }
476+
477+ static void wa_bb_append (struct wa_bb * wa_bb , u32 val )
478+ {
479+ if (wa_bb -> cs )
480+ wa_bb -> cs [wa_bb -> len ] = val ;
481+
482+ wa_bb -> len ++ ;
483+ }
484+
485+ static ssize_t parse_hex (const char * line , u32 * pval )
486+ {
487+ char numstr [12 ];
488+ const char * p ;
489+ ssize_t numlen ;
490+
491+ p = line + strspn (line , " \t" );
492+ if (!* p || * p == '\n' )
493+ return 0 ;
494+
495+ numlen = strcspn (p , " \t\n" );
496+ if (!numlen || numlen >= sizeof (numstr ) - 1 )
497+ return - EINVAL ;
498+
499+ memcpy (numstr , p , numlen );
500+ numstr [numlen ] = '\0' ;
501+ p += numlen ;
502+
503+ if (kstrtou32 (numstr , 16 , pval ))
504+ return - EINVAL ;
505+
506+ return p - line ;
507+ }
508+
509+ /*
510+ * Parse lines with the format
511+ *
512+ * <engine-class> cmd <u32> <u32...>
513+ * <engine-class> reg <u32_addr> <u32_val>
514+ *
515+ * and optionally save them in @wa_bb[i].cs is non-NULL.
516+ *
517+ * Return the number of dwords parsed.
518+ */
519+ static ssize_t parse_wa_bb_lines (const char * lines ,
520+ struct wa_bb wa_bb [static XE_ENGINE_CLASS_MAX ])
521+ {
522+ ssize_t dwords = 0 , ret ;
523+ const char * p ;
524+
525+ for (p = lines ; * p ; p ++ ) {
526+ const struct engine_info * info = NULL ;
527+ u32 val , val2 ;
528+
529+ /* Also allow empty lines */
530+ p += strspn (p , " \t\n" );
531+ if (!* p )
532+ break ;
533+
534+ ret = parse_engine (p , " \t\n" , NULL , & info );
535+ if (ret < 0 )
536+ return ret ;
537+
538+ p += ret ;
539+ p += strspn (p , " \t" );
540+
541+ if (str_has_prefix (p , "cmd" )) {
542+ for (p += strlen ("cmd" ); * p ;) {
543+ ret = parse_hex (p , & val );
544+ if (ret < 0 )
545+ return - EINVAL ;
546+ if (!ret )
547+ break ;
548+
549+ p += ret ;
550+ dwords ++ ;
551+ wa_bb_append (& wa_bb [info -> engine_class ], val );
552+ }
553+ } else if (str_has_prefix (p , "reg" )) {
554+ p += strlen ("reg" );
555+ ret = parse_hex (p , & val );
556+ if (ret <= 0 )
557+ return - EINVAL ;
558+
559+ p += ret ;
560+ ret = parse_hex (p , & val2 );
561+ if (ret <= 0 )
562+ return - EINVAL ;
563+
564+ p += ret ;
565+ dwords += 3 ;
566+ wa_bb_append (& wa_bb [info -> engine_class ],
567+ MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS (1 ));
568+ wa_bb_append (& wa_bb [info -> engine_class ], val );
569+ wa_bb_append (& wa_bb [info -> engine_class ], val2 );
570+ } else {
571+ return - EINVAL ;
572+ }
573+ }
574+
575+ return dwords ;
576+ }
577+
578+ static ssize_t wa_bb_store (struct wa_bb wa_bb [static XE_ENGINE_CLASS_MAX ],
579+ struct xe_config_group_device * dev ,
580+ const char * page , size_t len )
581+ {
582+ /* tmp_wa_bb must match wa_bb's size */
583+ struct wa_bb tmp_wa_bb [XE_ENGINE_CLASS_MAX ] = { };
584+ ssize_t count , class ;
585+ u32 * tmp ;
586+
587+ /* 1. Count dwords - wa_bb[i].cs is NULL for all classes */
588+ count = parse_wa_bb_lines (page , tmp_wa_bb );
589+ if (count < 0 )
590+ return count ;
591+
592+ guard (mutex )(& dev -> lock );
593+
594+ if (is_bound (dev ))
595+ return - EBUSY ;
596+
597+ /*
598+ * 2. Allocate a u32 array and set the pointers to the right positions
599+ * according to the length of each class' wa_bb
600+ */
601+ tmp = krealloc (wa_bb [0 ].cs , count * sizeof (u32 ), GFP_KERNEL );
602+ if (!tmp )
603+ return - ENOMEM ;
604+
605+ if (!count ) {
606+ memset (wa_bb , 0 , sizeof (tmp_wa_bb ));
607+ return len ;
608+ }
609+
610+ for (class = 0 , count = 0 ; class < XE_ENGINE_CLASS_MAX ; ++ class ) {
611+ tmp_wa_bb [class ].cs = tmp + count ;
612+ count += tmp_wa_bb [class ].len ;
613+ tmp_wa_bb [class ].len = 0 ;
614+ }
615+
616+ /* 3. Parse wa_bb lines again, this time saving the values */
617+ count = parse_wa_bb_lines (page , tmp_wa_bb );
618+ if (count < 0 )
619+ return count ;
620+
621+ memcpy (wa_bb , tmp_wa_bb , sizeof (tmp_wa_bb ));
622+
623+ return len ;
624+ }
625+
626+ static ssize_t ctx_restore_post_bb_store (struct config_item * item ,
627+ const char * data , size_t sz )
628+ {
629+ struct xe_config_group_device * dev = to_xe_config_group_device (item );
630+
631+ return wa_bb_store (dev -> config .ctx_restore_post_bb , dev , data , sz );
632+ }
633+
634+ CONFIGFS_ATTR (, ctx_restore_post_bb );
374635CONFIGFS_ATTR (, enable_psmi );
375636CONFIGFS_ATTR (, engines_allowed );
376637CONFIGFS_ATTR (, survivability_mode );
377638
378639static struct configfs_attribute * xe_config_device_attrs [] = {
640+ & attr_ctx_restore_post_bb ,
379641 & attr_enable_psmi ,
380642 & attr_engines_allowed ,
381643 & attr_survivability_mode ,
@@ -387,6 +649,8 @@ static void xe_config_device_release(struct config_item *item)
387649 struct xe_config_group_device * dev = to_xe_config_group_device (item );
388650
389651 mutex_destroy (& dev -> lock );
652+
653+ kfree (dev -> config .ctx_restore_post_bb [0 ].cs );
390654 kfree (dev );
391655}
392656
@@ -636,14 +900,26 @@ bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev)
636900/**
637901 * xe_configfs_get_ctx_restore_post_bb - get configfs ctx_restore_post_bb setting
638902 * @pdev: pci device
903+ * @class: hw engine class
904+ * @cs: pointer to the bb to use - only valid during probe
639905 *
640- * Return: post_ctx_restore setting in configfs
906+ * Return: Number of dwords used in the post_ctx_restore setting in configfs
641907 */
642908u32 xe_configfs_get_ctx_restore_post_bb (struct pci_dev * pdev ,
643909 enum xe_engine_class class ,
644910 const u32 * * cs )
645911{
646- return 0 ;
912+ struct xe_config_group_device * dev = find_xe_config_group_device (pdev );
913+ u32 len ;
914+
915+ if (!dev )
916+ return 0 ;
917+
918+ * cs = dev -> config .ctx_restore_post_bb [class ].cs ;
919+ len = dev -> config .ctx_restore_post_bb [class ].len ;
920+ config_group_put (& dev -> group );
921+
922+ return len ;
647923}
648924
649925int __init xe_configfs_init (void )
0 commit comments