SchOS/include/x86.h
Nexoniarz d0f7acf9eb Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
2026-08-09 13:47:01 +02:00

219 lines
4.6 KiB
C

#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;
}
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));
}
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/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));
}
// 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;
}