Skip to content

Commit 4148575

Browse files
committed
s390/uaccess: add cmpxchg_user_key()
Add cmpxchg_user_key() which allows to execute a compare and exchange on a user space address. This allows also to specify a storage key which makes sure that key-controlled protection is considered. This is based on a patch written by Janis Schoetterl-Glausch. Link: https://lore.kernel.org/all/[email protected] Cc: Janis Schoetterl-Glausch <[email protected]> Link: https://lore.kernel.org/r/Y2J8axs+bcQ2dO/l@osiris Signed-off-by: Heiko Carstens <[email protected]>
1 parent f39a8c4 commit 4148575

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

arch/s390/include/asm/uaccess.h

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)