summaryrefslogtreecommitdiff
path: root/arch/s390/include/asm/ap.h
blob: 5e77a81ab36ab4403e8ff6e1c21358c59cdb9060 (plain)
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
/* 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_

/**
 * 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)
{
	register unsigned long reg0 asm ("0") = AP_MKQID(0, 0);
	register unsigned long reg1 asm ("1") = 0;
	register unsigned long reg2 asm ("2") = 0;

	asm volatile(
		"   .long 0xb2af0000\n"		/* PQAP(TAPQ) */
		"0: la    %0,1\n"
		"1:\n"
		EX_TABLE(0b, 1b)
		: "+d" (reg1), "+d" (reg2)
		: "d" (reg0)
		: "cc");
	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)
{
	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;
}

/**
 * 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)
{
	register unsigned long reg0 asm ("0") = qid | (1UL << 24);
	register struct ap_queue_status reg1 asm ("1");

	asm volatile(
		".long 0xb2af0000"		/* PQAP(RAPQ) */
		: "=d" (reg1)
		: "d" (reg0)
		: "cc");
	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)
{
	register unsigned long reg0 asm ("0") = qid | (2UL << 24);
	register struct ap_queue_status reg1 asm ("1");

	asm volatile(
		".long 0xb2af0000"		/* PQAP(ZAPQ) */
		: "=d" (reg1)
		: "d" (reg0)
		: "cc");
	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)
{
	register unsigned long reg0 asm ("0") = 4UL << 24;
	register unsigned long reg1 asm ("1") = -EOPNOTSUPP;
	register struct ap_config_info *reg2 asm ("2") = config;

	asm volatile(
		".long 0xb2af0000\n"		/* PQAP(QCI) */
		"0: la    %0,0\n"
		"1:\n"
		EX_TABLE(0b, 1b)
		: "+d" (reg1)
		: "d" (reg0), "d" (reg2)
		: "cc", "memory");

	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)
 * @ind: 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,
					     void *ind)
{
	register unsigned long reg0 asm ("0") = qid | (3UL << 24);
	register union {
		unsigned long value;
		struct ap_qirq_ctrl qirqctrl;
		struct ap_queue_status status;
	} reg1 asm ("1");
	register void *reg2 asm ("2") = ind;

	reg1.qirqctrl = qirqctrl;

	asm volatile(
		".long 0xb2af0000"		/* PQAP(AQIC) */
		: "+d" (reg1)
		: "d" (reg0), "d" (reg2)
		: "cc");

	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)
{
	register unsigned long reg0 asm ("0") = qid | (5UL << 24)
		| ((ifbit & 0x01) << 22);
	register union {
		unsigned long value;
		struct ap_queue_status status;
	} reg1 asm ("1");
	register unsigned long reg2 asm ("2");

	reg1.value = apinfo->val;

	asm volatile(
		".long 0xb2af0000"		/* PQAP(QACT) */
		: "+d" (reg1), "=d" (reg2)
		: "d" (reg0)
		: "cc");
	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)
{
	register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
	register struct ap_queue_status reg1 asm ("1");
	register unsigned long reg2 asm ("2") = (unsigned long) msg;
	register unsigned long reg3 asm ("3") = (unsigned long) length;
	register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
	register unsigned long reg5 asm ("5") = psmid & 0xffffffff;

	asm volatile (
		"0: .long 0xb2ad0042\n"		/* NQAP */
		"   brc   2,0b"
		: "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)
		: "d" (reg4), "d" (reg5)
		: "cc", "memory");
	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)
{
	register unsigned long reg0 asm("0") =
		resgr0 && *resgr0 ? *resgr0 : qid | 0x80000000UL;
	register struct ap_queue_status reg1 asm ("1");
	register unsigned long reg2 asm("2") = 0UL;
	register unsigned long reg4 asm("4") = (unsigned long) msg;
	register unsigned long reg5 asm("5") = (unsigned long) length;
	register unsigned long reg6 asm("6") = 0UL;
	register unsigned long reg7 asm("7") = 0UL;

	asm volatile(
		"0: ltgr  %[bl],%[bl]\n" /* if avail. buf len is 0 then */
		"   jz    2f\n"		 /* go out of this asm block */
		"1: .long 0xb2ae0064\n"  /* DQAP */
		"   brc   6,0b\n"	 /* if partially do again */
		"2:\n"
		: "+d" (reg0), "=d" (reg1), "+d" (reg2),
		  "+d" (reg4), [bl] "+d" (reg5), "+d" (reg6), "+d" (reg7)
		: : "cc", "memory");

	if (reslength)
		*reslength = reg2;
	if (reg2 != 0 && reg5 == 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) reg6) << 32) + reg7;
		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_ */