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