cpufunc.h revision 74897
1/*-
2 * Copyright (c) 1993 The Regents of the University of California.
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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: head/sys/i386/include/cpufunc.h 74897 2001-03-28 02:31:54Z jhb $
34 */
35
36/*
37 * Functions to provide access to special i386 instructions.
38 */
39
40#ifndef _MACHINE_CPUFUNC_H_
41#define	_MACHINE_CPUFUNC_H_
42
43#define readb(va)	(*(volatile u_int8_t *) (va))
44#define readw(va)	(*(volatile u_int16_t *) (va))
45#define readl(va)	(*(volatile u_int32_t *) (va))
46
47#define writeb(va, d)	(*(volatile u_int8_t *) (va) = (d))
48#define writew(va, d)	(*(volatile u_int16_t *) (va) = (d))
49#define writel(va, d)	(*(volatile u_int32_t *) (va) = (d))
50
51#ifdef	__GNUC__
52
53#ifdef SWTCH_OPTIM_STATS
54extern	int	tlb_flush_count;	/* XXX */
55#endif
56
57static __inline void
58breakpoint(void)
59{
60	__asm __volatile("int $3");
61}
62
63static __inline u_int
64bsfl(u_int mask)
65{
66	u_int	result;
67
68	__asm __volatile("bsfl %0,%0" : "=r" (result) : "0" (mask));
69	return (result);
70}
71
72static __inline u_int
73bsrl(u_int mask)
74{
75	u_int	result;
76
77	__asm __volatile("bsrl %0,%0" : "=r" (result) : "0" (mask));
78	return (result);
79}
80
81static __inline void
82disable_intr(void)
83{
84	__asm __volatile("cli" : : : "memory");
85}
86
87static __inline void
88enable_intr(void)
89{
90	__asm __volatile("sti");
91}
92
93#define	HAVE_INLINE_FFS
94
95static __inline int
96ffs(int mask)
97{
98	/*
99	 * Note that gcc-2's builtin ffs would be used if we didn't declare
100	 * this inline or turn off the builtin.  The builtin is faster but
101	 * broken in gcc-2.4.5 and slower but working in gcc-2.5 and later
102	 * versions.
103	 */
104	 return (mask == 0 ? mask : bsfl((u_int)mask) + 1);
105}
106
107#define	HAVE_INLINE_FLS
108
109static __inline int
110fls(int mask)
111{
112	return (mask == 0 ? mask : bsrl((u_int)mask) + 1);
113}
114
115#if __GNUC__ < 2
116
117#define	inb(port)		inbv(port)
118#define	outb(port, data)	outbv(port, data)
119
120#else /* __GNUC >= 2 */
121
122/*
123 * The following complications are to get around gcc not having a
124 * constraint letter for the range 0..255.  We still put "d" in the
125 * constraint because "i" isn't a valid constraint when the port
126 * isn't constant.  This only matters for -O0 because otherwise
127 * the non-working version gets optimized away.
128 *
129 * Use an expression-statement instead of a conditional expression
130 * because gcc-2.6.0 would promote the operands of the conditional
131 * and produce poor code for "if ((inb(var) & const1) == const2)".
132 *
133 * The unnecessary test `(port) < 0x10000' is to generate a warning if
134 * the `port' has type u_short or smaller.  Such types are pessimal.
135 * This actually only works for signed types.  The range check is
136 * careful to avoid generating warnings.
137 */
138#define	inb(port) __extension__ ({					\
139	u_char	_data;							\
140	if (__builtin_constant_p(port) && ((port) & 0xffff) < 0x100	\
141	    && (port) < 0x10000)					\
142		_data = inbc(port);					\
143	else								\
144		_data = inbv(port);					\
145	_data; })
146
147#define	outb(port, data) (						\
148	__builtin_constant_p(port) && ((port) & 0xffff) < 0x100		\
149	&& (port) < 0x10000						\
150	? outbc(port, data) : outbv(port, data))
151
152static __inline u_char
153inbc(u_int port)
154{
155	u_char	data;
156
157	__asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port)));
158	return (data);
159}
160
161static __inline void
162outbc(u_int port, u_char data)
163{
164	__asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port)));
165}
166
167#endif /* __GNUC <= 2 */
168
169static __inline u_char
170inbv(u_int port)
171{
172	u_char	data;
173	/*
174	 * We use %%dx and not %1 here because i/o is done at %dx and not at
175	 * %edx, while gcc generates inferior code (movw instead of movl)
176	 * if we tell it to load (u_short) port.
177	 */
178	__asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
179	return (data);
180}
181
182static __inline u_int
183inl(u_int port)
184{
185	u_int	data;
186
187	__asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port));
188	return (data);
189}
190
191static __inline void
192insb(u_int port, void *addr, size_t cnt)
193{
194	__asm __volatile("cld; rep; insb"
195			 : "=D" (addr), "=c" (cnt)
196			 :  "0" (addr),  "1" (cnt), "d" (port)
197			 : "memory");
198}
199
200static __inline void
201insw(u_int port, void *addr, size_t cnt)
202{
203	__asm __volatile("cld; rep; insw"
204			 : "=D" (addr), "=c" (cnt)
205			 :  "0" (addr),  "1" (cnt), "d" (port)
206			 : "memory");
207}
208
209static __inline void
210insl(u_int port, void *addr, size_t cnt)
211{
212	__asm __volatile("cld; rep; insl"
213			 : "=D" (addr), "=c" (cnt)
214			 :  "0" (addr),  "1" (cnt), "d" (port)
215			 : "memory");
216}
217
218static __inline void
219invd(void)
220{
221	__asm __volatile("invd");
222}
223
224#if defined(SMP) && defined(_KERNEL)
225
226/*
227 * When using APIC IPI's, invlpg() is not simply the invlpg instruction
228 * (this is a bug) and the inlining cost is prohibitive since the call
229 * executes into the IPI transmission system.
230 */
231void	invlpg		__P((u_int addr));
232void	invltlb		__P((void));
233
234static __inline void
235cpu_invlpg(void *addr)
236{
237	__asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
238}
239
240static __inline void
241cpu_invltlb(void)
242{
243	u_int	temp;
244	/*
245	 * This should be implemented as load_cr3(rcr3()) when load_cr3()
246	 * is inlined.
247	 */
248	__asm __volatile("movl %%cr3, %0; movl %0, %%cr3" : "=r" (temp)
249			 : : "memory");
250#if defined(SWTCH_OPTIM_STATS)
251	++tlb_flush_count;
252#endif
253}
254
255#else /* !(SMP && _KERNEL) */
256
257static __inline void
258invlpg(u_int addr)
259{
260	__asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
261}
262
263static __inline void
264invltlb(void)
265{
266	u_int	temp;
267	/*
268	 * This should be implemented as load_cr3(rcr3()) when load_cr3()
269	 * is inlined.
270	 */
271	__asm __volatile("movl %%cr3, %0; movl %0, %%cr3" : "=r" (temp)
272			 : : "memory");
273#ifdef SWTCH_OPTIM_STATS
274	++tlb_flush_count;
275#endif
276}
277
278#endif /* SMP && _KERNEL */
279
280static __inline u_short
281inw(u_int port)
282{
283	u_short	data;
284
285	__asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port));
286	return (data);
287}
288
289static __inline void
290outbv(u_int port, u_char data)
291{
292	u_char	al;
293	/*
294	 * Use an unnecessary assignment to help gcc's register allocator.
295	 * This make a large difference for gcc-1.40 and a tiny difference
296	 * for gcc-2.6.0.  For gcc-1.40, al had to be ``asm("ax")'' for
297	 * best results.  gcc-2.6.0 can't handle this.
298	 */
299	al = data;
300	__asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
301}
302
303static __inline void
304outl(u_int port, u_int data)
305{
306	/*
307	 * outl() and outw() aren't used much so we haven't looked at
308	 * possible micro-optimizations such as the unnecessary
309	 * assignment for them.
310	 */
311	__asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port));
312}
313
314static __inline void
315outsb(u_int port, const void *addr, size_t cnt)
316{
317	__asm __volatile("cld; rep; outsb"
318			 : "=S" (addr), "=c" (cnt)
319			 :  "0" (addr),  "1" (cnt), "d" (port));
320}
321
322static __inline void
323outsw(u_int port, const void *addr, size_t cnt)
324{
325	__asm __volatile("cld; rep; outsw"
326			 : "=S" (addr), "=c" (cnt)
327			 :  "0" (addr),  "1" (cnt), "d" (port));
328}
329
330static __inline void
331outsl(u_int port, const void *addr, size_t cnt)
332{
333	__asm __volatile("cld; rep; outsl"
334			 : "=S" (addr), "=c" (cnt)
335			 :  "0" (addr),  "1" (cnt), "d" (port));
336}
337
338static __inline void
339outw(u_int port, u_short data)
340{
341	__asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port));
342}
343
344static __inline u_int
345rcr2(void)
346{
347	u_int	data;
348
349	__asm __volatile("movl %%cr2,%0" : "=r" (data));
350	return (data);
351}
352
353static __inline u_int
354read_eflags(void)
355{
356	u_int	ef;
357
358	__asm __volatile("pushfl; popl %0" : "=r" (ef));
359	return (ef);
360}
361
362static __inline u_int64_t
363rdmsr(u_int msr)
364{
365	u_int64_t rv;
366
367	__asm __volatile("rdmsr" : "=A" (rv) : "c" (msr));
368	return (rv);
369}
370
371static __inline u_int64_t
372rdpmc(u_int pmc)
373{
374	u_int64_t rv;
375
376	__asm __volatile("rdpmc" : "=A" (rv) : "c" (pmc));
377	return (rv);
378}
379
380static __inline u_int64_t
381rdtsc(void)
382{
383	u_int64_t rv;
384
385	__asm __volatile("rdtsc" : "=A" (rv));
386	return (rv);
387}
388
389static __inline void
390wbinvd(void)
391{
392	__asm __volatile("wbinvd");
393}
394
395static __inline void
396write_eflags(u_int ef)
397{
398	__asm __volatile("pushl %0; popfl" : : "r" (ef));
399}
400
401static __inline void
402wrmsr(u_int msr, u_int64_t newval)
403{
404	__asm __volatile("wrmsr" : : "A" (newval), "c" (msr));
405}
406
407static __inline u_int
408rfs(void)
409{
410	u_int sel;
411	__asm __volatile("movl %%fs,%0" : "=rm" (sel));
412	return (sel);
413}
414
415static __inline u_int
416rgs(void)
417{
418	u_int sel;
419	__asm __volatile("movl %%gs,%0" : "=rm" (sel));
420	return (sel);
421}
422
423static __inline void
424load_fs(u_int sel)
425{
426	__asm __volatile("movl %0,%%fs" : : "rm" (sel));
427}
428
429static __inline void
430load_gs(u_int sel)
431{
432	__asm __volatile("movl %0,%%gs" : : "rm" (sel));
433}
434
435static __inline u_int
436rdr0(void)
437{
438	u_int	data;
439	__asm __volatile("movl %%dr0,%0" : "=r" (data));
440	return (data);
441}
442
443static __inline u_int
444rdr1(void)
445{
446	u_int	data;
447	__asm __volatile("movl %%dr1,%0" : "=r" (data));
448	return (data);
449}
450
451static __inline u_int
452rdr2(void)
453{
454	u_int	data;
455	__asm __volatile("movl %%dr2,%0" : "=r" (data));
456	return (data);
457}
458
459static __inline u_int
460rdr3(void)
461{
462	u_int	data;
463	__asm __volatile("movl %%dr3,%0" : "=r" (data));
464	return (data);
465}
466
467static __inline u_int
468rdr6(void)
469{
470	u_int	data;
471	__asm __volatile("movl %%dr6,%0" : "=r" (data));
472	return (data);
473}
474
475static __inline u_int
476rdr7(void)
477{
478	u_int	data;
479	__asm __volatile("movl %%dr7,%0" : "=r" (data));
480	return (data);
481}
482
483static __inline critical_t
484critical_enter(void)
485{
486	critical_t eflags;
487
488	eflags = read_eflags();
489	disable_intr();
490	return (eflags);
491}
492
493static __inline void
494critical_exit(critical_t eflags)
495{
496	write_eflags(eflags);
497}
498
499#else /* !__GNUC__ */
500
501int	breakpoint	__P((void));
502u_int	bsfl		__P((u_int mask));
503u_int	bsrl		__P((u_int mask));
504void	disable_intr	__P((void));
505void	enable_intr	__P((void));
506u_char	inb		__P((u_int port));
507u_int	inl		__P((u_int port));
508void	insb		__P((u_int port, void *addr, size_t cnt));
509void	insl		__P((u_int port, void *addr, size_t cnt));
510void	insw		__P((u_int port, void *addr, size_t cnt));
511void	invd		__P((void));
512void	invlpg		__P((u_int addr));
513void	invltlb		__P((void));
514u_short	inw		__P((u_int port));
515void	outb		__P((u_int port, u_char data));
516void	outl		__P((u_int port, u_int data));
517void	outsb		__P((u_int port, void *addr, size_t cnt));
518void	outsl		__P((u_int port, void *addr, size_t cnt));
519void	outsw		__P((u_int port, void *addr, size_t cnt));
520void	outw		__P((u_int port, u_short data));
521u_int	rcr2		__P((void));
522u_int64_t rdmsr		__P((u_int msr));
523u_int64_t rdpmc		__P((u_int pmc));
524u_int64_t rdtsc		__P((void));
525u_int	read_eflags	__P((void));
526void	wbinvd		__P((void));
527void	write_eflags	__P((u_int ef));
528void	wrmsr		__P((u_int msr, u_int64_t newval));
529u_int	rfs		__P((void));
530u_int	rgs		__P((void));
531void	load_fs		__P((u_int sel));
532void	load_gs		__P((u_int sel));
533critical_t critical_enter __P((void));
534void	critical_exit	__P((critical_t eflags));
535
536#endif	/* __GNUC__ */
537
538void	load_cr0	__P((u_int cr0));
539void	load_cr3	__P((u_int cr3));
540void	load_cr4	__P((u_int cr4));
541void	ltr		__P((u_short sel));
542u_int	rcr0		__P((void));
543u_int	rcr3		__P((void));
544u_int	rcr4		__P((void));
545void    load_dr6        __P((u_int dr6));
546void    reset_dbregs    __P((void));
547
548#endif /* !_MACHINE_CPUFUNC_H_ */
549