1/*-
2 * Copyright (c) Peter Wemm <peter@netplex.com.au>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#ifndef _MACHINE_PCPU_H_
30#define	_MACHINE_PCPU_H_
31
32#ifndef _SYS_CDEFS_H_
33#error "sys/cdefs.h is a prerequisite for this file"
34#endif
35
36/*
37 * The SMP parts are setup in pmap.c and locore.s for the BSP, and
38 * mp_machdep.c sets up the data for the AP's to "see" when they awake.
39 * The reason for doing it via a struct is so that an array of pointers
40 * to each CPU's data can be set up for things like "check curproc on all
41 * other processors"
42 */
43#define	PCPU_MD_FIELDS							\
44	char	pc_monitorbuf[128] __aligned(128); /* cache line */	\
45	struct	pcpu *pc_prvspace;	/* Self-reference */		\
46	struct	pmap *pc_curpmap;					\
47	struct	amd64tss *pc_tssp;	/* TSS segment active on CPU */	\
48	struct	amd64tss *pc_commontssp;/* Common TSS for the CPU */	\
49	register_t pc_rsp0;						\
50	register_t pc_scratch_rsp;	/* User %rsp in syscall */	\
51	u_int	pc_apic_id;						\
52	u_int   pc_acpi_id;		/* ACPI CPU id */		\
53	/* Pointer to the CPU %fs descriptor */				\
54	struct user_segment_descriptor	*pc_fs32p;			\
55	/* Pointer to the CPU %gs descriptor */				\
56	struct user_segment_descriptor	*pc_gs32p;			\
57	/* Pointer to the CPU LDT descriptor */				\
58	struct system_segment_descriptor *pc_ldt;			\
59	/* Pointer to the CPU TSS descriptor */				\
60	struct system_segment_descriptor *pc_tss;			\
61	uint64_t	pc_pm_save_cnt;					\
62	u_int	pc_cmci_mask;		/* MCx banks for CMCI */	\
63	uint64_t pc_dbreg[16];		/* ddb debugging regs */	\
64	int pc_dbreg_cmd;		/* ddb debugging reg cmd */	\
65	u_int	pc_vcpu_id;		/* Xen vCPU ID */		\
66	char	__pad[157]		/* be divisor of PAGE_SIZE	\
67					   after cache alignment */
68
69#define	PC_DBREG_CMD_NONE	0
70#define	PC_DBREG_CMD_LOAD	1
71
72#ifdef _KERNEL
73
74#ifdef lint
75
76extern struct pcpu *pcpup;
77
78#define	PCPU_GET(member)	(pcpup->pc_ ## member)
79#define	PCPU_ADD(member, val)	(pcpup->pc_ ## member += (val))
80#define	PCPU_INC(member)	PCPU_ADD(member, 1)
81#define	PCPU_PTR(member)	(&pcpup->pc_ ## member)
82#define	PCPU_SET(member, val)	(pcpup->pc_ ## member = (val))
83
84#elif defined(__GNUCLIKE_ASM) && defined(__GNUCLIKE___TYPEOF)
85
86/*
87 * Evaluates to the byte offset of the per-cpu variable name.
88 */
89#define	__pcpu_offset(name)						\
90	__offsetof(struct pcpu, name)
91
92/*
93 * Evaluates to the type of the per-cpu variable name.
94 */
95#define	__pcpu_type(name)						\
96	__typeof(((struct pcpu *)0)->name)
97
98/*
99 * Evaluates to the address of the per-cpu variable name.
100 */
101#define	__PCPU_PTR(name) __extension__ ({				\
102	__pcpu_type(name) *__p;						\
103									\
104	__asm __volatile("movq %%gs:%1,%0; addq %2,%0"			\
105	    : "=r" (__p)						\
106	    : "m" (*(struct pcpu *)(__pcpu_offset(pc_prvspace))),	\
107	      "i" (__pcpu_offset(name)));				\
108									\
109	__p;								\
110})
111
112/*
113 * Evaluates to the value of the per-cpu variable name.
114 */
115#define	__PCPU_GET(name) __extension__ ({				\
116	__pcpu_type(name) __res;					\
117	struct __s {							\
118		u_char	__b[MIN(sizeof(__pcpu_type(name)), 8)];		\
119	} __s;								\
120									\
121	if (sizeof(__res) == 1 || sizeof(__res) == 2 ||			\
122	    sizeof(__res) == 4 || sizeof(__res) == 8) {			\
123		__asm __volatile("mov %%gs:%1,%0"			\
124		    : "=r" (__s)					\
125		    : "m" (*(struct __s *)(__pcpu_offset(name))));	\
126		*(struct __s *)(void *)&__res = __s;			\
127	} else {							\
128		__res = *__PCPU_PTR(name);				\
129	}								\
130	__res;								\
131})
132
133/*
134 * Adds the value to the per-cpu counter name.  The implementation
135 * must be atomic with respect to interrupts.
136 */
137#define	__PCPU_ADD(name, val) do {					\
138	__pcpu_type(name) __val;					\
139	struct __s {							\
140		u_char	__b[MIN(sizeof(__pcpu_type(name)), 8)];		\
141	} __s;								\
142									\
143	__val = (val);							\
144	if (sizeof(__val) == 1 || sizeof(__val) == 2 ||			\
145	    sizeof(__val) == 4 || sizeof(__val) == 8) {			\
146		__s = *(struct __s *)(void *)&__val;			\
147		__asm __volatile("add %1,%%gs:%0"			\
148		    : "=m" (*(struct __s *)(__pcpu_offset(name)))	\
149		    : "r" (__s));					\
150	} else								\
151		*__PCPU_PTR(name) += __val;				\
152} while (0)
153
154/*
155 * Increments the value of the per-cpu counter name.  The implementation
156 * must be atomic with respect to interrupts.
157 */
158#define	__PCPU_INC(name) do {						\
159	CTASSERT(sizeof(__pcpu_type(name)) == 1 ||			\
160	    sizeof(__pcpu_type(name)) == 2 ||				\
161	    sizeof(__pcpu_type(name)) == 4 ||				\
162	    sizeof(__pcpu_type(name)) == 8);				\
163	if (sizeof(__pcpu_type(name)) == 1) {				\
164		__asm __volatile("incb %%gs:%0"				\
165		    : "=m" (*(__pcpu_type(name) *)(__pcpu_offset(name)))\
166		    : "m" (*(__pcpu_type(name) *)(__pcpu_offset(name))));\
167	} else if (sizeof(__pcpu_type(name)) == 2) {			\
168		__asm __volatile("incw %%gs:%0"				\
169		    : "=m" (*(__pcpu_type(name) *)(__pcpu_offset(name)))\
170		    : "m" (*(__pcpu_type(name) *)(__pcpu_offset(name))));\
171	} else if (sizeof(__pcpu_type(name)) == 4) {			\
172		__asm __volatile("incl %%gs:%0"				\
173		    : "=m" (*(__pcpu_type(name) *)(__pcpu_offset(name)))\
174		    : "m" (*(__pcpu_type(name) *)(__pcpu_offset(name))));\
175	} else if (sizeof(__pcpu_type(name)) == 8) {			\
176		__asm __volatile("incq %%gs:%0"				\
177		    : "=m" (*(__pcpu_type(name) *)(__pcpu_offset(name)))\
178		    : "m" (*(__pcpu_type(name) *)(__pcpu_offset(name))));\
179	}								\
180} while (0)
181
182/*
183 * Sets the value of the per-cpu variable name to value val.
184 */
185#define	__PCPU_SET(name, val) {						\
186	__pcpu_type(name) __val;					\
187	struct __s {							\
188		u_char	__b[MIN(sizeof(__pcpu_type(name)), 8)];		\
189	} __s;								\
190									\
191	__val = (val);							\
192	if (sizeof(__val) == 1 || sizeof(__val) == 2 ||			\
193	    sizeof(__val) == 4 || sizeof(__val) == 8) {			\
194		__s = *(struct __s *)(void *)&__val;			\
195		__asm __volatile("mov %1,%%gs:%0"			\
196		    : "=m" (*(struct __s *)(__pcpu_offset(name)))	\
197		    : "r" (__s));					\
198	} else {							\
199		*__PCPU_PTR(name) = __val;				\
200	}								\
201}
202
203#define	PCPU_GET(member)	__PCPU_GET(pc_ ## member)
204#define	PCPU_ADD(member, val)	__PCPU_ADD(pc_ ## member, val)
205#define	PCPU_INC(member)	__PCPU_INC(pc_ ## member)
206#define	PCPU_PTR(member)	__PCPU_PTR(pc_ ## member)
207#define	PCPU_SET(member, val)	__PCPU_SET(pc_ ## member, val)
208
209#define	OFFSETOF_CURTHREAD	0
210#ifdef __clang__
211#pragma clang diagnostic push
212#pragma clang diagnostic ignored "-Wnull-dereference"
213#endif
214static __inline __pure2 struct thread *
215__curthread(void)
216{
217	struct thread *td;
218
219	__asm("movq %%gs:%1,%0" : "=r" (td)
220	    : "m" (*(char *)OFFSETOF_CURTHREAD));
221	return (td);
222}
223#ifdef __clang__
224#pragma clang diagnostic pop
225#endif
226#define	curthread		(__curthread())
227
228#define	OFFSETOF_CURPCB		32
229static __inline __pure2 struct pcb *
230__curpcb(void)
231{
232	struct pcb *pcb;
233
234	__asm("movq %%gs:%1,%0" : "=r" (pcb) : "m" (*(char *)OFFSETOF_CURPCB));
235	return (pcb);
236}
237#define	curpcb		(__curpcb())
238
239#define	IS_BSP()	(PCPU_GET(cpuid) == 0)
240
241#else /* !lint || defined(__GNUCLIKE_ASM) && defined(__GNUCLIKE___TYPEOF) */
242
243#error "this file needs to be ported to your compiler"
244
245#endif /* lint, etc. */
246
247#endif /* _KERNEL */
248
249#endif /* !_MACHINE_PCPU_H_ */
250