664 lines
25 KiB
C
664 lines
25 KiB
C
#ifndef _E2K_MACHDEP_H_
|
|
#define _E2K_MACHDEP_H_
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <uapi/asm/iset_ver.h>
|
|
|
|
#include <asm/aau_regs_types.h>
|
|
#include <asm/cpu_features.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/mmu_types.h>
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
struct cpuinfo_e2k;
|
|
struct pt_regs;
|
|
struct seq_file;
|
|
struct global_regs;
|
|
struct kernel_gregs;
|
|
struct local_gregs;
|
|
struct e2k_aau_context;
|
|
struct e2k_mlt;
|
|
struct kvm_vcpu_arch;
|
|
struct thread_info;
|
|
union e2k_dimtp;
|
|
|
|
#include <asm/kvm/machdep.h> /* virtualization support */
|
|
|
|
typedef void (*restore_gregs_fn_t)(const struct global_regs *);
|
|
typedef void (*save_gregs_fn_t)(struct global_regs *);
|
|
typedef struct machdep {
|
|
int native_id; /* machine Id */
|
|
int native_rev; /* cpu revision */
|
|
e2k_iset_ver_t native_iset_ver; /* Instruction set version */
|
|
bool cmdline_iset_ver; /* iset specified in cmdline */
|
|
bool mmu_pt_v6; /* MMU is setting up to use */
|
|
/* new page table structures */
|
|
bool mmu_separate_pt; /* MMU was set to use */
|
|
/* separate PTs for kernel */
|
|
/* and users */
|
|
bool L3_enable; /* cache L3 is enable */
|
|
bool gmi; /* is hardware virtualized */
|
|
/* guest VM */
|
|
e2k_addr_t x86_io_area_base;
|
|
e2k_addr_t x86_io_area_size;
|
|
u8 max_nr_node_cpus;
|
|
u8 nr_node_cpus;
|
|
u8 node_iolinks;
|
|
e2k_addr_t pcicfg_area_phys_base;
|
|
e2k_size_t pcicfg_area_size;
|
|
e2k_addr_t nsr_area_phys_base;
|
|
u64 tlb_addr_line_num;
|
|
u64 tlb_addr_line_num2;
|
|
u8 tlb_addr_line_num_shift2;
|
|
u8 tlb_addr_set_num;
|
|
u8 tlb_addr_set_num_shift;
|
|
e2k_size_t sic_mc_size;
|
|
u8 sic_mc_count;
|
|
u32 sic_mc1_ecc;
|
|
u32 sic_io_str1;
|
|
|
|
unsigned long cpu_features[(NR_CPU_FEATURES + 63) / 64];
|
|
|
|
e2k_addr_t (*get_nsr_area_phys_base)(void);
|
|
void (*setup_apic_vector_handlers)(void);
|
|
#ifdef CONFIG_SMP
|
|
void (*clk_off)(void);
|
|
void (*clk_on)(int);
|
|
#endif
|
|
void (*C1_enter)(void);
|
|
void (*C3_enter)(void);
|
|
|
|
/* Often used pointers are placed close to each other */
|
|
|
|
void (*save_kernel_gregs)(struct kernel_gregs *);
|
|
void (*save_gregs)(struct global_regs *);
|
|
void (*save_local_gregs)(struct local_gregs *, bool is_signal);
|
|
save_gregs_fn_t save_gregs_dirty_bgr;
|
|
restore_gregs_fn_t restore_gregs;
|
|
void (*save_gregs_on_mask)(struct global_regs *, bool dirty_bgr,
|
|
unsigned long not_save_gregs_mask);
|
|
void (*restore_local_gregs)(const struct local_gregs *, bool is_signal);
|
|
void (*restore_gregs_on_mask)(struct global_regs *, bool dirty_bgr,
|
|
unsigned long not_restore_gregs_mask);
|
|
|
|
void (*save_dimtp)(union e2k_dimtp *);
|
|
void (*restore_dimtp)(const union e2k_dimtp *);
|
|
void (*clear_dimtp)(void);
|
|
|
|
void (*save_kvm_context)(struct kvm_vcpu_arch *);
|
|
void (*restore_kvm_context)(const struct kvm_vcpu_arch *);
|
|
|
|
void (*calculate_aau_aaldis_aaldas)(const struct pt_regs *regs,
|
|
e2k_aalda_t *aaldas, struct e2k_aau_context *context);
|
|
void (*do_aau_fault)(int aa_field, struct pt_regs *regs);
|
|
void (*save_aaldi)(u64 *aaldis);
|
|
void (*get_aau_context)(struct e2k_aau_context *, e2k_aasr_t);
|
|
|
|
unsigned long (*rrd)(int reg);
|
|
void (*rwd)(int reg, unsigned long value);
|
|
unsigned long (*boot_rrd)(int reg);
|
|
void (*boot_rwd)(int reg, unsigned long value);
|
|
|
|
u64 (*get_cu_hw1)(void);
|
|
void (*set_cu_hw1)(u64);
|
|
|
|
#ifdef CONFIG_MLT_STORAGE
|
|
void (*invalidate_MLT)(void);
|
|
void (*get_and_invalidate_MLT_context)(struct e2k_mlt *mlt_state);
|
|
#endif
|
|
|
|
void (*setup_arch)(void);
|
|
void (*setup_cpu_info)(struct cpuinfo_e2k *c);
|
|
int (*show_cpuinfo)(struct seq_file *m, void *v);
|
|
void (*init_IRQ)(void);
|
|
|
|
int (*set_wallclock)(unsigned long nowtime);
|
|
unsigned long (*get_wallclock)(void);
|
|
|
|
void (*restart)(char *cmd);
|
|
void (*power_off)(void);
|
|
void (*halt)(void);
|
|
void (*arch_reset)(char *cmd);
|
|
void (*arch_halt)(void);
|
|
|
|
int (*get_irq_vector)(void);
|
|
|
|
/* virtualization support: guest kernel and host/hypervisor */
|
|
host_machdep_t host; /* host additional fields (used only by */
|
|
/* host at arch/e2k/kvm/xxx) */
|
|
guest_machdep_t guest; /* guest additional fields (used only by */
|
|
/* guest at arch/e2k/kvm/guest/xxx) */
|
|
} machdep_t;
|
|
|
|
|
|
/*
|
|
* When executing on pure guest kernel, guest_cpu will be set to
|
|
* 'machine.guest.id', i.e. to what hardware guest *thinks* it's
|
|
* being executed on.
|
|
*/
|
|
typedef void (*cpuhas_initcall_t)(int cpu, int revision, int iset_ver,
|
|
int guest_cpu, bool is_hardware_guest, struct machdep *machine);
|
|
extern cpuhas_initcall_t __cpuhas_initcalls[], __cpuhas_initcalls_end[];
|
|
|
|
/*
|
|
* feature =
|
|
* if ('is_static')
|
|
* 'static_cond' checked at build time;
|
|
* else
|
|
* 'dynamic_cond' checked in runtime;
|
|
*/
|
|
#ifndef BUILD_CPUHAS_INITIALIZERS
|
|
# define CPUHAS(feat, is_static, static_cond, dynamic_cond) \
|
|
static const char feat##_is_static = !!(is_static); \
|
|
static const char feat##_is_set_statically = !!(static_cond);
|
|
|
|
#else /* #ifdef BUILD_CPUHAS_INITIALIZERS */
|
|
# include <linux/bitops.h>
|
|
# define CPUHAS(feat, _is_static, static_cond, dynamic_cond) \
|
|
__init \
|
|
static void feat##_initializer(const int cpu, const int revision, \
|
|
const int iset_ver, const int guest_cpu, \
|
|
bool is_hardware_guest, struct machdep *const machine) { \
|
|
bool is_static = (_is_static); \
|
|
if (is_static && (static_cond) || !is_static && (dynamic_cond)) \
|
|
set_bit(feat, (machine)->cpu_features); \
|
|
} \
|
|
static cpuhas_initcall_t __cpuhas_initcall_##feat __used \
|
|
__section(".cpuhas_initcall") = &feat##_initializer;
|
|
#endif
|
|
|
|
|
|
/* Most of these bugs are not emulated on simulator but
|
|
* set them anyway to make kernel running on a simulator
|
|
* behave in the same way as on real hardware. */
|
|
|
|
/* #58397, #76626 - CLW does not work.
|
|
* Workaround - do not use it. */
|
|
CPUHAS(CPU_HWBUG_CLW,
|
|
!IS_ENABLED(CONFIG_CPU_E2S),
|
|
false,
|
|
cpu == IDR_E2S_MDL && revision == 0);
|
|
/* #78411 - Sometimes exc_illegal_instr_addr is generated
|
|
* instead of exc_instr_page_miss.
|
|
* Workaround - always return to user from exc_illegal_instr_addr. */
|
|
CPUHAS(CPU_HWBUG_SPURIOUS_EXC_ILL_INSTR_ADDR,
|
|
!IS_ENABLED(CONFIG_CPU_E2S),
|
|
false,
|
|
cpu == IDR_E2S_MDL && revision <= 1);
|
|
/* #100984 - e8c: DMA to neighbour node slows down.
|
|
* #136177 - no DMA through the links B and C.
|
|
* Workaround - allocate DMA buffers only in the device node. */
|
|
CPUHAS(CPU_HWBUG_CANNOT_DO_DMA_IN_NEIGHBOUR_NODE,
|
|
!IS_ENABLED(CONFIG_CPU_E8C) && !IS_ENABLED(CONFIG_CPU_E16C),
|
|
false,
|
|
cpu == IDR_E8C_MDL && revision <= 2 ||
|
|
cpu == IDR_E16C_MDL && revision == 0);
|
|
/* #88644 - data profiling events are lost if overflow happens
|
|
* under closed NM interrupts; also DDMCR writing does not clear
|
|
* pending exc_data_debug exceptions.
|
|
* Workaround - disable data monitor profiling in kernel. */
|
|
CPUHAS(CPU_HWBUG_KERNEL_DATA_MONITOR,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL);
|
|
/* #89495 - write barrier does not work (even for atomics).
|
|
* Workaround - special command sequence after every read-acquire. */
|
|
CPUHAS(CPU_HWBUG_WRITE_MEMORY_BARRIER,
|
|
!IS_ENABLED(CONFIG_CPU_E8C),
|
|
false,
|
|
cpu == IDR_E8C_MDL && revision <= 1);
|
|
/* #89653 - some hw counter won't reset, which may cause corruption of DMA.
|
|
* Workaround - reset machine until the counter sets in good value */
|
|
CPUHAS(CPU_HWBUG_BAD_RESET,
|
|
!IS_ENABLED(CONFIG_CPU_E8C),
|
|
false,
|
|
cpu == IDR_E8C_MDL && revision <= 1);
|
|
/* #90514 - hardware hangs after modifying code with a breakpoint.
|
|
* Workaround - use HS.lng from the instruction being replaced. */
|
|
CPUHAS(CPU_HWBUG_BREAKPOINT_INSTR,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E8C2_MDL);
|
|
/* #92834, #96516 - hang because of hardware problems.
|
|
* Workaround - boot activates watchdog, kernel should disable it */
|
|
CPUHAS(CPU_HWBUG_E8C_WATCHDOG,
|
|
!IS_ENABLED(CONFIG_CPU_E8C),
|
|
false,
|
|
cpu == IDR_E8C_MDL && revision <= 1);
|
|
/* #94466 */
|
|
CPUHAS(CPU_HWBUG_IOMMU,
|
|
!IS_ENABLED(CONFIG_CPU_E2S),
|
|
false,
|
|
cpu == IDR_E2S_MDL && revision <= 2);
|
|
/* #95860 - WC memory conflicts with DAM.
|
|
* Workaround - "wait st_c" between WC writes and cacheable loads */
|
|
CPUHAS(CPU_HWBUG_WC_DAM,
|
|
!IS_ENABLED(CONFIG_CPU_E2S) && !IS_ENABLED(CONFIG_CPU_E8C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E8C2),
|
|
false,
|
|
cpu == IDR_E2S_MDL && revision <= 2 ||
|
|
cpu == IDR_E8C_MDL && revision <= 1 ||
|
|
cpu == IDR_E8C2_MDL && revision == 0);
|
|
/* 96719 - combination of flags s_f=0, store=1, sru=1 is possible
|
|
* Workaround - treat it as s_f=1, store=1, sru=1 */
|
|
CPUHAS(CPU_HWBUG_TRAP_CELLAR_S_F,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E8C2),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL && revision == 0);
|
|
/* #97594 - %cr1_lo.ss flag is lost if ext. interrupt arrives faster.
|
|
* Workaround - manually set %cr1_lo.ss again in interrupt handler */
|
|
CPUHAS(CPU_HWBUG_SS,
|
|
!IS_ENABLED(CONFIG_CPU_E2S) && !IS_ENABLED(CONFIG_CPU_E8C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E1CP) && !IS_ENABLED(CONFIG_CPU_E8C2),
|
|
false,
|
|
cpu == IDR_E2S_MDL && revision <= 2 ||
|
|
cpu == IDR_E8C_MDL && revision <= 2 ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL && revision == 0);
|
|
/* #99302 - %aaldv sometimes is not restored properly.
|
|
* Workaround - insert 'wait ma_c' barrier */
|
|
CPUHAS(CPU_HWBUG_AAU_AALDV,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E8C2),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL && revision == 0);
|
|
/* #103223 - LAPIC does not send EoI to IO_APIC for level interrupts.
|
|
* Workaround - wait under closed interrupts until APIC_ISR clears */
|
|
CPUHAS(CPU_HWBUG_LEVEL_EOI,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP) || IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL);
|
|
/* #104865 - hardware might generate a false single step interrupt
|
|
* Workaround - clean frame 0 of PCS during the allocation */
|
|
CPUHAS(CPU_HWBUG_FALSE_SS,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E2S) &&
|
|
!IS_ENABLED(CONFIG_CPU_E8C),
|
|
IS_ENABLED(CONFIG_CPU_E1CP) || IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E2S_MDL && revision <= 2 ||
|
|
cpu == IDR_E8C_MDL && revision <= 2 ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL);
|
|
/* #117649 - false exc_data_debug are generated based on _previous_
|
|
* values in ld/st address registers.
|
|
* Workaround - forbid data breakpoint on the first 31 bytes
|
|
* (hardware prefetch works with 32 bytes blocks). */
|
|
CPUHAS(CPU_HWBUG_SPURIOUS_EXC_DATA_DEBUG,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E16C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E2C3),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP) || IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL ||
|
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0);
|
|
/* #119084 - several TBL flushes in a row might fail to flush L1D.
|
|
* Workaround - insert "wait fl_c" immediately after every TLB flush */
|
|
CPUHAS(CPU_HWBUG_TLB_FLUSH_L1D,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E8C2_MDL);
|
|
/* #121311 - asynchronous entries in INTC_INFO_MU always have "pm" bit set.
|
|
* Workaround - use "pm" bit saved in guest's chain stack. */
|
|
CPUHAS(CPU_HWBUG_GUEST_ASYNC_PM,
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E2C3),
|
|
false,
|
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0);
|
|
/* #122946 - conflict new interrupt while sync signal turning off.
|
|
* Workaround - wating for C0 after "wait int=1" */
|
|
CPUHAS(CPU_HWBUG_E16C_SLEEP,
|
|
!IS_ENABLED(CONFIG_CPU_E16C),
|
|
false,
|
|
cpu == IDR_E16C_MDL && revision == 0);
|
|
/* #124206 - instruction buffer stops working
|
|
* Workaround - prepare %ctpr's in glaunch/trap handler entry;
|
|
* avoid rbranch in glaunch/trap handler entry and exit. */
|
|
CPUHAS(CPU_HWBUG_L1I_STOPS_WORKING,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E16C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E2C3),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP) || IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL ||
|
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0);
|
|
/* #124947 - CLW clearing by OS must be done on the same CPU that started the
|
|
* hardware clearing operation to avoid creating a stale L1 entry.
|
|
* Workaround - forbid migration until CLW clearing is finished in software. */
|
|
CPUHAS(CPU_HWBUG_CLW_STALE_L1_ENTRY,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E16C),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL || cpu == IDR_E8C2_MDL ||
|
|
cpu == IDR_E16C_MDL && revision == 0);
|
|
/* #125405 - CPU pipeline freeze feature conflicts with performance monitoring.
|
|
* Workaround - disable pipeline freeze when monitoring is enabled.
|
|
*
|
|
* Note (#132311): disable workaround on e16c.rev0/e2c3.rev0 since it conflicts
|
|
* with #134929 workaround. */
|
|
CPUHAS(CPU_HWBUG_PIPELINE_FREEZE_MONITORS,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) && !IS_ENABLED(CONFIG_CPU_E16C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E2C3),
|
|
IS_ENABLED(CONFIG_CPU_E8C2) || IS_ENABLED(CONFIG_CPU_E12C),
|
|
cpu == IDR_E8C2_MDL || cpu == IDR_E12C_MDL ||
|
|
cpu == IDR_E16C_MDL && revision > 0 ||
|
|
cpu == IDR_E2C3_MDL && revision > 0);
|
|
/* #126587 - "wait ma_c=1" does not wait for all L2$ writebacks to complete
|
|
* when disabling CPU core with "wait trap=1" algorithm.
|
|
* Workaround - manually insert 66 NOPs before "wait trap=1" */
|
|
CPUHAS(CPU_HWBUG_C3_WAIT_MA_C,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL || cpu == IDR_E1CP_MDL);
|
|
/* #128127 - Intercepting SCLKM3 write does not prevent guest from writing it.
|
|
* Workaround - Update SH_SCLKM3 in intercept handler */
|
|
CPUHAS(CPU_HWBUG_VIRT_SCLKM3_INTC,
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E2C3),
|
|
false,
|
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0);
|
|
/* #128350 - glaunch increases guest's pusd.psl by 1 on phase 1
|
|
* Workaround - decrease guest's pusd.psl by 1 before glaunch */
|
|
CPUHAS(CPU_HWBUG_VIRT_PUSD_PSL,
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E2C3),
|
|
false,
|
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0);
|
|
/* #129848 - alignment of usd_hi write depends on current usd_lo.p
|
|
* Workaround - write usd_lo before usd_hi, while keeping 2 tact distance from sbr write.
|
|
* Valid sequences are: sbr, nop, usd.lo, usd.hi OR sbr, usd.lo, usd.hi, usd.lo */
|
|
CPUHAS(CPU_HWBUG_USD_ALIGNMENT,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E2S) || IS_ENABLED(CONFIG_CPU_E8C) ||
|
|
IS_ENABLED(CONFIG_CPU_E1CP) || IS_ENABLED(CONFIG_CPU_E8C2) ||
|
|
IS_ENABLED(CONFIG_CPU_E16C) || IS_ENABLED(CONFIG_CPU_E2C3) ||
|
|
IS_ENABLED(CONFIG_CPU_E12C),
|
|
cpu == IDR_E2S_MDL || cpu == IDR_E8C_MDL ||
|
|
cpu == IDR_E1CP_MDL || cpu == IDR_E8C2_MDL ||
|
|
cpu == IDR_E16C_MDL || cpu == IDR_E2C3_MDL ||
|
|
cpu == IDR_E12C_MDL);
|
|
/* #130039 - intercepting some specific sequences of call/return/setwd
|
|
* (that change WD.psize in a specific way) does not work.
|
|
* Workaround - avoid those sequences. */
|
|
CPUHAS(CPU_HWBUG_VIRT_PSIZE_INTERCEPTION,
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E2C3),
|
|
false,
|
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0);
|
|
/* #130066, #134351 - L1/L2 do not respect "lal"/"las"/"sas"/"st_rel" barriers.
|
|
* Workaround - do not use "las"/"sas"/"st_rel", and add 5 nops after "lal".
|
|
* #133605 - "lal"/"las"/"sas"/"sal" barriers do not work in certain conditions.
|
|
* Workaround - add {nop} before them.
|
|
*
|
|
* Note that #133605 workaround is split into two parts:
|
|
* CPU_NO_HWBUG_SOFT_WAIT - for e16c/e2c3
|
|
* CPU_HWBUG_SOFT_WAIT_E8C2 - for e8c2
|
|
* This is done because it is very convenient to merge #130066, #134351
|
|
* and #133605 bugs workarounds together for e16c/e2c3. */
|
|
CPUHAS(CPU_NO_HWBUG_SOFT_WAIT,
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E2C3),
|
|
true,
|
|
!(cpu == IDR_E16C_MDL && revision == 0 ||
|
|
cpu == IDR_E2C3_MDL && revision == 0));
|
|
CPUHAS(CPU_HWBUG_SOFT_WAIT_E8C2,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
IS_ENABLED(CONFIG_CPU_E8C2),
|
|
cpu == IDR_E8C2_MDL);
|
|
/* #132693 - C3 idle state does not work.
|
|
* Workaround - do not use it. */
|
|
CPUHAS(CPU_HWBUG_C3,
|
|
!IS_ENABLED(CONFIG_CPU_E16C),
|
|
false,
|
|
cpu == IDR_E16C_MDL && revision == 0);
|
|
|
|
/*
|
|
* Not bugs but features go here
|
|
*/
|
|
|
|
/* Rely on IDR instead of iset version to choose between APIC and EPIC.
|
|
* For guest we use it's own fake IDR so that we choose between APIC and
|
|
* EPIC based on what hardware guest *thinks* it's being executed on. */
|
|
CPUHAS(CPU_FEAT_EPIC,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE) &&
|
|
!IS_ENABLED(CONFIG_KVM_GUEST_KERNEL),
|
|
!IS_ENABLED(CONFIG_CPU_E2S) && !IS_ENABLED(CONFIG_CPU_E8C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E1CP) && !IS_ENABLED(CONFIG_CPU_E8C2),
|
|
guest_cpu != IDR_E2S_MDL && guest_cpu != IDR_E8C_MDL &&
|
|
guest_cpu != IDR_E1CP_MDL && guest_cpu != IDR_E8C2_MDL);
|
|
/* Shows which user registers must be saved upon trap entry/exit */
|
|
CPUHAS(CPU_FEAT_TRAP_V5,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET == 5,
|
|
iset_ver == E2K_ISET_V5);
|
|
CPUHAS(CPU_FEAT_TRAP_V6,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET >= 6,
|
|
iset_ver >= E2K_ISET_V6);
|
|
/* QP registers: only since iset V5 */
|
|
CPUHAS(CPU_FEAT_QPREG,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET >= 5,
|
|
iset_ver >= E2K_ISET_V5);
|
|
/* Hardware prefetcher that resides in L2 and works on phys. addresses */
|
|
CPUHAS(CPU_FEAT_HW_PREFETCHER,
|
|
IS_ENABLED(CONFIG_E2K_MACHINE),
|
|
!IS_ENABLED(CONFIG_CPU_E2S) && !IS_ENABLED(CONFIG_CPU_E8C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E1CP) && !IS_ENABLED(CONFIG_CPU_E8C2) &&
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E2C3),
|
|
cpu != IDR_E2S_MDL && cpu != IDR_E8C_MDL &&
|
|
cpu != IDR_E1CP_MDL && cpu != IDR_E8C2_MDL &&
|
|
cpu != IDR_E16C_MDL && cpu != IDR_E2C3_MDL);
|
|
/* When flushing high order page table entries we must also flush
|
|
* all links below it. E.g. when flushing PMD also flush PMD->PTE
|
|
* link (i.e. DTLB entry for address 0xff8000000000|(address >> 9)).
|
|
*
|
|
* Otherwise the following can happen:
|
|
* 1) High-order page is allocated.
|
|
* 2) Someone accesses the PMD->PTE link (e.g. half-spec. load) and
|
|
* creates invalid entry in DTLB.
|
|
* 3) High-order page is split into 4 Kb pages.
|
|
* 4) Someone accesses the PMD->PTE link address (e.g. DTLB entry
|
|
* probe) and reads the invalid entry created earlier.
|
|
*
|
|
* Since v6 we have separate TLBs for intermediate page table levels
|
|
* (TLU_CACHE.PWC) and for last level and invalid records (TLB).
|
|
* So the invalid entry created in 2) would go into TLB while access
|
|
* in 4) will search TLU_CACHE.PWC rendering this flush unneeded. */
|
|
CPUHAS(CPU_FEAT_SEPARATE_TLU_CACHE,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET >= 6,
|
|
iset_ver >= E2K_ISET_V6);
|
|
/* Set if FILLR instruction is supported.
|
|
*
|
|
* #135233 - FILLR does not work in hardware guests.
|
|
* Workaround - do not use it in hardware guests. */
|
|
CPUHAS(CPU_FEAT_FILLR,
|
|
!IS_ENABLED(CONFIG_CPU_E16C) && !IS_ENABLED(CONFIG_CPU_E12C) &&
|
|
!IS_ENABLED(CONFIG_CPU_E2C3),
|
|
CONFIG_CPU_ISET >= 6,
|
|
iset_ver >= E2K_ISET_V6 &&
|
|
!((cpu == IDR_E16C_MDL || cpu == IDR_E12C_MDL ||
|
|
cpu == IDR_E2C3_MDL) && is_hardware_guest));
|
|
/* Set if FILLC instruction is supported.
|
|
*
|
|
* #135233 - software emulation of FILLC does not work in hardware guests.
|
|
* Workaround - use FILLC in hardware guests. */
|
|
CPUHAS(CPU_FEAT_FILLC,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET >= 6,
|
|
iset_ver >= E2K_ISET_V6);
|
|
/* Optimized version of machine.iset check */
|
|
CPUHAS(CPU_FEAT_ISET_V5,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET >= 5,
|
|
iset_ver >= E2K_ISET_V5);
|
|
CPUHAS(CPU_FEAT_ISET_V6,
|
|
CONFIG_CPU_ISET != 0,
|
|
CONFIG_CPU_ISET >= 6,
|
|
iset_ver >= E2K_ISET_V6);
|
|
|
|
|
|
static inline unsigned long test_feature_dynamic(struct machdep *machine, int feature)
|
|
{
|
|
unsigned long *addr = machine->cpu_features;
|
|
|
|
return 1UL & (addr[feature / 64] >> (feature & 63));
|
|
}
|
|
|
|
#define test_feature(machine, feature) \
|
|
((feature##_is_static) ? \
|
|
(feature##_is_set_statically) : \
|
|
test_feature_dynamic(machine, feature))
|
|
|
|
#define boot_machine_has(machine_p, feature) \
|
|
test_feature(machine_p, feature)
|
|
#define boot_cpu_has(feature) boot_machine_has(&boot_machine, feature)
|
|
|
|
#ifdef CONFIG_BOOT_E2K
|
|
# define cpu_has(feature) test_feature(&machine, feature)
|
|
#elif defined(E2K_P2V)
|
|
# define cpu_has(feature) boot_cpu_has(feature)
|
|
#else
|
|
# define cpu_has(feature) test_feature(&machine, feature)
|
|
#endif
|
|
|
|
/* Normally cpu_has() is passed symbolic name of feature (e.g. CPU_FEAT_*),
|
|
* use this one instead if only numeric value of feature is known. */
|
|
#define cpu_has_by_value(feature) test_feature_dynamic(&machine, feature)
|
|
|
|
extern void cpu_set_feature(struct machdep *machine, int feature);
|
|
extern void cpu_clear_feature(struct machdep *machine, int feature);
|
|
|
|
|
|
extern __nodedata machdep_t machine;
|
|
extern __nodedata pt_struct_t pgtable_struct;
|
|
|
|
#define boot_machine (boot_get_vo_value(machine))
|
|
#define boot_pgtable_struct ((pt_struct_t)boot_get_vo_value(pgtable_struct))
|
|
#define boot_pgtable_struct_p boot_vp_to_pp(&pgtable_struct)
|
|
|
|
#if CONFIG_CPU_ISET >= 6
|
|
# define IS_CPU_ISET_V6() true
|
|
#elif CONFIG_CPU_ISET >= 1
|
|
# define IS_CPU_ISET_V6() false
|
|
#elif CONFIG_CPU_ISET == 0
|
|
# ifdef E2K_P2V
|
|
# define IS_CPU_ISET_V6() \
|
|
(boot_machine.native_iset_ver >= E2K_ISET_V6)
|
|
# else /* ! E2K_P2V */
|
|
# define IS_CPU_ISET_V6() \
|
|
(machine.native_iset_ver >= E2K_ISET_V6)
|
|
# endif /* E2K_P2V */
|
|
#else /* CONFIG_CPU_ISET undefined or negative */
|
|
# warning "Undefined CPU ISET VERSION #, IS_CPU_ISET_V6 is defined dinamicaly"
|
|
# ifdef E2K_P2V
|
|
# define IS_CPU_ISET_V6() \
|
|
(boot_machine.native_iset_ver >= E2K_ISET_V6)
|
|
# else /* ! E2K_P2V */
|
|
# define IS_CPU_ISET_V6() \
|
|
(machine.native_iset_ver >= E2K_ISET_V6)
|
|
# endif /* E2K_P2V */
|
|
#endif /* CONFIG_CPU_ISET 0-6 */
|
|
|
|
/* Returns true in guest running with hardware virtualization support */
|
|
#if CONFIG_CPU_ISET >= 3 && !defined E2K_P2V
|
|
# define IS_HV_GM() (cpu_has(CPU_FEAT_ISET_V6) && READ_CORE_MODE_REG().gmi)
|
|
#else
|
|
# define IS_HV_GM() (machine.gmi)
|
|
#endif
|
|
|
|
extern void save_kernel_gregs_v3(struct kernel_gregs *);
|
|
extern void save_kernel_gregs_v5(struct kernel_gregs *);
|
|
extern void save_gregs_v3(struct global_regs *);
|
|
extern void save_gregs_v5(struct global_regs *);
|
|
extern void save_local_gregs_v3(struct local_gregs *, bool is_signal);
|
|
extern void save_local_gregs_v5(struct local_gregs *, bool is_signal);
|
|
extern void save_gregs_dirty_bgr_v3(struct global_regs *);
|
|
extern void save_gregs_dirty_bgr_v5(struct global_regs *);
|
|
extern void save_gregs_on_mask_v3(struct global_regs *, bool dirty_bgr,
|
|
unsigned long mask_not_save);
|
|
extern void save_gregs_on_mask_v5(struct global_regs *, bool dirty_bgr,
|
|
unsigned long mask_not_save);
|
|
extern void restore_gregs_v3(const struct global_regs *);
|
|
extern void restore_gregs_v5(const struct global_regs *);
|
|
extern void restore_local_gregs_v3(const struct local_gregs *, bool is_signal);
|
|
extern void restore_local_gregs_v5(const struct local_gregs *, bool is_signal);
|
|
extern void restore_gregs_on_mask_v3(struct global_regs *, bool dirty_bgr,
|
|
unsigned long mask_not_restore);
|
|
extern void restore_gregs_on_mask_v5(struct global_regs *, bool dirty_bgr,
|
|
unsigned long mask_not_restore);
|
|
extern void save_dimtp_v6(union e2k_dimtp *);
|
|
extern void restore_dimtp_v6(const union e2k_dimtp *);
|
|
extern void clear_dimtp_v6(void);
|
|
extern void save_kvm_context_v6(struct kvm_vcpu_arch *);
|
|
extern void restore_kvm_context_v6(const struct kvm_vcpu_arch *);
|
|
extern void qpswitchd_sm(int);
|
|
|
|
extern void calculate_aau_aaldis_aaldas_v3(const struct pt_regs *regs,
|
|
e2k_aalda_t *aaldas, struct e2k_aau_context *context);
|
|
extern void calculate_aau_aaldis_aaldas_v5(const struct pt_regs *regs,
|
|
e2k_aalda_t *aaldas, struct e2k_aau_context *context);
|
|
extern void calculate_aau_aaldis_aaldas_v6(const struct pt_regs *regs,
|
|
e2k_aalda_t *aaldas, struct e2k_aau_context *context);
|
|
extern void do_aau_fault_v3(int aa_field, struct pt_regs *regs);
|
|
extern void do_aau_fault_v5(int aa_field, struct pt_regs *regs);
|
|
extern void do_aau_fault_v6(int aa_field, struct pt_regs *regs);
|
|
extern void save_aaldi_v3(u64 *aaldis);
|
|
extern void save_aaldi_v5(u64 *aaldis);
|
|
extern void get_aau_context_v3(struct e2k_aau_context *, e2k_aasr_t);
|
|
extern void get_aau_context_v5(struct e2k_aau_context *, e2k_aasr_t);
|
|
|
|
extern unsigned long boot_native_read_IDR_reg_value(void);
|
|
|
|
unsigned long rrd_v3(int);
|
|
unsigned long rrd_v3(int);
|
|
unsigned long rrd_v6(int);
|
|
void rwd_v3(int reg, unsigned long value);
|
|
void rwd_v6(int reg, unsigned long value);
|
|
unsigned long boot_rrd_v3(int);
|
|
unsigned long boot_rrd_v6(int);
|
|
void boot_rwd_v3(int reg, unsigned long value);
|
|
void boot_rwd_v6(int reg, unsigned long value);
|
|
|
|
/* Supported registers for machine->rrd()/rwd() */
|
|
enum {
|
|
E2K_REG_CORE_MODE,
|
|
E2K_REG_HCEM,
|
|
E2K_REG_HCEB,
|
|
E2K_REG_OSCUTD,
|
|
E2K_REG_OSCUIR,
|
|
};
|
|
|
|
u64 native_get_cu_hw1_v3(void);
|
|
u64 native_get_cu_hw1_v5(void);
|
|
void native_set_cu_hw1_v3(u64);
|
|
void native_set_cu_hw1_v5(u64);
|
|
|
|
void invalidate_MLT_v3(void);
|
|
void get_and_invalidate_MLT_context_v3(struct e2k_mlt *mlt_state);
|
|
void get_and_invalidate_MLT_context_v6(struct e2k_mlt *mlt_state);
|
|
|
|
#ifdef CONFIG_SMP
|
|
void native_clock_off_v3(void);
|
|
void native_clock_on_v3(int cpu);
|
|
#endif
|
|
|
|
void C1_enter_v3(void);
|
|
void C1_enter_v6(void);
|
|
void C3_enter_v3(void);
|
|
void C3_enter_v6(void);
|
|
#endif /* __KERNEL__ */
|
|
|
|
#endif /* _E2K_MACHDEP_H_ */
|