Loading...
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 | /* 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. */ struct ap_queue_status { unsigned int queue_empty : 1; unsigned int replies_waiting : 1; unsigned int queue_full : 1; unsigned int _pad1 : 4; unsigned int irq_enabled : 1; unsigned int response_code : 8; unsigned int _pad2 : 16; }; /** * ap_intructions_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; } /** * ap_tapq(): Test adjunct processor queue. * @qid: The AP queue number * @info: Pointer to queue descriptor * * Returns AP queue status structure. */ static inline struct ap_queue_status ap_tapq(ap_qid_t qid, unsigned long *info) { struct ap_queue_status 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\n" /* gr2 into reg2 */ : [reg1] "=&d" (reg1), [reg2] "=&d" (reg2) : [qid] "d" (qid) : "cc", "0", "1", "2"); if (info) *info = reg2; return reg1; } /** * ap_test_queue(): Test adjunct processor queue. * @qid: The AP queue number * @tbit: Test facilities bit * @info: Pointer to queue descriptor * * Returns AP queue status structure. */ static inline struct ap_queue_status ap_test_queue(ap_qid_t qid, int tbit, unsigned long *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 * * Returns AP queue status structure. */ static inline struct ap_queue_status ap_rapq(ap_qid_t qid) { unsigned long reg0 = qid | (1UL << 24); /* fc 1UL is RAPQ */ struct ap_queue_status reg1; asm volatile( " lgr 0,%[reg0]\n" /* qid arg into gr0 */ " .insn rre,0xb2af0000,0,0\n" /* PQAP(RAPQ) */ " lgr %[reg1],1\n" /* gr1 (status) into reg1 */ : [reg1] "=&d" (reg1) : [reg0] "d" (reg0) : "cc", "0", "1"); return reg1; } /** * ap_pqap_zapq(): Reset and zeroize adjunct processor queue. * @qid: The AP queue number * * Returns AP queue status structure. */ static inline struct ap_queue_status ap_zapq(ap_qid_t qid) { unsigned long reg0 = qid | (2UL << 24); /* fc 2UL is ZAPQ */ struct ap_queue_status reg1; asm volatile( " lgr 0,%[reg0]\n" /* qid arg into gr0 */ " .insn rre,0xb2af0000,0,0\n" /* PQAP(ZAPQ) */ " lgr %[reg1],1\n" /* gr1 (status) into reg1 */ : [reg1] "=&d" (reg1) : [reg0] "d" (reg0) : "cc", "0", "1"); return reg1; } /** * struct ap_config_info - convenience struct for AP crypto * config info as returned by the ap_qci() function. */ struct ap_config_info { 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 char _reserved1 : 4; unsigned char _reserved2[3]; unsigned char Na; /* max # of APs - 1 */ unsigned char Nd; /* max # of Domains - 1 */ unsigned char _reserved3[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 _reserved4[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. */ struct ap_qirq_ctrl { unsigned int _res1 : 8; unsigned int zone : 8; /* zone info */ unsigned int ir : 1; /* ir flag: enable (1) or disable (0) irq */ unsigned int _res2 : 4; unsigned int gisc : 3; /* guest isc field */ unsigned int _res3 : 6; unsigned int gf : 2; /* gisa format */ unsigned int _res4 : 1; unsigned int gisa : 27; /* gisa origin */ unsigned int _res5 : 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, struct ap_qirq_ctrl qirqctrl, phys_addr_t pa_ind) { unsigned long reg0 = qid | (3UL << 24); /* fc 3UL is AQIC */ union { unsigned long value; struct ap_qirq_ctrl qirqctrl; struct ap_queue_status status; } reg1; unsigned long reg2 = pa_ind; reg1.qirqctrl = qirqctrl; 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\n" /* gr1 (status) into reg1 */ : [reg1] "+&d" (reg1) : [reg0] "d" (reg0), [reg2] "d" (reg2) : "cc", "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 combatibility 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 { unsigned long value; struct ap_queue_status status; } 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\n" /* qact out info into reg2 */ : [reg1] "+&d" (reg1), [reg2] "=&d" (reg2) : [reg0] "d" (reg0) : "cc", "0", "1", "2"); apinfo->val = reg2; 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; struct ap_queue_status 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\n" /* gr1 (status) into reg1 */ : [reg0] "+&d" (reg0), [reg1] "=&d" (reg1), [nqap_r2] "+&d" (nqap_r2.pair) : [nqap_r1] "d" (nqap_r1.pair) : "cc", "memory", "0", "1"); return reg1; } /** * ap_dqap(): Receive message from adjunct processor queue. * @qid: The AP queue number * @psmid: Pointer to program supplied message identifier * @msg: The message text * @length: The message length * @reslength: Resitual length on return * @resgr0: input: gr0 value (only used if != 0), output: resitual 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 long *psmid, void *msg, size_t length, size_t *reslength, unsigned long *resgr0) { unsigned long reg0 = resgr0 && *resgr0 ? *resgr0 : qid | 0x80000000UL; struct ap_queue_status reg1; unsigned long reg2; union register_pair rp1, rp2; rp1.even = 0UL; rp1.odd = 0UL; rp2.even = (unsigned long)msg; rp2.odd = (unsigned long)length; 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\n" /* gr2 (res length) into reg2 */ : [reg0] "+&d" (reg0), [reg1] "=&d" (reg1), [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.response_code = 0xFF; if (resgr0) *resgr0 = reg0; } else { *psmid = (((unsigned long long)rp1.even) << 32) + rp1.odd; if (resgr0) *resgr0 = 0; } return reg1; } /* * Interface to tell the AP bus code that a configuration * change has happened. The bus code should at least do * an ap bus resource rescan. */ #if IS_ENABLED(CONFIG_ZCRYPT) void ap_bus_cfg_chg(void); #else static inline void ap_bus_cfg_chg(void){} #endif #endif /* _ASM_S390_AP_H_ */ |