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