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