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