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