cpufunc.h revision 132888
1/*-
2 * Copyright (c) 2003 Peter Wemm.
3 * Copyright (c) 1993 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 4. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD: head/sys/amd64/include/cpufunc.h 132888 2004-07-30 16:44:29Z ps $
31 */
32
33/*
34 * Functions to provide access to special i386 instructions.
35 * This in included in sys/systm.h, and that file should be
36 * used in preference to this.
37 */
38
39#ifndef _MACHINE_CPUFUNC_H_
40#define	_MACHINE_CPUFUNC_H_
41
42struct region_descriptor;
43
44#define readb(va)	(*(volatile u_int8_t *) (va))
45#define readw(va)	(*(volatile u_int16_t *) (va))
46#define readl(va)	(*(volatile u_int32_t *) (va))
47#define readq(va)	(*(volatile u_int64_t *) (va))
48
49#define writeb(va, d)	(*(volatile u_int8_t *) (va) = (d))
50#define writew(va, d)	(*(volatile u_int16_t *) (va) = (d))
51#define writel(va, d)	(*(volatile u_int32_t *) (va) = (d))
52#define writeq(va, d)	(*(volatile u_int64_t *) (va) = (d))
53
54#ifdef	__GNUC__
55
56static __inline void
57breakpoint(void)
58{
59	__asm __volatile("int $3");
60}
61
62static __inline u_int
63bsfl(u_int mask)
64{
65	u_int	result;
66
67	__asm __volatile("bsfl %1,%0" : "=r" (result) : "rm" (mask));
68	return (result);
69}
70
71static __inline u_long
72bsfq(u_long mask)
73{
74	u_long	result;
75
76	__asm __volatile("bsfq %1,%0" : "=r" (result) : "rm" (mask));
77	return (result);
78}
79
80static __inline u_int
81bsrl(u_int mask)
82{
83	u_int	result;
84
85	__asm __volatile("bsrl %1,%0" : "=r" (result) : "rm" (mask));
86	return (result);
87}
88
89static __inline u_long
90bsrq(u_long mask)
91{
92	u_long	result;
93
94	__asm __volatile("bsrq %1,%0" : "=r" (result) : "rm" (mask));
95	return (result);
96}
97
98static __inline void
99disable_intr(void)
100{
101	__asm __volatile("cli" : : : "memory");
102}
103
104static __inline void
105do_cpuid(u_int ax, u_int *p)
106{
107	__asm __volatile("cpuid"
108			 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
109			 :  "0" (ax));
110}
111
112static __inline void
113enable_intr(void)
114{
115	__asm __volatile("sti");
116}
117
118#ifdef _KERNEL
119
120#define	HAVE_INLINE_FFS
121#define        ffs(x)  __builtin_ffs(x)
122
123#define	HAVE_INLINE_FFSL
124
125static __inline int
126ffsl(long mask)
127{
128	return (mask == 0 ? mask : (int)bsfq((u_long)mask) + 1);
129}
130
131#define	HAVE_INLINE_FLS
132
133static __inline int
134fls(int mask)
135{
136	return (mask == 0 ? mask : (int)bsrl((u_int)mask) + 1);
137}
138
139#define	HAVE_INLINE_FLSL
140
141static __inline int
142flsl(long mask)
143{
144	return (mask == 0 ? mask : (int)bsrq((u_long)mask) + 1);
145}
146
147#endif /* _KERNEL */
148
149static __inline void
150halt(void)
151{
152	__asm __volatile("hlt");
153}
154
155#if __GNUC__ < 2
156
157#define	inb(port)		inbv(port)
158#define	outb(port, data)	outbv(port, data)
159
160#else /* __GNUC >= 2 */
161
162/*
163 * The following complications are to get around gcc not having a
164 * constraint letter for the range 0..255.  We still put "d" in the
165 * constraint because "i" isn't a valid constraint when the port
166 * isn't constant.  This only matters for -O0 because otherwise
167 * the non-working version gets optimized away.
168 *
169 * Use an expression-statement instead of a conditional expression
170 * because gcc-2.6.0 would promote the operands of the conditional
171 * and produce poor code for "if ((inb(var) & const1) == const2)".
172 *
173 * The unnecessary test `(port) < 0x10000' is to generate a warning if
174 * the `port' has type u_short or smaller.  Such types are pessimal.
175 * This actually only works for signed types.  The range check is
176 * careful to avoid generating warnings.
177 */
178#define	inb(port) __extension__ ({					\
179	u_char	_data;							\
180	if (__builtin_constant_p(port) && ((port) & 0xffff) < 0x100	\
181	    && (port) < 0x10000)					\
182		_data = inbc(port);					\
183	else								\
184		_data = inbv(port);					\
185	_data; })
186
187#define	outb(port, data) (						\
188	__builtin_constant_p(port) && ((port) & 0xffff) < 0x100		\
189	&& (port) < 0x10000						\
190	? outbc(port, data) : outbv(port, data))
191
192static __inline u_char
193inbc(u_int port)
194{
195	u_char	data;
196
197	__asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port)));
198	return (data);
199}
200
201static __inline void
202outbc(u_int port, u_char data)
203{
204	__asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port)));
205}
206
207#endif /* __GNUC <= 2 */
208
209static __inline u_char
210inbv(u_int port)
211{
212	u_char	data;
213	/*
214	 * We use %%dx and not %1 here because i/o is done at %dx and not at
215	 * %edx, while gcc generates inferior code (movw instead of movl)
216	 * if we tell it to load (u_short) port.
217	 */
218	__asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
219	return (data);
220}
221
222static __inline u_int
223inl(u_int port)
224{
225	u_int	data;
226
227	__asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port));
228	return (data);
229}
230
231static __inline void
232insb(u_int port, void *addr, size_t cnt)
233{
234	__asm __volatile("cld; rep; insb"
235			 : "+D" (addr), "+c" (cnt)
236			 : "d" (port)
237			 : "memory");
238}
239
240static __inline void
241insw(u_int port, void *addr, size_t cnt)
242{
243	__asm __volatile("cld; rep; insw"
244			 : "+D" (addr), "+c" (cnt)
245			 : "d" (port)
246			 : "memory");
247}
248
249static __inline void
250insl(u_int port, void *addr, size_t cnt)
251{
252	__asm __volatile("cld; rep; insl"
253			 : "+D" (addr), "+c" (cnt)
254			 : "d" (port)
255			 : "memory");
256}
257
258static __inline void
259invd(void)
260{
261	__asm __volatile("invd");
262}
263
264static __inline u_short
265inw(u_int port)
266{
267	u_short	data;
268
269	__asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port));
270	return (data);
271}
272
273static __inline void
274outbv(u_int port, u_char data)
275{
276	u_char	al;
277	/*
278	 * Use an unnecessary assignment to help gcc's register allocator.
279	 * This make a large difference for gcc-1.40 and a tiny difference
280	 * for gcc-2.6.0.  For gcc-1.40, al had to be ``asm("ax")'' for
281	 * best results.  gcc-2.6.0 can't handle this.
282	 */
283	al = data;
284	__asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
285}
286
287static __inline void
288outl(u_int port, u_int data)
289{
290	/*
291	 * outl() and outw() aren't used much so we haven't looked at
292	 * possible micro-optimizations such as the unnecessary
293	 * assignment for them.
294	 */
295	__asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port));
296}
297
298static __inline void
299outsb(u_int port, const void *addr, size_t cnt)
300{
301	__asm __volatile("cld; rep; outsb"
302			 : "+S" (addr), "+c" (cnt)
303			 : "d" (port));
304}
305
306static __inline void
307outsw(u_int port, const void *addr, size_t cnt)
308{
309	__asm __volatile("cld; rep; outsw"
310			 : "+S" (addr), "+c" (cnt)
311			 : "d" (port));
312}
313
314static __inline void
315outsl(u_int port, const void *addr, size_t cnt)
316{
317	__asm __volatile("cld; rep; outsl"
318			 : "+S" (addr), "+c" (cnt)
319			 : "d" (port));
320}
321
322static __inline void
323outw(u_int port, u_short data)
324{
325	__asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port));
326}
327
328static __inline void
329ia32_pause(void)
330{
331	__asm __volatile("pause");
332}
333
334static __inline u_long
335read_rflags(void)
336{
337	u_long	rf;
338
339	__asm __volatile("pushfq; popq %0" : "=r" (rf));
340	return (rf);
341}
342
343static __inline u_int64_t
344rdmsr(u_int msr)
345{
346	u_int32_t low, high;
347
348	__asm __volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr));
349	return (low | ((u_int64_t)high << 32));
350}
351
352static __inline u_int64_t
353rdpmc(u_int pmc)
354{
355	u_int32_t low, high;
356
357	__asm __volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (pmc));
358	return (low | ((u_int64_t)high << 32));
359}
360
361static __inline u_int64_t
362rdtsc(void)
363{
364	u_int32_t low, high;
365
366	__asm __volatile("rdtsc" : "=a" (low), "=d" (high));
367	return (low | ((u_int64_t)high << 32));
368}
369
370static __inline void
371wbinvd(void)
372{
373	__asm __volatile("wbinvd");
374}
375
376static __inline void
377write_rflags(u_long rf)
378{
379	__asm __volatile("pushq %0;  popfq" : : "r" (rf));
380}
381
382static __inline void
383wrmsr(u_int msr, u_int64_t newval)
384{
385	u_int32_t low, high;
386
387	low = newval;
388	high = newval >> 32;
389	__asm __volatile("wrmsr" : : "a" (low), "d" (high), "c" (msr));
390}
391
392static __inline void
393load_cr0(u_long data)
394{
395
396	__asm __volatile("movq %0,%%cr0" : : "r" (data));
397}
398
399static __inline u_long
400rcr0(void)
401{
402	u_long	data;
403
404	__asm __volatile("movq %%cr0,%0" : "=r" (data));
405	return (data);
406}
407
408static __inline u_long
409rcr2(void)
410{
411	u_long	data;
412
413	__asm __volatile("movq %%cr2,%0" : "=r" (data));
414	return (data);
415}
416
417static __inline void
418load_cr3(u_long data)
419{
420
421	__asm __volatile("movq %0,%%cr3" : : "r" (data) : "memory");
422}
423
424static __inline u_long
425rcr3(void)
426{
427	u_long	data;
428
429	__asm __volatile("movq %%cr3,%0" : "=r" (data));
430	return (data);
431}
432
433static __inline void
434load_cr4(u_long data)
435{
436	__asm __volatile("movq %0,%%cr4" : : "r" (data));
437}
438
439static __inline u_long
440rcr4(void)
441{
442	u_long	data;
443
444	__asm __volatile("movq %%cr4,%0" : "=r" (data));
445	return (data);
446}
447
448/*
449 * Global TLB flush (except for thise for pages marked PG_G)
450 */
451static __inline void
452invltlb(void)
453{
454
455	load_cr3(rcr3());
456}
457
458/*
459 * TLB flush for an individual page (even if it has PG_G).
460 * Only works on 486+ CPUs (i386 does not have PG_G).
461 */
462static __inline void
463invlpg(u_long addr)
464{
465
466	__asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
467}
468
469static __inline u_int
470rfs(void)
471{
472	u_int sel;
473	__asm __volatile("movl %%fs,%0" : "=rm" (sel));
474	return (sel);
475}
476
477static __inline u_int
478rgs(void)
479{
480	u_int sel;
481	__asm __volatile("movl %%gs,%0" : "=rm" (sel));
482	return (sel);
483}
484
485static __inline u_int
486rss(void)
487{
488	u_int sel;
489	__asm __volatile("movl %%ss,%0" : "=rm" (sel));
490	return (sel);
491}
492
493static __inline void
494load_ds(u_int sel)
495{
496	__asm __volatile("movl %0,%%ds" : : "rm" (sel));
497}
498
499static __inline void
500load_es(u_int sel)
501{
502	__asm __volatile("movl %0,%%es" : : "rm" (sel));
503}
504
505#ifdef _KERNEL
506/* This is defined in <machine/specialreg.h> but is too painful to get to */
507#ifndef	MSR_FSBASE
508#define	MSR_FSBASE	0xc0000100
509#endif
510static __inline void
511load_fs(u_int sel)
512{
513	register u_int32_t fsbase __asm("ecx");
514
515	/* Preserve the fsbase value across the selector load */
516	fsbase = MSR_FSBASE;
517        __asm __volatile("rdmsr; movl %0,%%fs; wrmsr"
518            : : "rm" (sel), "c" (fsbase) : "eax", "edx");
519}
520
521#ifndef	MSR_GSBASE
522#define	MSR_GSBASE	0xc0000101
523#endif
524static __inline void
525load_gs(u_int sel)
526{
527	register u_int32_t gsbase __asm("ecx");
528
529	/*
530	 * Preserve the gsbase value across the selector load.
531	 * Note that we have to disable interrupts because the gsbase
532	 * being trashed happens to be the kernel gsbase at the time.
533	 */
534	gsbase = MSR_GSBASE;
535        __asm __volatile("pushfq; cli; rdmsr; movl %0,%%gs; wrmsr; popfq"
536            : : "rm" (sel), "c" (gsbase) : "eax", "edx");
537}
538#else
539/* Usable by userland */
540static __inline void
541load_fs(u_int sel)
542{
543	__asm __volatile("movl %0,%%fs" : : "rm" (sel));
544}
545
546static __inline void
547load_gs(u_int sel)
548{
549	__asm __volatile("movl %0,%%gs" : : "rm" (sel));
550}
551#endif
552
553static __inline void
554lidt(struct region_descriptor *addr)
555{
556	__asm __volatile("lidt (%0)" : : "r" (addr));
557}
558
559static __inline void
560lldt(u_short sel)
561{
562	__asm __volatile("lldt %0" : : "r" (sel));
563}
564
565static __inline void
566ltr(u_short sel)
567{
568	__asm __volatile("ltr %0" : : "r" (sel));
569}
570
571static __inline u_int64_t
572rdr0(void)
573{
574	u_int64_t data;
575	__asm __volatile("movq %%dr0,%0" : "=r" (data));
576	return (data);
577}
578
579static __inline void
580load_dr0(u_int64_t dr0)
581{
582	__asm __volatile("movq %0,%%dr0" : : "r" (dr0));
583}
584
585static __inline u_int64_t
586rdr1(void)
587{
588	u_int64_t data;
589	__asm __volatile("movq %%dr1,%0" : "=r" (data));
590	return (data);
591}
592
593static __inline void
594load_dr1(u_int64_t dr1)
595{
596	__asm __volatile("movq %0,%%dr1" : : "r" (dr1));
597}
598
599static __inline u_int64_t
600rdr2(void)
601{
602	u_int64_t data;
603	__asm __volatile("movq %%dr2,%0" : "=r" (data));
604	return (data);
605}
606
607static __inline void
608load_dr2(u_int64_t dr2)
609{
610	__asm __volatile("movq %0,%%dr2" : : "r" (dr2));
611}
612
613static __inline u_int64_t
614rdr3(void)
615{
616	u_int64_t data;
617	__asm __volatile("movq %%dr3,%0" : "=r" (data));
618	return (data);
619}
620
621static __inline void
622load_dr3(u_int64_t dr3)
623{
624	__asm __volatile("movq %0,%%dr3" : : "r" (dr3));
625}
626
627static __inline u_int64_t
628rdr4(void)
629{
630	u_int64_t data;
631	__asm __volatile("movq %%dr4,%0" : "=r" (data));
632	return (data);
633}
634
635static __inline void
636load_dr4(u_int64_t dr4)
637{
638	__asm __volatile("movq %0,%%dr4" : : "r" (dr4));
639}
640
641static __inline u_int64_t
642rdr5(void)
643{
644	u_int64_t data;
645	__asm __volatile("movq %%dr5,%0" : "=r" (data));
646	return (data);
647}
648
649static __inline void
650load_dr5(u_int64_t dr5)
651{
652	__asm __volatile("movq %0,%%dr5" : : "r" (dr5));
653}
654
655static __inline u_int64_t
656rdr6(void)
657{
658	u_int64_t data;
659	__asm __volatile("movq %%dr6,%0" : "=r" (data));
660	return (data);
661}
662
663static __inline void
664load_dr6(u_int64_t dr6)
665{
666	__asm __volatile("movq %0,%%dr6" : : "r" (dr6));
667}
668
669static __inline u_int64_t
670rdr7(void)
671{
672	u_int64_t data;
673	__asm __volatile("movq %%dr7,%0" : "=r" (data));
674	return (data);
675}
676
677static __inline void
678load_dr7(u_int64_t dr7)
679{
680	__asm __volatile("movq %0,%%dr7" : : "r" (dr7));
681}
682
683static __inline register_t
684intr_disable(void)
685{
686	register_t rflags;
687
688	rflags = read_rflags();
689	disable_intr();
690	return (rflags);
691}
692
693static __inline void
694intr_restore(register_t rflags)
695{
696	write_rflags(rflags);
697}
698
699#else /* !__GNUC__ */
700
701int	breakpoint(void);
702u_int	bsfl(u_int mask);
703u_int	bsrl(u_int mask);
704void	disable_intr(void);
705void	do_cpuid(u_int ax, u_int *p);
706void	enable_intr(void);
707void	halt(void);
708void	ia32_pause(void);
709u_char	inb(u_int port);
710u_int	inl(u_int port);
711void	insb(u_int port, void *addr, size_t cnt);
712void	insl(u_int port, void *addr, size_t cnt);
713void	insw(u_int port, void *addr, size_t cnt);
714register_t	intr_disable(void);
715void	intr_restore(register_t rf);
716void	invd(void);
717void	invlpg(u_int addr);
718void	invltlb(void);
719u_short	inw(u_int port);
720void	lidt(struct region_descriptor *addr);
721void	lldt(u_short sel);
722void	load_cr0(u_long cr0);
723void	load_cr3(u_long cr3);
724void	load_cr4(u_long cr4);
725void	load_dr0(u_int64_t dr0);
726void	load_dr1(u_int64_t dr1);
727void	load_dr2(u_int64_t dr2);
728void	load_dr3(u_int64_t dr3);
729void	load_dr4(u_int64_t dr4);
730void	load_dr5(u_int64_t dr5);
731void	load_dr6(u_int64_t dr6);
732void	load_dr7(u_int64_t dr7);
733void	load_fs(u_int sel);
734void	load_gs(u_int sel);
735void	ltr(u_short sel);
736void	outb(u_int port, u_char data);
737void	outl(u_int port, u_int data);
738void	outsb(u_int port, const void *addr, size_t cnt);
739void	outsl(u_int port, const void *addr, size_t cnt);
740void	outsw(u_int port, const void *addr, size_t cnt);
741void	outw(u_int port, u_short data);
742u_long	rcr0(void);
743u_long	rcr2(void);
744u_long	rcr3(void);
745u_long	rcr4(void);
746u_int64_t rdmsr(u_int msr);
747u_int64_t rdpmc(u_int pmc);
748u_int64_t rdr0(void);
749u_int64_t rdr1(void);
750u_int64_t rdr2(void);
751u_int64_t rdr3(void);
752u_int64_t rdr4(void);
753u_int64_t rdr5(void);
754u_int64_t rdr6(void);
755u_int64_t rdr7(void);
756u_int64_t rdtsc(void);
757u_int	read_rflags(void);
758u_int	rfs(void);
759u_int	rgs(void);
760void	wbinvd(void);
761void	write_rflags(u_int rf);
762void	wrmsr(u_int msr, u_int64_t newval);
763
764#endif	/* __GNUC__ */
765
766void	reset_dbregs(void);
767
768#endif /* !_MACHINE_CPUFUNC_H_ */
769