s390/ap: get rid of register asm

Using register asm statements has been proven to be very error prone,
especially when using code instrumentation where gcc may add function
calls, which clobbers register contents in an unexpected way.

Therefore get rid of register asm statements in ap code. There are also
potential bugs, depending on inline decisions of the compiler.

E.g. for:

static inline struct ap_queue_status ap_tapq(ap_qid_t qid, unsigned long *info)
{
	register unsigned long reg0 asm ("0") = qid;
	register struct ap_queue_status reg1 asm ("1");
	register unsigned long reg2 asm ("2");

	asm volatile(".long 0xb2af0000"         /* PQAP(TAPQ) */
		     : "=d" (reg1), "=d" (reg2)
		     : "d" (reg0)
		     : "cc");
	if (info)
		*info = reg2;
	return reg1;
}

In case of KCOV the "if (info)" line could cause a generated function
call, which could clobber the contents of both reg2, and reg1.

Similar can happen in case of KASAN for the "*info = reg2" line.

Even though compilers will likely inline the function and optimize
things away, this is not guaranteed.

To get rid of this bug class, simply get rid of register asm constructs.

Note: The inline function ap_dqap() will be handled in a
separate patch because this one requires an addressing of the
odd register of a register pair (which is done with %N[xxx] in
the assembler code) and that's currently not supported by clang.

Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
Heiko Carstens
2021-07-05 12:06:55 +02:00
committed by Vasily Gorbik
parent 0aa4ff7688
commit b9639b3155

View File

@@ -53,18 +53,20 @@ struct ap_queue_status {
*/ */
static inline bool ap_instructions_available(void) static inline bool ap_instructions_available(void)
{ {
register unsigned long reg0 asm ("0") = AP_MKQID(0, 0); unsigned long reg0 = AP_MKQID(0, 0);
register unsigned long reg1 asm ("1") = 0; unsigned long reg1 = 0;
register unsigned long reg2 asm ("2") = 0;
asm volatile( asm volatile(
" .long 0xb2af0000\n" /* PQAP(TAPQ) */ " lgr 0,%[reg0]\n" /* qid into gr0 */
"0: la %0,1\n" " lghi 1,0\n" /* 0 into gr1 */
" lghi 2,0\n" /* 0 into gr2 */
" .long 0xb2af0000\n" /* PQAP(TAPQ) */
"0: la %[reg1],1\n" /* 1 into reg1 */
"1:\n" "1:\n"
EX_TABLE(0b, 1b) EX_TABLE(0b, 1b)
: "+d" (reg1), "+d" (reg2) : [reg1] "+&d" (reg1)
: "d" (reg0) : [reg0] "d" (reg0)
: "cc"); : "cc", "0", "1", "2");
return reg1 != 0; return reg1 != 0;
} }
@@ -77,14 +79,18 @@ static inline bool ap_instructions_available(void)
*/ */
static inline struct ap_queue_status ap_tapq(ap_qid_t qid, unsigned long *info) static inline struct ap_queue_status ap_tapq(ap_qid_t qid, unsigned long *info)
{ {
register unsigned long reg0 asm ("0") = qid; struct ap_queue_status reg1;
register struct ap_queue_status reg1 asm ("1"); unsigned long reg2;
register unsigned long reg2 asm ("2");
asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */ asm volatile(
: "=d" (reg1), "=d" (reg2) " lgr 0,%[qid]\n" /* qid into gr0 */
: "d" (reg0) " lghi 2,0\n" /* 0 into gr2 */
: "cc"); " .long 0xb2af0000\n" /* PQAP(TAPQ) */
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
" lgr %[reg2],2\n" /* gr2 into reg2 */
: [reg1] "=&d" (reg1), [reg2] "=&d" (reg2)
: [qid] "d" (qid)
: "cc", "0", "1", "2");
if (info) if (info)
*info = reg2; *info = reg2;
return reg1; return reg1;
@@ -115,14 +121,16 @@ static inline struct ap_queue_status ap_test_queue(ap_qid_t qid,
*/ */
static inline struct ap_queue_status ap_rapq(ap_qid_t qid) static inline struct ap_queue_status ap_rapq(ap_qid_t qid)
{ {
register unsigned long reg0 asm ("0") = qid | (1UL << 24); unsigned long reg0 = qid | (1UL << 24); /* fc 1UL is RAPQ */
register struct ap_queue_status reg1 asm ("1"); struct ap_queue_status reg1;
asm volatile( asm volatile(
".long 0xb2af0000" /* PQAP(RAPQ) */ " lgr 0,%[reg0]\n" /* qid arg into gr0 */
: "=d" (reg1) " .long 0xb2af0000\n" /* PQAP(RAPQ) */
: "d" (reg0) " lgr %[reg1],1\n" /* gr1 (status) into reg1 */
: "cc"); : [reg1] "=&d" (reg1)
: [reg0] "d" (reg0)
: "cc", "0", "1");
return reg1; return reg1;
} }
@@ -134,14 +142,16 @@ static inline struct ap_queue_status ap_rapq(ap_qid_t qid)
*/ */
static inline struct ap_queue_status ap_zapq(ap_qid_t qid) static inline struct ap_queue_status ap_zapq(ap_qid_t qid)
{ {
register unsigned long reg0 asm ("0") = qid | (2UL << 24); unsigned long reg0 = qid | (2UL << 24); /* fc 2UL is ZAPQ */
register struct ap_queue_status reg1 asm ("1"); struct ap_queue_status reg1;
asm volatile( asm volatile(
".long 0xb2af0000" /* PQAP(ZAPQ) */ " lgr 0,%[reg0]\n" /* qid arg into gr0 */
: "=d" (reg1) " .long 0xb2af0000\n" /* PQAP(ZAPQ) */
: "d" (reg0) " lgr %[reg1],1\n" /* gr1 (status) into reg1 */
: "cc"); : [reg1] "=&d" (reg1)
: [reg0] "d" (reg0)
: "cc", "0", "1");
return reg1; return reg1;
} }
@@ -172,18 +182,20 @@ struct ap_config_info {
*/ */
static inline int ap_qci(struct ap_config_info *config) static inline int ap_qci(struct ap_config_info *config)
{ {
register unsigned long reg0 asm ("0") = 4UL << 24; unsigned long reg0 = 4UL << 24; /* fc 4UL is QCI */
register unsigned long reg1 asm ("1") = -EOPNOTSUPP; unsigned long reg1 = -EOPNOTSUPP;
register struct ap_config_info *reg2 asm ("2") = config; struct ap_config_info *reg2 = config;
asm volatile( asm volatile(
".long 0xb2af0000\n" /* PQAP(QCI) */ " lgr 0,%[reg0]\n" /* QCI fc into gr0 */
"0: la %0,0\n" " lgr 2,%[reg2]\n" /* ptr to config into gr2 */
" .long 0xb2af0000\n" /* PQAP(QCI) */
"0: la %[reg1],0\n" /* good case, QCI fc available */
"1:\n" "1:\n"
EX_TABLE(0b, 1b) EX_TABLE(0b, 1b)
: "+d" (reg1) : [reg1] "+&d" (reg1)
: "d" (reg0), "d" (reg2) : [reg0] "d" (reg0), [reg2] "d" (reg2)
: "cc", "memory"); : "cc", "memory", "0", "2");
return reg1; return reg1;
} }
@@ -220,21 +232,25 @@ static inline struct ap_queue_status ap_aqic(ap_qid_t qid,
struct ap_qirq_ctrl qirqctrl, struct ap_qirq_ctrl qirqctrl,
void *ind) void *ind)
{ {
register unsigned long reg0 asm ("0") = qid | (3UL << 24); unsigned long reg0 = qid | (3UL << 24); /* fc 3UL is AQIC */
register union { union {
unsigned long value; unsigned long value;
struct ap_qirq_ctrl qirqctrl; struct ap_qirq_ctrl qirqctrl;
struct ap_queue_status status; struct ap_queue_status status;
} reg1 asm ("1"); } reg1;
register void *reg2 asm ("2") = ind; void *reg2 = ind;
reg1.qirqctrl = qirqctrl; reg1.qirqctrl = qirqctrl;
asm volatile( asm volatile(
".long 0xb2af0000" /* PQAP(AQIC) */ " lgr 0,%[reg0]\n" /* qid param into gr0 */
: "+d" (reg1) " lgr 1,%[reg1]\n" /* irq ctrl into gr1 */
: "d" (reg0), "d" (reg2) " lgr 2,%[reg2]\n" /* ni addr into gr2 */
: "cc"); " .long 0xb2af0000\n" /* PQAP(AQIC) */
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
: [reg1] "+&d" (reg1)
: [reg0] "d" (reg0), [reg2] "d" (reg2)
: "cc", "0", "1", "2");
return reg1.status; return reg1.status;
} }
@@ -268,21 +284,24 @@ union ap_qact_ap_info {
static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit, static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit,
union ap_qact_ap_info *apinfo) union ap_qact_ap_info *apinfo)
{ {
register unsigned long reg0 asm ("0") = qid | (5UL << 24) unsigned long reg0 = qid | (5UL << 24) | ((ifbit & 0x01) << 22);
| ((ifbit & 0x01) << 22); union {
register union {
unsigned long value; unsigned long value;
struct ap_queue_status status; struct ap_queue_status status;
} reg1 asm ("1"); } reg1;
register unsigned long reg2 asm ("2"); unsigned long reg2;
reg1.value = apinfo->val; reg1.value = apinfo->val;
asm volatile( asm volatile(
".long 0xb2af0000" /* PQAP(QACT) */ " lgr 0,%[reg0]\n" /* qid param into gr0 */
: "+d" (reg1), "=d" (reg2) " lgr 1,%[reg1]\n" /* qact in info into gr1 */
: "d" (reg0) " .long 0xb2af0000\n" /* PQAP(QACT) */
: "cc"); " lgr %[reg1],1\n" /* gr1 (status) into reg1 */
" lgr %[reg2],2\n" /* qact out info into reg2 */
: [reg1] "+&d" (reg1), [reg2] "=&d" (reg2)
: [reg0] "d" (reg0)
: "cc", "0", "1", "2");
apinfo->val = reg2; apinfo->val = reg2;
return reg1.status; return reg1.status;
} }
@@ -303,19 +322,24 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid,
unsigned long long psmid, unsigned long long psmid,
void *msg, size_t length) void *msg, size_t length)
{ {
register unsigned long reg0 asm ("0") = qid | 0x40000000UL; unsigned long reg0 = qid | 0x40000000UL; /* 0x4... is last msg part */
register struct ap_queue_status reg1 asm ("1"); union register_pair nqap_r1, nqap_r2;
register unsigned long reg2 asm ("2") = (unsigned long) msg; struct ap_queue_status reg1;
register unsigned long reg3 asm ("3") = (unsigned long) length;
register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); nqap_r1.even = (unsigned int)(psmid >> 32);
register unsigned long reg5 asm ("5") = psmid & 0xffffffff; nqap_r1.odd = psmid & 0xffffffff;
nqap_r2.even = (unsigned long)msg;
nqap_r2.odd = (unsigned long)length;
asm volatile ( asm volatile (
"0: .long 0xb2ad0042\n" /* NQAP */ " lgr 0,%[reg0]\n" /* qid param in gr0 */
" brc 2,0b" "0: .insn rre,0xb2ad0000,%[nqap_r1],%[nqap_r2]\n"
: "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3) " brc 2,0b\n" /* handle partial completion */
: "d" (reg4), "d" (reg5) " lgr %[reg1],1\n" /* gr1 (status) into reg1 */
: "cc", "memory"); : [reg0] "+&d" (reg0), [reg1] "=&d" (reg1),
[nqap_r2] "+&d" (nqap_r2.pair)
: [nqap_r1] "d" (nqap_r1.pair)
: "cc", "memory", "0", "1");
return reg1; return reg1;
} }