Merge from vendor branch OPENSSH:
[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.1 2004/02/02 08:05:52 dillon 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 #define HAVE_INLINE_FFS
129
130 static __inline int
131 ffs(int mask)
132 {
133 #if 0
134         /*
135          * Note that gcc-2's builtin ffs would be used if we didn't declare
136          * this inline or turn off the builtin.  The builtin is faster but
137          * broken in gcc-2.4.5 and slower but working in gcc-2.5 and later
138          * versions.
139          */
140         return (mask == 0 ? mask : (int)bsfl((u_int)mask) + 1);
141 #else
142         /* Actually, the above is way out of date.  The builtins use cmov etc */
143         return (__builtin_ffs(mask));
144 #endif
145 }
146
147 #define HAVE_INLINE_FFSL
148
149 static __inline int
150 ffsl(long mask)
151 {
152         return (mask == 0 ? mask : (int)bsfq((u_long)mask) + 1);
153 }
154
155 #define HAVE_INLINE_FLS
156
157 static __inline int
158 fls(int mask)
159 {
160         return (mask == 0 ? mask : (int)bsrl((u_int)mask) + 1);
161 }
162
163 #define HAVE_INLINE_FLSL
164
165 static __inline int
166 flsl(long mask)
167 {
168         return (mask == 0 ? mask : (int)bsrq((u_long)mask) + 1);
169 }
170
171 static __inline void
172 halt(void)
173 {
174         __asm __volatile("hlt");
175 }
176
177 #if __GNUC__ < 2
178
179 #define inb(port)               inbv(port)
180 #define outb(port, data)        outbv(port, data)
181
182 #else /* __GNUC >= 2 */
183
184 /*
185  * The following complications are to get around gcc not having a
186  * constraint letter for the range 0..255.  We still put "d" in the
187  * constraint because "i" isn't a valid constraint when the port
188  * isn't constant.  This only matters for -O0 because otherwise
189  * the non-working version gets optimized away.
190  * 
191  * Use an expression-statement instead of a conditional expression
192  * because gcc-2.6.0 would promote the operands of the conditional
193  * and produce poor code for "if ((inb(var) & const1) == const2)".
194  *
195  * The unnecessary test `(port) < 0x10000' is to generate a warning if
196  * the `port' has type u_short or smaller.  Such types are pessimal.
197  * This actually only works for signed types.  The range check is
198  * careful to avoid generating warnings.
199  */
200 #define inb(port) __extension__ ({                                      \
201         u_char  _data;                                                  \
202         if (__builtin_constant_p(port) && ((port) & 0xffff) < 0x100     \
203             && (port) < 0x10000)                                        \
204                 _data = inbc(port);                                     \
205         else                                                            \
206                 _data = inbv(port);                                     \
207         _data; })
208
209 #define outb(port, data) (                                              \
210         __builtin_constant_p(port) && ((port) & 0xffff) < 0x100         \
211         && (port) < 0x10000                                             \
212         ? outbc(port, data) : outbv(port, data))
213
214 static __inline u_char
215 inbc(u_int port)
216 {
217         u_char  data;
218
219         __asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port)));
220         return (data);
221 }
222
223 static __inline void
224 outbc(u_int port, u_char data)
225 {
226         __asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port)));
227 }
228
229 #endif /* __GNUC <= 2 */
230
231 static __inline u_char
232 inbv(u_int port)
233 {
234         u_char  data;
235         /*
236          * We use %%dx and not %1 here because i/o is done at %dx and not at
237          * %edx, while gcc generates inferior code (movw instead of movl)
238          * if we tell it to load (u_short) port.
239          */
240         __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
241         return (data);
242 }
243
244 static __inline u_int
245 inl(u_int port)
246 {
247         u_int   data;
248
249         __asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port));
250         return (data);
251 }
252
253 static __inline void
254 insb(u_int port, void *addr, size_t cnt)
255 {
256         __asm __volatile("cld; rep; insb"
257                          : "+D" (addr), "+c" (cnt)
258                          : "d" (port)
259                          : "memory");
260 }
261
262 static __inline void
263 insw(u_int port, void *addr, size_t cnt)
264 {
265         __asm __volatile("cld; rep; insw"
266                          : "+D" (addr), "+c" (cnt)
267                          : "d" (port)
268                          : "memory");
269 }
270
271 static __inline void
272 insl(u_int port, void *addr, size_t cnt)
273 {
274         __asm __volatile("cld; rep; insl"
275                          : "+D" (addr), "+c" (cnt)
276                          : "d" (port)
277                          : "memory");
278 }
279
280 static __inline void
281 invd(void)
282 {
283         __asm __volatile("invd");
284 }
285
286 static __inline u_short
287 inw(u_int port)
288 {
289         u_short data;
290
291         __asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port));
292         return (data);
293 }
294
295 static __inline void
296 outbv(u_int port, u_char data)
297 {
298         u_char  al;
299         /*
300          * Use an unnecessary assignment to help gcc's register allocator.
301          * This make a large difference for gcc-1.40 and a tiny difference
302          * for gcc-2.6.0.  For gcc-1.40, al had to be ``asm("ax")'' for
303          * best results.  gcc-2.6.0 can't handle this.
304          */
305         al = data;
306         __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
307 }
308
309 static __inline void
310 outl(u_int port, u_int data)
311 {
312         /*
313          * outl() and outw() aren't used much so we haven't looked at
314          * possible micro-optimizations such as the unnecessary
315          * assignment for them.
316          */
317         __asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port));
318 }
319
320 static __inline void
321 outsb(u_int port, const void *addr, size_t cnt)
322 {
323         __asm __volatile("cld; rep; outsb"
324                          : "+S" (addr), "+c" (cnt)
325                          : "d" (port));
326 }
327
328 static __inline void
329 outsw(u_int port, const void *addr, size_t cnt)
330 {
331         __asm __volatile("cld; rep; outsw"
332                          : "+S" (addr), "+c" (cnt)
333                          : "d" (port));
334 }
335
336 static __inline void
337 outsl(u_int port, const void *addr, size_t cnt)
338 {
339         __asm __volatile("cld; rep; outsl"
340                          : "+S" (addr), "+c" (cnt)
341                          : "d" (port));
342 }
343
344 static __inline void
345 outw(u_int port, u_short data)
346 {
347         __asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port));
348 }
349
350 static __inline void
351 ia32_pause(void)
352 {
353         __asm __volatile("pause");
354 }
355
356 static __inline u_long
357 read_rflags(void)
358 {
359         u_long  rf;
360
361         __asm __volatile("pushfq; popq %0" : "=r" (rf));
362         return (rf);
363 }
364
365 static __inline u_int64_t
366 rdmsr(u_int msr)
367 {
368         u_int32_t low, high;
369
370         __asm __volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr));
371         return (low | ((u_int64_t)high << 32));
372 }
373
374 static __inline u_int64_t
375 rdpmc(u_int pmc)
376 {
377         u_int32_t low, high;
378
379         __asm __volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (pmc));
380         return (low | ((u_int64_t)high << 32));
381 }
382
383 static __inline u_int64_t
384 rdtsc(void)
385 {
386         u_int32_t low, high;
387
388         __asm __volatile("rdtsc" : "=a" (low), "=d" (high));
389         return (low | ((u_int64_t)high << 32));
390 }
391
392 static __inline void
393 wbinvd(void)
394 {
395         __asm __volatile("wbinvd");
396 }
397
398 static __inline void
399 write_rflags(u_long rf)
400 {
401         __asm __volatile("pushq %0;  popfq" : : "r" (rf));
402 }
403
404 static __inline void
405 wrmsr(u_int msr, u_int64_t newval)
406 {
407         u_int32_t low, high;
408
409         low = newval;
410         high = newval >> 32;
411         __asm __volatile("wrmsr" : : "a" (low), "d" (high), "c" (msr));
412 }
413
414 static __inline void
415 load_cr0(u_long data)
416 {
417
418         __asm __volatile("movq %0,%%cr0" : : "r" (data));
419 }
420
421 static __inline u_long
422 rcr0(void)
423 {
424         u_long  data;
425
426         __asm __volatile("movq %%cr0,%0" : "=r" (data));
427         return (data);
428 }
429
430 static __inline u_long
431 rcr2(void)
432 {
433         u_long  data;
434
435         __asm __volatile("movq %%cr2,%0" : "=r" (data));
436         return (data);
437 }
438
439 static __inline void
440 load_cr3(u_long data)
441 {
442
443         __asm __volatile("movq %0,%%cr3" : : "r" (data) : "memory");
444 }
445
446 static __inline u_long
447 rcr3(void)
448 {
449         u_long  data;
450
451         __asm __volatile("movq %%cr3,%0" : "=r" (data));
452         return (data);
453 }
454
455 static __inline void
456 load_cr4(u_long data)
457 {
458         __asm __volatile("movq %0,%%cr4" : : "r" (data));
459 }
460
461 static __inline u_long
462 rcr4(void)
463 {
464         u_long  data;
465
466         __asm __volatile("movq %%cr4,%0" : "=r" (data));
467         return (data);
468 }
469
470 /*
471  * Global TLB flush (except for thise for pages marked PG_G)
472  */
473 static __inline void
474 invltlb(void)
475 {
476
477         load_cr3(rcr3());
478 }
479
480 /*
481  * TLB flush for an individual page (even if it has PG_G).
482  * Only works on 486+ CPUs (i386 does not have PG_G).
483  */
484 static __inline void
485 invlpg(u_long addr)
486 {
487
488         __asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
489 }
490
491 static __inline u_int
492 rfs(void)
493 {
494         u_int sel;
495         __asm __volatile("movl %%fs,%0" : "=rm" (sel));
496         return (sel);
497 }
498
499 static __inline u_int
500 rgs(void)
501 {
502         u_int sel;
503         __asm __volatile("movl %%gs,%0" : "=rm" (sel));
504         return (sel);
505 }
506
507 static __inline void
508 load_ds(u_int sel)
509 {
510         __asm __volatile("movl %0,%%ds" : : "rm" (sel));
511 }
512
513 static __inline void
514 load_es(u_int sel)
515 {
516         __asm __volatile("movl %0,%%es" : : "rm" (sel));
517 }
518
519 #ifdef _KERNEL
520 /* This is defined in <machine/specialreg.h> but is too painful to get to */
521 #ifndef MSR_FSBASE
522 #define MSR_FSBASE      0xc0000100
523 #endif
524 static __inline void
525 load_fs(u_int sel)
526 {
527         register u_int32_t fsbase __asm("ecx");
528
529         /* Preserve the fsbase value across the selector load */
530         fsbase = MSR_FSBASE;
531         __asm __volatile("rdmsr; movl %0,%%fs; wrmsr"
532             : : "rm" (sel), "c" (fsbase) : "eax", "edx");
533 }
534
535 #ifndef MSR_GSBASE
536 #define MSR_GSBASE      0xc0000101
537 #endif
538 static __inline void
539 load_gs(u_int sel)
540 {
541         register u_int32_t gsbase __asm("ecx");
542
543         /*
544          * Preserve the gsbase value across the selector load.
545          * Note that we have to disable interrupts because the gsbase
546          * being trashed happens to be the kernel gsbase at the time.
547          */
548         gsbase = MSR_GSBASE;
549         __asm __volatile("pushfq; cli; rdmsr; movl %0,%%gs; wrmsr; popfq"
550             : : "rm" (sel), "c" (gsbase) : "eax", "edx");
551 }
552 #else
553 /* Usable by userland */
554 static __inline void
555 load_fs(u_int sel)
556 {
557         __asm __volatile("movl %0,%%fs" : : "rm" (sel));
558 }
559
560 static __inline void
561 load_gs(u_int sel)
562 {
563         __asm __volatile("movl %0,%%gs" : : "rm" (sel));
564 }
565 #endif
566
567 /* void lidt(struct region_descriptor *addr); */
568 static __inline void
569 lidt(struct region_descriptor *addr)
570 {
571         __asm __volatile("lidt (%0)" : : "r" (addr));
572 }
573
574 /* void lldt(u_short sel); */
575 static __inline void
576 lldt(u_short sel)
577 {
578         __asm __volatile("lldt %0" : : "r" (sel));
579 }
580
581 /* void ltr(u_short sel); */
582 static __inline void
583 ltr(u_short sel)
584 {
585         __asm __volatile("ltr %0" : : "r" (sel));
586 }
587
588 static __inline u_int64_t
589 rdr0(void)
590 {
591         u_int64_t data;
592         __asm __volatile("movq %%dr0,%0" : "=r" (data));
593         return (data);
594 }
595
596 static __inline void
597 load_dr0(u_int64_t dr0)
598 {
599         __asm __volatile("movq %0,%%dr0" : : "r" (dr0));
600 }
601
602 static __inline u_int64_t
603 rdr1(void)
604 {
605         u_int64_t data;
606         __asm __volatile("movq %%dr1,%0" : "=r" (data));
607         return (data);
608 }
609
610 static __inline void
611 load_dr1(u_int64_t dr1)
612 {
613         __asm __volatile("movq %0,%%dr1" : : "r" (dr1));
614 }
615
616 static __inline u_int64_t
617 rdr2(void)
618 {
619         u_int64_t data;
620         __asm __volatile("movq %%dr2,%0" : "=r" (data));
621         return (data);
622 }
623
624 static __inline void
625 load_dr2(u_int64_t dr2)
626 {
627         __asm __volatile("movq %0,%%dr2" : : "r" (dr2));
628 }
629
630 static __inline u_int64_t
631 rdr3(void)
632 {
633         u_int64_t data;
634         __asm __volatile("movq %%dr3,%0" : "=r" (data));
635         return (data);
636 }
637
638 static __inline void
639 load_dr3(u_int64_t dr3)
640 {
641         __asm __volatile("movq %0,%%dr3" : : "r" (dr3));
642 }
643
644 static __inline u_int64_t
645 rdr4(void)
646 {
647         u_int64_t data;
648         __asm __volatile("movq %%dr4,%0" : "=r" (data));
649         return (data);
650 }
651
652 static __inline void
653 load_dr4(u_int64_t dr4)
654 {
655         __asm __volatile("movq %0,%%dr4" : : "r" (dr4));
656 }
657
658 static __inline u_int64_t
659 rdr5(void)
660 {
661         u_int64_t data;
662         __asm __volatile("movq %%dr5,%0" : "=r" (data));
663         return (data);
664 }
665
666 static __inline void
667 load_dr5(u_int64_t dr5)
668 {
669         __asm __volatile("movq %0,%%dr5" : : "r" (dr5));
670 }
671
672 static __inline u_int64_t
673 rdr6(void)
674 {
675         u_int64_t data;
676         __asm __volatile("movq %%dr6,%0" : "=r" (data));
677         return (data);
678 }
679
680 static __inline void
681 load_dr6(u_int64_t dr6)
682 {
683         __asm __volatile("movq %0,%%dr6" : : "r" (dr6));
684 }
685
686 static __inline u_int64_t
687 rdr7(void)
688 {
689         u_int64_t data;
690         __asm __volatile("movq %%dr7,%0" : "=r" (data));
691         return (data);
692 }
693
694 static __inline void
695 load_dr7(u_int64_t dr7)
696 {
697         __asm __volatile("movq %0,%%dr7" : : "r" (dr7));
698 }
699
700 static __inline register_t
701 intr_disable(void)
702 {
703         register_t rflags;
704
705         rflags = read_rflags();
706         disable_intr();
707         return (rflags);
708 }
709
710 static __inline void
711 intr_restore(register_t rflags)
712 {
713         write_rflags(rflags);
714 }
715
716 #else /* !__GNUC__ */
717
718 int     breakpoint(void);
719 u_int   bsfl(u_int mask);
720 u_int   bsrl(u_int mask);
721 void    cpu_invlpg(u_long addr);
722 void    cpu_invlpg_range(u_long start, u_long end);
723 void    disable_intr(void);
724 void    do_cpuid(u_int ax, u_int *p);
725 void    enable_intr(void);
726 void    halt(void);
727 u_char  inb(u_int port);
728 u_int   inl(u_int port);
729 void    insb(u_int port, void *addr, size_t cnt);
730 void    insl(u_int port, void *addr, size_t cnt);
731 void    insw(u_int port, void *addr, size_t cnt);
732 void    invd(void);
733 void    invlpg(u_int addr);
734 void    invlpg_range(u_int start, u_int end);
735 void    invltlb(void);
736 u_short inw(u_int port);
737 void    load_cr0(u_int cr0);
738 void    load_cr3(u_int cr3);
739 void    load_cr4(u_int cr4);
740 void    load_fs(u_int sel);
741 void    load_gs(u_int sel);
742 struct region_descriptor;
743 void    lidt(struct region_descriptor *addr);
744 void    lldt(u_short sel);
745 void    ltr(u_short sel);
746 void    outb(u_int port, u_char data);
747 void    outl(u_int port, u_int data);
748 void    outsb(u_int port, void *addr, size_t cnt);
749 void    outsl(u_int port, void *addr, size_t cnt);
750 void    outsw(u_int port, void *addr, size_t cnt);
751 void    outw(u_int port, u_short data);
752 void    ia32_pause(void);
753 u_int   rcr0(void);
754 u_int   rcr2(void);
755 u_int   rcr3(void);
756 u_int   rcr4(void);
757 u_int   rfs(void);
758 u_int   rgs(void);
759 u_int64_t rdmsr(u_int msr);
760 u_int64_t rdpmc(u_int pmc);
761 u_int64_t rdtsc(void);
762 u_int   read_rflags(void);
763 void    wbinvd(void);
764 void    write_rflags(u_int rf);
765 void    wrmsr(u_int msr, u_int64_t newval);
766 u_int64_t       rdr0(void);
767 void    load_dr0(u_int64_t dr0);
768 u_int64_t       rdr1(void);
769 void    load_dr1(u_int64_t dr1);
770 u_int64_t       rdr2(void);
771 void    load_dr2(u_int64_t dr2);
772 u_int64_t       rdr3(void);
773 void    load_dr3(u_int64_t dr3);
774 u_int64_t       rdr4(void);
775 void    load_dr4(u_int64_t dr4);
776 u_int64_t       rdr5(void);
777 void    load_dr5(u_int64_t dr5);
778 u_int64_t       rdr6(void);
779 void    load_dr6(u_int64_t dr6);
780 u_int64_t       rdr7(void);
781 void    load_dr7(u_int64_t dr7);
782 register_t      intr_disable(void);
783 void    intr_restore(register_t rf);
784
785 #endif  /* __GNUC__ */
786
787 void    reset_dbregs(void);
788
789 __END_DECLS
790
791 #endif /* !_MACHINE_CPUFUNC_H_ */