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