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