xref: /freebsd/sys/amd64/include/cpufunc.h (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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 struct region_descriptor;
50 
51 __BEGIN_DECLS
52 #define readb(va)	(*(volatile u_int8_t *) (va))
53 #define readw(va)	(*(volatile u_int16_t *) (va))
54 #define readl(va)	(*(volatile u_int32_t *) (va))
55 #define readq(va)	(*(volatile u_int64_t *) (va))
56 
57 #define writeb(va, d)	(*(volatile u_int8_t *) (va) = (d))
58 #define writew(va, d)	(*(volatile u_int16_t *) (va) = (d))
59 #define writel(va, d)	(*(volatile u_int32_t *) (va) = (d))
60 #define writeq(va, d)	(*(volatile u_int64_t *) (va) = (d))
61 
62 #ifdef	__GNUC__
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 %1,%0" : "=r" (result) : "rm" (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 %1,%0" : "=r" (result) : "rm" (mask));
85 	return (result);
86 }
87 
88 static __inline void
89 disable_intr(void)
90 {
91 	__asm __volatile("cli" : : : "memory");
92 }
93 
94 static __inline void
95 do_cpuid(u_int ax, u_int *p)
96 {
97 	__asm __volatile("cpuid"
98 			 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
99 			 :  "0" (ax));
100 }
101 
102 static __inline void
103 enable_intr(void)
104 {
105 	__asm __volatile("sti");
106 }
107 
108 #define	HAVE_INLINE_FFS
109 
110 static __inline int
111 ffs(int mask)
112 {
113 	/*
114 	 * Note that gcc-2's builtin ffs would be used if we didn't declare
115 	 * this inline or turn off the builtin.  The builtin is faster but
116 	 * broken in gcc-2.4.5 and slower but working in gcc-2.5 and later
117 	 * versions.
118 	 */
119 	 return (mask == 0 ? mask : (int)bsfl((u_int)mask) + 1);
120 }
121 
122 #define	HAVE_INLINE_FLS
123 
124 static __inline int
125 fls(int mask)
126 {
127 	return (mask == 0 ? mask : (int)bsrl((u_int)mask) + 1);
128 }
129 
130 static __inline void
131 halt(void)
132 {
133 	__asm __volatile("hlt");
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 			 : "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 			 : "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 			 : "d" (port)
236 			 : "memory");
237 }
238 
239 static __inline void
240 invd(void)
241 {
242 	__asm __volatile("invd");
243 }
244 
245 static __inline u_short
246 inw(u_int port)
247 {
248 	u_short	data;
249 
250 	__asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port));
251 	return (data);
252 }
253 
254 static __inline void
255 outbv(u_int port, u_char data)
256 {
257 	u_char	al;
258 	/*
259 	 * Use an unnecessary assignment to help gcc's register allocator.
260 	 * This make a large difference for gcc-1.40 and a tiny difference
261 	 * for gcc-2.6.0.  For gcc-1.40, al had to be ``asm("ax")'' for
262 	 * best results.  gcc-2.6.0 can't handle this.
263 	 */
264 	al = data;
265 	__asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
266 }
267 
268 static __inline void
269 outl(u_int port, u_int data)
270 {
271 	/*
272 	 * outl() and outw() aren't used much so we haven't looked at
273 	 * possible micro-optimizations such as the unnecessary
274 	 * assignment for them.
275 	 */
276 	__asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port));
277 }
278 
279 static __inline void
280 outsb(u_int port, const void *addr, size_t cnt)
281 {
282 	__asm __volatile("cld; rep; outsb"
283 			 : "+S" (addr), "+c" (cnt)
284 			 : "d" (port));
285 }
286 
287 static __inline void
288 outsw(u_int port, const void *addr, size_t cnt)
289 {
290 	__asm __volatile("cld; rep; outsw"
291 			 : "+S" (addr), "+c" (cnt)
292 			 : "d" (port));
293 }
294 
295 static __inline void
296 outsl(u_int port, const void *addr, size_t cnt)
297 {
298 	__asm __volatile("cld; rep; outsl"
299 			 : "+S" (addr), "+c" (cnt)
300 			 : "d" (port));
301 }
302 
303 static __inline void
304 outw(u_int port, u_short data)
305 {
306 	__asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port));
307 }
308 
309 static __inline void
310 ia32_pause(void)
311 {
312 	__asm __volatile("pause");
313 }
314 
315 static __inline u_long
316 read_rflags(void)
317 {
318 	u_long	rf;
319 
320 	__asm __volatile("pushfq; popq %0" : "=r" (rf));
321 	return (rf);
322 }
323 
324 static __inline u_int64_t
325 rdmsr(u_int msr)
326 {
327 	u_int32_t low, high;
328 
329 	__asm __volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr));
330 	return (low | ((u_int64_t)high << 32));
331 }
332 
333 static __inline u_int64_t
334 rdpmc(u_int pmc)
335 {
336 	u_int32_t low, high;
337 
338 	__asm __volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (pmc));
339 	return (low | ((u_int64_t)high << 32));
340 }
341 
342 static __inline u_int64_t
343 rdtsc(void)
344 {
345 	u_int32_t low, high;
346 
347 	__asm __volatile("rdtsc" : "=a" (low), "=d" (high));
348 	return (low | ((u_int64_t)high << 32));
349 }
350 
351 static __inline void
352 wbinvd(void)
353 {
354 	__asm __volatile("wbinvd");
355 }
356 
357 static __inline void
358 write_rflags(u_long rf)
359 {
360 	__asm __volatile("pushq %0;  popfq" : : "r" (rf));
361 }
362 
363 static __inline void
364 wrmsr(u_int msr, u_int64_t newval)
365 {
366 	u_int32_t low, high;
367 
368 	low = newval;
369 	high = newval >> 32;
370 	__asm __volatile("wrmsr" : : "a" (low), "d" (high), "c" (msr));
371 }
372 
373 static __inline void
374 load_cr0(u_long data)
375 {
376 
377 	__asm __volatile("movq %0,%%cr0" : : "r" (data));
378 }
379 
380 static __inline u_long
381 rcr0(void)
382 {
383 	u_long	data;
384 
385 	__asm __volatile("movq %%cr0,%0" : "=r" (data));
386 	return (data);
387 }
388 
389 static __inline u_long
390 rcr2(void)
391 {
392 	u_long	data;
393 
394 	__asm __volatile("movq %%cr2,%0" : "=r" (data));
395 	return (data);
396 }
397 
398 static __inline void
399 load_cr3(u_long data)
400 {
401 
402 	__asm __volatile("movq %0,%%cr3" : : "r" (data) : "memory");
403 }
404 
405 static __inline u_long
406 rcr3(void)
407 {
408 	u_long	data;
409 
410 	__asm __volatile("movq %%cr3,%0" : "=r" (data));
411 	return (data);
412 }
413 
414 static __inline void
415 load_cr4(u_long data)
416 {
417 	__asm __volatile("movq %0,%%cr4" : : "r" (data));
418 }
419 
420 static __inline u_long
421 rcr4(void)
422 {
423 	u_long	data;
424 
425 	__asm __volatile("movq %%cr4,%0" : "=r" (data));
426 	return (data);
427 }
428 
429 /*
430  * Global TLB flush (except for thise for pages marked PG_G)
431  */
432 static __inline void
433 invltlb(void)
434 {
435 
436 	load_cr3(rcr3());
437 }
438 
439 /*
440  * TLB flush for an individual page (even if it has PG_G).
441  * Only works on 486+ CPUs (i386 does not have PG_G).
442  */
443 static __inline void
444 invlpg(u_long addr)
445 {
446 
447 	__asm __volatile("invlpg %0" : : "m" (*(char *)addr) : "memory");
448 }
449 
450 static __inline u_int
451 rfs(void)
452 {
453 	u_int sel;
454 	__asm __volatile("movl %%fs,%0" : "=rm" (sel));
455 	return (sel);
456 }
457 
458 static __inline u_int
459 rgs(void)
460 {
461 	u_int sel;
462 	__asm __volatile("movl %%gs,%0" : "=rm" (sel));
463 	return (sel);
464 }
465 
466 static __inline void
467 load_ds(u_int sel)
468 {
469 	__asm __volatile("movl %0,%%ds" : : "rm" (sel));
470 }
471 
472 static __inline void
473 load_es(u_int sel)
474 {
475 	__asm __volatile("movl %0,%%es" : : "rm" (sel));
476 }
477 
478 #ifdef _KERNEL
479 /* This is defined in <machine/specialreg.h> but is too painful to get to */
480 #ifndef	MSR_FSBASE
481 #define	MSR_FSBASE	0xc0000100
482 #endif
483 static __inline void
484 load_fs(u_int sel)
485 {
486 	register u_int32_t fsbase __asm("ecx");
487 
488 	/* Preserve the fsbase value across the selector load */
489 	fsbase = MSR_FSBASE;
490         __asm __volatile("rdmsr; movl %0,%%fs; wrmsr"
491             : : "rm" (sel), "c" (fsbase) : "eax", "edx");
492 }
493 
494 #ifndef	MSR_GSBASE
495 #define	MSR_GSBASE	0xc0000101
496 #endif
497 static __inline void
498 load_gs(u_int sel)
499 {
500 	register u_int32_t gsbase __asm("ecx");
501 
502 	/*
503 	 * Preserve the gsbase value across the selector load.
504 	 * Note that we have to disable interrupts because the gsbase
505 	 * being trashed happens to be the kernel gsbase at the time.
506 	 */
507 	gsbase = MSR_GSBASE;
508         __asm __volatile("pushfq; cli; rdmsr; movl %0,%%gs; wrmsr; popfq"
509             : : "rm" (sel), "c" (gsbase) : "eax", "edx");
510 }
511 #else
512 /* Usable by userland */
513 static __inline void
514 load_fs(u_int sel)
515 {
516 	__asm __volatile("movl %0,%%fs" : : "rm" (sel));
517 }
518 
519 static __inline void
520 load_gs(u_int sel)
521 {
522 	__asm __volatile("movl %0,%%gs" : : "rm" (sel));
523 }
524 #endif
525 
526 /* void lidt(struct region_descriptor *addr); */
527 static __inline void
528 lidt(struct region_descriptor *addr)
529 {
530 	__asm __volatile("lidt (%0)" : : "r" (addr));
531 }
532 
533 /* void lldt(u_short sel); */
534 static __inline void
535 lldt(u_short sel)
536 {
537 	__asm __volatile("lldt %0" : : "r" (sel));
538 }
539 
540 /* void ltr(u_short sel); */
541 static __inline void
542 ltr(u_short sel)
543 {
544 	__asm __volatile("ltr %0" : : "r" (sel));
545 }
546 
547 static __inline register_t
548 intr_disable(void)
549 {
550 	register_t rflags;
551 
552 	rflags = read_rflags();
553 	disable_intr();
554 	return (rflags);
555 }
556 
557 static __inline void
558 intr_restore(register_t rflags)
559 {
560 	write_rflags(rflags);
561 }
562 
563 #else /* !__GNUC__ */
564 
565 int	breakpoint(void);
566 u_int	bsfl(u_int mask);
567 u_int	bsrl(u_int mask);
568 void	cpu_invlpg(u_long addr);
569 void	cpu_invlpg_range(u_long start, u_long end);
570 void	disable_intr(void);
571 void	do_cpuid(u_int ax, u_int *p);
572 void	enable_intr(void);
573 void	halt(void);
574 u_char	inb(u_int port);
575 u_int	inl(u_int port);
576 void	insb(u_int port, void *addr, size_t cnt);
577 void	insl(u_int port, void *addr, size_t cnt);
578 void	insw(u_int port, void *addr, size_t cnt);
579 void	invd(void);
580 void	invlpg(u_int addr);
581 void	invlpg_range(u_int start, u_int end);
582 void	invltlb(void);
583 u_short	inw(u_int port);
584 void	load_cr0(u_int cr0);
585 void	load_cr3(u_int cr3);
586 void	load_cr4(u_int cr4);
587 void	load_fs(u_int sel);
588 void	load_gs(u_int sel);
589 struct region_descriptor;
590 void	lidt(struct region_descriptor *addr);
591 void	lldt(u_short sel);
592 void	ltr(u_short sel);
593 void	outb(u_int port, u_char data);
594 void	outl(u_int port, u_int data);
595 void	outsb(u_int port, void *addr, size_t cnt);
596 void	outsl(u_int port, void *addr, size_t cnt);
597 void	outsw(u_int port, void *addr, size_t cnt);
598 void	outw(u_int port, u_short data);
599 void	ia32_pause(void);
600 u_int	rcr0(void);
601 u_int	rcr2(void);
602 u_int	rcr3(void);
603 u_int	rcr4(void);
604 u_int	rfs(void);
605 u_int	rgs(void);
606 u_int64_t rdmsr(u_int msr);
607 u_int64_t rdpmc(u_int pmc);
608 u_int64_t rdtsc(void);
609 u_int	read_rflags(void);
610 void	wbinvd(void);
611 void	write_rflags(u_int rf);
612 void	wrmsr(u_int msr, u_int64_t newval);
613 void	load_dr7(u_int dr7);
614 register_t	intr_disable(void);
615 void	intr_restore(register_t rf);
616 
617 #endif	/* __GNUC__ */
618 
619 void    reset_dbregs(void);
620 
621 __END_DECLS
622 
623 #endif /* !_MACHINE_CPUFUNC_H_ */
624