zephyr/arch/x86_64/core/x86_64-hw.h

283 lines
7.5 KiB
C

/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _X86_64_HW_H
#define _X86_64_HW_H
/*
* Struct declarations and helper inlines for core x86_64 hardware
* functionality. Anything related to ioports, CR's MSR's, I/L/GDTs,
* PTEs or (IO-)APICs can be found here. Note that because this
* header is included in limited stub contexts, it should include
* declarations and inlines only: no data definitions, even extern
* ones!
*/
static inline unsigned long eflags(void)
{
int eflags;
__asm__ volatile("pushfq; pop %%rax" : "=a"(eflags));
return eflags;
}
/* PAE page table record. Note that "addr" is aligned naturally as an
* address, but of course must be masked to change only significant
* bits (which depend on whether it's storing a 4k, 2M or 1G memory
* block) so as to not clobber the bitfields (remember "negative"
* addresses must mask off the top bits too!). The natural idiom is
* to assign addr first, then write the bitfields.
*/
struct pte64 {
union {
unsigned long long addr;
struct {
unsigned long long present : 1;
unsigned long long writable : 1;
unsigned long long usermode : 1;
unsigned long long writethrough : 1;
unsigned long long uncached : 1;
unsigned long long accessed : 1;
unsigned long long dirty : 1;
unsigned long long pagesize_pat : 1;
unsigned long long global : 1;
unsigned long long _UNUSED1 : 3;
unsigned long long pat : 1;
unsigned long long _UNUSED2 : 50;
unsigned long long exdisable : 1;
};
};
};
struct gdt64 {
union {
unsigned int dwords[2];
struct {
unsigned long long limit_lo16 : 16;
unsigned long long base_lo16 : 16;
unsigned long long base_mid8 : 8;
unsigned long long accessed : 1;
unsigned long long readable : 1;
unsigned long long conforming : 1;
unsigned long long codeseg : 1;
unsigned long long notsystem : 1;
unsigned long long ring : 2;
unsigned long long present : 1;
unsigned long long limit_hi4 : 4;
unsigned long long available : 1;
unsigned long long long64 : 1;
unsigned long long default_size : 1;
unsigned long long page_granularity : 1;
unsigned long long base_hi8 : 8;
};
};
};
static inline void gdt64_set_base(struct gdt64 *g, unsigned int base)
{
g->base_lo16 = base & 0xffff;
g->base_mid8 = (base >> 16) & 0xff;
g->base_hi8 = base >> 24;
}
#define GDT_SELECTOR(seg) ((seg) << 3)
struct idt64 {
unsigned short offset_lo16;
unsigned short segment;
unsigned int ist : 3;
unsigned int _UNUSED1 : 5;
unsigned int type : 4;
unsigned int _UNUSED2 : 1;
unsigned int ring : 2;
unsigned int present : 1;
unsigned short offset_mid16;
unsigned int offset_hi32;
unsigned int _UNUSED3;
};
static inline void idt64_set_isr(struct idt64 *desc, void *isr)
{
unsigned long long addr = (unsigned long)isr;
desc->offset_lo16 = addr & 0xffff;
desc->offset_mid16 = (addr >> 16) & 0xffff;
desc->offset_hi32 = addr >> 32;
}
enum apic_delivery_mode {
FIXED = 0, LOWEST = 1, SMI = 2, NMI = 4,
INIT = 5, STARTUP = 6, EXTINT = 7,
};
struct apic_icr_lo {
unsigned int vector : 8;
enum apic_delivery_mode delivery_mode : 3;
unsigned int logical : 1;
unsigned int send_pending : 1;
unsigned int _unused : 1;
unsigned int assert : 1;
unsigned int level_trig : 1;
unsigned int _unused2 : 2;
enum { NONE, SELF, ALL, NOTSELF } shorthand : 2;
};
struct apic_icr_hi {
unsigned int _unused : 24;
unsigned int destination : 8;
};
/* Generic struct, not all field applicable to all LVT interrupts */
struct apic_lvt {
unsigned int vector : 8;
enum apic_delivery_mode delivery_mode : 4;
unsigned int _UNUSED : 1;
unsigned int send_pending : 1;
unsigned int polarity : 1;
unsigned int remote_irr : 1;
unsigned int level_trig : 1;
unsigned int masked : 1;
enum { ONESHOT, PERIODIC, TSCDEADLINE } mode : 2;
};
/* Memory-mapped local APIC registers. Note that the registers are
* always the first dword in a 16 byte block, the other 3 being
* unused. So each line represents one of these registers, or an
* array thereof. Lots of (_u)nused fields in the layout, but the usage
* becomes pleasingly clean.
*/
struct apic_regs {
unsigned int _u1[4][2];
unsigned int ID, _u2[3];
unsigned int VER, _u3[3];
unsigned int _u4[4][4];
unsigned int TPR, _u5[3];
unsigned int APR, _u6[3];
unsigned int PPR, _u7[3];
unsigned int EOI, _u8[3];
unsigned int RRD, _u9[3];
unsigned int LDR, _u10[3];
unsigned int DFR, _u11[3];
unsigned int SPURIOUS, _u12[3];
unsigned int ISR_BITS[4][8];
unsigned int TMR_BITS[4][8];
unsigned int IRR_BITS[4][8];
unsigned int ERR_STATUS, _u13[3];
unsigned int _u14[4][6];
struct apic_lvt LVT_CMCI; unsigned int _u15[3];
struct apic_icr_lo ICR_LO, _u16[3];
struct apic_icr_hi ICR_HI, _u17[3];
struct apic_lvt LVT_TIMER; unsigned int _u18[3];
struct apic_lvt LVT_THERMAL; unsigned int _u19[3];
struct apic_lvt LVT_PERF; unsigned int _u20[3];
struct apic_lvt LVT_LINT0; unsigned int _u21[3];
struct apic_lvt LVT_LINT1; unsigned int _u22[3];
struct apic_lvt LVT_ERROR; unsigned int _u23[3];
unsigned int INIT_COUNT, _u24[3];
unsigned int CURR_COUNT, _u25[3];
unsigned int _u26[4][4];
unsigned int DIVIDE_CONF, _u27[3];
};
#define _apic (*((volatile struct apic_regs *)0xfee00000ll))
/* Crazy encoding for this, but susceptable to a formula. Returns the
* DIVIDE_CONF register value that divides the input clock by 2^n (n
* in the range 0-7).
*/
#define APIC_DIVISOR(n) (((((n) - 1) << 1) & 8)|(((n) - 1) & 3))
#define IOREGSEL (*(volatile unsigned int *)0xfec00000l)
#define IOREGWIN (*(volatile unsigned int *)0xfec00010l)
/* Assumes one IO-APIC. Note that because of the way the register API
* works, this must be spinlocked or otherwise protected against other
* CPUs (e.g. do it all on cpu0 at startup, etc...).
*/
static inline unsigned int ioapic_read(int reg)
{
IOREGSEL = reg;
return IOREGWIN;
}
static inline void ioapic_write(int reg, unsigned int val)
{
IOREGSEL = reg;
IOREGWIN = val;
}
/* IOAPIC redirection table entry */
struct ioapic_red {
union {
unsigned int regvals[2];
struct {
unsigned int vector : 8;
enum apic_delivery_mode : 3;
unsigned int logical : 1;
unsigned int send_pending : 1;
unsigned int active_low : 1;
unsigned int remote_irr : 1;
unsigned int level_triggered : 1;
unsigned int masked : 1;
unsigned int _UNUSED1 : 15;
unsigned int _UNUSED2 : 24;
unsigned int destination : 8;
};
};
};
#define GET_CR(reg) ({ unsigned int _r; \
__asm__ volatile("movl %%" reg ", %0\n\t" \
: "=r"(_r)); \
_r; })
#define SET_CR(reg, val) \
do { \
int tmp = val; \
__asm__ volatile("movl %0, %%" reg "\n\t" :: "r"(tmp)); \
} while (0)
#define SET_CR_BIT(reg, bit) SET_CR(reg, GET_CR(reg) | (1 << bit))
static inline void ioport_out8(unsigned short port, unsigned char b)
{
__asm__ volatile("outb %0, %1;\n\t" : : "a"(b), "d"(port));
}
static inline unsigned char ioport_in8(unsigned short port)
{
unsigned char ret;
__asm__ volatile("inb %1, %0;\n\t" : "=a"(ret) : "d"(port));
return ret;
}
static inline void set_msr_bit(unsigned int msr, int bit)
{
unsigned int mask = 1 << bit;
__asm__ volatile("rdmsr; or %0, %%eax; wrmsr"
:: "r"(mask), "c"(msr) : "eax", "edx");
}
static inline unsigned int get_msr(unsigned int msr)
{
unsigned int val;
__asm__ volatile("rdmsr" : "=a"(val) : "c"(msr) : "edx");
return val;
}
static inline unsigned long long rdtsc(void)
{
unsigned long long rax, rdx;
__asm__ volatile("rdtsc" : "=a"(rax), "=d"(rdx));
return rdx << 32 | rax;
}
#endif /* _X86_64_HW_H */