/*
* Copyright (c) 2010-2017 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef X86_CPU_H
#define X86_CPU_H
#include
/*
* L1 cache line size.
*
* XXX Use this value until processor selection is available.
*
* TODO Add macros to specifically align to the cache line size, and to
* do so only in SMP configurations.
*/
#define CPU_L1_SIZE 64
/*
* Data alignment, normally the word size.
*/
#define CPU_DATA_ALIGN (LONG_BIT / 8)
/*
* Function alignment.
*
* Aligning functions improves instruction fetching.
*
* Used for assembly functions only.
*
* XXX Use this value until processor selection is available.
*/
#define CPU_TEXT_ALIGN 16
/*
* Processor privilege levels.
*/
#define CPU_PL_KERNEL 0
#define CPU_PL_USER 3
/*
* Control register 0 flags.
*/
#define CPU_CR0_PE 0x00000001
#define CPU_CR0_MP 0x00000002
#define CPU_CR0_TS 0x00000008
#define CPU_CR0_ET 0x00000010
#define CPU_CR0_NE 0x00000020
#define CPU_CR0_WP 0x00010000
#define CPU_CR0_AM 0x00040000
#define CPU_CR0_PG 0x80000000
/*
* Control register 4 flags.
*/
#define CPU_CR4_PSE 0x00000010
#define CPU_CR4_PAE 0x00000020
#define CPU_CR4_PGE 0x00000080
/*
* EFLAGS register flags.
*/
#define CPU_EFL_ONE 0x00000002 /* Reserved, must be one */
#define CPU_EFL_IF 0x00000200
/*
* Model specific registers.
*/
#define CPU_MSR_EFER 0xc0000080
#define CPU_MSR_FSBASE 0xc0000100
#define CPU_MSR_GSBASE 0xc0000101
/*
* EFER MSR flags.
*/
#define CPU_EFER_LME 0x00000100
/*
* Feature2 flags.
*
* TODO Better names.
*/
#define CPU_FEATURE2_FPU 0x00000001
#define CPU_FEATURE2_PSE 0x00000008
#define CPU_FEATURE2_PAE 0x00000040
#define CPU_FEATURE2_MSR 0x00000020
#define CPU_FEATURE2_CX8 0x00000100
#define CPU_FEATURE2_APIC 0x00000200
#define CPU_FEATURE2_PGE 0x00002000
#define CPU_FEATURE4_1GP 0x04000000
#define CPU_FEATURE4_LM 0x20000000
/*
* GDT segment selectors.
*/
#define CPU_GDT_SEL_NULL 0
#define CPU_GDT_SEL_CODE 8
#define CPU_GDT_SEL_DATA 16
#define CPU_GDT_SEL_TSS 24
#ifdef __LP64__
#define CPU_GDT_SIZE 40
#else /* __LP64__ */
#define CPU_GDT_SEL_DF_TSS 32
#define CPU_GDT_SEL_PERCPU 40
#define CPU_GDT_SEL_TLS 48
#define CPU_GDT_SIZE 56
#endif /* __LP64__ */
#define CPU_IDT_SIZE 256
#ifndef __ASSEMBLER__
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* Gate/segment descriptor bits and masks.
*/
#define CPU_DESC_TYPE_DATA 0x00000200
#define CPU_DESC_TYPE_CODE 0x00000a00
#define CPU_DESC_TYPE_TSS 0x00000900
#define CPU_DESC_TYPE_GATE_INTR 0x00000e00
#define CPU_DESC_TYPE_GATE_TASK 0x00000500
#define CPU_DESC_S 0x00001000
#define CPU_DESC_PRESENT 0x00008000
#define CPU_DESC_LONG 0x00200000
#define CPU_DESC_DB 0x00400000
#define CPU_DESC_GRAN_4KB 0x00800000
#define CPU_DESC_GATE_OFFSET_LOW_MASK 0x0000ffff
#define CPU_DESC_GATE_OFFSET_HIGH_MASK 0xffff0000
#define CPU_DESC_SEG_IST_MASK 0x00000007
#define CPU_DESC_SEG_BASE_LOW_MASK 0x0000ffff
#define CPU_DESC_SEG_BASE_MID_MASK 0x00ff0000
#define CPU_DESC_SEG_BASE_HIGH_MASK 0xff000000
#define CPU_DESC_SEG_LIMIT_LOW_MASK 0x0000ffff
#define CPU_DESC_SEG_LIMIT_HIGH_MASK 0x000f0000
/*
* Code or data segment descriptor.
*/
struct cpu_seg_desc {
uint32_t low;
uint32_t high;
};
/*
* Forward declaration.
*/
struct trap_frame;
/*
* IST indexes (0 is reserved).
*/
#define CPU_TSS_IST_DF 1
struct cpu_tss {
#ifdef __LP64__
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t ist[8];
uint64_t reserved1;
uint16_t reserved2;
#else /* __LP64__ */
uint32_t link;
uint32_t esp0;
uint32_t ss0;
uint32_t esp1;
uint32_t ss1;
uint32_t esp2;
uint32_t ss2;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint32_t es;
uint32_t cs;
uint32_t ss;
uint32_t ds;
uint32_t fs;
uint32_t gs;
uint32_t ldt;
uint16_t trap_bit;
#endif /* __LP64__ */
uint16_t iobp_base;
} __packed;
#define CPU_VENDOR_STR_SIZE 13
#define CPU_MODEL_NAME_SIZE 49
#define CPU_VENDOR_UNKNOWN 0
#define CPU_VENDOR_INTEL 1
#define CPU_VENDOR_AMD 2
/*
* CPU states.
*/
#define CPU_STATE_OFF 0
#define CPU_STATE_ON 1
struct cpu {
unsigned int id;
unsigned int apic_id;
char vendor_str[CPU_VENDOR_STR_SIZE];
char model_name[CPU_MODEL_NAME_SIZE];
unsigned int cpuid_max_basic;
unsigned int cpuid_max_extended;
unsigned int vendor_id;
unsigned int type;
unsigned int family;
unsigned int model;
unsigned int stepping;
unsigned int clflush_size;
unsigned int initial_apic_id;
unsigned int features1;
unsigned int features2;
unsigned int features3;
unsigned int features4;
unsigned short phys_addr_width;
unsigned short virt_addr_width;
alignas(8) char gdt[CPU_GDT_SIZE];
struct cpu_tss tss;
#ifndef __LP64__
struct cpu_tss double_fault_tss;
#endif /* __LP64__ */
volatile int state;
void *boot_stack;
void *double_fault_stack;
};
struct cpu_tls_seg {
uintptr_t unused[SSP_WORD_TLS_OFFSET];
uintptr_t ssp_guard_word;
};
/*
* Macro to create functions that read/write control registers.
*/
#define CPU_DECL_GETSET_CR(name) \
static __always_inline unsigned long \
cpu_get_ ## name(void) \
{ \
unsigned long name; \
\
asm volatile("mov %%" __QUOTE(name) ", %0" : "=r" (name) : : "memory"); \
return name; \
} \
\
static __always_inline void \
cpu_set_ ## name(unsigned long value) \
{ \
asm volatile("mov %0, %%" __QUOTE(name) : : "r" (value) : "memory"); \
}
/*
* Access to the processor control registers. CR1 is reserved.
*
* The caller should assume that these functions are declared as :
* static inline unsigned long cpu_get_crX(void);
* static inline void cpu_set_crX(unsigned long);
*
* They all imply a compiler barrier.
*/
CPU_DECL_GETSET_CR(cr0)
CPU_DECL_GETSET_CR(cr2)
CPU_DECL_GETSET_CR(cr3)
CPU_DECL_GETSET_CR(cr4)
/*
* Return the content of the EFLAGS register.
*
* Implies a compiler barrier.
*/
static __always_inline unsigned long
cpu_get_eflags(void)
{
unsigned long eflags;
asm volatile("pushf\n"
"pop %0\n"
: "=r" (eflags)
: : "memory");
return eflags;
}
/*
* Enable local interrupts.
*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_intr_enable(void)
{
asm volatile("sti" : : : "memory");
}
/*
* Disable local interrupts.
*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_intr_disable(void)
{
asm volatile("cli" : : : "memory");
}
/*
* Restore the content of the EFLAGS register, possibly enabling interrupts.
*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_intr_restore(unsigned long flags)
{
asm volatile("push %0\n"
"popf\n"
: : "r" (flags)
: "memory");
}
/*
* Disable local interrupts, returning the previous content of the EFLAGS
* register.
*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_intr_save(unsigned long *flags)
{
*flags = cpu_get_eflags();
cpu_intr_disable();
}
/*
* Return true if interrupts are enabled.
*
* Implies a compiler barrier.
*/
static __always_inline int
cpu_intr_enabled(void)
{
unsigned long eflags;
eflags = cpu_get_eflags();
return (eflags & CPU_EFL_IF) ? 1 : 0;
}
/*
* Spin-wait loop hint.
*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_pause(void)
{
asm volatile("pause" : : : "memory");
}
/*
* Make the CPU idle until the next interrupt.
*
* Interrupts are enabled. Besides, they're enabled in a way that doesn't
* allow the processor handling them before entering the idle state if they
* were disabled before calling this function.
*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_idle(void)
{
asm volatile("sti; hlt" : : : "memory");
}
/*
* Halt the CPU.
*
* Implies a compiler barrier.
*/
noreturn static __always_inline void
cpu_halt(void)
{
cpu_intr_disable();
for (;;) {
asm volatile("hlt" : : : "memory");
}
}
/*
* Halt all other processors.
*
* Interrupts must be disabled when calling this function.
*/
void cpu_halt_broadcast(void);
/*
* Interrupt handler for inter-processor halt requests.
*/
void cpu_halt_intr(struct trap_frame *frame);
/*
* This percpu variable contains the address of the percpu area for the local
* processor. This is normally the same value stored in the percpu module, but
* it can be directly accessed through a segment register.
*/
extern void *cpu_local_area;
#define cpu_local_ptr(var) \
MACRO_BEGIN \
typeof(var) *ptr_ = &(var); \
\
asm("add %%fs:%1, %0" \
: "+r" (ptr_) \
: "m" (cpu_local_area)); \
\
ptr_; \
MACRO_END
#define cpu_local_var(var) (*cpu_local_ptr(var))
/* Interrupt-safe percpu accessors for basic types */
#define cpu_local_assign(var, val) \
asm("mov %0, %%fs:%1" \
: : "r" (val), "m" (var));
#define cpu_local_read(var) \
MACRO_BEGIN \
typeof(var) val_; \
\
asm("mov %%fs:%1, %0" \
: "=r" (val_) \
: "m" (var)); \
\
val_; \
MACRO_END
static inline struct cpu *
cpu_current(void)
{
extern struct cpu cpu_desc;
return cpu_local_ptr(cpu_desc);
}
static inline unsigned int
cpu_id(void)
{
extern struct cpu cpu_desc;
return cpu_local_read(cpu_desc.id);
}
static inline unsigned int
cpu_count(void)
{
extern unsigned int cpu_nr_active;
return cpu_nr_active;
}
static inline struct cpu *
cpu_from_id(unsigned int cpu)
{
extern struct cpu cpu_desc;
return percpu_ptr(cpu_desc, cpu);
}
static __always_inline void
cpu_enable_pse(void)
{
cpu_set_cr4(cpu_get_cr4() | CPU_CR4_PSE);
}
static __always_inline void
cpu_enable_pae(void)
{
cpu_set_cr4(cpu_get_cr4() | CPU_CR4_PAE);
}
static inline int
cpu_has_global_pages(void)
{
return cpu_current()->features2 & CPU_FEATURE2_PGE;
}
/*
* Enable the use of global pages in the TLB.
*
* As a side effect, this function causes a complete TLB flush if global
* pages were previously disabled.
*
* Implies a full memory barrier.
*/
static __always_inline void
cpu_enable_global_pages(void)
{
cpu_set_cr4(cpu_get_cr4() | CPU_CR4_PGE);
}
/*
* CPUID instruction wrapper.
*
* The CPUID instruction is a serializing instruction, implying a full
* memory barrier.
*/
static __always_inline void
cpu_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
unsigned int *edx)
{
asm volatile("cpuid" : "+a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: : "memory");
}
/*
* Implies a compiler barrier.
*/
static __always_inline void
cpu_get_msr(uint32_t msr, uint32_t *high, uint32_t *low)
{
asm volatile("rdmsr" : "=a" (*low), "=d" (*high) : "c" (msr) : "memory");
}
/*
* Implies a compiler barrier.
*/
static __always_inline uint64_t
cpu_get_msr64(uint32_t msr)
{
uint32_t high, low;
cpu_get_msr(msr, &high, &low);
return (((uint64_t)high << 32) | low);
}
/*
* Implies a full memory barrier.
*/
static inline void
cpu_set_msr(uint32_t msr, uint32_t high, uint32_t low)
{
asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high) : "memory");
}
/*
* Implies a full memory barrier.
*/
static inline void
cpu_set_msr64(uint32_t msr, uint64_t value)
{
uint32_t low, high;
low = value & 0xffffffff;
high = value >> 32;
cpu_set_msr(msr, high, low);
}
static __always_inline uint64_t
cpu_get_tsc(void)
{
uint32_t high, low;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return ((uint64_t)high << 32) | low;
}
/*
* Flush non-global TLB entries.
*
* Implies a full memory barrier.
*/
static __always_inline void
cpu_tlb_flush(void)
{
cpu_set_cr3(cpu_get_cr3());
}
/*
* Flush all TLB entries, including global ones.
*
* Implies a full memory barrier.
*/
static __always_inline void
cpu_tlb_flush_all(void)
{
if (!cpu_has_global_pages()) {
cpu_tlb_flush();
} else {
unsigned long cr4;
cr4 = cpu_get_cr4();
if (!(cr4 & CPU_CR4_PGE)) {
cpu_tlb_flush();
} else {
cr4 &= ~CPU_CR4_PGE;
cpu_set_cr4(cr4);
cr4 |= CPU_CR4_PGE;
cpu_set_cr4(cr4);
}
}
}
/*
* Flush a single page table entry in the TLB.
*
* Implies a full memory barrier.
*/
static __always_inline void
cpu_tlb_flush_va(unsigned long va)
{
asm volatile("invlpg (%0)" : : "r" (va) : "memory");
}
/*
* Get cpu frequency in Hz.
*/
uint64_t cpu_get_freq(void);
/*
* Busy-wait for a given amount of time, in microseconds.
*/
void cpu_delay(unsigned long usecs);
/*
* Return the address of the boot stack allocated for the current processor.
*/
void * cpu_get_boot_stack(void);
/*
* Install an interrupt handler in the IDT.
*
* These functions may be called before the cpu module is initialized.
*/
void cpu_idt_set_gate(unsigned int vector, void (*isr)(void));
void cpu_idt_set_double_fault(void (*isr)(void));
/*
* Log processor information.
*/
void cpu_log_info(const struct cpu *cpu);
/*
* Register the presence of a local APIC.
*/
void cpu_mp_register_lapic(unsigned int apic_id, int is_bsp);
/*
* Start application processors.
*
* The x86 architecture uses per-CPU page tables, which are created as a
* side effect of this function. In order to synchronize their page tables,
* processors must be able to communicate very soon after calling this
* function. They communicate through interrupts and threading facilities.
* On return, physical mappings must not be altered until inter-processor
* communication is available.
*/
void cpu_mp_setup(void);
/*
* CPU initialization on APs.
*/
void cpu_ap_setup(void);
static inline unsigned int
cpu_apic_id(unsigned int cpu)
{
return cpu_from_id(cpu)->apic_id;
}
/*
* Send a cross-call interrupt to a remote processor.
*/
static inline void
cpu_send_xcall(unsigned int cpu)
{
lapic_ipi_send(cpu_apic_id(cpu), TRAP_XCALL);
}
/*
* Interrupt handler for cross-calls.
*/
void cpu_xcall_intr(struct trap_frame *frame);
/*
* Send a scheduling interrupt to a remote processor.
*/
static inline void
cpu_send_thread_schedule(unsigned int cpu)
{
lapic_ipi_send(cpu_apic_id(cpu), TRAP_THREAD_SCHEDULE);
}
/*
* Interrupt handler for scheduling requests.
*/
void cpu_thread_schedule_intr(struct trap_frame *frame);
/*
* This init operation provides :
* - initialization of the BSP structure.
* - cpu_delay()
* - cpu_local_ptr() and cpu_local_var()
*/
INIT_OP_DECLARE(cpu_setup);
/*
* This init operation provides :
* - cpu_count()
* - access to percpu variables on all processors
*/
INIT_OP_DECLARE(cpu_mp_probe);
/*
* This init operation provides :
* - cpu shutdown operations registered
*/
INIT_OP_DECLARE(cpu_setup_shutdown);
#endif /* __ASSEMBLER__ */
#endif /* X86_CPU_H */