mirror of
https://github.com/deadw00d/AROS.git
synced 2026-03-20 04:00:58 +00:00
368 lines
12 KiB
C
368 lines
12 KiB
C
#ifndef ASM_X86_64_CPU_H
|
|
#define ASM_X86_64_CPU_H
|
|
|
|
/*
|
|
Copyright © 1995-2025, The AROS Development Team. All rights reserved.
|
|
$Id$
|
|
|
|
Desc: assembler-level specific definitions for x86-64 CPU
|
|
Lang: english
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* CR0 bits */
|
|
#define _CR0_PE_B 0 /* RW: Protected mode enable */
|
|
#define _CR0_MP_B 1 /* RW: Monitor FPU? If 1 then #NM exception may be generated */
|
|
#define _CR0_EM_B 2 /* RW: Emulate FPU */
|
|
#define _CR0_TS_B 3 /* RW: Task switched */
|
|
#define _CR0_ET_B 4 /* RO: Exception type */
|
|
#define _CR0_NE_B 5 /* RW: Numeric error */
|
|
#define _CR0_WP_B 16 /* RW: Write protect for RO pages in supervisor mode */
|
|
#define _CR0_AM_B 18 /* RW: Require data alignment */
|
|
#define _CR0_NW_B 29 /* RW: IGNORED: Not writethrough */
|
|
#define _CR0_CD_B 30 /* RW: Cache disable */
|
|
#define _CR0_PG_B 31 /* RW: Paging enable */
|
|
|
|
#define _CR0_PE (1 << _CR0_PE_B)
|
|
#define _CR0_MP (1 << _CR0_MP_B)
|
|
#define _CR0_EM (1 << _CR0_EM_B)
|
|
#define _CR0_TS (1 << _CR0_TS_B)
|
|
#define _CR0_ET (1 << _CR0_ET_B)
|
|
#define _CR0_NE (1 << _CR0_NE_B)
|
|
#define _CR0_WP (1 << _CR0_WP_B)
|
|
#define _CR0_AM (1 << _CR0_AM_B)
|
|
#define _CR0_NW (1 << _CR0_NW_B)
|
|
#define _CR0_CD (1 << _CR0_CD_B)
|
|
#define _CR0_PG (1 << _CR0_PG_B)
|
|
|
|
/* CR3 bits */
|
|
#define _CR3_PWT_B 3 /* RW: Page writethrough */
|
|
#define _CR3_PCD_B 4 /* RW: Cache disable */
|
|
|
|
#define _CR3_PWT (1 << _CR3_PWT_B)
|
|
#define _CR3_PCD (1 << _CR3_PCD_B)
|
|
|
|
/* CR4 bits */
|
|
#define _CR4_VME_B 0 /* RW: Virtual-8086 enable */
|
|
#define _CR4_PVI_B 1 /* RW: Protected mode virtual interrupts */
|
|
#define _CR4_TSD_B 2 /* RW: Time stamp disable for usermode */
|
|
#define _CR4_DE_B 3 /* RW: Debug extensions */
|
|
#define _CR4_PSE_B 4 /* RW: Page size extensions */
|
|
#define _CR4_PAE_B 5 /* RW: Physical-address extensions */
|
|
#define _CR4_MCE_B 6 /* RW: Machine check enable */
|
|
#define _CR4_PGE_B 7 /* RW: Page-Global enable */
|
|
#define _CR4_PCE_B 8 /* RW: Performance monitoring counter enable */
|
|
#define _CR4_OSFXSR_B 9 /* RW: Operating system fxsave/fsrstor support */
|
|
#define _CR4_OSXMMEXCPT_B 10 /* RW: Operating system unmasked exception support */
|
|
#define _CR4_OSXSBV_B 18 /* RW: Operating system xsetbv/xgetbv support */
|
|
|
|
#define _CR4_VME (1 << _CR4_VME_B)
|
|
#define _CR4_PVI (1 << _CR4_PVI_B)
|
|
#define _CR4_TSD (1 << _CR4_TSD_B)
|
|
#define _CR4_DE (1 << _CR4_DE_B)
|
|
#define _CR4_PSE (1 << _CR4_PSE_B)
|
|
#define _CR4_PAE (1 << _CR4_PAE_B)
|
|
#define _CR4_MCE (1 << _CR4_MCE_B)
|
|
#define _CR4_PGE (1 << _CR4_PGE_B)
|
|
#define _CR4_PCE (1 << _CR4_PCE_B)
|
|
#define _CR4_OSFXSR (1 << _CR4_OSFXSR_B)
|
|
#define _CR4_OSXMMEXCPT (1 << _CR4_OSXMMEXCPT_B)
|
|
#define _CR4_OSXSBV (1 << _CR4_OSXSBV_B)
|
|
|
|
/* EFER */
|
|
#define EFER 0xc0000080 /* EFER number for rsmsr/wrmsr */
|
|
#define _EFER_SCE_B 0 /* RW: System call extensions */
|
|
#define _EFER_LME_B 8 /* RW: Long mode enable */
|
|
#define _EFER_LMA_B 10 /* RW: Long mode activated */
|
|
#define _EFER_NXE_B 11 /* RW: No-execute bit enable */
|
|
#define _EFER_FFXSR_B 14 /* RW: Fast fxsave/fxrstor */
|
|
|
|
/* SYSCALL/SYSRET registers */
|
|
#define IA32_STAR 0xc0000081
|
|
#define IA32_LSTAR 0xc0000082
|
|
#define IA32_FMASK 0xc0000084
|
|
|
|
#define _EFER_SCE (1 << _EFER_SCE_B)
|
|
#define _EFER_LME (1 << _EFER_LME_B)
|
|
#define _EFER_LMA (1 << _EFER_LMA_B)
|
|
#define _EFER_NXE (1 << _EFER_NXE_B)
|
|
#define _EFER_FFXSR (1 << _EFER_FFXSR_B)
|
|
|
|
#define HALT asm volatile("hlt")
|
|
|
|
/*
|
|
* Selector used for lgdt and lidt commands.
|
|
* Intentionally use unsigned long for base address since this file can be compiled also in 32-bit mode.
|
|
*/
|
|
struct segment_selector
|
|
{
|
|
unsigned short size;
|
|
unsigned long base;
|
|
} __attribute__((packed));
|
|
|
|
struct int_gate_64bit {
|
|
uint16_t offset_low;
|
|
uint16_t selector;
|
|
unsigned ist:3, __pad0:5, type:5, dpl:2, p:1;
|
|
uint16_t offset_mid;
|
|
uint32_t offset_high;
|
|
uint32_t __pad1;
|
|
} __attribute__((packed));
|
|
|
|
/* Segment descriptor in the GDT */
|
|
struct segment_desc
|
|
{
|
|
uint16_t limit_low;
|
|
uint16_t base_low;
|
|
uint8_t base_mid;
|
|
unsigned type:5, dpl:2, p:1;
|
|
unsigned limit_high:4, avl:1, l:1, d:1, g:1;
|
|
uint8_t base_high:8;
|
|
} __attribute__((packed));
|
|
|
|
struct segment_ext {
|
|
uint32_t base_ext;
|
|
uint32_t __pad0;
|
|
} __attribute__((packed));
|
|
|
|
struct tss_64bit {
|
|
uint32_t __pad0;
|
|
uint64_t rsp0;
|
|
uint64_t rsp1;
|
|
uint64_t rsp2;
|
|
uint64_t __pad1;
|
|
uint64_t ist1;
|
|
uint64_t ist2;
|
|
uint64_t ist3;
|
|
uint64_t ist4;
|
|
uint64_t ist5;
|
|
uint64_t ist6;
|
|
uint64_t ist7;
|
|
uint64_t __pad2;
|
|
uint16_t __pad3;
|
|
uint16_t iopb;
|
|
uint32_t bmp[];
|
|
} __attribute__((packed));
|
|
|
|
struct segment_tss
|
|
{
|
|
struct segment_desc tss_low;
|
|
struct segment_ext tss_high;
|
|
} __attribute__((packed));
|
|
|
|
struct gdt_64bit
|
|
{
|
|
struct segment_desc seg0; /* seg 0x00 */
|
|
struct segment_desc super_cs; /* seg 0x08 */
|
|
struct segment_desc super_ds; /* seg 0x10 */
|
|
struct segment_desc user_cs32; /* seg 0x18 */
|
|
struct segment_desc user_ds; /* seg 0x20 */
|
|
struct segment_desc user_cs; /* seg 0x28 */
|
|
struct segment_desc gs; /* seg 0x30 */
|
|
struct segment_desc ldt; /* seg 0x38 */
|
|
struct segment_tss tss[0];
|
|
} __attribute__((packed));
|
|
|
|
#define MMU_PAGEB_P 0
|
|
#define MMU_PAGEB_RW 1
|
|
#define MMU_PAGEB_US 2
|
|
#define MMU_PAGEB_PWT 3
|
|
#define MMU_PAGEB_PCD 4
|
|
#define MMU_PAGEB_A 5
|
|
|
|
struct PML4E
|
|
{
|
|
unsigned p:1,rw:1,us:1,pwt:1,pcd:1,a:1,mbz:3,avl:3,base_low:20;
|
|
unsigned base_high:20,avail:11,nx:1;
|
|
} __attribute__((packed));
|
|
|
|
struct PDPE {
|
|
unsigned p:1,rw:1,us:1,pwt:1,pcd:1,a:1,__pad0:1,mbz:2,avl:3,base_low:20;
|
|
unsigned base_high:20,avail:11,nx:1;
|
|
} __attribute__((packed));
|
|
|
|
struct PDE4K {
|
|
unsigned p:1,rw:1,us:1,pwt:1,pcd:1,a:1,__pad0:1,ps:1,_pad1:1,avl:3,base_low:20;
|
|
unsigned base_high:20,avail:11,nx:1;
|
|
} __attribute__((packed));
|
|
|
|
struct PDE2M {
|
|
unsigned p:1,rw:1,us:1,pwt:1,pcd:1,a:1,d:1,ps:1,g:1,avl:3,pat:1,base_low:19;
|
|
unsigned base_high:20,avail:11,nx:1;
|
|
} __attribute__((packed));
|
|
|
|
struct PTE {
|
|
unsigned p:1,rw:1,us:1,pwt:1,pcd:1,a:1,d:1,pat:1,g:1,avl:3,base_low:20;
|
|
unsigned base_high:20,avail:11,nx:1;
|
|
} __attribute__((packed));
|
|
|
|
#define _ljmp(seg, addr) \
|
|
do { asm volatile("ljmp $" #seg ", $" #addr); }while(0)
|
|
#define ljmp(s, a) _ljmp(s, a)
|
|
|
|
#define _ljmp_arg(seg, addr, arg) \
|
|
do { asm volatile("ljmp $" #seg ", $" #addr ::"D"(arg)); }while(0)
|
|
#define ljmp_arg(s, a, p) _ljmp_arg(s, a, p)
|
|
|
|
#define rdcr(reg) \
|
|
({ long val; asm volatile("mov %%" #reg ",%0":"=r"(val)); val; })
|
|
|
|
#define wrcr(reg, val) \
|
|
do { asm volatile("mov %0,%%" #reg::"r"(val)); } while(0)
|
|
|
|
#define cpuid(num, eax, ebx, ecx, edx) \
|
|
do { asm volatile("cpuid":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(num)); } while(0)
|
|
|
|
static inline __attribute__((always_inline))
|
|
void cpuid2(unsigned int leaf, unsigned int subleaf,
|
|
unsigned int *eax, unsigned int *ebx,
|
|
unsigned int *ecx, unsigned int *edx)
|
|
{
|
|
#if defined(__x86_64__)
|
|
unsigned int a = leaf, b, c = subleaf, d;
|
|
__asm__ volatile(
|
|
"mov %%rbx, %%rdi\n\t" /* save RBX (used by PIC) */
|
|
"cpuid\n\t"
|
|
"xchg %%rbx, %%rdi\n\t" /* restore RBX */
|
|
: "+a"(a), "=D"(b), "+c"(c), "=d"(d)
|
|
:
|
|
: "memory");
|
|
if (eax) *eax = a;
|
|
if (ebx) *ebx = b;
|
|
if (ecx) *ecx = c;
|
|
if (edx) *edx = d;
|
|
|
|
#elif defined(__i386__)
|
|
unsigned int a = leaf, b, c = subleaf, d;
|
|
__asm__ volatile(
|
|
"xchg %%ebx, %%edi\n\t" /* save EBX (PIC-safe) */
|
|
"cpuid\n\t"
|
|
"xchg %%ebx, %%edi\n\t" /* restore EBX */
|
|
: "=a"(a), "=D"(b), "=c"(c), "=d"(d)
|
|
: "a"(leaf), "c"(subleaf)
|
|
: "memory");
|
|
if (eax) *eax = a;
|
|
if (ebx) *ebx = b;
|
|
if (ecx) *ecx = c;
|
|
if (edx) *edx = d;
|
|
|
|
#else
|
|
# error "x86 only"
|
|
#endif
|
|
}
|
|
|
|
static inline void __attribute__((always_inline)) rdmsr(uint32_t msr_no, uint32_t *ret_lo, uint32_t *ret_hi)
|
|
{
|
|
uint32_t ret1,ret2;
|
|
asm volatile("rdmsr":"=a"(ret1),"=d"(ret2):"c"(msr_no));
|
|
*ret_lo=ret1;
|
|
*ret_hi=ret2;
|
|
}
|
|
|
|
static inline uint64_t __attribute__((always_inline)) rdmsrq(uint32_t msr_no)
|
|
{
|
|
uint32_t ret1,ret2;
|
|
asm volatile("rdmsr":"=a"(ret1),"=d"(ret2):"c"(msr_no));
|
|
return ((uint64_t)ret1 | ((uint64_t)ret2 << 32));
|
|
}
|
|
|
|
#define rdmsri rdmsrq
|
|
|
|
static inline void __attribute__((always_inline)) wrmsr(uint32_t msr_no, uint32_t val_lo, uint32_t val_hi)
|
|
{
|
|
asm volatile("wrmsr"::"a"(val_lo),"d"(val_hi),"c"(msr_no));
|
|
}
|
|
|
|
static inline void __attribute__((always_inline)) wrmsrq(uint32_t msr_no, uint64_t val)
|
|
{
|
|
asm volatile("wrmsr"::"a"((uint32_t)(val & 0xffffffff)),"d"((uint32_t)(val >> 32)),"c"(msr_no));
|
|
}
|
|
|
|
/*
|
|
Compare value stored at "addr" with "expected". If they are equal, function returns 1 and stores "xchg" value
|
|
at "addr". If *addr != expected, function returns 0. Either "expected" or current value at *addr are stored back
|
|
at *found. The operation is atomic
|
|
*/
|
|
static inline int compare_and_exchange_long(uint32_t *addr, uint32_t expected, uint32_t xchg, uint32_t *found)
|
|
{
|
|
char flag;
|
|
uint32_t ret;
|
|
asm volatile("lock cmpxchg %4, %0; setz %1":"+m"(*addr),"=r"(flag),"=a"(ret):"2"(expected),"r"(xchg):"memory","cc");
|
|
if (found)
|
|
*found = ret;
|
|
return flag;
|
|
}
|
|
|
|
static inline int compare_and_exchange_short(uint16_t *lock, uint16_t expected, uint16_t xchg, uint16_t *found)
|
|
{
|
|
char flag;
|
|
uint16_t ret;
|
|
asm volatile("lock cmpxchg %4, %0; setz %1":"+m"(*lock),"=r"(flag),"=a"(ret):"2"(expected),"r"(xchg):"memory","cc");
|
|
if (found)
|
|
*found = ret;
|
|
return flag;
|
|
}
|
|
|
|
static inline int compare_and_exchange_byte(uint8_t *lock, uint8_t expected, uint8_t xchg, uint8_t *found)
|
|
{
|
|
char flag;
|
|
uint16_t ret;
|
|
asm volatile("lock cmpxchg %4, %0; setz %1":"+m"(*lock),"=r"(flag),"=a"(ret):"2"(expected),"r"(xchg):"memory","cc");
|
|
if (found)
|
|
*found = ret;
|
|
return flag;
|
|
}
|
|
|
|
static inline int bit_test_and_set_long(uint32_t *addr, int32_t bit)
|
|
{
|
|
char retval = 0;
|
|
asm volatile("lock btsl %2, %0; setc %1":"+m"(*addr),"=q"(retval):"Ir"(bit):"memory");
|
|
return retval;
|
|
}
|
|
|
|
static inline int bit_test_and_set_short(uint16_t *addr, int32_t bit)
|
|
{
|
|
char retval = 0;
|
|
asm volatile("lock btsw %2, %0; setc %1":"+m"(*addr),"=q"(retval):"Ir"(bit):"memory");
|
|
return retval;
|
|
}
|
|
|
|
static inline int bit_test_and_clear_long(uint32_t *addr, int32_t bit)
|
|
{
|
|
char retval = 0;
|
|
asm volatile("lock btrl %2, %0; setc %1":"+m"(*addr),"=q"(retval):"Ir"(bit):"memory");
|
|
return retval;
|
|
}
|
|
|
|
static inline int bit_test_and_clear_short(uint16_t *addr, int32_t bit)
|
|
{
|
|
char retval = 0;
|
|
asm volatile("lock btrw %2, %0; setc %1":"+m"(*addr),"=q"(retval):"Ir"(bit):"memory");
|
|
return retval;
|
|
}
|
|
|
|
static inline int bit_test_and_complement_long(uint32_t *addr, int32_t bit)
|
|
{
|
|
char retval = 0;
|
|
asm volatile("lock btcl %2, %0; setc %1":"+m"(*addr),"=q"(retval):"Ir"(bit):"memory");
|
|
return retval;
|
|
}
|
|
|
|
static inline int bit_test_and_complement_short(uint16_t *addr, int32_t bit)
|
|
{
|
|
char retval = 0;
|
|
asm volatile("lock btcw %2, %0; setc %1":"+m"(*addr),"=q"(retval):"Ir"(bit):"memory");
|
|
return retval;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /*ASM_CPU_H*/
|