cpufunc.h revision 91260
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 91260 2002-02-25 23:49:51Z peter $
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#include <sys/cdefs.h>
44#include <machine/psl.h>
45
46__BEGIN_DECLS
47#define readb(va)	(*(volatile u_int8_t *) (va))
48#define readw(va)	(*(volatile u_int16_t *) (va))
49#define readl(va)	(*(volatile u_int32_t *) (va))
50
51#define writeb(va, d)	(*(volatile u_int8_t *) (va) = (d))
52#define writew(va, d)	(*(volatile u_int16_t *) (va) = (d))
53#define writel(va, d)	(*(volatile u_int32_t *) (va) = (d))
54
55#define	CRITICAL_FORK	(read_eflags() | PSL_I)
56
57#ifdef	__GNUC__
58
59#ifdef SWTCH_OPTIM_STATS
60extern	int	tlb_flush_count;	/* XXX */
61#endif
62
63static __inline void
64breakpoint(void)
65{
66	__asm __volatile("int $3");
67}
68
69static __inline u_int
70bsfl(u_int mask)
71{
72	u_int	result;
73
74	__asm __volatile("bsfl %1,%0" : "=r" (result) : "rm" (mask));
75	return (result);
76}
77
78static __inline u_int
79bsrl(u_int mask)
80{
81	u_int	result;
82
83	__asm __volatile("bsrl %1,%0" : "=r" (result) : "rm" (mask));
84	return (result);
85}
86
87static __inline void
88disable_intr(void)
89{
90	__asm __volatile("cli" : : : "memory");
91}
92
93static __inline void
94enable_intr(void)
95{
96	__asm __volatile("sti");
97}
98
99#define	HAVE_INLINE_FFS
100
101static __inline int
102ffs(int mask)
103{
104	/*
105	 * Note that gcc-2's builtin ffs would be used if we didn't declare
106	 * this inline or turn off the builtin.  The builtin is faster but
107	 * broken in gcc-2.4.5 and slower but working in gcc-2.5 and later
108	 * versions.
109	 */
110	 return (mask == 0 ? mask : bsfl((u_int)mask) + 1);
111}
112
113#define	HAVE_INLINE_FLS
114
115static __inline int
116fls(int mask)
117{
118	return (mask == 0 ? mask : bsrl((u_int)mask) + 1);
119}
120
121#if __GNUC__ < 2
122
123#define	inb(port)		inbv(port)
124#define	outb(port, data)	outbv(port, data)
125
126#else /* __GNUC >= 2 */
127
128/*
129 * The following complications are to get around gcc not having a
130 * constraint letter for the range 0..255.  We still put "d" in the
131 * constraint because "i" isn't a valid constraint when the port
132 * isn't constant.  This only matters for -O0 because otherwise
133 * the non-working version gets optimized away.
134 *
135 * Use an expression-statement instead of a conditional expression
136 * because gcc-2.6.0 would promote the operands of the conditional
137 * and produce poor code for "if ((inb(var) & const1) == const2)".
138 *
139 * The unnecessary test `(port) < 0x10000' is to generate a warning if
140 * the `port' has type u_short or smaller.  Such types are pessimal.
141 * This actually only works for signed types.  The range check is
142 * careful to avoid generating warnings.
143 */
144#define	inb(port) __extension__ ({					\
145	u_char	_data;							\
146	if (__builtin_constant_p(port) && ((port) & 0xffff) < 0x100	\
147	    && (port) < 0x10000)					\
148		_data = inbc(port);					\
149	else								\
150		_data = inbv(port);					\
151	_data; })
152
153#define	outb(port, data) (						\
154	__builtin_constant_p(port) && ((port) & 0xffff) < 0x100		\
155	&& (port) < 0x10000						\
156	? outbc(port, data) : outbv(port, data))
157
158static __inline u_char
159inbc(u_int port)
160{
161	u_char	data;
162
163	__asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port)));
164	return (data);
165}
166
167static __inline void
168outbc(u_int port, u_char data)
169{
170	__asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port)));
171}
172
173#endif /* __GNUC <= 2 */
174
175static __inline u_char
176inbv(u_int port)
177{
178	u_char	data;
179	/*
180	 * We use %%dx and not %1 here because i/o is done at %dx and not at
181	 * %edx, while gcc generates inferior code (movw instead of movl)
182	 * if we tell it to load (u_short) port.
183	 */
184	__asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
185	return (data);
186}
187
188static __inline u_int
189inl(u_int port)
190{
191	u_int	data;
192
193	__asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port));
194	return (data);
195}
196
197static __inline void
198insb(u_int port, void *addr, size_t cnt)
199{
200	__asm __volatile("cld; rep; insb"
201			 : "+D" (addr), "+c" (cnt)
202			 : "d" (port)
203			 : "memory");
204}
205
206static __inline void
207insw(u_int port, void *addr, size_t cnt)
208{
209	__asm __volatile("cld; rep; insw"
210			 : "+D" (addr), "+c" (cnt)
211			 : "d" (port)
212			 : "memory");
213}
214
215static __inline void
216insl(u_int port, void *addr, size_t cnt)
217{
218	__asm __volatile("cld; rep; insl"
219			 : "+D" (addr), "+c" (cnt)
220			 : "d" (port)
221			 : "memory");
222}
223
224static __inline void
225invd(void)
226{
227	__asm __volatile("invd");
228}
229
230static __inline u_short
231inw(u_int port)
232{
233	u_short	data;
234
235	__asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port));
236	return (data);
237}
238
239static __inline void
240outbv(u_int port, u_char data)
241{
242	u_char	al;
243	/*
244	 * Use an unnecessary assignment to help gcc's register allocator.
245	 * This make a large difference for gcc-1.40 and a tiny difference
246	 * for gcc-2.6.0.  For gcc-1.40, al had to be ``asm("ax")'' for
247	 * best results.  gcc-2.6.0 can't handle this.
248	 */
249	al = data;
250	__asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
251}
252
253static __inline void
254outl(u_int port, u_int data)
255{
256	/*
257	 * outl() and outw() aren't used much so we haven't looked at
258	 * possible micro-optimizations such as the unnecessary
259	 * assignment for them.
260	 */
261	__asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port));
262}
263
264static __inline void
265outsb(u_int port, const void *addr, size_t cnt)
266{
267	__asm __volatile("cld; rep; outsb"
268			 : "+S" (addr), "+c" (cnt)
269			 : "d" (port));
270}
271
272static __inline void
273outsw(u_int port, const void *addr, size_t cnt)
274{
275	__asm __volatile("cld; rep; outsw"
276			 : "+S" (addr), "+c" (cnt)
277			 : "d" (port));
278}
279
280static __inline void
281outsl(u_int port, const void *addr, size_t cnt)
282{
283	__asm __volatile("cld; rep; outsl"
284			 : "+S" (addr), "+c" (cnt)
285			 : "d" (port));
286}
287
288static __inline void
289outw(u_int port, u_short data)
290{
291	__asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port));
292}
293
294static __inline u_int
295read_eflags(void)
296{
297	u_int	ef;
298
299	__asm __volatile("pushfl; popl %0" : "=r" (ef));
300	return (ef);
301}
302
303static __inline void
304do_cpuid(u_int ax, u_int *p)
305{
306	__asm __volatile(
307	"cpuid"
308	: "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
309	:  "0" (ax)
310	);
311}
312
313static __inline u_int64_t
314rdmsr(u_int msr)
315{
316	u_int64_t rv;
317
318	__asm __volatile("rdmsr" : "=A" (rv) : "c" (msr));
319	return (rv);
320}
321
322static __inline u_int64_t
323rdpmc(u_int pmc)
324{
325	u_int64_t rv;
326
327	__asm __volatile("rdpmc" : "=A" (rv) : "c" (pmc));
328	return (rv);
329}
330
331static __inline u_int64_t
332rdtsc(void)
333{
334	u_int64_t rv;
335
336	__asm __volatile("rdtsc" : "=A" (rv));
337	return (rv);
338}
339
340static __inline void
341wbinvd(void)
342{
343	__asm __volatile("wbinvd");
344}
345
346static __inline void
347write_eflags(u_int ef)
348{
349	__asm __volatile("pushl %0; popfl" : : "r" (ef));
350}
351
352static __inline void
353wrmsr(u_int msr, u_int64_t newval)
354{
355	__asm __volatile("wrmsr" : : "A" (newval), "c" (msr));
356}
357
358static __inline void
359load_cr0(u_int data)
360{
361
362	__asm __volatile("movl %0,%%cr0" : : "r" (data));
363}
364
365static __inline u_int
366rcr0(void)
367{
368	u_int	data;
369
370	__asm __volatile("movl %%cr0,%0" : "=r" (data));
371	return (data);
372}
373
374static __inline u_int
375rcr2(void)
376{
377	u_int	data;
378
379	__asm __volatile("movl %%cr2,%0" : "=r" (data));
380	return (data);
381}
382
383static __inline void
384load_cr3(u_int data)
385{
386
387	__asm __volatile("movl %0,%%cr3" : : "r" (data) : "memory");
388#if defined(SWTCH_OPTIM_STATS)
389	++tlb_flush_count;
390#endif
391}
392
393static __inline u_int
394rcr3(void)
395{
396	u_int	data;
397
398	__asm __volatile("movl %%cr3,%0" : "=r" (data));
399	return (data);
400}
401
402static __inline void
403load_cr4(u_int data)
404{
405	__asm __volatile("movl %0,%%cr4" : : "r" (data));
406}
407
408static __inline u_int
409rcr4(void)
410{
411	u_int	data;
412
413	__asm __volatile("movl %%cr4,%0" : "=r" (data));
414	return (data);
415}
416
417/*
418 * Global TLB flush (except for thise for pages marked PG_G)
419 */
420static __inline void
421cpu_invltlb(void)
422{
423
424	load_cr3(rcr3());
425}
426
427/*
428 * TLB flush for an individual page (even if it has PG_G).
429 * Only works on 486+ CPUs (i386 does not have PG_G).
430 */
431static __inline void
432cpu_invlpg(u_int addr)
433{
434
435#ifndef I386_CPU
436	__asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
437#else
438	cpu_invltlb();
439#endif
440}
441
442#ifdef PAGE_SIZE	/* Avoid this file depending on sys/param.h */
443/*
444 * Same as above but for a range of pages.
445 */
446static __inline void
447cpu_invlpg_range(u_int startva, u_int endva)
448{
449#ifndef I386_CPU
450	u_int addr;
451
452	for (addr = startva; addr < endva; addr += PAGE_SIZE)
453		__asm __volatile("invlpg %0" : : "m" (*(char *)addr));
454	__asm __volatile("" : : : "memory");
455#else
456	cpu_invltlb();
457#endif
458}
459#endif
460
461#ifdef SMP
462extern void	smp_invlpg(u_int addr);
463extern void	smp_masked_invlpg(u_int mask, u_int addr);
464#ifdef PAGE_SIZE	/* Avoid this file depending on sys/param.h */
465extern void	smp_invlpg_range(u_int startva, u_int endva);
466extern void	smp_masked_invlpg_range(u_int mask, u_int startva, u_int endva);
467#endif
468extern void	smp_invltlb(void);
469extern void	smp_masked_invltlb(u_int mask);
470#endif
471
472/*
473 * Generic page TLB flush.  Takes care of SMP.
474 */
475static __inline void
476invlpg(u_int addr)
477{
478
479	cpu_invlpg(addr);
480#ifdef SMP
481	smp_invlpg(addr);
482#endif
483}
484
485#ifdef PAGE_SIZE	/* Avoid this file depending on sys/param.h */
486/*
487 * Generic TLB flush for a range of pages. Takes care of SMP.
488 * Saves many IPIs for SMP mode.
489 */
490static __inline void
491invlpg_range(u_int startva, u_int endva)
492{
493
494	cpu_invlpg_range(startva, endva);
495#ifdef SMP
496	smp_invlpg_range(startva, endva);
497#endif
498}
499#endif
500
501/*
502 * Generic global TLB flush (except for thise for pages marked PG_G)
503 */
504static __inline void
505invltlb(void)
506{
507
508	cpu_invltlb();
509#ifdef SMP
510	smp_invltlb();
511#endif
512}
513
514static __inline u_int
515rfs(void)
516{
517	u_int sel;
518	__asm __volatile("movl %%fs,%0" : "=rm" (sel));
519	return (sel);
520}
521
522static __inline u_int
523rgs(void)
524{
525	u_int sel;
526	__asm __volatile("movl %%gs,%0" : "=rm" (sel));
527	return (sel);
528}
529
530static __inline void
531load_fs(u_int sel)
532{
533	__asm __volatile("movl %0,%%fs" : : "rm" (sel));
534}
535
536static __inline void
537load_gs(u_int sel)
538{
539	__asm __volatile("movl %0,%%gs" : : "rm" (sel));
540}
541
542static __inline u_int
543rdr0(void)
544{
545	u_int	data;
546	__asm __volatile("movl %%dr0,%0" : "=r" (data));
547	return (data);
548}
549
550static __inline void
551load_dr0(u_int sel)
552{
553	__asm __volatile("movl %0,%%dr0" : : "r" (sel));
554}
555
556static __inline u_int
557rdr1(void)
558{
559	u_int	data;
560	__asm __volatile("movl %%dr1,%0" : "=r" (data));
561	return (data);
562}
563
564static __inline void
565load_dr1(u_int sel)
566{
567	__asm __volatile("movl %0,%%dr1" : : "r" (sel));
568}
569
570static __inline u_int
571rdr2(void)
572{
573	u_int	data;
574	__asm __volatile("movl %%dr2,%0" : "=r" (data));
575	return (data);
576}
577
578static __inline void
579load_dr2(u_int sel)
580{
581	__asm __volatile("movl %0,%%dr2" : : "r" (sel));
582}
583
584static __inline u_int
585rdr3(void)
586{
587	u_int	data;
588	__asm __volatile("movl %%dr3,%0" : "=r" (data));
589	return (data);
590}
591
592static __inline void
593load_dr3(u_int sel)
594{
595	__asm __volatile("movl %0,%%dr3" : : "r" (sel));
596}
597
598static __inline u_int
599rdr4(void)
600{
601	u_int	data;
602	__asm __volatile("movl %%dr4,%0" : "=r" (data));
603	return (data);
604}
605
606static __inline void
607load_dr4(u_int sel)
608{
609	__asm __volatile("movl %0,%%dr4" : : "r" (sel));
610}
611
612static __inline u_int
613rdr5(void)
614{
615	u_int	data;
616	__asm __volatile("movl %%dr5,%0" : "=r" (data));
617	return (data);
618}
619
620static __inline void
621load_dr5(u_int sel)
622{
623	__asm __volatile("movl %0,%%dr5" : : "r" (sel));
624}
625
626static __inline u_int
627rdr6(void)
628{
629	u_int	data;
630	__asm __volatile("movl %%dr6,%0" : "=r" (data));
631	return (data);
632}
633
634static __inline void
635load_dr6(u_int sel)
636{
637	__asm __volatile("movl %0,%%dr6" : : "r" (sel));
638}
639
640static __inline u_int
641rdr7(void)
642{
643	u_int	data;
644	__asm __volatile("movl %%dr7,%0" : "=r" (data));
645	return (data);
646}
647
648static __inline void
649load_dr7(u_int sel)
650{
651	__asm __volatile("movl %0,%%dr7" : : "r" (sel));
652}
653
654static __inline critical_t
655cpu_critical_enter(void)
656{
657	critical_t eflags;
658
659	eflags = read_eflags();
660	disable_intr();
661	return (eflags);
662}
663
664static __inline void
665cpu_critical_exit(critical_t eflags)
666{
667	write_eflags(eflags);
668}
669
670#else /* !__GNUC__ */
671
672int	breakpoint	__P((void));
673u_int	bsfl		__P((u_int mask));
674u_int	bsrl		__P((u_int mask));
675void	cpu_invlpg	__P((u_int addr));
676void	cpu_invlpg_range __P((u_int start, u_int end));
677void	disable_intr	__P((void));
678void	do_cpuid	__P((u_int ax, u_int *p));
679void	enable_intr	__P((void));
680u_char	inb		__P((u_int port));
681u_int	inl		__P((u_int port));
682void	insb		__P((u_int port, void *addr, size_t cnt));
683void	insl		__P((u_int port, void *addr, size_t cnt));
684void	insw		__P((u_int port, void *addr, size_t cnt));
685void	invd		__P((void));
686void	invlpg		__P((u_int addr));
687void	invlpg_range	__P((u_int start, u_int end));
688void	invltlb		__P((void));
689u_short	inw		__P((u_int port));
690void	load_cr0	__P((u_int cr0));
691void	load_cr3	__P((u_int cr3));
692void	load_cr4	__P((u_int cr4));
693void	load_fs		__P((u_int sel));
694void	load_gs		__P((u_int sel));
695void	outb		__P((u_int port, u_char data));
696void	outl		__P((u_int port, u_int data));
697void	outsb		__P((u_int port, void *addr, size_t cnt));
698void	outsl		__P((u_int port, void *addr, size_t cnt));
699void	outsw		__P((u_int port, void *addr, size_t cnt));
700void	outw		__P((u_int port, u_short data));
701u_int	rcr0		__P((void));
702u_int	rcr2		__P((void));
703u_int	rcr3		__P((void));
704u_int	rcr4		__P((void));
705u_int	rfs		__P((void));
706u_int	rgs		__P((void));
707u_int64_t rdmsr		__P((u_int msr));
708u_int64_t rdpmc		__P((u_int pmc));
709u_int64_t rdtsc		__P((void));
710u_int	read_eflags	__P((void));
711void	wbinvd		__P((void));
712void	write_eflags	__P((u_int ef));
713void	wrmsr		__P((u_int msr, u_int64_t newval));
714critical_t cpu_critical_enter __P((void));
715void	cpu_critical_exit __P((critical_t eflags));
716
717#endif	/* __GNUC__ */
718
719void	ltr		__P((u_short sel));
720void    reset_dbregs    __P((void));
721__END_DECLS
722
723#endif /* !_MACHINE_CPUFUNC_H_ */
724