283 lines
7.5 KiB
C
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 */
|