root/arch/s390/include/asm/ap.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Adjunct processor (AP) interfaces
 *
 * Copyright IBM Corp. 2017
 *
 * Author(s): Tony Krowiak <akrowia@linux.vnet.ibm.com>
 *            Martin Schwidefsky <schwidefsky@de.ibm.com>
 *            Harald Freudenberger <freude@de.ibm.com>
 */

#ifndef _ASM_S390_AP_H_
#define _ASM_S390_AP_H_

#include <linux/io.h>
#include <asm/asm-extable.h>

/**
 * The ap_qid_t identifier of an ap queue.
 * If the AP facilities test (APFT) facility is available,
 * card and queue index are 8 bit values, otherwise
 * card index is 6 bit and queue index a 4 bit value.
 */
typedef unsigned int ap_qid_t;

#define AP_MKQID(_card, _queue) (((_card) & 0xff) << 8 | ((_queue) & 0xff))
#define AP_QID_CARD(_qid) (((_qid) >> 8) & 0xff)
#define AP_QID_QUEUE(_qid) ((_qid) & 0xff)

/**
 * struct ap_queue_status - Holds the AP queue status.
 * @queue_empty: Shows if queue is empty
 * @replies_waiting: Waiting replies
 * @queue_full: Is 1 if the queue is full
 * @irq_enabled: Shows if interrupts are enabled for the AP
 * @response_code: Holds the 8 bit response code
 *
 * The ap queue status word is returned by all three AP functions
 * (PQAP, NQAP and DQAP).  There's a set of flags in the first
 * byte, followed by a 1 byte response code.
 *
 * For convenience the 'value' field is a 32 bit access of the
 * whole status and the 'status_bits' and 'rc' fields comprise
 * the leftmost 8 status bits and the response_code.
 */
struct ap_queue_status {
        union {
                unsigned int value                      : 32;
                struct {
                        unsigned int status_bits        : 8;
                        unsigned int rc                 : 8;
                        unsigned int                    : 16;
                };
                struct {
                        unsigned int queue_empty        : 1;
                        unsigned int replies_waiting    : 1;
                        unsigned int queue_full         : 1;
                        unsigned int                    : 3;
                        unsigned int async              : 1;
                        unsigned int irq_enabled        : 1;
                        unsigned int response_code      : 8;
                        unsigned int                    : 16;
                };
        };
};

/*
 * AP queue status reg union to access the reg1
 * register with the lower 32 bits comprising the
 * ap queue status.
 */
union ap_queue_status_reg {
        unsigned long value;
        struct {
                u32 _pad;
                struct ap_queue_status status;
        };
};

/**
 * ap_instructions_available() - Test if AP instructions are available.
 *
 * Returns true if the AP instructions are installed, otherwise false.
 */
static inline bool ap_instructions_available(void)
{
        unsigned long reg0 = AP_MKQID(0, 0);
        unsigned long reg1 = 0;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid into gr0 */
                "       lghi    1,0\n"                  /* 0 into gr1 */
                "       lghi    2,0\n"                  /* 0 into gr2 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(TAPQ) */
                "0:     la      %[reg1],1\n"            /* 1 into reg1 */
                "1:\n"
                EX_TABLE(0b, 1b)
                : [reg1] "+&d" (reg1)
                : [reg0] "d" (reg0)
                : "cc", "0", "1", "2");
        return reg1 != 0;
}

/* TAPQ register GR2 response struct */
struct ap_tapq_hwinfo {
        union {
                unsigned long value;
                struct {
                        unsigned int fac    : 32; /* facility bits */
                        unsigned int apinfo : 32; /* ap type, ... */
                };
                struct {
                        unsigned int apsc  :  1; /* APSC */
                        unsigned int mex4k :  1; /* AP4KM */
                        unsigned int crt4k :  1; /* AP4KC */
                        unsigned int cca   :  1; /* D */
                        unsigned int accel :  1; /* A */
                        unsigned int ep11  :  1; /* X */
                        unsigned int apxa  :  1; /* APXA */
                        unsigned int slcf  :  1; /* Cmd filtering avail. */
                        unsigned int class :  8;
                        unsigned int bs    :  2; /* SE bind/assoc */
                        unsigned int       : 14;
                        unsigned int at    :  8; /* ap type */
                        unsigned int nd    :  8; /* nr of domains */
                        unsigned int       :  4;
                        unsigned int ml    :  4; /* apxl ml */
                        unsigned int       :  4;
                        unsigned int qd    :  4; /* queue depth */
                };
        };
};

/*
 * Convenience defines to be used with the bs field from struct ap_tapq_gr2
 */
#define AP_BS_Q_USABLE                0
#define AP_BS_Q_USABLE_NO_SECURE_KEY  1
#define AP_BS_Q_AVAIL_FOR_BINDING     2
#define AP_BS_Q_UNUSABLE              3

/**
 * ap_tapq(): Test adjunct processor queue.
 * @qid: The AP queue number
 * @info: Pointer to tapq hwinfo struct
 *
 * Returns AP queue status structure.
 */
static inline struct ap_queue_status ap_tapq(ap_qid_t qid,
                                             struct ap_tapq_hwinfo *info)
{
        union ap_queue_status_reg reg1;
        unsigned long reg2;

        asm volatile(
                "       lgr     0,%[qid]\n"             /* qid into gr0 */
                "       lghi    2,0\n"                  /* 0 into gr2 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(TAPQ) */
                "       lgr     %[reg1],1\n"            /* gr1 (status) into reg1 */
                "       lgr     %[reg2],2"              /* gr2 into reg2 */
                : [reg1] "=&d" (reg1.value), [reg2] "=&d" (reg2)
                : [qid] "d" (qid)
                : "cc", "0", "1", "2");
        if (info)
                info->value = reg2;
        return reg1.status;
}

/**
 * ap_test_queue(): Test adjunct processor queue.
 * @qid: The AP queue number
 * @tbit: Test facilities bit
 * @info: Ptr to tapq gr2 struct
 *
 * Returns AP queue status structure.
 */
static inline struct ap_queue_status ap_test_queue(ap_qid_t qid, int tbit,
                                                   struct ap_tapq_hwinfo *info)
{
        if (tbit)
                qid |= 1UL << 23; /* set T bit*/
        return ap_tapq(qid, info);
}

/**
 * ap_pqap_rapq(): Reset adjunct processor queue.
 * @qid: The AP queue number
 * @fbit: if != 0 set F bit
 *
 * Returns AP queue status structure.
 */
static inline struct ap_queue_status ap_rapq(ap_qid_t qid, int fbit)
{
        unsigned long reg0 = qid | (1UL << 24);  /* fc 1UL is RAPQ */
        union ap_queue_status_reg reg1;

        if (fbit)
                reg0 |= 1UL << 22;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid arg into gr0 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(RAPQ) */
                "       lgr     %[reg1],1"              /* gr1 (status) into reg1 */
                : [reg1] "=&d" (reg1.value)
                : [reg0] "d" (reg0)
                : "cc", "0", "1");
        return reg1.status;
}

/**
 * ap_pqap_zapq(): Reset and zeroize adjunct processor queue.
 * @qid: The AP queue number
 * @fbit: if != 0 set F bit
 *
 * Returns AP queue status structure.
 */
static inline struct ap_queue_status ap_zapq(ap_qid_t qid, int fbit)
{
        unsigned long reg0 = qid | (2UL << 24);  /* fc 2UL is ZAPQ */
        union ap_queue_status_reg reg1;

        if (fbit)
                reg0 |= 1UL << 22;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid arg into gr0 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(ZAPQ) */
                "       lgr     %[reg1],1"              /* gr1 (status) into reg1 */
                : [reg1] "=&d" (reg1.value)
                : [reg0] "d" (reg0)
                : "cc", "0", "1");
        return reg1.status;
}

/**
 * struct ap_config_info - convenience struct for AP crypto
 * config info as returned by the ap_qci() function.
 */
struct ap_config_info {
        union {
                unsigned int flags;
                struct {
                        unsigned int apsc        : 1;   /* S bit */
                        unsigned int apxa        : 1;   /* N bit */
                        unsigned int qact        : 1;   /* C bit */
                        unsigned int rc8a        : 1;   /* R bit */
                        unsigned int             : 4;
                        unsigned int apsb        : 1;   /* B bit */
                        unsigned int             : 23;
                };
        };
        unsigned char na;               /* max # of APs - 1 */
        unsigned char nd;               /* max # of Domains - 1 */
        unsigned char _reserved0[10];
        unsigned int apm[8];            /* AP ID mask */
        unsigned int aqm[8];            /* AP (usage) queue mask */
        unsigned int adm[8];            /* AP (control) domain mask */
        unsigned char _reserved1[16];
} __aligned(8);

/**
 * ap_qci(): Get AP configuration data
 *
 * Returns 0 on success, or -EOPNOTSUPP.
 */
static inline int ap_qci(struct ap_config_info *config)
{
        unsigned long reg0 = 4UL << 24;  /* fc 4UL is QCI */
        unsigned long reg1 = -EOPNOTSUPP;
        struct ap_config_info *reg2 = config;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* QCI fc into gr0 */
                "       lgr     2,%[reg2]\n"            /* ptr to config into gr2 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(QCI) */
                "0:     la      %[reg1],0\n"            /* good case, QCI fc available */
                "1:\n"
                EX_TABLE(0b, 1b)
                : [reg1] "+&d" (reg1)
                : [reg0] "d" (reg0), [reg2] "d" (reg2)
                : "cc", "memory", "0", "2");

        return reg1;
}

/*
 * struct ap_qirq_ctrl - convenient struct for easy invocation
 * of the ap_aqic() function. This struct is passed as GR1
 * parameter to the PQAP(AQIC) instruction. For details please
 * see the AR documentation.
 */
union ap_qirq_ctrl {
        unsigned long value;
        struct {
                unsigned int       : 8;
                unsigned int zone  : 8; /* zone info */
                unsigned int ir    : 1; /* ir flag: enable (1) or disable (0) irq */
                unsigned int       : 4;
                unsigned int gisc  : 3; /* guest isc field */
                unsigned int       : 6;
                unsigned int gf    : 2; /* gisa format */
                unsigned int       : 1;
                unsigned int gisa  : 27;        /* gisa origin */
                unsigned int       : 1;
                unsigned int isc   : 3; /* irq sub class */
        };
};

/**
 * ap_aqic(): Control interruption for a specific AP.
 * @qid: The AP queue number
 * @qirqctrl: struct ap_qirq_ctrl (64 bit value)
 * @pa_ind: Physical address of the notification indicator byte
 *
 * Returns AP queue status.
 */
static inline struct ap_queue_status ap_aqic(ap_qid_t qid,
                                             union ap_qirq_ctrl qirqctrl,
                                             phys_addr_t pa_ind)
{
        unsigned long reg0 = qid | (3UL << 24);  /* fc 3UL is AQIC */
        union ap_queue_status_reg reg1;
        unsigned long reg2 = pa_ind;

        reg1.value = qirqctrl.value;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid param into gr0 */
                "       lgr     1,%[reg1]\n"            /* irq ctrl into gr1 */
                "       lgr     2,%[reg2]\n"            /* ni addr into gr2 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(AQIC) */
                "       lgr     %[reg1],1"              /* gr1 (status) into reg1 */
                : [reg1] "+&d" (reg1.value)
                : [reg0] "d" (reg0), [reg2] "d" (reg2)
                : "cc", "memory", "0", "1", "2");

        return reg1.status;
}

/*
 * union ap_qact_ap_info - used together with the
 * ap_aqic() function to provide a convenient way
 * to handle the ap info needed by the qact function.
 */
union ap_qact_ap_info {
        unsigned long val;
        struct {
                unsigned int      : 3;
                unsigned int mode : 3;
                unsigned int      : 26;
                unsigned int cat  : 8;
                unsigned int      : 8;
                unsigned char ver[2];
        };
};

/**
 * ap_qact(): Query AP compatibility type.
 * @qid: The AP queue number
 * @apinfo: On input the info about the AP queue. On output the
 *          alternate AP queue info provided by the qact function
 *          in GR2 is stored in.
 *
 * Returns AP queue status. Check response_code field for failures.
 */
static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit,
                                             union ap_qact_ap_info *apinfo)
{
        unsigned long reg0 = qid | (5UL << 24) | ((ifbit & 0x01) << 22);
        union ap_queue_status_reg reg1;
        unsigned long reg2;

        reg1.value = apinfo->val;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid param into gr0 */
                "       lgr     1,%[reg1]\n"            /* qact in info into gr1 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(QACT) */
                "       lgr     %[reg1],1\n"            /* gr1 (status) into reg1 */
                "       lgr     %[reg2],2"              /* qact out info into reg2 */
                : [reg1] "+&d" (reg1.value), [reg2] "=&d" (reg2)
                : [reg0] "d" (reg0)
                : "cc", "0", "1", "2");
        apinfo->val = reg2;
        return reg1.status;
}

/*
 * ap_bapq(): SE bind AP queue.
 * @qid: The AP queue number
 *
 * Returns AP queue status structure.
 *
 * Invoking this function in a non-SE environment
 * may case a specification exception.
 */
static inline struct ap_queue_status ap_bapq(ap_qid_t qid)
{
        unsigned long reg0 = qid | (7UL << 24);  /* fc 7 is BAPQ */
        union ap_queue_status_reg reg1;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid arg into gr0 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(BAPQ) */
                "       lgr     %[reg1],1"              /* gr1 (status) into reg1 */
                : [reg1] "=&d" (reg1.value)
                : [reg0] "d" (reg0)
                : "cc", "0", "1");

        return reg1.status;
}

/*
 * ap_aapq(): SE associate AP queue.
 * @qid: The AP queue number
 * @sec_idx: The secret index
 *
 * Returns AP queue status structure.
 *
 * Invoking this function in a non-SE environment
 * may case a specification exception.
 */
static inline struct ap_queue_status ap_aapq(ap_qid_t qid, unsigned int sec_idx)
{
        unsigned long reg0 = qid | (8UL << 24);  /* fc 8 is AAPQ */
        unsigned long reg2 = sec_idx;
        union ap_queue_status_reg reg1;

        asm volatile(
                "       lgr     0,%[reg0]\n"            /* qid arg into gr0 */
                "       lgr     2,%[reg2]\n"            /* secret index into gr2 */
                "       .insn   rre,0xb2af0000,0,0\n"   /* PQAP(AAPQ) */
                "       lgr     %[reg1],1"              /* gr1 (status) into reg1 */
                : [reg1] "=&d" (reg1.value)
                : [reg0] "d" (reg0), [reg2] "d" (reg2)
                : "cc", "0", "1", "2");

        return reg1.status;
}

/**
 * ap_nqap(): Send message to adjunct processor queue.
 * @qid: The AP queue number
 * @psmid: The program supplied message identifier
 * @msg: The message text
 * @length: The message length
 *
 * Returns AP queue status structure.
 * Condition code 1 on NQAP can't happen because the L bit is 1.
 * Condition code 2 on NQAP also means the send is incomplete,
 * because a segment boundary was reached. The NQAP is repeated.
 */
static inline struct ap_queue_status ap_nqap(ap_qid_t qid,
                                             unsigned long long psmid,
                                             void *msg, size_t length)
{
        unsigned long reg0 = qid | 0x40000000UL;  /* 0x4... is last msg part */
        union register_pair nqap_r1, nqap_r2;
        union ap_queue_status_reg reg1;

        nqap_r1.even = (unsigned int)(psmid >> 32);
        nqap_r1.odd  = psmid & 0xffffffff;
        nqap_r2.even = (unsigned long)msg;
        nqap_r2.odd  = (unsigned long)length;

        asm volatile (
                "       lgr     0,%[reg0]\n"  /* qid param in gr0 */
                "0:     .insn   rre,0xb2ad0000,%[nqap_r1],%[nqap_r2]\n"
                "       brc     2,0b\n"       /* handle partial completion */
                "       lgr     %[reg1],1"    /* gr1 (status) into reg1 */
                : [reg0] "+&d" (reg0), [reg1] "=&d" (reg1.value),
                  [nqap_r2] "+&d" (nqap_r2.pair)
                : [nqap_r1] "d" (nqap_r1.pair)
                : "cc", "memory", "0", "1");
        return reg1.status;
}

/**
 * ap_dqap(): Receive message from adjunct processor queue.
 * @qid: The AP queue number
 * @psmid: Pointer to program supplied message identifier
 * @msg: Pointer to message buffer
 * @msglen: Message buffer size
 * @length: Pointer to length of actually written bytes
 * @reslength: Residual length on return
 * @resgr0: input: gr0 value (only used if != 0), output: residual gr0 content
 *
 * Returns AP queue status structure.
 * Condition code 1 on DQAP means the receive has taken place
 * but only partially.  The response is incomplete, hence the
 * DQAP is repeated.
 * Condition code 2 on DQAP also means the receive is incomplete,
 * this time because a segment boundary was reached. Again, the
 * DQAP is repeated.
 * Note that gpr2 is used by the DQAP instruction to keep track of
 * any 'residual' length, in case the instruction gets interrupted.
 * Hence it gets zeroed before the instruction.
 * If the message does not fit into the buffer, this function will
 * return with a truncated message and the reply in the firmware queue
 * is not removed. This is indicated to the caller with an
 * ap_queue_status response_code value of all bits on (0xFF) and (if
 * the reslength ptr is given) the remaining length is stored in
 * *reslength and (if the resgr0 ptr is given) the updated gr0 value
 * for further processing of this msg entry is stored in *resgr0. The
 * caller needs to detect this situation and should invoke ap_dqap
 * with a valid resgr0 ptr and a value in there != 0 to indicate that
 * *resgr0 is to be used instead of qid to further process this entry.
 */
static inline struct ap_queue_status ap_dqap(ap_qid_t qid,
                                             unsigned long *psmid,
                                             void *msg, size_t msglen,
                                             size_t *length,
                                             size_t *reslength,
                                             unsigned long *resgr0)
{
        unsigned long reg0 = resgr0 && *resgr0 ? *resgr0 : qid | 0x80000000UL;
        union ap_queue_status_reg reg1;
        unsigned long reg2;
        union register_pair rp1, rp2;

        rp1.even = 0UL;
        rp1.odd  = 0UL;
        rp2.even = (unsigned long)msg;
        rp2.odd  = (unsigned long)msglen;

        asm volatile(
                "       lgr     0,%[reg0]\n"   /* qid param into gr0 */
                "       lghi    2,0\n"         /* 0 into gr2 (res length) */
                "0:     ltgr    %N[rp2],%N[rp2]\n" /* check buf len */
                "       jz      2f\n"          /* go out if buf len is 0 */
                "1:     .insn   rre,0xb2ae0000,%[rp1],%[rp2]\n"
                "       brc     6,0b\n"        /* handle partial complete */
                "2:     lgr     %[reg0],0\n"   /* gr0 (qid + info) into reg0 */
                "       lgr     %[reg1],1\n"   /* gr1 (status) into reg1 */
                "       lgr     %[reg2],2"     /* gr2 (res length) into reg2 */
                : [reg0] "+&d" (reg0), [reg1] "=&d" (reg1.value),
                  [reg2] "=&d" (reg2), [rp1] "+&d" (rp1.pair),
                  [rp2] "+&d" (rp2.pair)
                :
                : "cc", "memory", "0", "1", "2");

        if (reslength)
                *reslength = reg2;
        if (reg2 != 0 && rp2.odd == 0) {
                /*
                 * Partially complete, status in gr1 is not set.
                 * Signal the caller that this dqap is only partially received
                 * with a special status response code 0xFF and *resgr0 updated
                 */
                reg1.status.response_code = 0xFF;
                if (resgr0)
                        *resgr0 = reg0;
        } else {
                *psmid = (rp1.even << 32) + rp1.odd;
                if (resgr0)
                        *resgr0 = 0;
        }

        /* update *length with the nr of bytes stored into the msg buffer */
        if (length)
                *length = msglen - rp2.odd;

        return reg1.status;
}

#endif /* _ASM_S390_AP_H_ */