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