summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/sys/apix.h
blob: a1e9c80f3ef0cf244a1cfa48a42634eab0d75c5a (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
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2017 Joyent, Inc.
 */

#ifndef __SYS_APIX_APIX_H
#define	__SYS_APIX_APIX_H

#include <sys/note.h>
#include <sys/avintr.h>
#include <sys/traptrace.h>
#include <sys/apic.h>
#include <sys/apic_common.h>
#include <sys/apic_timer.h>

#ifdef	__cplusplus
extern	"C" {
#endif

#ifdef	DEBUG
#ifndef	TRAPTRACE
#define	TRAPTRACE
#endif
#endif

#define	APIX_NAME		"apix"

#define	APIX_NVECTOR		256	/* max number of per-cpu vectors */
#define	APIX_NIRQ		256	/* maximum number of IRQs */
#define	APIX_INVALID_VECT	0	/* invalid vector */

/* vector type */
#define	APIX_TYPE_FIXED	DDI_INTR_TYPE_FIXED	/* 1 */
#define	APIX_TYPE_MSI		DDI_INTR_TYPE_MSI	/* 2 */
#define	APIX_TYPE_MSIX	DDI_INTR_TYPE_MSIX	/* 4 */
#define	APIX_TYPE_IPI		8

/* vector states */
enum {
	APIX_STATE_FREED = 0,
	APIX_STATE_OBSOLETED,	/* 1 */
	APIX_STATE_ALLOCED,	/* 2 */
	APIX_STATE_ENABLED,	/* 3 */
	APIX_STATE_DISABLED	/* 4 */
};
#define	IS_VECT_FREE(p)		\
	(((p) == NULL) || ((p)->v_state == APIX_STATE_FREED))
#define	IS_VECT_OBSOL(p)	\
	(((p) != NULL) && ((p)->v_state == APIX_STATE_OBSOLETED))
#define	IS_VECT_ENABLED(p)	\
	(((p) != NULL) && ((p)->v_state == APIX_STATE_ENABLED))

/* flags */
#define	APIX_VECT_USER_BOUND	0x1
#define	APIX_VECT_MASKABLE	0x2

/*
 * Number of interrupt vectors reserved by software on each LOCAL APIC:
 *	1. Dtrace
 *	2. int80
 *	3. system-call
 *	4. fast-trap
 *	5. apix-reserved
 */
#define	APIX_SW_RESERVED_VECTORS	5

/*
 * Macros to help deal with shared interrupts and to differentiate
 * between vector and irq number when passing arguments to interfaces
 * xxx_avintr()
 */
#define	APIX_VIRTVEC_VECMASK		0xff
#define	APIX_VIRTVEC_FLAG		0x80000000
#define	APIX_VIRTVECTOR(cpuid, v)	\
	(APIX_VIRTVEC_FLAG | ((cpuid) << 8) | (v))
#define	APIX_IS_VIRTVEC(vv)		\
	((vv) & APIX_VIRTVEC_FLAG)
#define	APIX_VIRTVEC_VECTOR(vv)	\
	(((uchar_t)(vv)) & APIX_VIRTVEC_VECMASK)
#define	APIX_VIRTVEC_CPU(vv)		\
	(((uint32_t)(vv) & ~APIX_VIRTVEC_FLAG) >> 8)

struct apix_dev_vector;
typedef struct apix_vector {
	ushort_t		v_state;
	ushort_t		v_type;	/* interrupt type */
	processorid_t		v_cpuid;	/* current target cpu */
	uchar_t			v_vector;	/* vector */
	uchar_t			v_share;	/* intrs at this vector */
	int			v_inum;	/* irq for fixed, inum for msi/x */
	uint_t			v_flags;
	processorid_t		v_bound_cpuid;	/* binding cpu */
	uint_t			v_busy;	/* How frequently did clock */
					/* find us in this */
	uint_t			v_pri;	/* maximum priority */
	struct autovec		*v_autovect;	/* ISR linked list */
	void			*v_intrmap_private; /* intr remap data */
	struct apix_dev_vector *v_devp;	/* pointer to device */
	struct apix_vector	*v_next; /* next on per-cpu obosoletes chain */
} apix_vector_t;

typedef struct apix_impl {
	processorid_t		x_cpuid;	/* cpu number */

	uint16_t		x_intr_pending;	/* pending intr by IPL */
	/* pointer to head of interrupt pending list */
	struct autovec		*x_intr_head[PIL_MAX + 1];
	/* pointer to tail of interrupt pending list */
	struct autovec		*x_intr_tail[PIL_MAX + 1];

	apix_vector_t		*x_obsoletes;	/* obosoleted vectors */
	apix_vector_t		*x_vectbl[APIX_NVECTOR]; /* vector table */

	lock_t			x_lock;
} apix_impl_t;

#define	HILEVEL_PENDING(cpu)	\
	(apixs[(cpu)->cpu_id]->x_intr_pending & CPU_INTR_ACTV_HIGH_LEVEL_MASK)
#define	LOWLEVEL_PENDING(cpu)	\
	(apixs[(cpu)->cpu_id]->x_intr_pending & ~CPU_INTR_ACTV_HIGH_LEVEL_MASK)
#define	IS_HILEVEL_RUNNING(cpu)	\
	(((ushort_t)((cpu)->intr_actv)) & CPU_INTR_ACTV_HIGH_LEVEL_MASK)
#define	IS_LOWLEVEL_RUNNING(cpu)	\
	(((ushort_t)((cpu)->intr_actv)) & ~CPU_INTR_ACTV_HIGH_LEVEL_MASK)

#define	INTR_PENDING(apixp, ipl)			\
	((ipl) <= LOCK_LEVEL ?				\
	((apixp)->x_intr_pending & (1 << (ipl))) :	\
	((apixp)->x_intr_pending >> (LOCK_LEVEL + 1)))

/*
 * We need a way to find allocated vector for a device. One option
 * is to maintain a mapping table in pcplusmp. Another option would
 * be to record vector or irq with interrupt handler hdlp->ih_vector or
 * hdlp->ih_irq.
 * Second option requires interface changes, such as, a new interface
 * for  noticing vector changes caused by interrupt re-targeting.
 * Currently we choose the first option cause it doesn't require
 * new interfaces.
 */
typedef struct apix_dev_vector {
	dev_info_t		*dv_dip;
	int			dv_inum;	/* interrupt number */
	int			dv_type;	/* interrupt type */
	apix_vector_t		*dv_vector;	/* vector */
	struct apix_dev_vector *dv_next;	/* per major chain */
} apix_dev_vector_t;

extern lock_t apix_lock;
extern apix_impl_t *apixs[];
extern int apix_nipis;
extern int apix_cpu_nvectors;
extern apix_dev_vector_t **apix_dev_vector;
extern processorid_t *apix_major_to_cpu;
extern kmutex_t apix_mutex;

#define	xv_vector(cpu, v)	apixs[(cpu)]->x_vectbl[(v)]
#define	xv_intrmap_private(cpu, v)	(xv_vector(cpu, v))->v_intrmap_private

#define	APIX_IPI_MAX		APIC_MAX_VECTOR
#define	APIX_IPI_MIN		(APIX_NVECTOR - apix_nipis)
#define	APIX_AVINTR_MIN	0x20
#define	APIX_NAVINTR		\
	(apix_cpu_nvectors - apix_nipis - APIX_AVINTR_MIN)
#define	APIX_AVINTR_MAX	\
	((APIX_NAVINTR <= 0) ? 0 : \
	(((APIX_AVINTR_MIN + APIX_NAVINTR) > APIX_IPI_MIN) ? \
	(APIX_IPI_MIN - 2) : \
	(APIX_AVINTR_MIN + APIX_NAVINTR - 2)))
#define	APIX_RESV_VECTOR	(APIX_AVINTR_MAX + 1)

#define	IS_VALID_AVINTR(v)		\
	((v) >= APIX_AVINTR_MIN && (v) <= APIX_AVINTR_MAX)

#define	APIX_ENTER_CPU_LOCK(cpuid)	lock_set(&apixs[(cpuid)]->x_lock)
#define	APIX_LEAVE_CPU_LOCK(cpuid)	lock_clear(&apixs[(cpuid)]->x_lock)
#define	APIX_CPU_LOCK_HELD(cpuid)	LOCK_HELD(&apixs[(cpuid)]->x_lock)

/* Get dip for msi/x */
#define	APIX_GET_DIP(v)		\
	((v)->v_devp->dv_dip)

/*
 * For irq
 */
extern apic_irq_t *apic_irq_table[APIC_MAX_VECTOR+1];
#define	IS_IRQ_FREE(p)		\
	((p) == NULL || ((p)->airq_mps_intr_index == FREE_INDEX))

#define	UNREFERENCED_1PARAMETER(_p)		_NOTE(ARGUNUSED(_p))
#define	UNREFERENCED_3PARAMETER(_p, _q, _r)	_NOTE(ARGUNUSED(_p, _q, _r))

/*
 * From mp_platform_common.c
 */
extern int apic_intr_policy;
extern iflag_t apic_sci_flags;
extern int apic_hpet_vect;
extern iflag_t apic_hpet_flags;
extern int	apic_redist_cpu_skip;
extern int	apic_num_imbalance;
extern int	apic_num_rebind;
extern struct apic_io_intr *apic_io_intrp;
extern int	apic_use_acpi_madt_only;
extern uint32_t	eisa_level_intr_mask;
extern int	apic_pci_bus_total;
extern uchar_t	apic_single_pci_busid;

extern ACPI_MADT_INTERRUPT_OVERRIDE *acpi_isop;
extern int acpi_iso_cnt;

extern int	apic_defconf;
extern int	apic_irq_translate;

extern int apic_max_reps_clear_pending;

extern int apic_probe_common(char *modname);
extern uchar_t acpi_find_ioapic(int irq);
extern int apic_find_bus_id(int bustype);
extern int apic_find_intin(uchar_t ioapic, uchar_t intin);
extern struct apic_io_intr *apic_find_io_intr_w_busid(int irqno, int busid);
extern int apic_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid,
    int ipin, int *pci_irqp, iflag_t *intr_flagp);
extern int apic_handle_pci_pci_bridge(dev_info_t *idip, int child_devno,
    int child_ipin, struct apic_io_intr **intrp);
extern void apic_record_rdt_entry(apic_irq_t *irqptr, int irq);

/*
 * apix_intr.c
 */
extern void apix_do_interrupt(struct regs *rp, trap_trace_rec_t *ttp);

/*
 * apix_utils.c
 */

typedef struct apix_rebind_info {
	int		i_go;	/* if rebinding op is in progress */
	uint_t		i_pri;
	processorid_t	i_old_cpuid;
	struct autovec	*i_old_av;
	processorid_t	i_new_cpuid;
	struct autovec	*i_new_av;
} apix_rebind_info_t;

extern struct apix_rebind_info apix_rebindinfo;

#define	APIX_SET_REBIND_INFO(_ovp, _nvp)\
	if (((_ovp)->v_flags & APIX_VECT_MASKABLE) == 0) {\
		apix_rebindinfo.i_pri = (_ovp)->v_pri;\
		apix_rebindinfo.i_old_cpuid = (_ovp)->v_cpuid;\
		apix_rebindinfo.i_old_av = (_ovp)->v_autovect;\
		apix_rebindinfo.i_new_cpuid = (_nvp)->v_cpuid;\
		apix_rebindinfo.i_new_av = (_nvp)->v_autovect;\
		apix_rebindinfo.i_go = 1;\
	}

#define	APIX_CLR_REBIND_INFO() \
	apix_rebindinfo.i_go = 0

#define	APIX_IS_FAKE_INTR(_vector)\
	(apix_rebindinfo.i_go && (_vector) == APIX_RESV_VECTOR)

#define	APIX_DO_FAKE_INTR(_cpu, _vector)\
	if (APIX_IS_FAKE_INTR(_vector)) {\
		struct autovec *tp = NULL;\
		if ((_cpu) == apix_rebindinfo.i_old_cpuid)\
			tp = apix_rebindinfo.i_old_av;\
		else if ((_cpu) == apix_rebindinfo.i_new_cpuid)\
			tp = apix_rebindinfo.i_new_av;\
		ASSERT(tp != NULL);\
		if (tp->av_vector != NULL &&\
		    (tp->av_flags & AV_PENTRY_PEND) == 0) {\
			tp->av_flags |= AV_PENTRY_PEND;\
			apix_insert_pending_av(apixs[(_cpu)], tp,\
			    tp->av_prilevel);\
			apixs[(_cpu)]->x_intr_pending |=\
			    (1 << tp->av_prilevel);\
		}\
	}

extern int apix_add_avintr(void *intr_id, int ipl, avfunc xxintr, char *name,
    int vector, caddr_t arg1, caddr_t arg2, uint64_t *ticksp, dev_info_t *dip);
extern void apix_rem_avintr(void *intr_id, int ipl, avfunc xxintr,
    int virt_vect);

extern uint32_t apix_bind_cpu_locked(dev_info_t *dip);
extern apix_vector_t *apix_rebind(apix_vector_t *vecp, processorid_t tocpu,
    int count);

extern uchar_t apix_alloc_ipi(int ipl);
extern apix_vector_t *apix_alloc_intx(dev_info_t *dip, int inum, int irqno);
extern int apix_alloc_msi(dev_info_t *dip, int inum, int count, int behavior);
extern int apix_alloc_msix(dev_info_t *dip, int inum, int count, int behavior);
extern void apix_free_vectors(dev_info_t *dip, int inum, int count, int type);
extern void apix_enable_vector(apix_vector_t *vecp);
extern void apix_disable_vector(apix_vector_t *vecp);
extern int apix_obsolete_vector(apix_vector_t *vecp);
extern int apix_find_cont_vector_oncpu(uint32_t cpuid, int count);

extern void apix_set_dev_map(apix_vector_t *vecp, dev_info_t *dip, int inum);
extern apix_vector_t *apix_get_dev_map(dev_info_t *dip, int inum, int type);
extern apix_vector_t *apix_setup_io_intr(apix_vector_t *vecp);
extern void ioapix_init_intr(int mask_apic);
extern int apix_get_min_dev_inum(dev_info_t *dip, int type);
extern int apix_get_max_dev_inum(dev_info_t *dip, int type);

/*
 * apix.c
 */
extern int apix_addspl(int virtvec, int ipl, int min_ipl, int max_ipl);
extern int apix_delspl(int virtvec, int ipl, int min_ipl, int max_ipl);
extern void apix_intx_set_vector(int irqno, uint32_t cpuid, uchar_t vector);
extern apix_vector_t *apix_intx_get_vector(int irqno);
extern void apix_intx_enable(int irqno);
extern void apix_intx_disable(int irqno);
extern void apix_intx_free(int irqno);
extern int apix_intx_rebind(int irqno, processorid_t cpuid, uchar_t vector);
extern apix_vector_t *apix_set_cpu(apix_vector_t *vecp, int new_cpu,
    int *result);
extern apix_vector_t *apix_grp_set_cpu(apix_vector_t *vecp, int new_cpu,
    int *result);
extern void apix_level_intr_pre_eoi(int irq);
extern void apix_level_intr_post_dispatch(int irq);

#ifdef	__cplusplus
}
#endif

#endif	/* __SYS_APIX_APIX_H */