1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright IBM Corp. 1999, 2009
*
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __ASM_FACILITY_H
#define __ASM_FACILITY_H
#include <asm/facility-defs.h>
#include <linux/minmax.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/preempt.h>
#include <asm/alternative.h>
#include <asm/lowcore.h>
#define MAX_FACILITY_BIT (sizeof(stfle_fac_list) * 8)
extern u64 stfle_fac_list[16];
static inline void __set_facility(unsigned long nr, void *facilities)
{
unsigned char *ptr = (unsigned char *) facilities;
if (nr >= MAX_FACILITY_BIT)
return;
ptr[nr >> 3] |= 0x80 >> (nr & 7);
}
static inline void __clear_facility(unsigned long nr, void *facilities)
{
unsigned char *ptr = (unsigned char *) facilities;
if (nr >= MAX_FACILITY_BIT)
return;
ptr[nr >> 3] &= ~(0x80 >> (nr & 7));
}
static __always_inline bool __test_facility(unsigned long nr, void *facilities)
{
unsigned char *ptr;
if (nr >= MAX_FACILITY_BIT)
return false;
ptr = (unsigned char *) facilities + (nr >> 3);
return (*ptr & (0x80 >> (nr & 7))) != 0;
}
/*
* __test_facility_constant() generates a single instruction branch. If the
* tested facility is available (likely) the branch is patched into a nop.
*
* Do not use this function unless you know what you are doing. All users are
* supposed to use test_facility() which will do the right thing.
*/
static __always_inline bool __test_facility_constant(unsigned long nr)
{
asm goto(
ALTERNATIVE("brcl 15,%l[l_no]", "brcl 0,0", ALT_FACILITY(%[nr]))
:
: [nr] "i" (nr)
:
: l_no);
return true;
l_no:
return false;
}
/*
* The test_facility function uses the bit ordering where the MSB is bit 0.
* That makes it easier to query facility bits with the bit number as
* documented in the Principles of Operation.
*/
static __always_inline bool test_facility(unsigned long nr)
{
unsigned long facilities_als[] = { FACILITIES_ALS };
if (!__is_defined(__DECOMPRESSOR) && __builtin_constant_p(nr)) {
if (nr < sizeof(facilities_als) * 8) {
if (__test_facility(nr, &facilities_als))
return true;
}
return __test_facility_constant(nr);
}
return __test_facility(nr, &stfle_fac_list);
}
static inline unsigned long __stfle_asm(u64 *fac_list, int size)
{
unsigned long reg0 = size - 1;
asm volatile(
" lgr 0,%[reg0]\n"
" .insn s,0xb2b00000,%[list]\n" /* stfle */
" lgr %[reg0],0\n"
: [reg0] "+&d" (reg0), [list] "+Q" (*fac_list)
:
: "memory", "cc", "0");
return reg0;
}
/**
* stfle - Store facility list extended
* @fac_list: array where facility list can be stored
* @size: size of passed in array in double words
*/
static inline void __stfle(u64 *fac_list, int size)
{
unsigned long nr;
u32 stfl_fac_list;
asm volatile(
" stfl 0(0)\n"
: "=m" (get_lowcore()->stfl_fac_list));
stfl_fac_list = get_lowcore()->stfl_fac_list;
memcpy(fac_list, &stfl_fac_list, 4);
nr = 4; /* bytes stored by stfl */
if (stfl_fac_list & 0x01000000) {
/* More facility bits available with stfle */
nr = __stfle_asm(fac_list, size);
nr = min_t(unsigned long, (nr + 1) * 8, size * 8);
}
memset((char *)fac_list + nr, 0, size * 8 - nr);
}
static inline void stfle(u64 *fac_list, int size)
{
preempt_disable();
__stfle(fac_list, size);
preempt_enable();
}
/**
* stfle_size - Actual size of the facility list as specified by stfle
* (number of double words)
*/
unsigned int stfle_size(void);
#endif /* __ASM_FACILITY_H */
|