@@ -390,4 +390,187 @@ do { \
390390 goto err_label; \
391391} while (0)
392392
393+ void __cmpxchg_user_key_called_with_bad_pointer (void );
394+
395+ static __always_inline int __cmpxchg_user_key (unsigned long address , void * uval ,
396+ __uint128_t old , __uint128_t new ,
397+ unsigned long key , int size )
398+ {
399+ int rc = 0 ;
400+
401+ switch (size ) {
402+ case 1 : {
403+ unsigned int prev , tmp , shift ;
404+
405+ shift = (3 ^ (address & 3 )) << 3 ;
406+ address ^= address & 3 ;
407+ asm volatile (
408+ " spka 0(%[key])\n"
409+ " sacf 256\n"
410+ "0: l %[prev],%[address]\n"
411+ "1: nr %[prev],%[mask]\n"
412+ " lr %[tmp],%[prev]\n"
413+ " or %[prev],%[old]\n"
414+ " or %[tmp],%[new]\n"
415+ "2: cs %[prev],%[tmp],%[address]\n"
416+ "3: jnl 4f\n"
417+ " xr %[tmp],%[prev]\n"
418+ " nr %[tmp],%[mask]\n"
419+ " jnz 1b\n"
420+ "4: sacf 768\n"
421+ " spka %[default_key]\n"
422+ EX_TABLE_UA_LOAD_REG (0b , 4b , %[rc ], %[prev ])
423+ EX_TABLE_UA_LOAD_REG (1b , 4b , %[rc ], %[prev ])
424+ EX_TABLE_UA_LOAD_REG (2b , 4b , %[rc ], %[prev ])
425+ EX_TABLE_UA_LOAD_REG (3b , 4b , %[rc ], %[prev ])
426+ : [rc ] "+ & d " (rc),
427+ [prev ] "= & d " (prev),
428+ [tmp ] "= & d " (tmp),
429+ [address ] "+ Q " (*(int *)address)
430+ : [old ] "d" (((unsigned int )old & 0xff ) << shift ),
431+ [new ] "d" (((unsigned int )new & 0xff ) << shift ),
432+ [mask ] "d" (~(0xff << shift )),
433+ [key ] "a" (key << 4 ),
434+ [default_key ] "J" (PAGE_DEFAULT_KEY )
435+ : "memory ", " cc ");
436+ * (unsigned char * )uval = prev >> shift ;
437+ return rc ;
438+ }
439+ case 2 : {
440+ unsigned int prev , tmp , shift ;
441+
442+ shift = (2 ^ (address & 2 )) << 3 ;
443+ address ^= address & 2 ;
444+ asm volatile (
445+ " spka 0(%[key])\n"
446+ " sacf 256\n"
447+ "0: l %[prev],%[address]\n"
448+ "1: nr %[prev],%[mask]\n"
449+ " lr %[tmp],%[prev]\n"
450+ " or %[prev],%[old]\n"
451+ " or %[tmp],%[new]\n"
452+ "2: cs %[prev],%[tmp],%[address]\n"
453+ "3: jnl 4f\n"
454+ " xr %[tmp],%[prev]\n"
455+ " nr %[tmp],%[mask]\n"
456+ " jnz 1b\n"
457+ "4: sacf 768\n"
458+ " spka %[default_key]\n"
459+ EX_TABLE_UA_LOAD_REG (0b , 4b , %[rc ], %[prev ])
460+ EX_TABLE_UA_LOAD_REG (1b , 4b , %[rc ], %[prev ])
461+ EX_TABLE_UA_LOAD_REG (2b , 4b , %[rc ], %[prev ])
462+ EX_TABLE_UA_LOAD_REG (3b , 4b , %[rc ], %[prev ])
463+ : [rc ] "+ & d " (rc),
464+ [prev ] "= & d " (prev),
465+ [tmp ] "= & d " (tmp),
466+ [address ] "+ Q " (*(int *)address)
467+ : [old ] "d" (((unsigned int )old & 0xffff ) << shift ),
468+ [new ] "d" (((unsigned int )new & 0xffff ) << shift ),
469+ [mask ] "d" (~(0xffff << shift )),
470+ [key ] "a" (key << 4 ),
471+ [default_key ] "J" (PAGE_DEFAULT_KEY )
472+ : "memory ", " cc ");
473+ * (unsigned short * )uval = prev >> shift ;
474+ return rc ;
475+ }
476+ case 4 : {
477+ unsigned int prev = old ;
478+
479+ asm volatile (
480+ " spka 0(%[key])\n"
481+ " sacf 256\n"
482+ "0: cs %[prev],%[new],%[address]\n"
483+ "1: sacf 768\n"
484+ " spka %[default_key]\n"
485+ EX_TABLE_UA_LOAD_REG (0b , 1b , %[rc ], %[prev ])
486+ EX_TABLE_UA_LOAD_REG (1b , 1b , %[rc ], %[prev ])
487+ : [rc ] "+ & d " (rc),
488+ [prev ] "+ & d " (prev),
489+ [address ] "+ Q " (*(int *)address)
490+ : [new ] "d" ((unsigned int )new ),
491+ [key ] "a" (key << 4 ),
492+ [default_key ] "J" (PAGE_DEFAULT_KEY )
493+ : "memory ", " cc ");
494+ * (unsigned int * )uval = prev ;
495+ return rc ;
496+ }
497+ case 8 : {
498+ unsigned long prev = old ;
499+
500+ asm volatile (
501+ " spka 0(%[key])\n"
502+ " sacf 256\n"
503+ "0: csg %[prev],%[new],%[address]\n"
504+ "1: sacf 768\n"
505+ " spka %[default_key]\n"
506+ EX_TABLE_UA_LOAD_REG (0b , 1b , %[rc ], %[prev ])
507+ EX_TABLE_UA_LOAD_REG (1b , 1b , %[rc ], %[prev ])
508+ : [rc ] "+ & d " (rc),
509+ [prev ] "+ & d " (prev),
510+ [address ] "+ QS " (*(long *)address)
511+ : [new ] "d" ((unsigned long )new ),
512+ [key ] "a" (key << 4 ),
513+ [default_key ] "J" (PAGE_DEFAULT_KEY )
514+ : "memory ", " cc ");
515+ * (unsigned long * )uval = prev ;
516+ return rc ;
517+ }
518+ case 16 : {
519+ __uint128_t prev = old ;
520+
521+ asm volatile (
522+ " spka 0(%[key])\n"
523+ " sacf 256\n"
524+ "0: cdsg %[prev],%[new],%[address]\n"
525+ "1: sacf 768\n"
526+ " spka %[default_key]\n"
527+ EX_TABLE_UA_LOAD_REGPAIR (0b , 1b , %[rc ], %[prev ])
528+ EX_TABLE_UA_LOAD_REGPAIR (1b , 1b , %[rc ], %[prev ])
529+ : [rc ] "+ & d " (rc),
530+ [prev ] "+ & d " (prev),
531+ [address ] "+ QS " (*(__int128_t *)address)
532+ : [new ] "d" (new ),
533+ [key ] "a" (key << 4 ),
534+ [default_key ] "J" (PAGE_DEFAULT_KEY )
535+ : "memory ", " cc ");
536+ * (__uint128_t * )uval = prev ;
537+ return rc ;
538+ }
539+ }
540+ __cmpxchg_user_key_called_with_bad_pointer ();
541+ return rc ;
542+ }
543+
544+ /**
545+ * cmpxchg_user_key() - cmpxchg with user space target, honoring storage keys
546+ * @ptr: User space address of value to compare to @old and exchange with
547+ * @new. Must be aligned to sizeof(*@ptr).
548+ * @uval: Address where the old value of *@ptr is written to.
549+ * @old: Old value. Compared to the content pointed to by @ptr in order to
550+ * determine if the exchange occurs. The old value read from *@ptr is
551+ * written to *@uval.
552+ * @new: New value to place at *@ptr.
553+ * @key: Access key to use for checking storage key protection.
554+ *
555+ * Perform a cmpxchg on a user space target, honoring storage key protection.
556+ * @key alone determines how key checking is performed, neither
557+ * storage-protection-override nor fetch-protection-override apply.
558+ * The caller must compare *@uval and @old to determine if values have been
559+ * exchanged. In case of an exception *@uval is set to zero.
560+ *
561+ * Return: 0: cmpxchg executed
562+ * -EFAULT: an exception happened when trying to access *@ptr
563+ */
564+ #define cmpxchg_user_key (ptr , uval , old , new , key ) \
565+ ({ \
566+ __typeof__(ptr) __ptr = (ptr); \
567+ __typeof__(uval) __uval = (uval); \
568+ \
569+ BUILD_BUG_ON(sizeof(*(__ptr)) != sizeof(*(__uval))); \
570+ might_fault(); \
571+ __chk_user_ptr(__ptr); \
572+ __cmpxchg_user_key((unsigned long)(__ptr), (void *)(__uval), \
573+ (old), (new), (key), sizeof(*(__ptr))); \
574+ })
575+
393576#endif /* __S390_UACCESS_H */
0 commit comments