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