nexus-am/am/include/x86.h

353 lines
9.9 KiB
C

// CPU rings
#define DPL_KERN 0x0 // Kernel (ring 0)
#define DPL_USER 0x3 // User (ring 3)
// Application Segment type bits
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
// System Segment type bits
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_IG 0xe // 32/64-bit Interrupt Gate
#define STS_TG 0xf // 32/64-bit Trap Gate
// EFLAGS register
#define FL_IF 0x00000200 // Interrupt Enable
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_PG 0x80000000 // Paging
#define CR4_PAE 0x00000020 // Physical Address Extension
// Page table/directory entry flags
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PS 0x080 // Large Page (1 GiB or 2 MiB)
// GDT selectors
#define KSEL(seg) (((seg) << 3) | DPL_KERN)
#define USEL(seg) (((seg) << 3) | DPL_USER)
// Interrupts and exceptions
#define T_IRQ0 32
#define IRQ_TIMER 0
#define IRQ_KBD 1
#define IRQ_ERROR 19
#define IRQ_SPURIOUS 31
#define EX_DE 0
#define EX_UD 6
#define EX_NM 7
#define EX_DF 8
#define EX_TS 10
#define EX_NP 11
#define EX_SS 12
#define EX_GP 13
#define EX_PF 14
#define EX_MF 15
#define EX_SYSCALL 0x80
#define EX_YIELD 0x81
// List of interrupts and exceptions (#irq, DPL, hardware errorcode)
#define IRQS(_) \
_( 0, KERN, NOERR) \
_( 1, KERN, NOERR) \
_( 2, KERN, NOERR) \
_( 3, KERN, NOERR) \
_( 4, KERN, NOERR) \
_( 5, KERN, NOERR) \
_( 6, KERN, NOERR) \
_( 7, KERN, NOERR) \
_( 8, KERN, ERR) \
_( 9, KERN, NOERR) \
_( 10, KERN, ERR) \
_( 11, KERN, ERR) \
_( 12, KERN, ERR) \
_( 13, KERN, ERR) \
_( 14, KERN, ERR) \
_( 15, KERN, NOERR) \
_( 16, KERN, NOERR) \
_( 19, KERN, NOERR) \
_( 31, KERN, NOERR) \
_( 32, KERN, NOERR) \
_( 33, KERN, NOERR) \
_( 34, KERN, NOERR) \
_( 35, KERN, NOERR) \
_( 36, KERN, NOERR) \
_( 37, KERN, NOERR) \
_( 38, KERN, NOERR) \
_( 39, KERN, NOERR) \
_( 40, KERN, NOERR) \
_( 41, KERN, NOERR) \
_( 42, KERN, NOERR) \
_( 43, KERN, NOERR) \
_( 44, KERN, NOERR) \
_( 45, KERN, NOERR) \
_( 46, KERN, NOERR) \
_( 47, KERN, NOERR) \
_(128, USER, NOERR) \
_(129, USER, NOERR)
// AM-specific configurations
#define MAX_CPU 8
#define BOOTREC_ADDR 0x07000
#define MAINARG_ADDR 0x10000
// Below are only visible to c/cpp files
#ifndef __ASSEMBLER__
#include <stdint.h>
// Segment Descriptor
typedef struct {
uint32_t lim_15_0 : 16; // Low bits of segment limit
uint32_t base_15_0 : 16; // Low bits of segment base address
uint32_t base_23_16 : 8; // Middle bits of segment base address
uint32_t type : 4; // Segment type (see STS_ constants)
uint32_t s : 1; // 0 = system, 1 = application
uint32_t dpl : 2; // Descriptor Privilege Level
uint32_t p : 1; // Present
uint32_t lim_19_16 : 4; // High bits of segment limit
uint32_t avl : 1; // Unused (available for software use)
uint32_t l : 1; // 64-bit segment
uint32_t db : 1; // 32-bit segment
uint32_t g : 1; // Granularity: limit scaled by 4K when set
uint32_t base_31_24 : 8; // High bits of segment base address
} SegDesc;
// Gate descriptors for interrupts and traps
typedef struct {
uint32_t off_15_0 : 16; // Low 16 bits of offset in segment
uint32_t cs : 16; // Code segment selector
uint32_t args : 5; // # args, 0 for interrupt/trap gates
uint32_t rsv1 : 3; // Reserved(should be zero I guess)
uint32_t type : 4; // Type(STS_{TG,IG32,TG32})
uint32_t s : 1; // Must be 0 (system)
uint32_t dpl : 2; // Descriptor(meaning new) privilege level
uint32_t p : 1; // Present
uint32_t off_31_16 : 16; // High bits of offset in segment
} GateDesc32;
typedef struct {
uint32_t off_15_0 : 16;
uint32_t cs : 16;
uint32_t isv : 3;
uint32_t zero1 : 5;
uint32_t type : 4;
uint32_t zero2 : 1;
uint32_t dpl : 2;
uint32_t p : 1;
uint32_t off_31_16 : 16;
uint32_t off_63_32 : 32;
uint32_t rsv : 32;
} GateDesc64;
// Task State Segment (TSS)
typedef struct {
uint32_t link; // Unused
uint32_t esp0; // Stack pointers and segment selectors
uint32_t ss0; // after an increase in privilege level
uint32_t padding[23];
} __attribute__((packed)) TSS32;
typedef struct {
uint32_t rsv;
uint64_t rsp0, rsp1, rsp2;
uint32_t padding[19];
} __attribute__((packed)) TSS64;
// Multiprocesor configuration
typedef struct { // configuration table header
uint8_t signature[4]; // "PCMP"
uint16_t length; // total table length
uint8_t version; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t product[20]; // product id
uint32_t oemtable; // OEM table pointer
uint16_t oemlength; // OEM table length
uint16_t entry; // entry count
uint32_t lapicaddr; // address of local APIC
uint16_t xlength; // extended table length
uint8_t xchecksum; // extended table checksum
uint8_t reserved;
} MPConf;
typedef struct {
int magic;
uint32_t conf; // MP config table addr
uint8_t length; // 1
uint8_t specrev; // [14]
uint8_t checksum; // all bytes add to 0
uint8_t type; // config type
uint8_t imcrp;
uint8_t reserved[3];
} MPDesc;
typedef struct {
uint32_t jmp_code;
int32_t is_ap;
} BootRecord;
#define SEG16(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uintptr_t)(base) & 0xffff, \
((uintptr_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uintptr_t)(lim) >> 16, 0, 0, 1, 0, (uintptr_t)(base) >> 24 }
#define SEG32(type, base, lim, dpl) (SegDesc) \
{ ((lim) >> 12) & 0xffff, (uintptr_t)(base) & 0xffff, \
((uintptr_t)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uintptr_t)(lim) >> 28, 0, 0, 1, 1, (uintptr_t)(base) >> 24 }
#define SEG64(type, dpl) (SegDesc) \
{ 0, 0, 0, type, 1, dpl, 1, 0, 0, 1, 0, 0 }
#define SEGTSS64(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uint32_t)(base) & 0xffff, \
((uint32_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uint32_t)(lim) >> 16, 0, 0, 0, 0, (uint32_t)(base) >> 24 }
#define GATE32(type, cs, entry, dpl) (GateDesc32) \
{ (uint32_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, (uint32_t)(entry) >> 16 }
#define GATE64(type, cs, entry, dpl) (GateDesc64) \
{ (uint64_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, ((uint64_t)(entry) >> 16) & 0xffff, (uint64_t)(entry) >> 32, 0 }
// Instruction wrappers
static inline uint8_t inb(int port) {
uint8_t data;
asm volatile ("inb %1, %0" : "=a"(data) : "d"((uint16_t)port));
return data;
}
static inline uint16_t inw(int port) {
uint16_t data;
asm volatile ("inw %1, %0" : "=a"(data) : "d"((uint16_t)port));
return data;
}
static inline uint32_t inl(int port) {
uint32_t data;
asm volatile ("inl %1, %0" : "=a"(data) : "d"((uint16_t)port));
return data;
}
static inline void outb(int port, uint8_t data) {
asm volatile ("outb %%al, %%dx" : : "a"(data), "d"((uint16_t)port));
}
static inline void outw(int port, uint16_t data) {
asm volatile ("outw %%ax, %%dx" : : "a"(data), "d"((uint16_t)port));
}
static inline void outl(int port, uint32_t data) {
asm volatile ("outl %%eax, %%dx" : : "a"(data), "d"((uint16_t)port));
}
static inline void cli() {
asm volatile ("cli");
}
static inline void sti() {
asm volatile ("sti");
}
static inline void hlt() {
asm volatile ("hlt");
}
static inline void pause() {
asm volatile ("pause");
}
static inline uint32_t get_efl() {
volatile uintptr_t efl;
asm volatile ("pushf; pop %0": "=r"(efl));
return efl;
}
static inline uintptr_t get_cr0(void) {
volatile uintptr_t val;
asm volatile ("mov %%cr0, %0" : "=r"(val));
return val;
}
static inline void set_cr0(uintptr_t cr0) {
asm volatile ("mov %0, %%cr0" : : "r"(cr0));
}
static inline void set_idt(void *idt, int size) {
static volatile struct {
int16_t size;
void *idt;
} __attribute__((packed)) data;
data.size = size;
data.idt = idt;
asm volatile ("lidt (%0)" : : "r"(&data));
}
static inline void set_gdt(void *gdt, int size) {
static volatile struct {
int16_t size;
void *gdt;
} __attribute__((packed)) data;
data.size = size;
data.gdt = gdt;
asm volatile ("lgdt (%0)" : : "r"(&data));
}
static inline void set_tr(int selector) {
asm volatile ("ltr %0" : : "r"((uint16_t)selector));
}
static inline uintptr_t get_cr2() {
volatile uintptr_t val;
asm volatile ("mov %%cr2, %0" : "=r"(val));
return val;
}
static inline uintptr_t get_cr3() {
volatile uintptr_t val;
asm volatile ("mov %%cr3, %0" : "=r"(val));
return val;
}
static inline void set_cr3(void *pdir) {
asm volatile ("mov %0, %%cr3" : : "r"(pdir));
}
static inline intptr_t xchg(volatile intptr_t *addr, intptr_t newval) {
intptr_t result;
asm volatile ("lock xchg %0, %1":
"+m"(*addr), "=a"(result) : "1"(newval) : "cc");
return result;
}
static inline uint64_t rdtsc() {
uint32_t lo, hi;
asm volatile ("rdtsc": "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
#define interrupt(id) \
asm volatile ("int $" #id);
static inline void stack_switch_call(void *sp, void *entry, uintptr_t arg) {
asm volatile (
#if __x86_64__
"movq %0, %%rsp; movq %2, %%rdi; jmp *%1" : : "b"((uintptr_t)sp), "d"(entry), "a"(arg)
#else
"movl %0, %%esp; movl %2, 4(%0); jmp *%1" : : "b"((uintptr_t)sp - 8), "d"(entry), "a"(arg)
#endif
);
}
static inline volatile BootRecord *boot_record() {
return (BootRecord *)BOOTREC_ADDR;
}
#endif // __ASSEMBLER__