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