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