cpufunc.h revision 146170
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sys/i386/include/cpufunc.h 146170 2005-05-13 00:10:56Z nectar $
30 */
31
32/*
33 * Functions to provide access to special i386 instructions.
34 * This in included in sys/systm.h, and that file should be
35 * used in preference to this.
36 */
37
38#ifndef _MACHINE_CPUFUNC_H_
39#define	_MACHINE_CPUFUNC_H_
40
41#ifndef _SYS_CDEFS_H_
42#error this file needs sys/cdefs.h as a prerequisite
43#endif
44
45struct region_descriptor;
46
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#if defined(__GNUCLIKE_ASM) && defined(__CC_SUPPORTS___INLINE)
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 %1,%0" : "=r" (result) : "rm" (mask));
69	return (result);
70}
71
72static __inline u_int
73bsrl(u_int mask)
74{
75	u_int	result;
76
77	__asm __volatile("bsrl %1,%0" : "=r" (result) : "rm" (mask));
78	return (result);
79}
80
81static __inline void
82disable_intr(void)
83{
84	__asm __volatile("cli" : : : "memory");
85}
86
87static __inline void
88do_cpuid(u_int ax, u_int *p)
89{
90	__asm __volatile("cpuid"
91			 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
92			 :  "0" (ax));
93}
94
95static __inline void
96cpuid_count(u_int ax, u_int cx, u_int *p)
97{
98	__asm __volatile("cpuid"
99			 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
100			 :  "0" (ax), "c" (cx));
101}
102
103static __inline void
104enable_intr(void)
105{
106	__asm __volatile("sti");
107}
108
109#ifdef _KERNEL
110
111#define	HAVE_INLINE_FFS
112
113static __inline int
114ffs(int mask)
115{
116	/*
117	 * Note that gcc-2's builtin ffs would be used if we didn't declare
118	 * this inline or turn off the builtin.  The builtin is faster but
119	 * broken in gcc-2.4.5 and slower but working in gcc-2.5 and later
120	 * versions.
121	 */
122	 return (mask == 0 ? mask : (int)bsfl((u_int)mask) + 1);
123}
124
125#define	HAVE_INLINE_FLS
126
127static __inline int
128fls(int mask)
129{
130	return (mask == 0 ? mask : (int)bsrl((u_int)mask) + 1);
131}
132
133#endif /* _KERNEL */
134
135static __inline void
136halt(void)
137{
138	__asm __volatile("hlt");
139}
140
141#if !defined(__GNUCLIKE_BUILTIN_CONSTANT_P) || __GNUCLIKE_ASM < 3
142
143#define	inb(port)		inbv(port)
144#define	outb(port, data)	outbv(port, data)
145
146#else /* __GNUCLIKE_BUILTIN_CONSTANT_P && __GNUCLIKE_ASM >= 3 */
147
148/*
149 * The following complications are to get around gcc not having a
150 * constraint letter for the range 0..255.  We still put "d" in the
151 * constraint because "i" isn't a valid constraint when the port
152 * isn't constant.  This only matters for -O0 because otherwise
153 * the non-working version gets optimized away.
154 *
155 * Use an expression-statement instead of a conditional expression
156 * because gcc-2.6.0 would promote the operands of the conditional
157 * and produce poor code for "if ((inb(var) & const1) == const2)".
158 *
159 * The unnecessary test `(port) < 0x10000' is to generate a warning if
160 * the `port' has type u_short or smaller.  Such types are pessimal.
161 * This actually only works for signed types.  The range check is
162 * careful to avoid generating warnings.
163 */
164#define	inb(port) __extension__ ({					\
165	u_char	_data;							\
166	if (__builtin_constant_p(port) && ((port) & 0xffff) < 0x100	\
167	    && (port) < 0x10000)					\
168		_data = inbc(port);					\
169	else								\
170		_data = inbv(port);					\
171	_data; })
172
173#define	outb(port, data) (						\
174	__builtin_constant_p(port) && ((port) & 0xffff) < 0x100		\
175	&& (port) < 0x10000						\
176	? outbc(port, data) : outbv(port, data))
177
178static __inline u_char
179inbc(u_int port)
180{
181	u_char	data;
182
183	__asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port)));
184	return (data);
185}
186
187static __inline void
188outbc(u_int port, u_char data)
189{
190	__asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port)));
191}
192
193#endif /* __GNUCLIKE_BUILTIN_CONSTANT_P  && __GNUCLIKE_ASM >= 3*/
194
195static __inline u_char
196inbv(u_int port)
197{
198	u_char	data;
199	/*
200	 * We use %%dx and not %1 here because i/o is done at %dx and not at
201	 * %edx, while gcc generates inferior code (movw instead of movl)
202	 * if we tell it to load (u_short) port.
203	 */
204	__asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
205	return (data);
206}
207
208static __inline u_int
209inl(u_int port)
210{
211	u_int	data;
212
213	__asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port));
214	return (data);
215}
216
217static __inline void
218insb(u_int port, void *addr, size_t cnt)
219{
220	__asm __volatile("cld; rep; insb"
221			 : "+D" (addr), "+c" (cnt)
222			 : "d" (port)
223			 : "memory");
224}
225
226static __inline void
227insw(u_int port, void *addr, size_t cnt)
228{
229	__asm __volatile("cld; rep; insw"
230			 : "+D" (addr), "+c" (cnt)
231			 : "d" (port)
232			 : "memory");
233}
234
235static __inline void
236insl(u_int port, void *addr, size_t cnt)
237{
238	__asm __volatile("cld; rep; insl"
239			 : "+D" (addr), "+c" (cnt)
240			 : "d" (port)
241			 : "memory");
242}
243
244static __inline void
245invd(void)
246{
247	__asm __volatile("invd");
248}
249
250static __inline u_short
251inw(u_int port)
252{
253	u_short	data;
254
255	__asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port));
256	return (data);
257}
258
259static __inline void
260outbv(u_int port, u_char data)
261{
262	u_char	al;
263	/*
264	 * Use an unnecessary assignment to help gcc's register allocator.
265	 * This make a large difference for gcc-1.40 and a tiny difference
266	 * for gcc-2.6.0.  For gcc-1.40, al had to be ``asm("ax")'' for
267	 * best results.  gcc-2.6.0 can't handle this.
268	 */
269	al = data;
270	__asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
271}
272
273static __inline void
274outl(u_int port, u_int data)
275{
276	/*
277	 * outl() and outw() aren't used much so we haven't looked at
278	 * possible micro-optimizations such as the unnecessary
279	 * assignment for them.
280	 */
281	__asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port));
282}
283
284static __inline void
285outsb(u_int port, const void *addr, size_t cnt)
286{
287	__asm __volatile("cld; rep; outsb"
288			 : "+S" (addr), "+c" (cnt)
289			 : "d" (port));
290}
291
292static __inline void
293outsw(u_int port, const void *addr, size_t cnt)
294{
295	__asm __volatile("cld; rep; outsw"
296			 : "+S" (addr), "+c" (cnt)
297			 : "d" (port));
298}
299
300static __inline void
301outsl(u_int port, const void *addr, size_t cnt)
302{
303	__asm __volatile("cld; rep; outsl"
304			 : "+S" (addr), "+c" (cnt)
305			 : "d" (port));
306}
307
308static __inline void
309outw(u_int port, u_short data)
310{
311	__asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port));
312}
313
314static __inline void
315ia32_pause(void)
316{
317	__asm __volatile("pause");
318}
319
320static __inline u_int
321read_eflags(void)
322{
323	u_int	ef;
324
325	__asm __volatile("pushfl; popl %0" : "=r" (ef));
326	return (ef);
327}
328
329static __inline u_int64_t
330rdmsr(u_int msr)
331{
332	u_int64_t rv;
333
334	__asm __volatile("rdmsr" : "=A" (rv) : "c" (msr));
335	return (rv);
336}
337
338static __inline u_int64_t
339rdpmc(u_int pmc)
340{
341	u_int64_t rv;
342
343	__asm __volatile("rdpmc" : "=A" (rv) : "c" (pmc));
344	return (rv);
345}
346
347static __inline u_int64_t
348rdtsc(void)
349{
350	u_int64_t rv;
351
352	__asm __volatile("rdtsc" : "=A" (rv));
353	return (rv);
354}
355
356static __inline void
357wbinvd(void)
358{
359	__asm __volatile("wbinvd");
360}
361
362static __inline void
363write_eflags(u_int ef)
364{
365	__asm __volatile("pushl %0; popfl" : : "r" (ef));
366}
367
368static __inline void
369wrmsr(u_int msr, u_int64_t newval)
370{
371	__asm __volatile("wrmsr" : : "A" (newval), "c" (msr));
372}
373
374static __inline void
375load_cr0(u_int data)
376{
377
378	__asm __volatile("movl %0,%%cr0" : : "r" (data));
379}
380
381static __inline u_int
382rcr0(void)
383{
384	u_int	data;
385
386	__asm __volatile("movl %%cr0,%0" : "=r" (data));
387	return (data);
388}
389
390static __inline u_int
391rcr2(void)
392{
393	u_int	data;
394
395	__asm __volatile("movl %%cr2,%0" : "=r" (data));
396	return (data);
397}
398
399static __inline void
400load_cr3(u_int data)
401{
402
403	__asm __volatile("movl %0,%%cr3" : : "r" (data) : "memory");
404}
405
406static __inline u_int
407rcr3(void)
408{
409	u_int	data;
410
411	__asm __volatile("movl %%cr3,%0" : "=r" (data));
412	return (data);
413}
414
415static __inline void
416load_cr4(u_int data)
417{
418	__asm __volatile("movl %0,%%cr4" : : "r" (data));
419}
420
421static __inline u_int
422rcr4(void)
423{
424	u_int	data;
425
426	__asm __volatile("movl %%cr4,%0" : "=r" (data));
427	return (data);
428}
429
430/*
431 * Global TLB flush (except for thise for pages marked PG_G)
432 */
433static __inline void
434invltlb(void)
435{
436
437	load_cr3(rcr3());
438}
439
440/*
441 * TLB flush for an individual page (even if it has PG_G).
442 * Only works on 486+ CPUs (i386 does not have PG_G).
443 */
444static __inline void
445invlpg(u_int addr)
446{
447
448	__asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
449}
450
451static __inline u_int
452rfs(void)
453{
454	u_int sel;
455	__asm __volatile("movl %%fs,%0" : "=rm" (sel));
456	return (sel);
457}
458
459static __inline u_int
460rgs(void)
461{
462	u_int sel;
463	__asm __volatile("movl %%gs,%0" : "=rm" (sel));
464	return (sel);
465}
466
467static __inline u_int
468rss(void)
469{
470	u_int sel;
471	__asm __volatile("movl %%ss,%0" : "=rm" (sel));
472	return (sel);
473}
474
475static __inline void
476load_fs(u_int sel)
477{
478	__asm __volatile("movl %0,%%fs" : : "rm" (sel));
479}
480
481static __inline void
482load_gs(u_int sel)
483{
484	__asm __volatile("movl %0,%%gs" : : "rm" (sel));
485}
486
487static __inline void
488lidt(struct region_descriptor *addr)
489{
490	__asm __volatile("lidt (%0)" : : "r" (addr));
491}
492
493static __inline void
494lldt(u_short sel)
495{
496	__asm __volatile("lldt %0" : : "r" (sel));
497}
498
499static __inline void
500ltr(u_short sel)
501{
502	__asm __volatile("ltr %0" : : "r" (sel));
503}
504
505static __inline u_int
506rdr0(void)
507{
508	u_int	data;
509	__asm __volatile("movl %%dr0,%0" : "=r" (data));
510	return (data);
511}
512
513static __inline void
514load_dr0(u_int dr0)
515{
516	__asm __volatile("movl %0,%%dr0" : : "r" (dr0));
517}
518
519static __inline u_int
520rdr1(void)
521{
522	u_int	data;
523	__asm __volatile("movl %%dr1,%0" : "=r" (data));
524	return (data);
525}
526
527static __inline void
528load_dr1(u_int dr1)
529{
530	__asm __volatile("movl %0,%%dr1" : : "r" (dr1));
531}
532
533static __inline u_int
534rdr2(void)
535{
536	u_int	data;
537	__asm __volatile("movl %%dr2,%0" : "=r" (data));
538	return (data);
539}
540
541static __inline void
542load_dr2(u_int dr2)
543{
544	__asm __volatile("movl %0,%%dr2" : : "r" (dr2));
545}
546
547static __inline u_int
548rdr3(void)
549{
550	u_int	data;
551	__asm __volatile("movl %%dr3,%0" : "=r" (data));
552	return (data);
553}
554
555static __inline void
556load_dr3(u_int dr3)
557{
558	__asm __volatile("movl %0,%%dr3" : : "r" (dr3));
559}
560
561static __inline u_int
562rdr4(void)
563{
564	u_int	data;
565	__asm __volatile("movl %%dr4,%0" : "=r" (data));
566	return (data);
567}
568
569static __inline void
570load_dr4(u_int dr4)
571{
572	__asm __volatile("movl %0,%%dr4" : : "r" (dr4));
573}
574
575static __inline u_int
576rdr5(void)
577{
578	u_int	data;
579	__asm __volatile("movl %%dr5,%0" : "=r" (data));
580	return (data);
581}
582
583static __inline void
584load_dr5(u_int dr5)
585{
586	__asm __volatile("movl %0,%%dr5" : : "r" (dr5));
587}
588
589static __inline u_int
590rdr6(void)
591{
592	u_int	data;
593	__asm __volatile("movl %%dr6,%0" : "=r" (data));
594	return (data);
595}
596
597static __inline void
598load_dr6(u_int dr6)
599{
600	__asm __volatile("movl %0,%%dr6" : : "r" (dr6));
601}
602
603static __inline u_int
604rdr7(void)
605{
606	u_int	data;
607	__asm __volatile("movl %%dr7,%0" : "=r" (data));
608	return (data);
609}
610
611static __inline void
612load_dr7(u_int dr7)
613{
614	__asm __volatile("movl %0,%%dr7" : : "r" (dr7));
615}
616
617static __inline register_t
618intr_disable(void)
619{
620	register_t eflags;
621
622	eflags = read_eflags();
623	disable_intr();
624	return (eflags);
625}
626
627static __inline void
628intr_restore(register_t eflags)
629{
630	write_eflags(eflags);
631}
632
633#else /* !(__GNUCLIKE_ASM && __CC_SUPPORTS___INLINE) */
634
635int	breakpoint(void);
636u_int	bsfl(u_int mask);
637u_int	bsrl(u_int mask);
638void	disable_intr(void);
639void	do_cpuid(u_int ax, u_int *p);
640void	enable_intr(void);
641void	halt(void);
642void	ia32_pause(void);
643u_char	inb(u_int port);
644u_int	inl(u_int port);
645void	insb(u_int port, void *addr, size_t cnt);
646void	insl(u_int port, void *addr, size_t cnt);
647void	insw(u_int port, void *addr, size_t cnt);
648register_t	intr_disable(void);
649void	intr_restore(register_t ef);
650void	invd(void);
651void	invlpg(u_int addr);
652void	invltlb(void);
653u_short	inw(u_int port);
654void	lidt(struct region_descriptor *addr);
655void	lldt(u_short sel);
656void	load_cr0(u_int cr0);
657void	load_cr3(u_int cr3);
658void	load_cr4(u_int cr4);
659void	load_dr0(u_int dr0);
660void	load_dr1(u_int dr1);
661void	load_dr2(u_int dr2);
662void	load_dr3(u_int dr3);
663void	load_dr4(u_int dr4);
664void	load_dr5(u_int dr5);
665void	load_dr6(u_int dr6);
666void	load_dr7(u_int dr7);
667void	load_fs(u_int sel);
668void	load_gs(u_int sel);
669void	ltr(u_short sel);
670void	outb(u_int port, u_char data);
671void	outl(u_int port, u_int data);
672void	outsb(u_int port, const void *addr, size_t cnt);
673void	outsl(u_int port, const void *addr, size_t cnt);
674void	outsw(u_int port, const void *addr, size_t cnt);
675void	outw(u_int port, u_short data);
676u_int	rcr0(void);
677u_int	rcr2(void);
678u_int	rcr3(void);
679u_int	rcr4(void);
680u_int64_t rdmsr(u_int msr);
681u_int64_t rdpmc(u_int pmc);
682u_int	rdr0(void);
683u_int	rdr1(void);
684u_int	rdr2(void);
685u_int	rdr3(void);
686u_int	rdr4(void);
687u_int	rdr5(void);
688u_int	rdr6(void);
689u_int	rdr7(void);
690u_int64_t rdtsc(void);
691u_int	read_eflags(void);
692u_int	rfs(void);
693u_int	rgs(void);
694void	wbinvd(void);
695void	write_eflags(u_int ef);
696void	wrmsr(u_int msr, u_int64_t newval);
697
698#endif	/* __GNUCLIKE_ASM && __CC_SUPPORTS___INLINE */
699
700void    reset_dbregs(void);
701
702#endif /* !_MACHINE_CPUFUNC_H_ */
703