/* * 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 */