#pragma once struct dtr { ushort limit; uint64 base; } __attribute__((packed)); static inline uchar inb(ushort port) { uchar data; asm volatile("in %1,%0" : "=a" (data) : "d" (port)); return data; } // Cycle counter. Not a clock -- it does not convert to time without knowing the // frequency, and it is not synchronised across cores -- but for "which of these // two paths costs more" it is exactly the right instrument, and the only one // with fine enough resolution to see inside a single syscall. static inline uint64 rdtsc(void) { uint lo, hi; asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); return ((uint64)hi << 32) | lo; } // The x87/MMX/SSE register file, to and from a 512-byte, 16-byte-aligned area. static inline void fxsave(void *area) { asm volatile("fxsave64 (%0)" : : "r"(area) : "memory"); } static inline void fxrstor(const void *area) { asm volatile("fxrstor64 (%0)" : : "r"(area) : "memory"); } static inline void insl(int port, void *addr, int cnt) { asm volatile("cld; rep insl" : "=D" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "memory", "cc"); } static inline void outb(ushort port, uchar data) { asm volatile("out %0,%1" : : "a" (data), "d" (port)); } // 16 bits. Added for AC'97, whose mixer is entirely 16-bit registers: reading // one of them a byte at a time returns the low half twice on some codecs, // because the read of the low byte is what latches the pair. static inline ushort inw(ushort port) { ushort data; asm volatile("inw %1,%0" : "=a" (data) : "d" (port)); return data; } static inline uint inl(ushort port) { uint data; asm volatile("inl %1,%0" : "=a" (data) : "d" (port)); return data; } static inline void outw(ushort port, ushort data) { asm volatile("out %0,%1" : : "a" (data), "d" (port)); } // PCI configuration space is reached through a pair of 32-bit ports, so this // is not optional decoration -- see kernel/dev/pci.c. static inline void outl(ushort port, uint data) { asm volatile("outl %0,%1" : : "a" (data), "d" (port)); } static inline void outsl(int port, const void *addr, int cnt) { asm volatile("cld; rep outsl" : "=S" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "cc"); } static inline void stosb(void *addr, int data, int cnt) { asm volatile("cld; rep stosb" : "=D" (addr), "=c" (cnt) : "0" (addr), "1" (cnt), "a" (data) : "memory", "cc"); } // 64-bit-only helpers. `rep stosq` and `xchgq` do not exist in 32-bit mode, so // these must not even be parsed by the -m32 bootloader build -- they compiled // there before only because uint64 was accidentally 32 bits wide, which is the // bug include/core/types.h now asserts against. #ifdef __x86_64__ static inline void stosq(void *addr, uint64 data, int cnt) { asm volatile("cld; rep stosq" : "=D" (addr), "=c" (cnt) : "0" (addr), "1" (cnt), "a" (data) : "memory", "cc"); } struct segdesc; static inline void lgdt(struct segdesc *p, int size) { struct dtr d; d.limit = size - 1; d.base = (uint64)p; asm volatile("lgdt (%0)" : : "r" (&d)); } struct gatedesc; static inline void lidt(struct gatedesc *p, int size) { struct dtr d; d.limit = size - 1; d.base = (uint64)p; asm volatile("lidt (%0)" : : "r" (&d)); } static inline void ltr(ushort sel) { asm volatile("ltr %0" : : "r" (sel)); } static inline uint64 readeflags(void) { uint64 flags; asm volatile("pushfq; pop %0" : "=r" (flags)); return flags; } static inline void cli(void) { asm volatile("cli"); } static inline void sti(void) { asm volatile("sti"); } static inline void hlt(void) { asm volatile("hlt"); } static inline uint64 xchg(volatile uint64 *addr, uint64 newval) { uint64 result; asm volatile("lock; xchgq %0, %1" : "+m" (*addr), "=a" (result) : "1" (newval) : "cc"); return result; } #endif /* __x86_64__ */ static inline uint64 rcr2(void) { uint64 val; asm volatile("mov %%cr2,%0" : "=r" (val)); return val; } static inline void lcr3(uint64 val) { asm volatile("mov %0,%%cr3" : : "r" (val)); } static inline uint64 rcr3(void) { uint64 val; asm volatile("mov %%cr3,%0" : "=r" (val)); return val; } // Invalidate the TLB entry for one page. Needed any time a PTE is changed // or cleared for an address space that may still be the active CR3 — // otherwise the CPU can keep translating through the stale entry until the // next full CR3 reload (context switch), silently reading/writing the // wrong physical page. static inline void invlpg(void *va) { asm volatile("invlpg (%0)" : : "r" (va) : "memory"); } static inline void wrmsr(uint32 msr, uint64 val) { uint32 low = (uint32)val; uint32 high = (uint32)(val >> 32); asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high)); } static inline uint64 rdmsr(uint32 msr) { uint32 low, high; asm volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr)); return ((uint64)high << 32) | low; } static inline uint64 readcs(void) { uint64 val; asm volatile("mov %%cs, %0" : "=r" (val)); return val; } // CPUID. `leaf` in EAX, results in EAX/EBX/ECX/EDX. // // %rbx is callee-saved *and* the PIC base register on some builds, so it is // exchanged out rather than named as an operand -- naming it directly is the // classic way to get "inline asm clobbers reserved register" or, worse, silent // corruption in the caller. static inline void cpuid_leaf(uint32 leaf, uint32 *a, uint32 *b, uint32 *c, uint32 *d) { uint32 ra, rb, rc, rd; asm volatile("xchgq %%rbx, %q1\n\t" "cpuid\n\t" "xchgq %%rbx, %q1" : "=a"(ra), "=&r"(rb), "=c"(rc), "=d"(rd) : "0"(leaf), "2"(0)); if (a) *a = ra; if (b) *b = rb; if (c) *c = rc; if (d) *d = rd; }