linux-headers-5.4.0-2.11
This commit is contained in:
parent
f99e7af53c
commit
f830966167
2
Makefile
2
Makefile
@ -2,7 +2,7 @@
|
|||||||
VERSION = 5
|
VERSION = 5
|
||||||
PATCHLEVEL = 4
|
PATCHLEVEL = 4
|
||||||
SUBLEVEL = 91
|
SUBLEVEL = 91
|
||||||
EXTRAVERSION = -2.9
|
EXTRAVERSION = -2.11
|
||||||
NAME = Kleptomaniac Octopus
|
NAME = Kleptomaniac Octopus
|
||||||
|
|
||||||
# *DOCUMENTATION*
|
# *DOCUMENTATION*
|
||||||
|
@ -77,6 +77,15 @@ extern void epic_send_IPI_mask_allbutself(const struct cpumask *mask,
|
|||||||
extern void epic_wait_icr_idle(void);
|
extern void epic_wait_icr_idle(void);
|
||||||
extern void clear_cepic(void);
|
extern void clear_cepic(void);
|
||||||
|
|
||||||
|
extern bool pcsm_adjust_enable;
|
||||||
|
|
||||||
|
struct pcs_handle {
|
||||||
|
void (*pcs_interrupt)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void register_pcs_handle(const struct pcs_handle *handle);
|
||||||
|
extern void unregister_pcs_handle(void);
|
||||||
|
|
||||||
extern __visible void epic_smp_timer_interrupt(struct pt_regs *regs);
|
extern __visible void epic_smp_timer_interrupt(struct pt_regs *regs);
|
||||||
extern __visible void epic_smp_spurious_interrupt(struct pt_regs *regs);
|
extern __visible void epic_smp_spurious_interrupt(struct pt_regs *regs);
|
||||||
extern __visible void epic_smp_error_interrupt(struct pt_regs *regs);
|
extern __visible void epic_smp_error_interrupt(struct pt_regs *regs);
|
||||||
|
852
arch/e2k/include/asm/copy-hw-stacks.h
Normal file
852
arch/e2k/include/asm/copy-hw-stacks.h
Normal file
@ -0,0 +1,852 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
|
/*
|
||||||
|
* include/asm-e2k/copy-hw-stacks.h
|
||||||
|
*
|
||||||
|
* Copyright 2021 mcst.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _E2K_COPY_HW_STACKS_H
|
||||||
|
#define _E2K_COPY_HW_STACKS_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <asm/mman.h>
|
||||||
|
#include <asm/pv_info.h>
|
||||||
|
#include <asm/process.h>
|
||||||
|
|
||||||
|
#include <asm/kvm/trace-hw-stacks.h>
|
||||||
|
|
||||||
|
#undef DEBUG_PV_UST_MODE
|
||||||
|
#undef DebugUST
|
||||||
|
#define DEBUG_PV_UST_MODE 0 /* guest user stacks debug */
|
||||||
|
#define DebugUST(fmt, args...) \
|
||||||
|
({ \
|
||||||
|
if (debug_guest_ust) \
|
||||||
|
pr_info("%s(): " fmt, __func__, ##args); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#undef DEBUG_PV_SYSCALL_MODE
|
||||||
|
#define DEBUG_PV_SYSCALL_MODE 0 /* syscall injection debugging */
|
||||||
|
|
||||||
|
#if DEBUG_PV_UST_MODE || DEBUG_PV_SYSCALL_MODE
|
||||||
|
extern bool debug_guest_ust;
|
||||||
|
#else
|
||||||
|
#define debug_guest_ust false
|
||||||
|
#endif /* DEBUG_PV_UST_MODE || DEBUG_PV_SYSCALL_MODE */
|
||||||
|
|
||||||
|
#ifndef CONFIG_VIRTUALIZATION
|
||||||
|
/* it native kernel without virtualization support */
|
||||||
|
#else /* CONFIG_VIRTUALIZATION */
|
||||||
|
/* It is native host kernel with virtualization support */
|
||||||
|
/* or paravirtualized host and guest */
|
||||||
|
/* or native guest kernel
|
||||||
|
#include <asm/kvm/process.h>
|
||||||
|
*/
|
||||||
|
#endif /* ! CONFIG_VIRTUALIZATION */
|
||||||
|
|
||||||
|
typedef void (*trace_ps_frame_func_t)(kernel_mem_ps_t *base, kernel_mem_ps_t *frame);
|
||||||
|
typedef void (*trace_pcs_frame_func_t)(e2k_mem_crs_t *base, e2k_mem_crs_t *frame);
|
||||||
|
|
||||||
|
static inline void trace_proc_stack_frames(kernel_mem_ps_t *dst_ps_base,
|
||||||
|
kernel_mem_ps_t *src_ps_base, u64 ps_size,
|
||||||
|
trace_ps_frame_func_t trace_func)
|
||||||
|
{
|
||||||
|
int qreg, qreg_num;
|
||||||
|
kernel_mem_ps_t *dst_ps_frame, *src_ps_frame;
|
||||||
|
kernel_mem_ps_t rw;
|
||||||
|
|
||||||
|
qreg_num = ps_size / EXT_4_NR_SZ;
|
||||||
|
for (qreg = qreg_num - 1; qreg >= 0; qreg--) {
|
||||||
|
dst_ps_frame = &dst_ps_base[qreg];
|
||||||
|
src_ps_frame = &src_ps_base[qreg];
|
||||||
|
rw.word_lo = src_ps_frame->word_lo;
|
||||||
|
if (machine.native_iset_ver < E2K_ISET_V5) {
|
||||||
|
rw.word_hi = src_ps_frame->word_hi;
|
||||||
|
rw.ext_lo = src_ps_frame->ext_lo;
|
||||||
|
rw.ext_hi = src_ps_frame->ext_hi;
|
||||||
|
} else {
|
||||||
|
rw.word_hi = src_ps_frame->ext_lo;
|
||||||
|
rw.ext_lo = src_ps_frame->word_hi;
|
||||||
|
rw.ext_hi = src_ps_frame->ext_hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_func(dst_ps_frame, &rw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void trace_chain_stack_frames(e2k_mem_crs_t *dst_pcs_base,
|
||||||
|
e2k_mem_crs_t *src_pcs_base, u64 pcs_size,
|
||||||
|
trace_pcs_frame_func_t trace_func)
|
||||||
|
{
|
||||||
|
int crs_no, crs_num;
|
||||||
|
e2k_mem_crs_t *dst_pcs_frame, *src_pcs_frame;
|
||||||
|
e2k_mem_crs_t crs;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
crs_num = pcs_size / sizeof(crs);
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
for (crs_no = crs_num - 1; crs_no >= 0; crs_no--) {
|
||||||
|
dst_pcs_frame = &dst_pcs_base[crs_no];
|
||||||
|
src_pcs_frame = &src_pcs_base[crs_no];
|
||||||
|
crs = *src_pcs_frame;
|
||||||
|
|
||||||
|
trace_func(dst_pcs_frame, &crs);
|
||||||
|
}
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void trace_host_hva_area(u64 *hva_base, u64 hva_size)
|
||||||
|
{
|
||||||
|
int line_no, line_num;
|
||||||
|
u64 *dst_hva_line;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
line_num = hva_size / (sizeof(u64) * 4);
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
for (line_no = line_num - 1; line_no >= 0; line_no--) {
|
||||||
|
dst_hva_line = &hva_base[line_no * 4];
|
||||||
|
trace_host_hva_area_line(dst_hva_line, (sizeof(u64) * 4));
|
||||||
|
}
|
||||||
|
if (line_num * (sizeof(u64) * 4) < hva_size) {
|
||||||
|
dst_hva_line = &hva_base[line_no * 4];
|
||||||
|
trace_host_hva_area_line(dst_hva_line,
|
||||||
|
hva_size - line_num * (sizeof(u64) * 4));
|
||||||
|
}
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
native_kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
||||||
|
{
|
||||||
|
void *dst_tail;
|
||||||
|
const void *src_tail;
|
||||||
|
u64 copied;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kernel does not use FP registers so do not copy them.
|
||||||
|
* This only applies to CPUs before V5 instruction set
|
||||||
|
* (since V5 FP registers become general-purpose QP registers).
|
||||||
|
*/
|
||||||
|
if (cpu_has(CPU_FEAT_QPREG)) {
|
||||||
|
#pragma loop count (10)
|
||||||
|
for (i = 0; i < size / 64; i++)
|
||||||
|
E2K_TAGGED_MEMMOVE_64(&dst[8 * i], &src[8 * i]);
|
||||||
|
|
||||||
|
copied = round_down(size, 64);
|
||||||
|
dst_tail = (void *) dst + copied;
|
||||||
|
src_tail = (void *) src + copied;
|
||||||
|
} else {
|
||||||
|
#pragma loop count (5)
|
||||||
|
for (i = 0; i < size / 128; i++)
|
||||||
|
E2K_TAGGED_MEMMOVE_128_RF_V2(&dst[16 * i],
|
||||||
|
&src[16 * i]);
|
||||||
|
|
||||||
|
copied = round_down(size, 128);
|
||||||
|
dst_tail = (void *) dst + copied;
|
||||||
|
src_tail = (void *) src + copied;
|
||||||
|
|
||||||
|
if (size & 64) {
|
||||||
|
E2K_TAGGED_MEMMOVE_64(dst_tail, src_tail);
|
||||||
|
dst_tail += 64;
|
||||||
|
src_tail += 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size & 32)
|
||||||
|
E2K_TAGGED_MEMMOVE_32(dst_tail, src_tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
native_collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
e2k_pcsp_hi_t k_pcsp_hi;
|
||||||
|
u64 size;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DebugUST("current host chain stack index 0x%x, PCSHTP 0x%llx\n",
|
||||||
|
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_ind,
|
||||||
|
NATIVE_READ_PCSHTP_REG_SVALUE());
|
||||||
|
|
||||||
|
NATIVE_FLUSHC;
|
||||||
|
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
|
||||||
|
size = k_pcsp_hi.PCSP_hi_ind - spilled_size;
|
||||||
|
BUG_ON(!IS_ALIGNED(size, ALIGN_PCSTACK_TOP_SIZE) || (s64) size < 0);
|
||||||
|
#pragma loop count (2)
|
||||||
|
for (i = 0; i < size / 32; i++) {
|
||||||
|
u64 v0, v1, v2, v3;
|
||||||
|
|
||||||
|
v0 = src[4 * i];
|
||||||
|
v1 = src[4 * i + 1];
|
||||||
|
v2 = src[4 * i + 2];
|
||||||
|
v3 = src[4 * i + 3];
|
||||||
|
dst[4 * i] = v0;
|
||||||
|
dst[4 * i + 1] = v1;
|
||||||
|
dst[4 * i + 2] = v2;
|
||||||
|
dst[4 * i + 3] = v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_pcsp_hi.PCSP_hi_ind -= spilled_size;
|
||||||
|
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
||||||
|
|
||||||
|
DebugUST("move spilled chain part from host top %px to "
|
||||||
|
"bottom %px, size 0x%llx\n",
|
||||||
|
src, dst, size);
|
||||||
|
DebugUST("host kernel chain stack index is now 0x%x, "
|
||||||
|
"guest user PCSHTP 0x%llx\n",
|
||||||
|
k_pcsp_hi.PCSP_hi_ind, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
native_collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
e2k_psp_hi_t k_psp_hi;
|
||||||
|
u64 size;
|
||||||
|
|
||||||
|
DebugUST("current host procedure stack index 0x%x, PSHTP 0x%x\n",
|
||||||
|
NATIVE_NV_READ_PSP_HI_REG().PSP_hi_ind,
|
||||||
|
NATIVE_NV_READ_PSHTP_REG().PSHTP_ind);
|
||||||
|
|
||||||
|
NATIVE_FLUSHR;
|
||||||
|
k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
||||||
|
|
||||||
|
size = k_psp_hi.PSP_hi_ind - spilled_size;
|
||||||
|
BUG_ON(!IS_ALIGNED(size, ALIGN_PSTACK_TOP_SIZE) || (s64) size < 0);
|
||||||
|
|
||||||
|
prefetchw_range(src, size);
|
||||||
|
native_kernel_hw_stack_frames_copy(dst, src, size);
|
||||||
|
|
||||||
|
k_psp_hi.PSP_hi_ind -= spilled_size;
|
||||||
|
NATIVE_NV_NOIRQ_WRITE_PSP_HI_REG(k_psp_hi);
|
||||||
|
|
||||||
|
DebugUST("move spilled procedure part from host top %px to "
|
||||||
|
"bottom %px, size 0x%llx\n",
|
||||||
|
src, dst, size);
|
||||||
|
DebugUST("host kernel procedure stack index is now 0x%x, "
|
||||||
|
"guest user PSHTP 0x%llx\n",
|
||||||
|
k_psp_hi.PSP_hi_ind, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PARAVIRT_GUEST)
|
||||||
|
/* paravirtualized kernel (host and guest) */
|
||||||
|
#include <asm/paravirt/copy-hw-stacks.h>
|
||||||
|
#elif defined(CONFIG_KVM_GUEST_KERNEL)
|
||||||
|
/* It is native guest kernel (without paravirtualization) */
|
||||||
|
#include <asm/kvm/guest/copy-hw-stacks.h>
|
||||||
|
#elif defined(CONFIG_VIRTUALIZATION) || !defined(CONFIG_VIRTUALIZATION)
|
||||||
|
/* native kernel with virtualization support */
|
||||||
|
/* native kernel without virtualization support */
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
||||||
|
{
|
||||||
|
native_kernel_hw_stack_frames_copy(dst, src, size);
|
||||||
|
}
|
||||||
|
static __always_inline void
|
||||||
|
collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
native_collapse_kernel_pcs(dst, src, spilled_size);
|
||||||
|
}
|
||||||
|
static __always_inline void
|
||||||
|
collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
native_collapse_kernel_ps(dst, src, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* ??? */
|
||||||
|
#error "Undefined virtualization mode"
|
||||||
|
#endif /* CONFIG_PARAVIRT_GUEST */
|
||||||
|
|
||||||
|
static __always_inline u64 get_wsz(enum restore_caller from)
|
||||||
|
{
|
||||||
|
return NATIVE_READ_WD_REG().size >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline u64 get_ps_clear_size(u64 cur_window_q,
|
||||||
|
e2k_pshtp_t pshtp)
|
||||||
|
{
|
||||||
|
s64 u_pshtp_size_q;
|
||||||
|
|
||||||
|
u_pshtp_size_q = GET_PSHTP_Q_INDEX(pshtp);
|
||||||
|
if (u_pshtp_size_q > E2K_MAXSR - cur_window_q)
|
||||||
|
u_pshtp_size_q = E2K_MAXSR - cur_window_q;
|
||||||
|
|
||||||
|
return E2K_MAXSR - (cur_window_q + u_pshtp_size_q);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline s64 get_ps_copy_size(u64 cur_window_q, s64 u_pshtp_size)
|
||||||
|
{
|
||||||
|
return u_pshtp_size - (E2K_MAXSR - cur_window_q) * EXT_4_NR_SZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_CPU_HAS_FILL_INSTRUCTION
|
||||||
|
# define E2K_CF_MAX_FILL (E2K_CF_MAX_FILL_FILLC_q * 0x10)
|
||||||
|
#else
|
||||||
|
extern int cf_max_fill_return;
|
||||||
|
# define E2K_CF_MAX_FILL cf_max_fill_return
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static __always_inline s64 get_pcs_copy_size(s64 u_pcshtp_size)
|
||||||
|
{
|
||||||
|
/* Before v6 it was possible to fill no more than 16 registers.
|
||||||
|
* Since E2K_MAXCR_q is much bigger than 16 we can be sure that
|
||||||
|
* there is enough space in CF for the FILL, so there is no
|
||||||
|
* need to take into account space taken by current window. */
|
||||||
|
return u_pcshtp_size - E2K_CF_MAX_FILL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy hardware stack from user to *current* kernel stack.
|
||||||
|
* One has to be careful to avoid hardware FILL of this stack.
|
||||||
|
*/
|
||||||
|
static inline int __copy_user_to_current_hw_stack(void *dst, void __user *src,
|
||||||
|
unsigned long size, const pt_regs_t *regs, bool chain)
|
||||||
|
{
|
||||||
|
unsigned long min_flt, maj_flt, ts_flag;
|
||||||
|
|
||||||
|
if (likely(!host_test_intc_emul_mode(regs))) {
|
||||||
|
if (!__range_ok((unsigned long __force) src, size,
|
||||||
|
PAGE_OFFSET))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Every page fault here has a chance of FILL'ing the frame
|
||||||
|
* that is being copied, in which case we repeat the copy.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
min_flt = READ_ONCE(current->min_flt);
|
||||||
|
maj_flt = READ_ONCE(current->maj_flt);
|
||||||
|
|
||||||
|
if (chain)
|
||||||
|
E2K_FLUSHC;
|
||||||
|
else
|
||||||
|
E2K_FLUSHR;
|
||||||
|
|
||||||
|
SET_USR_PFAULT("$.recovery_memcpy_fault");
|
||||||
|
fast_tagged_memory_copy_from_user(dst, src, size, regs,
|
||||||
|
TAGGED_MEM_STORE_REC_OPC |
|
||||||
|
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
||||||
|
TAGGED_MEM_LOAD_REC_OPC |
|
||||||
|
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
||||||
|
true);
|
||||||
|
if (RESTORE_USR_PFAULT) {
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
} while (unlikely(min_flt != READ_ONCE(current->min_flt) ||
|
||||||
|
maj_flt != READ_ONCE(current->maj_flt)));
|
||||||
|
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int copy_user_to_current_hw_stack(void *dst, void __user *src,
|
||||||
|
unsigned long size, pt_regs_t *regs, bool chain)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
ret = __copy_user_to_current_hw_stack(dst, src, size, regs, chain);
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int copy_e2k_stack_from_user(void *dst, void __user *src,
|
||||||
|
unsigned long size, pt_regs_t *regs)
|
||||||
|
{
|
||||||
|
unsigned long ts_flag;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (likely(!host_test_intc_emul_mode(regs))) {
|
||||||
|
if (!__range_ok((unsigned long __force) src, size, PAGE_OFFSET))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
||||||
|
ret = host_copy_from_user_with_tags(dst, src, size, regs);
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
|
||||||
|
return (ret) ? -EFAULT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int copy_e2k_stack_to_user(void __user *dst, void *src,
|
||||||
|
unsigned long size, pt_regs_t *regs)
|
||||||
|
{
|
||||||
|
unsigned long ts_flag;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (likely(!host_test_intc_emul_mode(regs))) {
|
||||||
|
if (!__range_ok((unsigned long __force) dst, size, PAGE_OFFSET))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
||||||
|
ret = host_copy_to_user_with_tags(dst, src, size, regs);
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
|
||||||
|
return (ret) ? -EFAULT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
user_hw_stack_frames_copy(void __user *dst, void *src, unsigned long copy_size,
|
||||||
|
const pt_regs_t *regs, unsigned long hw_stack_ind, bool is_pcsp)
|
||||||
|
{
|
||||||
|
unsigned long ts_flag;
|
||||||
|
|
||||||
|
if (unlikely(hw_stack_ind < copy_size)) {
|
||||||
|
unsigned long flags;
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
if (is_pcsp) {
|
||||||
|
E2K_FLUSHC;
|
||||||
|
} else {
|
||||||
|
E2K_FLUSHR;
|
||||||
|
}
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
SET_USR_PFAULT("$.recovery_memcpy_fault");
|
||||||
|
|
||||||
|
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
||||||
|
fast_tagged_memory_copy_to_user(dst, src, copy_size, regs,
|
||||||
|
TAGGED_MEM_STORE_REC_OPC |
|
||||||
|
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
||||||
|
TAGGED_MEM_LOAD_REC_OPC |
|
||||||
|
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT, true);
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
|
||||||
|
if (RESTORE_USR_PFAULT) {
|
||||||
|
pr_err("process %s (%d) %s stack could not be copied "
|
||||||
|
"from %px to %px size 0x%lx (out of memory?)\n",
|
||||||
|
current->comm, current->pid,
|
||||||
|
(is_pcsp) ? "chain" : "procedure",
|
||||||
|
src, dst, copy_size);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
DebugUST("copying guest %s stack spilled to host from %px "
|
||||||
|
"to guest kernel stack from %px, size 0x%lx\n",
|
||||||
|
(is_pcsp) ? "chain" : "procedure", src, dst, copy_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
user_crs_frames_copy(e2k_mem_crs_t __user *u_frame, pt_regs_t *regs,
|
||||||
|
e2k_mem_crs_t *crs)
|
||||||
|
{
|
||||||
|
unsigned long ts_flag;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
||||||
|
ret = host_copy_to_user(u_frame, crs, sizeof(*crs), regs);
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
if (unlikely(ret))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int user_psp_stack_copy(e2k_psp_lo_t u_psp_lo,
|
||||||
|
e2k_psp_hi_t u_psp_hi, s64 u_pshtp_size,
|
||||||
|
e2k_psp_lo_t k_psp_lo, e2k_psp_hi_t k_psp_hi,
|
||||||
|
unsigned long copy_size, const pt_regs_t *regs)
|
||||||
|
{
|
||||||
|
void __user *dst;
|
||||||
|
void *src;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dst = (void __user *) (AS(u_psp_lo).base + AS(u_psp_hi).ind -
|
||||||
|
u_pshtp_size);
|
||||||
|
src = (void *) AS(k_psp_lo).base;
|
||||||
|
|
||||||
|
if (host_test_intc_emul_mode(regs) && trace_host_copy_hw_stack_enabled())
|
||||||
|
trace_host_copy_hw_stack(dst, src, copy_size, false);
|
||||||
|
|
||||||
|
ret = user_hw_stack_frames_copy(dst, src, copy_size,
|
||||||
|
regs, k_psp_hi.PSP_hi_ind, false);
|
||||||
|
|
||||||
|
if (host_test_intc_emul_mode(regs) && trace_host_proc_stack_frame_enabled())
|
||||||
|
trace_proc_stack_frames((kernel_mem_ps_t *)dst,
|
||||||
|
(kernel_mem_ps_t *)src, copy_size,
|
||||||
|
trace_host_proc_stack_frame);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int user_pcsp_stack_copy(e2k_pcsp_lo_t u_pcsp_lo,
|
||||||
|
e2k_pcsp_hi_t u_pcsp_hi, s64 u_pcshtp_size,
|
||||||
|
e2k_pcsp_lo_t k_pcsp_lo, e2k_pcsp_hi_t k_pcsp_hi,
|
||||||
|
unsigned long copy_size, const pt_regs_t *regs)
|
||||||
|
{
|
||||||
|
void __user *dst;
|
||||||
|
void *src;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dst = (void __user *)(AS(u_pcsp_lo).base + AS(u_pcsp_hi).ind -
|
||||||
|
u_pcshtp_size);
|
||||||
|
src = (void *) AS(k_pcsp_lo).base;
|
||||||
|
|
||||||
|
if (host_test_intc_emul_mode(regs) && trace_host_copy_hw_stack_enabled())
|
||||||
|
trace_host_copy_hw_stack(dst, src, copy_size, true);
|
||||||
|
|
||||||
|
ret = user_hw_stack_frames_copy(dst, src, copy_size,
|
||||||
|
regs, k_pcsp_hi.PCSP_hi_ind, true);
|
||||||
|
|
||||||
|
if (host_test_intc_emul_mode(regs) && trace_host_chain_stack_frame_enabled())
|
||||||
|
trace_chain_stack_frames((e2k_mem_crs_t *)dst,
|
||||||
|
(e2k_mem_crs_t *)src, copy_size,
|
||||||
|
trace_host_chain_stack_frame);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user_hw_stacks_copy - copy user hardware stacks that have been
|
||||||
|
* SPILLed to kernel back to user space
|
||||||
|
* @stacks - saved user stack registers
|
||||||
|
* @cur_window_q - size of current window in procedure stack,
|
||||||
|
* needed only if @copy_full is not set
|
||||||
|
* @copy_full - set if want to copy _all_ of SPILLed stacks
|
||||||
|
*
|
||||||
|
* This does not update stacks->pshtp and stacks->pcshtp. Main reason is
|
||||||
|
* signals: if a signal arrives after copying then it must see a coherent
|
||||||
|
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
||||||
|
* data from user space is spilled to kernel space.
|
||||||
|
*/
|
||||||
|
static __always_inline int
|
||||||
|
native_user_hw_stacks_copy(struct e2k_stacks *stacks,
|
||||||
|
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
||||||
|
{
|
||||||
|
trap_pt_regs_t *trap = regs->trap;
|
||||||
|
e2k_psp_lo_t u_psp_lo = stacks->psp_lo,
|
||||||
|
k_psp_lo = current_thread_info()->k_psp_lo;
|
||||||
|
e2k_psp_hi_t u_psp_hi = stacks->psp_hi;
|
||||||
|
e2k_pcsp_lo_t u_pcsp_lo = stacks->pcsp_lo,
|
||||||
|
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
||||||
|
e2k_pcsp_hi_t u_pcsp_hi = stacks->pcsp_hi;
|
||||||
|
s64 u_pshtp_size, u_pcshtp_size, ps_copy_size, pcs_copy_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
u_pshtp_size = GET_PSHTP_MEM_INDEX(stacks->pshtp);
|
||||||
|
u_pcshtp_size = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy user's part from kernel stacks into user stacks
|
||||||
|
* Update user's stack registers
|
||||||
|
*/
|
||||||
|
if (copy_full) {
|
||||||
|
pcs_copy_size = u_pcshtp_size;
|
||||||
|
ps_copy_size = u_pshtp_size;
|
||||||
|
} else {
|
||||||
|
pcs_copy_size = get_pcs_copy_size(u_pcshtp_size);
|
||||||
|
ps_copy_size = get_ps_copy_size(cur_window_q, u_pshtp_size);
|
||||||
|
|
||||||
|
/* Make sure there is enough space in CF for the FILL */
|
||||||
|
BUG_ON((E2K_MAXCR_q - 4) * 16 < E2K_CF_MAX_FILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(pcs_copy_size <= 0 && ps_copy_size <= 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (unlikely(pcs_copy_size > 0)) {
|
||||||
|
e2k_pcsp_hi_t k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
|
||||||
|
/* Since not all user data has been SPILL'ed it is possible
|
||||||
|
* that we have already overflown user's hardware stack. */
|
||||||
|
if (unlikely(AS(u_pcsp_hi).ind > AS(u_pcsp_hi).size)) {
|
||||||
|
ret = handle_chain_stack_bounds(stacks, trap);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
pr_warning("process %s (%d) chain stack overflow (out of memory?)\n",
|
||||||
|
current->comm, current->pid);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_pcsp_lo = stacks->pcsp_lo;
|
||||||
|
u_pcsp_hi = stacks->pcsp_hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = user_pcsp_stack_copy(u_pcsp_lo, u_pcsp_hi, u_pcshtp_size,
|
||||||
|
k_pcsp_lo, k_pcsp_hi, pcs_copy_size, regs);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(ps_copy_size > 0)) {
|
||||||
|
e2k_psp_hi_t k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
||||||
|
|
||||||
|
/* Since not all user data has been SPILL'ed it is possible
|
||||||
|
* that we have already overflowed user's hardware stack. */
|
||||||
|
if (unlikely(AS(u_psp_hi).ind > AS(u_psp_hi).size)) {
|
||||||
|
ret = handle_proc_stack_bounds(stacks, trap);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
pr_warning("process %s (%d) procedure stack overflow (out of memory?)\n",
|
||||||
|
current->comm, current->pid);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_psp_lo = stacks->psp_lo;
|
||||||
|
u_psp_hi = stacks->psp_hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = user_psp_stack_copy(u_psp_lo, u_psp_hi, u_pshtp_size,
|
||||||
|
k_psp_lo, k_psp_hi, ps_copy_size, regs);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void collapse_kernel_hw_stacks(struct e2k_stacks *stacks)
|
||||||
|
{
|
||||||
|
e2k_pcsp_lo_t k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
||||||
|
e2k_psp_lo_t k_psp_lo = current_thread_info()->k_psp_lo;
|
||||||
|
unsigned long flags, spilled_pc_size, spilled_p_size;
|
||||||
|
e2k_pshtp_t pshtp = stacks->pshtp;
|
||||||
|
u64 *dst;
|
||||||
|
const u64 *src;
|
||||||
|
|
||||||
|
spilled_pc_size = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
||||||
|
spilled_p_size = GET_PSHTP_MEM_INDEX(pshtp);
|
||||||
|
DebugUST("guest user spilled to host kernel stack part: chain 0x%lx "
|
||||||
|
"procedure 0x%lx\n",
|
||||||
|
spilled_pc_size, spilled_p_size);
|
||||||
|
/* When user tries to return from the last user frame
|
||||||
|
* we will have pcshtp = pcsp_hi.ind = 0. But situation
|
||||||
|
* with pcsp_hi.ind != 0 and pcshtp = 0 is impossible. */
|
||||||
|
if (WARN_ON_ONCE(spilled_pc_size < SZ_OF_CR &&
|
||||||
|
AS(stacks->pcsp_hi).ind != 0))
|
||||||
|
do_exit(SIGKILL);
|
||||||
|
|
||||||
|
/* Keep the last user frame (see user_hw_stacks_copy_full()) */
|
||||||
|
if (spilled_pc_size >= SZ_OF_CR) {
|
||||||
|
spilled_pc_size -= SZ_OF_CR;
|
||||||
|
DebugUST("Keep the prev user chain frame, so spilled chain "
|
||||||
|
"size is now 0x%lx\n",
|
||||||
|
spilled_pc_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
|
||||||
|
if (spilled_pc_size) {
|
||||||
|
dst = (u64 *) AS(k_pcsp_lo).base;
|
||||||
|
src = (u64 *) (AS(k_pcsp_lo).base + spilled_pc_size);
|
||||||
|
collapse_kernel_pcs(dst, src, spilled_pc_size);
|
||||||
|
|
||||||
|
stacks->pcshtp = SZ_OF_CR;
|
||||||
|
|
||||||
|
apply_graph_tracer_delta(-spilled_pc_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spilled_p_size) {
|
||||||
|
dst = (u64 *) AS(k_psp_lo).base;
|
||||||
|
src = (u64 *) (AS(k_psp_lo).base + spilled_p_size);
|
||||||
|
collapse_kernel_ps(dst, src, spilled_p_size);
|
||||||
|
|
||||||
|
AS(pshtp).ind = 0;
|
||||||
|
stacks->pshtp = pshtp;
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user_hw_stacks_prepare - prepare user hardware stacks that have been
|
||||||
|
* SPILLed to kernel back to user space
|
||||||
|
* @stacks - saved user stack registers
|
||||||
|
* @cur_window_q - size of current window in procedure stack,
|
||||||
|
* needed only if @copy_full is not set
|
||||||
|
* @syscall - true if called upon direct system call exit (no signal handlers)
|
||||||
|
*
|
||||||
|
* This does two things:
|
||||||
|
*
|
||||||
|
* 1) It is possible that upon kernel entry pcshtp == 0 in some cases:
|
||||||
|
* - user signal handler had pcshtp==0x20 before return to sigreturn()
|
||||||
|
* - user context had pcshtp==0x20 before return to makecontext_trampoline()
|
||||||
|
* - chain stack underflow happened
|
||||||
|
* So it is possible in sigreturn() and traps, but not in system calls.
|
||||||
|
* If we are using the trick with return to FILL user hardware stacks than
|
||||||
|
* we must have frame in chain stack to return to. So in this case kernel's
|
||||||
|
* chain stack is moved up by one frame (0x20 bytes).
|
||||||
|
* We also fill the new frame with actual user data and update stacks->pcshtp,
|
||||||
|
* this is needed to keep the coherent state where saved stacks->pcshtp values
|
||||||
|
* shows how much data from user space has been spilled to kernel space.
|
||||||
|
*
|
||||||
|
* 2) It is not possible to always FILL all of user data that have been
|
||||||
|
* SPILLed to kernel stacks. So we manually copy the leftovers that can
|
||||||
|
* not be FILLed to user space.
|
||||||
|
* This copy does not update stacks->pshtp and stacks->pcshtp. Main reason
|
||||||
|
* is signals: if a signal arrives after copying then it must see a coherent
|
||||||
|
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
||||||
|
* data from user space has been spilled to kernel space.
|
||||||
|
*/
|
||||||
|
static __always_inline void native_user_hw_stacks_prepare(
|
||||||
|
struct e2k_stacks *stacks, pt_regs_t *regs,
|
||||||
|
u64 cur_window_q, enum restore_caller from, int syscall)
|
||||||
|
{
|
||||||
|
e2k_pcshtp_t u_pcshtp = stacks->pcshtp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
BUG_ON(from & FROM_PV_VCPU_MODE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1) Make sure there is free space in kernel chain stack to return to
|
||||||
|
*/
|
||||||
|
if (!syscall && u_pcshtp == 0) {
|
||||||
|
unsigned long flags;
|
||||||
|
e2k_pcsp_lo_t u_pcsp_lo = stacks->pcsp_lo,
|
||||||
|
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
||||||
|
e2k_pcsp_hi_t u_pcsp_hi = stacks->pcsp_hi, k_pcsp_hi;
|
||||||
|
e2k_mem_crs_t __user *u_cframe;
|
||||||
|
e2k_mem_crs_t *k_crs;
|
||||||
|
u64 u_cbase;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
E2K_FLUSHC;
|
||||||
|
k_pcsp_hi = READ_PCSP_HI_REG();
|
||||||
|
BUG_ON(AS(k_pcsp_hi).ind);
|
||||||
|
AS(k_pcsp_hi).ind += SZ_OF_CR;
|
||||||
|
WRITE_PCSP_HI_REG(k_pcsp_hi);
|
||||||
|
|
||||||
|
k_crs = (e2k_mem_crs_t *) AS(k_pcsp_lo).base;
|
||||||
|
u_cframe = (e2k_mem_crs_t __user *) (AS(u_pcsp_lo).base +
|
||||||
|
AS(u_pcsp_hi).ind);
|
||||||
|
u_cbase = ((from & FROM_RETURN_PV_VCPU_TRAP) ||
|
||||||
|
host_test_intc_emul_mode(regs)) ?
|
||||||
|
u_pcsp_lo.PCSP_lo_base :
|
||||||
|
(u64) CURRENT_PCS_BASE();
|
||||||
|
if ((u64) u_cframe > u_cbase) {
|
||||||
|
ret = __copy_user_to_current_hw_stack(k_crs,
|
||||||
|
u_cframe - 1, sizeof(*k_crs), regs, true);
|
||||||
|
}
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
|
||||||
|
/* Can happen if application returns until runs out of
|
||||||
|
* chain stack or there is no free memory for stacks.
|
||||||
|
* There is no user stack to return to - die. */
|
||||||
|
if (ret) {
|
||||||
|
SIGDEBUG_PRINT("SIGKILL. %s\n",
|
||||||
|
(ret == -EINVAL) ? "tried to return to kernel" :
|
||||||
|
"ran into Out-of-Memory on user stacks");
|
||||||
|
force_sig(SIGKILL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS(u_pcsp_hi).ind < SZ_OF_CR) {
|
||||||
|
update_pcsp_regs(AS(u_pcsp_lo).base,
|
||||||
|
&u_pcsp_lo, &u_pcsp_hi);
|
||||||
|
stacks->pcsp_lo = u_pcsp_lo;
|
||||||
|
stacks->pcsp_hi = u_pcsp_hi;
|
||||||
|
BUG_ON(AS(u_pcsp_hi).ind < SZ_OF_CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
u_pcshtp = SZ_OF_CR;
|
||||||
|
stacks->pcshtp = u_pcshtp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2) Copy user data that cannot be FILLed
|
||||||
|
*/
|
||||||
|
ret = native_user_hw_stacks_copy(stacks, regs, cur_window_q, false);
|
||||||
|
if (unlikely(ret))
|
||||||
|
do_exit(SIGKILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_VIRTUALIZATION
|
||||||
|
/* native kernel without virtualization support */
|
||||||
|
static __always_inline int
|
||||||
|
user_hw_stacks_copy(struct e2k_stacks *stacks,
|
||||||
|
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
||||||
|
{
|
||||||
|
return native_user_hw_stacks_copy(stacks, regs, cur_window_q, copy_full);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
host_user_hw_stacks_prepare(struct e2k_stacks *stacks, pt_regs_t *regs,
|
||||||
|
u64 cur_window_q, enum restore_caller from, int syscall)
|
||||||
|
{
|
||||||
|
native_user_hw_stacks_prepare(stacks, regs, cur_window_q,
|
||||||
|
from, syscall);
|
||||||
|
}
|
||||||
|
#elif defined(CONFIG_KVM_GUEST_KERNEL)
|
||||||
|
/* It is native guest kernel (without paravirtualization) */
|
||||||
|
#include <asm/kvm/guest/copy-hw-stacks.h>
|
||||||
|
#elif defined(CONFIG_PARAVIRT_GUEST)
|
||||||
|
/* It is paravirtualized kernel (host and guest) */
|
||||||
|
#include <asm/paravirt/copy-hw-stacks.h>
|
||||||
|
#elif defined(CONFIG_KVM_HOST_MODE)
|
||||||
|
/* It is host kernel with virtualization support */
|
||||||
|
#include <asm/kvm/copy-hw-stacks.h>
|
||||||
|
#else /* unknow mode */
|
||||||
|
#error "unknown virtualization mode"
|
||||||
|
#endif /* !CONFIG_VIRTUALIZATION */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user_hw_stacks_copy_full - copy part of user stacks that was SPILLed
|
||||||
|
* into kernel back to user stacks.
|
||||||
|
* @stacks - saved user stack registers
|
||||||
|
* @regs - pt_regs pointer
|
||||||
|
* @crs - last frame to copy
|
||||||
|
*
|
||||||
|
* If @crs is not NULL then the frame pointed to by it will also be copied
|
||||||
|
* to userspace. Note that 'stacks->pcsp_hi.ind' is _not_ updated after
|
||||||
|
* copying since it would leave stack in inconsistent state (with two
|
||||||
|
* copies of the same @crs frame), this is left to the caller. *
|
||||||
|
*
|
||||||
|
* Inlining this reduces the amount of memory to copy in
|
||||||
|
* collapse_kernel_hw_stacks().
|
||||||
|
*/
|
||||||
|
static inline int do_user_hw_stacks_copy_full(struct e2k_stacks *stacks,
|
||||||
|
pt_regs_t *regs, e2k_mem_crs_t *crs)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy part of user stacks that were SPILLed into kernel stacks
|
||||||
|
*/
|
||||||
|
ret = user_hw_stacks_copy(stacks, regs, 0, true);
|
||||||
|
if (unlikely(ret))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nothing to FILL so remove the resulting hole from kernel stacks.
|
||||||
|
*
|
||||||
|
* IMPORTANT: there is always at least one user frame at the top of
|
||||||
|
* kernel stack - the one that issued a system call (in case of an
|
||||||
|
* exception we uphold this rule manually, see user_hw_stacks_prepare())
|
||||||
|
* We keep this ABI and _always_ leave space for one user frame,
|
||||||
|
* this way we can later FILL using return trick (otherwise there
|
||||||
|
* would be no space in chain stack for the trick).
|
||||||
|
*/
|
||||||
|
collapse_kernel_hw_stacks(stacks);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy saved %cr registers
|
||||||
|
*
|
||||||
|
* Caller must take care of filling of resulting hole
|
||||||
|
* (last user frame from pcshtp == SZ_OF_CR).
|
||||||
|
*/
|
||||||
|
if (crs) {
|
||||||
|
e2k_mem_crs_t __user *u_frame;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
u_frame = (void __user *) (AS(stacks->pcsp_lo).base +
|
||||||
|
AS(stacks->pcsp_hi).ind);
|
||||||
|
ret = user_crs_frames_copy(u_frame, regs, ®s->crs);
|
||||||
|
if (unlikely(ret))
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _E2K_COPY_HW_STACKS_H */
|
||||||
|
|
@ -2241,6 +2241,7 @@ typedef union e2k_fpsr {
|
|||||||
#define FPSR_reg word
|
#define FPSR_reg word
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
|
u32 half_word[2];
|
||||||
struct {
|
struct {
|
||||||
u32 user : 1;
|
u32 user : 1;
|
||||||
u32 system : 1;
|
u32 system : 1;
|
||||||
|
@ -1038,19 +1038,26 @@ _Pragma("no_asm_inline") \
|
|||||||
: clobbers); \
|
: clobbers); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
#define NATIVE_EXIT_HANDLE_SYSCALL(sbr, usd_hi, usd_lo, upsr) \
|
#define NATIVE_EXIT_HANDLE_SYSCALL(sbr, usd_hi, usd_lo, upsr) \
|
||||||
({ \
|
({ \
|
||||||
asm volatile ("{rwd %0, %%sbr}" \
|
asm volatile (ALTERNATIVE_1_ALTINSTR \
|
||||||
"{rwd %1, %%usd.hi}" \
|
/* CPU_HWBUG_USD_ALIGNMENT version */ \
|
||||||
|
"{rwd %0, %%sbr;" \
|
||||||
|
" nop}" \
|
||||||
|
ALTERNATIVE_2_OLDINSTR \
|
||||||
|
/* Default version */ \
|
||||||
|
"{rwd %0, %%sbr}" \
|
||||||
|
ALTERNATIVE_3_FEATURE(%[facility]) \
|
||||||
"{rwd %2, %%usd.lo}" \
|
"{rwd %2, %%usd.lo}" \
|
||||||
|
"{rwd %1, %%usd.hi}" \
|
||||||
"{rws %3, %%upsr;" \
|
"{rws %3, %%upsr;" \
|
||||||
" nop 4}\n" \
|
" nop 4}\n" \
|
||||||
: \
|
: \
|
||||||
: "ri" ((__e2k_u64_t) (sbr)), \
|
: "ri" ((__e2k_u64_t) (sbr)), \
|
||||||
"ri" ((__e2k_u64_t) (usd_hi)), \
|
"ri" ((__e2k_u64_t) (usd_hi)), \
|
||||||
"ri" ((__e2k_u64_t) (usd_lo)), \
|
"ri" ((__e2k_u64_t) (usd_lo)), \
|
||||||
"ri" ((__e2k_u32_t) (upsr))); \
|
"ri" ((__e2k_u32_t) (upsr)), \
|
||||||
|
[facility] "i" (CPU_HWBUG_USD_ALIGNMENT)); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -1093,6 +1100,15 @@ _Pragma("no_asm_inline") \
|
|||||||
: "r" ((__e2k_u64_t) (val))); \
|
: "r" ((__e2k_u64_t) (val))); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#define NATIVE_SET_MMUREG_CLOSED(reg_mnemonic, val, nop) \
|
||||||
|
({ \
|
||||||
|
asm volatile ("{nop " #nop "\n" \
|
||||||
|
" mmurw %0, %%" #reg_mnemonic "}" \
|
||||||
|
: \
|
||||||
|
: "r" ((u64) (val))); \
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
#define NATIVE_TAGGED_LOAD_TO_MMUREG(reg_mnemonic, _addr) \
|
#define NATIVE_TAGGED_LOAD_TO_MMUREG(reg_mnemonic, _addr) \
|
||||||
do { \
|
do { \
|
||||||
unsigned long long _tmp; \
|
unsigned long long _tmp; \
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
#include <asm/e2k.h>
|
#include <asm/e2k.h>
|
||||||
#include <asm/pgtable_def.h>
|
#include <asm/pgtable_def.h>
|
||||||
|
|
||||||
#define CHK_DEBUGGER(trapnr, signr, error_code, address, regs, after)
|
|
||||||
|
|
||||||
#define IS_KERNEL_THREAD(task, mm) \
|
#define IS_KERNEL_THREAD(task, mm) \
|
||||||
({ \
|
({ \
|
||||||
e2k_addr_t ps_base; \
|
e2k_addr_t ps_base; \
|
||||||
@ -248,6 +246,8 @@ host_ftrace_dump(void)
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const bool kvm_debug = false;
|
||||||
#else /* CONFIG_VIRTUALIZATION */
|
#else /* CONFIG_VIRTUALIZATION */
|
||||||
/* it is native host kernel with virtualization support */
|
/* it is native host kernel with virtualization support */
|
||||||
/* or it is paravirtualized host/guest kernel */
|
/* or it is paravirtualized host/guest kernel */
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
|
void do_sic_error_interrupt(void);
|
||||||
|
|
||||||
static inline bool cpu_has_epic(void)
|
static inline bool cpu_has_epic(void)
|
||||||
{
|
{
|
||||||
if (cpu_has(CPU_FEAT_EPIC))
|
if (cpu_has(CPU_FEAT_EPIC))
|
||||||
|
@ -35,18 +35,12 @@ extern int hw_breakpoint_exceptions_notify(
|
|||||||
extern void hw_breakpoint_pmu_read(struct perf_event *bp);
|
extern void hw_breakpoint_pmu_read(struct perf_event *bp);
|
||||||
|
|
||||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||||
extern int bp_data_overflow_handle(struct pt_regs *regs);
|
extern void bp_data_overflow_handle(struct pt_regs *regs);
|
||||||
extern int bp_instr_overflow_handle(struct pt_regs *regs);
|
extern void bp_instr_overflow_handle(struct pt_regs *regs);
|
||||||
extern void clear_ptrace_hw_breakpoint(struct task_struct *tsk);
|
extern void clear_ptrace_hw_breakpoint(struct task_struct *tsk);
|
||||||
#else /* ! CONFIG_HAVE_HW_BREAKPOINT */
|
#else /* ! CONFIG_HAVE_HW_BREAKPOINT */
|
||||||
static inline int bp_data_overflow_handle(struct pt_regs *regs)
|
static inline void bp_data_overflow_handle(struct pt_regs *regs) { }
|
||||||
{
|
static inline void bp_instr_overflow_handle(struct pt_regs *regs) { }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int bp_instr_overflow_handle(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline void clear_ptrace_hw_breakpoint(struct task_struct *tsk) {}
|
static inline void clear_ptrace_hw_breakpoint(struct task_struct *tsk) {}
|
||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
||||||
|
|
||||||
|
@ -61,18 +61,14 @@ static inline int is_kprobe_break1_trap(struct pt_regs *regs)
|
|||||||
return *instr == KPROBE_BREAK_1;
|
return *instr == KPROBE_BREAK_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int kprobe_instr_debug_handle(struct pt_regs *);
|
extern void kprobe_instr_debug_handle(struct pt_regs *);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static inline int is_kprobe_break1_trap(struct pt_regs *regs)
|
static inline int is_kprobe_break1_trap(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int kprobe_instr_debug_handle(struct pt_regs *regs)
|
static inline void kprobe_instr_debug_handle(struct pt_regs *regs) { }
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* #ifdef CONFIG_KPROBES */
|
#endif /* #ifdef CONFIG_KPROBES */
|
||||||
|
|
||||||
#ifdef CONFIG_KRETPROBES
|
#ifdef CONFIG_KRETPROBES
|
||||||
|
462
arch/e2k/include/asm/kvm/copy-hw-stacks.h
Normal file
462
arch/e2k/include/asm/kvm/copy-hw-stacks.h
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
/*
|
||||||
|
* KVM guest kernel processes support
|
||||||
|
* Copyright 2011 Salavat S. Guiliazov (atic@mcst.ru)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _E2K_KVM_COPY_HW_STACKS_H
|
||||||
|
#define _E2K_KVM_COPY_HW_STACKS_H
|
||||||
|
|
||||||
|
#include <linux/kvm_host.h>
|
||||||
|
|
||||||
|
#include <asm/cpu_regs.h>
|
||||||
|
#include <asm/regs_state.h>
|
||||||
|
#include <asm/kvm/thread_info.h>
|
||||||
|
#include <asm/kvm/mmu.h>
|
||||||
|
#include <asm/kvm/page.h>
|
||||||
|
#include <asm/kvm/switch.h>
|
||||||
|
|
||||||
|
#undef DEBUG_KVM_GUEST_STACKS_MODE
|
||||||
|
#undef DebugGUST
|
||||||
|
#define DEBUG_KVM_GUEST_STACKS_MODE 0 /* guest user stacks */
|
||||||
|
/* copy debug */
|
||||||
|
#define DebugGUST(fmt, args...) \
|
||||||
|
({ \
|
||||||
|
if (DEBUG_KVM_GUEST_STACKS_MODE) \
|
||||||
|
pr_info("%s(): " fmt, __func__, ##args); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_HOST_MODE
|
||||||
|
static inline void
|
||||||
|
prepare_pv_vcpu_inject_stacks(struct kvm_vcpu *vcpu, pt_regs_t *regs)
|
||||||
|
{
|
||||||
|
e2k_stacks_t *stacks, *g_stacks;
|
||||||
|
gthread_info_t *gti = pv_vcpu_get_gti(vcpu);
|
||||||
|
|
||||||
|
if (regs->g_stacks_valid) {
|
||||||
|
/* already prepared */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all stacks at empty state, because of guest user recursion */
|
||||||
|
/* of trap/system calls can not be */
|
||||||
|
g_stacks = ®s->g_stacks;
|
||||||
|
g_stacks->usd_lo = gti->g_usd_lo;
|
||||||
|
g_stacks->usd_hi = gti->g_usd_hi;
|
||||||
|
g_stacks->top = gti->g_sbr.SBR_base;
|
||||||
|
g_stacks->psp_lo = gti->g_psp_lo;
|
||||||
|
g_stacks->psp_hi = gti->g_psp_hi;
|
||||||
|
g_stacks->pcsp_lo = gti->g_pcsp_lo;
|
||||||
|
g_stacks->pcsp_hi = gti->g_pcsp_hi;
|
||||||
|
|
||||||
|
/* pshtp & pcshtp from guest user stack real state upon trap/syscall */
|
||||||
|
stacks = ®s->stacks;
|
||||||
|
g_stacks->pshtp = stacks->pshtp;
|
||||||
|
g_stacks->pcshtp = stacks->pcshtp;
|
||||||
|
|
||||||
|
regs->g_stacks_valid = true;
|
||||||
|
regs->g_stacks_active = false;
|
||||||
|
regs->need_inject = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef EMULATE_EMPTY_CHAIN_STACK /* only to debug */
|
||||||
|
|
||||||
|
#ifdef EMULATE_EMPTY_CHAIN_STACK
|
||||||
|
static __always_inline void
|
||||||
|
pv_vcpu_emulate_empty_chain_staks(struct kvm_vcpu *vcpu, pt_regs_t *regs,
|
||||||
|
e2k_stacks_t *stacks, bool guest_user)
|
||||||
|
{
|
||||||
|
e2k_pcshtp_t pcshtp;
|
||||||
|
unsigned long flags;
|
||||||
|
e2k_pcsp_lo_t g_pcsp_lo, k_pcsp_lo;
|
||||||
|
e2k_pcsp_hi_t g_pcsp_hi, k_pcsp_hi;
|
||||||
|
e2k_mem_crs_t __user *g_cframe;
|
||||||
|
e2k_mem_crs_t *k_crs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pcshtp = stacks->pcshtp;
|
||||||
|
if (!(guest_user && pcshtp <= 0x40))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_pcsp_lo = regs->stacks.pcsp_lo;
|
||||||
|
g_pcsp_hi = regs->stacks.pcsp_hi;
|
||||||
|
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
NATIVE_FLUSHC;
|
||||||
|
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
k_pcsp_lo = NATIVE_NV_READ_PCSP_LO_REG();
|
||||||
|
BUG_ON(AS(k_pcsp_hi).ind != pcshtp);
|
||||||
|
|
||||||
|
k_crs = (e2k_mem_crs_t *) AS(k_pcsp_lo).base;
|
||||||
|
g_cframe = (e2k_mem_crs_t __user *) (AS(g_pcsp_lo).base +
|
||||||
|
AS(g_pcsp_hi).ind - pcshtp);
|
||||||
|
ret = user_hw_stack_frames_copy(g_cframe, k_crs, pcshtp, regs,
|
||||||
|
k_pcsp_hi.PCSP_hi_ind, true);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s(): copy to user stack failed\n", __func__);
|
||||||
|
BUG_ON(true);
|
||||||
|
}
|
||||||
|
k_pcsp_hi.PCSP_hi_ind -= pcshtp;
|
||||||
|
pcshtp = 0;
|
||||||
|
regs->stacks.pcshtp = pcshtp;
|
||||||
|
stacks->pcshtp = pcshtp;
|
||||||
|
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
}
|
||||||
|
#else /* !EMULATE_EMPTY_CHAIN_STACK */
|
||||||
|
static __always_inline void
|
||||||
|
pv_vcpu_emulate_empty_chain_staks(struct kvm_vcpu *vcpu, pt_regs_t *regs,
|
||||||
|
e2k_stacks_t *stacks, bool guest_user)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* EMULATE_EMPTY_CHAIN_STACK */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pv_vcpu_user_hw_stacks_copy - check size of user hardware stacks that have
|
||||||
|
* been SPILLed to kernel back to guest space
|
||||||
|
* @regs - saved guest user stack registers
|
||||||
|
* @cur_window_q - size of current window in procedure stack
|
||||||
|
*
|
||||||
|
* All guest user's stacks part were already copied to guest kernel stacks,
|
||||||
|
* so it need only check that it was full size and nothing to copy here
|
||||||
|
*/
|
||||||
|
static __always_inline int
|
||||||
|
pv_vcpu_user_hw_stacks_copy(pt_regs_t *regs, e2k_stacks_t *stacks,
|
||||||
|
u64 cur_window_q, bool guest_user)
|
||||||
|
{
|
||||||
|
e2k_psp_lo_t g_psp_lo = stacks->psp_lo,
|
||||||
|
k_psp_lo = current_thread_info()->k_psp_lo;
|
||||||
|
e2k_psp_hi_t g_psp_hi = stacks->psp_hi;
|
||||||
|
e2k_pcsp_lo_t g_pcsp_lo = stacks->pcsp_lo,
|
||||||
|
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
||||||
|
e2k_pcsp_hi_t g_pcsp_hi = stacks->pcsp_hi;
|
||||||
|
s64 g_pshtp_size, g_pcshtp_size, ps_copy_size, pcs_copy_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
DebugUST("guest kernel chain state: base 0x%llx ind 0x%x size 0x%x\n",
|
||||||
|
g_pcsp_lo.PCSP_lo_base, g_pcsp_hi.PCSP_hi_ind,
|
||||||
|
g_pcsp_hi.PCSP_hi_size);
|
||||||
|
DebugUST("guest kernel proc state: base 0x%llx ind 0x%x size 0x%x\n",
|
||||||
|
g_psp_lo.PSP_lo_base, g_psp_hi.PSP_hi_ind,
|
||||||
|
g_psp_hi.PSP_hi_size);
|
||||||
|
g_pshtp_size = GET_PSHTP_MEM_INDEX(stacks->pshtp);
|
||||||
|
g_pcshtp_size = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
||||||
|
DebugUST("guest kernel chain stack PCSHTP 0x%llx, "
|
||||||
|
"proc stack PSHTP 0x%llx cur window 0x%llx\n",
|
||||||
|
g_pcshtp_size, g_pshtp_size, cur_window_q);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: the current implementation of the guest user signal handler
|
||||||
|
* injection uses direct copying to guest hardware stacks.
|
||||||
|
* It is bad decision, needs to be corrected
|
||||||
|
KVM_BUG_ON(is_paging(current_thread_info()->vcpu) &&
|
||||||
|
(g_psp_lo.PSP_lo_base < GUEST_TASK_SIZE ||
|
||||||
|
g_pcsp_lo.PCSP_lo_base < GUEST_TASK_SIZE));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate size of user's part to copy from kernel stacks
|
||||||
|
* into guest kernel stacks
|
||||||
|
*/
|
||||||
|
pcs_copy_size = get_pcs_copy_size(g_pcshtp_size);
|
||||||
|
ps_copy_size = get_ps_copy_size(cur_window_q, g_pshtp_size);
|
||||||
|
/* Make sure there is enough space in CF for the FILL */
|
||||||
|
BUG_ON((E2K_MAXCR_q - 4) * 16 < E2K_CF_MAX_FILL);
|
||||||
|
DebugUST("to copy chain stack 0x%llx, proc stack 0x%llx\n",
|
||||||
|
pcs_copy_size, ps_copy_size);
|
||||||
|
|
||||||
|
if (likely(pcs_copy_size <= 0 && ps_copy_size <= 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (unlikely(pcs_copy_size > 0)) {
|
||||||
|
e2k_pcsp_hi_t k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
void __user *dst;
|
||||||
|
void *src;
|
||||||
|
|
||||||
|
/* Since SPILL'ed guest user data will be copyed to guest */
|
||||||
|
/* kernel stacks then cannot be any overflow of user's */
|
||||||
|
/* hardware stack. */
|
||||||
|
if (unlikely(AS(g_pcsp_hi).ind > AS(g_pcsp_hi).size)) {
|
||||||
|
pr_err("%s(): guest kernel chain stack overflow "
|
||||||
|
"(out of memory?): ind 0x%x size 0x%x\n",
|
||||||
|
__func__, g_pcsp_hi.PCSP_hi_ind,
|
||||||
|
g_pcsp_hi.PCSP_hi_size);
|
||||||
|
KVM_BUG_ON(true);
|
||||||
|
}
|
||||||
|
dst = (void __user *)(g_pcsp_lo.PCSP_lo_base +
|
||||||
|
g_pcsp_hi.PCSP_hi_ind);
|
||||||
|
if (!guest_user) {
|
||||||
|
/* stack index has been incremented on PCSHTP */
|
||||||
|
dst -= g_pcshtp_size;
|
||||||
|
}
|
||||||
|
src = (void *)k_pcsp_lo.PCSP_lo_base;
|
||||||
|
|
||||||
|
if (trace_host_copy_hw_stack_enabled())
|
||||||
|
trace_host_copy_hw_stack(dst, src, pcs_copy_size, true);
|
||||||
|
|
||||||
|
ret = user_hw_stack_frames_copy(dst, src, pcs_copy_size, regs,
|
||||||
|
k_pcsp_hi.PCSP_hi_ind, true);
|
||||||
|
if (trace_host_chain_stack_frame_enabled())
|
||||||
|
trace_chain_stack_frames((e2k_mem_crs_t *)dst,
|
||||||
|
(e2k_mem_crs_t *)src, pcs_copy_size,
|
||||||
|
trace_host_chain_stack_frame);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (guest_user) {
|
||||||
|
g_pcsp_hi.PCSP_hi_ind += pcs_copy_size;
|
||||||
|
stacks->pcsp_hi = g_pcsp_hi;
|
||||||
|
DebugGUST("guest user chain stack frames copied from "
|
||||||
|
"host %px to guest kernel from %px size 0x%llx "
|
||||||
|
"PCSP.ind 0x%x\n",
|
||||||
|
src, dst, pcs_copy_size, g_pcsp_hi.PCSP_hi_ind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(ps_copy_size > 0)) {
|
||||||
|
e2k_psp_hi_t k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
||||||
|
void __user *dst;
|
||||||
|
void *src;
|
||||||
|
|
||||||
|
/* Since SPILL'ed guest user data will be copyed to guest */
|
||||||
|
/* kernel stacks then cannot be any overflow of user's */
|
||||||
|
/* hardware stack. */
|
||||||
|
if (unlikely(AS(g_psp_hi).ind > AS(g_psp_hi).size)) {
|
||||||
|
pr_err("%s(): guest kernel proc stack overflow "
|
||||||
|
"(out of memory?): ind 0x%x size 0x%x\n",
|
||||||
|
__func__, g_psp_hi.PSP_hi_ind,
|
||||||
|
g_psp_hi.PSP_hi_size);
|
||||||
|
KVM_BUG_ON(true);
|
||||||
|
}
|
||||||
|
dst = (void __user *)(g_psp_lo.PSP_lo_base +
|
||||||
|
g_psp_hi.PSP_hi_ind);
|
||||||
|
if (!guest_user) {
|
||||||
|
/* stack index has been incremented on PSHTP */
|
||||||
|
dst -= g_pshtp_size;
|
||||||
|
}
|
||||||
|
src = (void *)k_psp_lo.PSP_lo_base;
|
||||||
|
|
||||||
|
if (trace_host_copy_hw_stack_enabled())
|
||||||
|
trace_host_copy_hw_stack(dst, src, ps_copy_size, false);
|
||||||
|
|
||||||
|
ret = user_hw_stack_frames_copy(dst, src, ps_copy_size, regs,
|
||||||
|
k_psp_hi.PSP_hi_ind, false);
|
||||||
|
if (trace_host_proc_stack_frame_enabled())
|
||||||
|
trace_proc_stack_frames((kernel_mem_ps_t *)dst,
|
||||||
|
(kernel_mem_ps_t *)src, ps_copy_size,
|
||||||
|
trace_host_proc_stack_frame);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (guest_user) {
|
||||||
|
g_psp_hi.PSP_hi_ind += ps_copy_size;
|
||||||
|
stacks->psp_hi = g_psp_hi;
|
||||||
|
DebugGUST("guest user proc stack frames copied from "
|
||||||
|
"host %px to guest kernel from %px size 0x%llx "
|
||||||
|
"PSP.ind 0x%x\n",
|
||||||
|
src, dst, ps_copy_size, g_psp_hi.PSP_hi_ind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pv_vcpu_user_hw_stacks_prepare - prepare guest user hardware stacks
|
||||||
|
that have been SPILLed to kernel back
|
||||||
|
to guest user space
|
||||||
|
* @regs - saved guest user stack registers
|
||||||
|
* @cur_window_q - size of current window in procedure stack
|
||||||
|
* @syscall - true if called upon direct system call exit (no signal handlers)
|
||||||
|
*
|
||||||
|
* This does two things:
|
||||||
|
*
|
||||||
|
* 1) It is possible that upon kernel entry pcshtp == 0 in some cases:
|
||||||
|
* - user signal handler had pcshtp==0x20 before return to sigreturn()
|
||||||
|
* - user context had pcshtp==0x20 before return to makecontext_trampoline()
|
||||||
|
* - chain stack underflow happened
|
||||||
|
* So it is possible in sigreturn() and traps, but not in system calls.
|
||||||
|
* If we are using the trick with return to FILL user hardware stacks than
|
||||||
|
* we must have frame in chain stack to return to. So in this case kernel's
|
||||||
|
* chain stack is moved up by one frame (0x20 bytes).
|
||||||
|
* We also fill the new frame with actual user data and update stacks->pcshtp,
|
||||||
|
* this is needed to keep the coherent state where saved stacks->pcshtp values
|
||||||
|
* shows how much data from user space has been spilled to kernel space.
|
||||||
|
*
|
||||||
|
* 2) It is not possible to always FILL all of user data that have been
|
||||||
|
* SPILLed to kernel stacks. So we manually copy the leftovers that can
|
||||||
|
* not be FILLed to user space.
|
||||||
|
* This copy does not update stacks->pshtp and stacks->pcshtp. Main reason
|
||||||
|
* is signals: if a signal arrives after copying then it must see a coherent
|
||||||
|
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
||||||
|
* data from user space has been spilled to kernel space.
|
||||||
|
*/
|
||||||
|
static __always_inline void
|
||||||
|
pv_vcpu_user_hw_stacks_prepare(struct kvm_vcpu *vcpu, pt_regs_t *regs,
|
||||||
|
u64 cur_window_q, enum restore_caller from, int syscall)
|
||||||
|
{
|
||||||
|
e2k_stacks_t *stacks;
|
||||||
|
e2k_pcshtp_t pcshtp;
|
||||||
|
bool guest_user;
|
||||||
|
bool paging = is_paging(vcpu);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (likely(paging)) {
|
||||||
|
guest_user = !!(syscall || !pv_vcpu_trap_on_guest_kernel(regs));
|
||||||
|
} else {
|
||||||
|
guest_user = false;
|
||||||
|
}
|
||||||
|
if (guest_user) {
|
||||||
|
if (from & FROM_PV_VCPU_MODE) {
|
||||||
|
/* all preparation has been made */
|
||||||
|
/* by host & guest handler */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* trap on/syscall from guest user, so regs keeps user */
|
||||||
|
/* registers state and it need use guest kernel stacks */
|
||||||
|
/* in empty state to handle this trap/syscall */
|
||||||
|
if (!regs->g_stacks_valid) {
|
||||||
|
prepare_pv_vcpu_inject_stacks(vcpu, regs);
|
||||||
|
}
|
||||||
|
stacks = ®s->g_stacks;
|
||||||
|
} else {
|
||||||
|
/* trap on guest kernel, so regs already points to guest */
|
||||||
|
/* kernel stacks and trap will be handled by host */
|
||||||
|
/* same as other user's processes traps */
|
||||||
|
stacks = ®s->stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* only to debug on simulator : pcshtp == 0 */
|
||||||
|
pv_vcpu_emulate_empty_chain_staks(vcpu, regs, stacks, guest_user);
|
||||||
|
|
||||||
|
pcshtp = stacks->pcshtp;
|
||||||
|
DebugUST("guest kernel chain stack state: base 0x%llx ind 0x%x "
|
||||||
|
"size 0x%x\n",
|
||||||
|
stacks->pcsp_lo.PCSP_lo_base,
|
||||||
|
stacks->pcsp_hi.PCSP_hi_ind,
|
||||||
|
stacks->pcsp_hi.PCSP_hi_size);
|
||||||
|
DebugUST("host kernel chain stack state: base 0x%llx ind 0x%x "
|
||||||
|
"size 0x%x\n",
|
||||||
|
NATIVE_NV_READ_PCSP_LO_REG().PCSP_lo_base,
|
||||||
|
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_ind,
|
||||||
|
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_size);
|
||||||
|
DebugUST("guest kernel chain stack size to fill PCSHTP 0x%x\n",
|
||||||
|
pcshtp);
|
||||||
|
/*
|
||||||
|
* 1) Make sure there is free space in kernel chain stack to return to
|
||||||
|
*/
|
||||||
|
if (!syscall && pcshtp == 0 && !guest_user) {
|
||||||
|
unsigned long flags;
|
||||||
|
e2k_pcsp_lo_t g_pcsp_lo = stacks->pcsp_lo,
|
||||||
|
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
||||||
|
e2k_pcsp_hi_t g_pcsp_hi = stacks->pcsp_hi, k_pcsp_hi;
|
||||||
|
e2k_mem_crs_t __user *g_cframe;
|
||||||
|
e2k_mem_crs_t *k_crs;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
NATIVE_FLUSHC;
|
||||||
|
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
BUG_ON(AS(k_pcsp_hi).ind);
|
||||||
|
AS(k_pcsp_hi).ind += SZ_OF_CR;
|
||||||
|
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
||||||
|
|
||||||
|
k_crs = (e2k_mem_crs_t *) AS(k_pcsp_lo).base;
|
||||||
|
g_cframe = (e2k_mem_crs_t __user *) (AS(g_pcsp_lo).base +
|
||||||
|
AS(g_pcsp_hi).ind);
|
||||||
|
if ((u64) g_cframe > (u64) AS(g_pcsp_lo).base) {
|
||||||
|
ret = __copy_user_to_current_hw_stack(k_crs,
|
||||||
|
g_cframe - 1, sizeof(*k_crs), regs, true);
|
||||||
|
}
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
|
||||||
|
/* Can happen if application returns until runs out of
|
||||||
|
* chain stack or there is no free memory for stacks.
|
||||||
|
* There is no user stack to return to - die. */
|
||||||
|
if (ret) {
|
||||||
|
E2K_LMS_HALT_OK;
|
||||||
|
pr_err("%s(): SIGKILL. %s\n",
|
||||||
|
__func__,
|
||||||
|
(ret == -EINVAL) ?
|
||||||
|
"tried to return to kernel"
|
||||||
|
:
|
||||||
|
"ran into Out-of-Memory on user stacks");
|
||||||
|
force_sig(SIGKILL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DebugUST("copy guest user chain frame from %px to kernel "
|
||||||
|
"bottom from %px\n",
|
||||||
|
g_cframe - 1, k_crs);
|
||||||
|
|
||||||
|
if (AS(g_pcsp_hi).ind < SZ_OF_CR) {
|
||||||
|
pr_err("%s(): guest kernel chain stack underflow\n",
|
||||||
|
__func__);
|
||||||
|
KVM_BUG_ON(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcshtp = SZ_OF_CR;
|
||||||
|
stacks->pcshtp = pcshtp;
|
||||||
|
DebugUST("guest kernel chain stack to FILL PCSHTP "
|
||||||
|
"set to 0x%x\n",
|
||||||
|
stacks->pcshtp);
|
||||||
|
} else if (!syscall && pcshtp == 0 && guest_user) {
|
||||||
|
e2k_pcsp_hi_t k_pcsp_hi;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/* set flag for unconditional injection to do not copy */
|
||||||
|
/* from guest user space */
|
||||||
|
regs->need_inject = true;
|
||||||
|
|
||||||
|
/* reserve one bottom frames for trampoline */
|
||||||
|
/* the guest handler replaces guest user trapped frame */
|
||||||
|
raw_all_irq_save(flags);
|
||||||
|
NATIVE_FLUSHC;
|
||||||
|
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
BUG_ON(k_pcsp_hi.PCSP_hi_ind);
|
||||||
|
k_pcsp_hi.PCSP_hi_ind += 1 * SZ_OF_CR;
|
||||||
|
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
||||||
|
raw_all_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2) Copy user data that cannot be FILLed
|
||||||
|
*/
|
||||||
|
ret = pv_vcpu_user_hw_stacks_copy(regs, stacks, cur_window_q,
|
||||||
|
guest_user);
|
||||||
|
if (unlikely(ret))
|
||||||
|
do_exit(SIGKILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same as for native kernel without virtualization support */
|
||||||
|
static __always_inline int
|
||||||
|
user_hw_stacks_copy(struct e2k_stacks *stacks,
|
||||||
|
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
||||||
|
{
|
||||||
|
return native_user_hw_stacks_copy(stacks, regs, cur_window_q, copy_full);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
host_user_hw_stacks_prepare(struct e2k_stacks *stacks, pt_regs_t *regs,
|
||||||
|
u64 cur_window_q, enum restore_caller from, int syscall)
|
||||||
|
{
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
||||||
|
if (likely(!kvm_test_intc_emul_flag(regs))) {
|
||||||
|
/* trap on/syscall from host user processes */
|
||||||
|
return native_user_hw_stacks_prepare(stacks, regs,
|
||||||
|
cur_window_q, from, syscall);
|
||||||
|
}
|
||||||
|
|
||||||
|
vcpu = current_thread_info()->vcpu;
|
||||||
|
KVM_BUG_ON(vcpu == NULL);
|
||||||
|
pv_vcpu_user_hw_stacks_prepare(vcpu, regs, cur_window_q, from, syscall);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KVM_HOST_MODE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_GUEST_KERNEL
|
||||||
|
/* it is native guest kernel */
|
||||||
|
#include <asm/kvm/guest/copy-hw-stacks.h>
|
||||||
|
#else /* CONFIG_VIRTUALIZATION && ! CONFIG_KVM_GUEST_KERNEL */
|
||||||
|
/* it is native host kernel with virtualization support */
|
||||||
|
/* or it is paravirtualized host and guest kernel */
|
||||||
|
#endif /* ! CONFIG_KVM_GUEST_KERNEL */
|
||||||
|
|
||||||
|
#endif /* ! _E2K_KVM_COPY_HW_STACKS_H */
|
@ -11,6 +11,8 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <asm/kvm/vcpu-regs-debug-inline.h>
|
#include <asm/kvm/vcpu-regs-debug-inline.h>
|
||||||
|
|
||||||
|
extern bool kvm_debug;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some definitions to print/dump/show stacks
|
* Some definitions to print/dump/show stacks
|
||||||
*/
|
*/
|
||||||
|
@ -86,6 +86,7 @@ kvm_mmu_set_init_gmm_root(struct kvm_vcpu *vcpu, hpa_t root)
|
|||||||
}
|
}
|
||||||
gmm->u_pptb = vcpu->arch.mmu.get_vcpu_u_pptb(vcpu);
|
gmm->u_pptb = vcpu->arch.mmu.get_vcpu_u_pptb(vcpu);
|
||||||
gmm->os_pptb = vcpu->arch.mmu.get_vcpu_os_pptb(vcpu);
|
gmm->os_pptb = vcpu->arch.mmu.get_vcpu_os_pptb(vcpu);
|
||||||
|
gmm->u_vptb = vcpu->arch.mmu.get_vcpu_u_vptb(vcpu);
|
||||||
}
|
}
|
||||||
static inline pgd_t *
|
static inline pgd_t *
|
||||||
kvm_mmu_get_gmm_root(struct gmm_struct *gmm)
|
kvm_mmu_get_gmm_root(struct gmm_struct *gmm)
|
||||||
@ -208,15 +209,12 @@ switch_guest_pgd(pgd_t *next_pgd)
|
|||||||
pgd_to_set = next_pgd;
|
pgd_to_set = next_pgd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KVM_BUG_ON(PCSHTP_SIGN_EXTEND(NATIVE_READ_PCSHTP_REG_SVALUE()) != 0);
|
||||||
|
|
||||||
reload_root_pgd(pgd_to_set);
|
reload_root_pgd(pgd_to_set);
|
||||||
/* FIXME: support of guest secondary space is not yet implemented
|
/* FIXME: support of guest secondary space is not yet implemented
|
||||||
reload_secondary_page_dir(mm);
|
reload_secondary_page_dir(mm);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* any function call can fill old state of hardware stacks */
|
|
||||||
/* so after all calls do flush stacks again */
|
|
||||||
NATIVE_FLUSHCPU;
|
|
||||||
E2K_WAIT(_all_e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DO_NOT_USE_ACTIVE_GMM /* turn OFF optimization */
|
#define DO_NOT_USE_ACTIVE_GMM /* turn OFF optimization */
|
||||||
|
568
arch/e2k/include/asm/kvm/guest/copy-hw-stacks.h
Normal file
568
arch/e2k/include/asm/kvm/guest/copy-hw-stacks.h
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
/*
|
||||||
|
* KVM guest kernel processes support
|
||||||
|
* Copyright 2011 Salavat S. Guiliazov (atic@mcst.ru)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _E2K_KVM_GUEST_COPY_HW_STACKS_H
|
||||||
|
#define _E2K_KVM_GUEST_COPY_HW_STACKS_H
|
||||||
|
|
||||||
|
#include <asm/kvm/hypercall.h>
|
||||||
|
#include <asm/cpu_regs_types.h>
|
||||||
|
#include <asm/stacks.h>
|
||||||
|
|
||||||
|
#include <asm/kvm/guest/trace-hw-stacks.h>
|
||||||
|
|
||||||
|
extern bool debug_ustacks;
|
||||||
|
#undef DEBUG_USER_STACKS_MODE
|
||||||
|
#undef DebugUST
|
||||||
|
#define DEBUG_USER_STACKS_MODE 0 /* guest user stacks debug mode */
|
||||||
|
#define DebugUST(fmt, args...) \
|
||||||
|
({ \
|
||||||
|
if (debug_ustacks) \
|
||||||
|
pr_info("%s(): " fmt, __func__, ##args); \
|
||||||
|
})
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
kvm_kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
||||||
|
{
|
||||||
|
fast_tagged_memory_copy(dst, src, size,
|
||||||
|
TAGGED_MEM_STORE_REC_OPC |
|
||||||
|
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
||||||
|
TAGGED_MEM_LOAD_REC_OPC |
|
||||||
|
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
kvm_collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
e2k_psp_hi_t k_psp_hi;
|
||||||
|
u64 ps_ind, ps_size;
|
||||||
|
u64 size;
|
||||||
|
|
||||||
|
DebugUST("current host procedure stack index 0x%x, PSHTP 0x%x\n",
|
||||||
|
NATIVE_NV_READ_PSP_HI_REG().PSP_hi_ind,
|
||||||
|
NATIVE_NV_READ_PSHTP_REG().PSHTP_ind);
|
||||||
|
|
||||||
|
KVM_COPY_STACKS_TO_MEMORY();
|
||||||
|
ATOMIC_GET_HW_PS_SIZES(ps_ind, ps_size);
|
||||||
|
|
||||||
|
size = ps_ind - spilled_size;
|
||||||
|
BUG_ON(!IS_ALIGNED(size, ALIGN_PSTACK_TOP_SIZE) || (s64) size < 0);
|
||||||
|
|
||||||
|
kvm_kernel_hw_stack_frames_copy(dst, src, size);
|
||||||
|
|
||||||
|
k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
||||||
|
k_psp_hi.PSP_hi_ind = size;
|
||||||
|
HYPERVISOR_update_psp_hi(k_psp_hi.PSP_hi_half);
|
||||||
|
|
||||||
|
DebugUST("move spilled procedure part from host top %px to "
|
||||||
|
"bottom %px, size 0x%llx\n",
|
||||||
|
src, dst, size);
|
||||||
|
DebugUST("host kernel procedure stack index is now 0x%x, "
|
||||||
|
"guest user PSHTP 0x%llx\n",
|
||||||
|
k_psp_hi.PSP_hi_ind, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
kvm_collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
e2k_pcsp_hi_t k_pcsp_hi;
|
||||||
|
u64 pcs_ind, pcs_size;
|
||||||
|
u64 size;
|
||||||
|
|
||||||
|
DebugUST("current host chain stack index 0x%x, PCSHTP 0x%llx\n",
|
||||||
|
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_ind,
|
||||||
|
NATIVE_READ_PCSHTP_REG_SVALUE());
|
||||||
|
|
||||||
|
KVM_COPY_STACKS_TO_MEMORY();
|
||||||
|
ATOMIC_GET_HW_PCS_SIZES(pcs_ind, pcs_size);
|
||||||
|
|
||||||
|
size = pcs_ind - spilled_size;
|
||||||
|
BUG_ON(!IS_ALIGNED(size, ALIGN_PCSTACK_TOP_SIZE) || (s64) size < 0);
|
||||||
|
|
||||||
|
kvm_kernel_hw_stack_frames_copy(dst, src, size);
|
||||||
|
|
||||||
|
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
||||||
|
k_pcsp_hi.PCSP_hi_ind = size;
|
||||||
|
HYPERVISOR_update_pcsp_hi(k_pcsp_hi.PCSP_hi_half);
|
||||||
|
|
||||||
|
DebugUST("move spilled chain part from host top %px to "
|
||||||
|
"bottom %px, size 0x%llx\n",
|
||||||
|
src, dst, size);
|
||||||
|
DebugUST("host kernel chain stack index is now 0x%x, "
|
||||||
|
"guest user PCSHTP 0x%llx\n",
|
||||||
|
k_pcsp_hi.PCSP_hi_ind, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
copy_stack_page_from_kernel(void __user *dst, void *src, e2k_size_t to_copy,
|
||||||
|
bool is_chain)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = HYPERVISOR_copy_hw_stacks_frames(dst, src, to_copy, is_chain);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
copy_stack_page_to_user(void __user *dst, void *src, e2k_size_t to_copy,
|
||||||
|
bool is_chain)
|
||||||
|
{
|
||||||
|
struct page *page = NULL;
|
||||||
|
unsigned long addr = (unsigned long)dst;
|
||||||
|
void *k_dst;
|
||||||
|
e2k_size_t offset;
|
||||||
|
mm_segment_t seg;
|
||||||
|
unsigned long ts_flag;
|
||||||
|
int npages;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (to_copy == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
DebugUST("started to copy %s stack from kernel stack %px to user %px "
|
||||||
|
"size 0x%lx\n",
|
||||||
|
(is_chain) ? "chain" : "procedure",
|
||||||
|
src, dst, to_copy);
|
||||||
|
seg = get_fs();
|
||||||
|
set_fs(K_USER_DS);
|
||||||
|
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
||||||
|
do {
|
||||||
|
npages = __get_user_pages_fast(addr, 1, 1, &page);
|
||||||
|
if (npages == 1)
|
||||||
|
break;
|
||||||
|
npages = get_user_pages_unlocked(addr, 1, &page, FOLL_WRITE);
|
||||||
|
if (npages == 1)
|
||||||
|
break;
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
set_fs(seg);
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto failed;
|
||||||
|
} while (npages != 1);
|
||||||
|
clear_ts_flag(ts_flag);
|
||||||
|
set_fs(seg);
|
||||||
|
|
||||||
|
offset = addr & ~PAGE_MASK;
|
||||||
|
k_dst = page_address(page) + offset;
|
||||||
|
DebugUST("copy stack frames from kernel %px to user %px, size 0x%lx\n",
|
||||||
|
src, k_dst, to_copy);
|
||||||
|
ret = copy_stack_page_from_kernel(k_dst, src, to_copy, is_chain);
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("%s(): copy %s stack to user %px from kernel %px, "
|
||||||
|
"size 0x%lx failed, error %d\n",
|
||||||
|
__func__, (is_chain) ? "chain" : "procedure",
|
||||||
|
src, k_dst, to_copy, ret);
|
||||||
|
goto failed_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
failed_copy:
|
||||||
|
put_page(page);
|
||||||
|
failed:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
kvm_copy_user_stack_from_kernel(void __user *dst, void *src,
|
||||||
|
e2k_size_t to_copy, bool is_chain)
|
||||||
|
{
|
||||||
|
e2k_size_t offset, len, copied = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (to_copy == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
DebugUST("started to copy %s stack from kernel stack %px to user %px "
|
||||||
|
"size 0x%lx\n",
|
||||||
|
(is_chain) ? "chain" : "procedure",
|
||||||
|
src, dst, to_copy);
|
||||||
|
|
||||||
|
if (trace_guest_copy_hw_stack_enabled())
|
||||||
|
trace_guest_copy_hw_stack(dst, src, to_copy, is_chain);
|
||||||
|
|
||||||
|
do {
|
||||||
|
offset = (unsigned long)dst & ~PAGE_MASK;
|
||||||
|
len = min(to_copy, PAGE_SIZE - offset);
|
||||||
|
ret = copy_stack_page_to_user(dst, src, len, is_chain);
|
||||||
|
if (ret != 0)
|
||||||
|
goto failed;
|
||||||
|
dst += len;
|
||||||
|
src += len;
|
||||||
|
to_copy -= len;
|
||||||
|
copied += len;
|
||||||
|
} while (to_copy > 0);
|
||||||
|
|
||||||
|
if (!is_chain && trace_guest_proc_stack_frame_enabled()) {
|
||||||
|
if (trace_guest_va_tlb_state_enabled()) {
|
||||||
|
trace_guest_va_tlb_state((e2k_addr_t)dst);
|
||||||
|
}
|
||||||
|
trace_proc_stack_frames((kernel_mem_ps_t *)(src - copied),
|
||||||
|
(kernel_mem_ps_t *)(src - copied), copied,
|
||||||
|
trace_guest_proc_stack_frame);
|
||||||
|
trace_proc_stack_frames((kernel_mem_ps_t *)(dst - copied),
|
||||||
|
(kernel_mem_ps_t *)(dst - copied), copied,
|
||||||
|
trace_guest_proc_stack_frame);
|
||||||
|
}
|
||||||
|
if (is_chain && trace_guest_chain_stack_frame_enabled()) {
|
||||||
|
if (trace_guest_va_tlb_state_enabled()) {
|
||||||
|
trace_guest_va_tlb_state((e2k_addr_t)dst);
|
||||||
|
}
|
||||||
|
trace_chain_stack_frames((e2k_mem_crs_t *)(src - copied),
|
||||||
|
(e2k_mem_crs_t *)(src - copied), copied,
|
||||||
|
trace_guest_chain_stack_frame);
|
||||||
|
trace_chain_stack_frames((e2k_mem_crs_t *)(dst - copied),
|
||||||
|
(e2k_mem_crs_t *)(dst - copied), copied,
|
||||||
|
trace_guest_chain_stack_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
pr_err("%s(): failed, error %d\n", __func__, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
kvm_user_hw_stacks_copy(pt_regs_t *regs)
|
||||||
|
{
|
||||||
|
e2k_psp_lo_t psp_lo;
|
||||||
|
e2k_psp_hi_t psp_hi;
|
||||||
|
e2k_pshtp_t pshtp;
|
||||||
|
e2k_pcsp_lo_t pcsp_lo;
|
||||||
|
e2k_pcsp_hi_t pcsp_hi;
|
||||||
|
e2k_pcshtp_t pcshtp;
|
||||||
|
e2k_stacks_t *stacks;
|
||||||
|
void __user *dst;
|
||||||
|
void *src;
|
||||||
|
long copyed_ps_size, copyed_pcs_size, to_copy, from, there_are;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (unlikely(irqs_disabled())) {
|
||||||
|
pr_err("%s() called with IRQs disabled PSP: 0x%lx UPSR: 0x%lx "
|
||||||
|
"under UPSR %d\n",
|
||||||
|
__func__, KVM_READ_PSR_REG_VALUE(),
|
||||||
|
KVM_READ_UPSR_REG_VALUE(),
|
||||||
|
kvm_get_vcpu_state()->irqs_under_upsr);
|
||||||
|
local_irq_enable();
|
||||||
|
WARN_ON(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
stacks = ®s->stacks;
|
||||||
|
copyed_ps_size = regs->copyed.ps_size;
|
||||||
|
copyed_pcs_size = regs->copyed.pcs_size;
|
||||||
|
if (unlikely(copyed_ps_size || copyed_pcs_size)) {
|
||||||
|
/* stacks have been already copyed */
|
||||||
|
BUG_ON(copyed_ps_size != GET_PSHTP_MEM_INDEX(stacks->pshtp) &&
|
||||||
|
GET_PSHTP_MEM_INDEX(stacks->pshtp) != 0);
|
||||||
|
BUG_ON(copyed_pcs_size != PCSHTP_SIGN_EXTEND(stacks->pcshtp) &&
|
||||||
|
PCSHTP_SIGN_EXTEND(stacks->pcshtp) != SZ_OF_CR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = HYPERVISOR_copy_stacks_to_memory();
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("%s(): flush of kernel stacks failed, error %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy user part of procedure stack from kernel back to user */
|
||||||
|
ATOMIC_READ_HW_STACKS_REGS(psp_lo.PSP_lo_half, psp_hi.PSP_hi_half,
|
||||||
|
pshtp.PSHTP_reg,
|
||||||
|
pcsp_lo.PCSP_lo_half, pcsp_hi.PCSP_hi_half,
|
||||||
|
pcshtp);
|
||||||
|
src = (void *)psp_lo.PSP_lo_base;
|
||||||
|
DebugUST("procedure stack at kernel from %px, size 0x%x, ind 0x%x, "
|
||||||
|
"pshtp 0x%llx\n",
|
||||||
|
src, psp_hi.PSP_hi_size, psp_hi.PSP_hi_ind, pshtp.PSHTP_reg);
|
||||||
|
BUG_ON(psp_hi.PSP_hi_ind > psp_hi.PSP_hi_size);
|
||||||
|
|
||||||
|
if (stacks->psp_hi.PSP_hi_ind >= stacks->psp_hi.PSP_hi_size) {
|
||||||
|
/* procedure stack overflow, need expand */
|
||||||
|
ret = handle_proc_stack_bounds(stacks, regs->trap);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
pr_err("%s(): could not handle process %s (%d) "
|
||||||
|
"procedure stack overflow, error %d\n",
|
||||||
|
__func__, current->comm, current->pid, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_copy = GET_PSHTP_MEM_INDEX(stacks->pshtp);
|
||||||
|
BUG_ON(to_copy < 0);
|
||||||
|
from = stacks->psp_hi.PSP_hi_ind - to_copy;
|
||||||
|
BUG_ON(from < 0);
|
||||||
|
dst = (void __user *)stacks->psp_lo.PSP_lo_base + from;
|
||||||
|
DebugUST("procedure stack at user from %px, ind 0x%x, "
|
||||||
|
"pshtp size to copy 0x%lx\n",
|
||||||
|
dst, stacks->psp_hi.PSP_hi_ind, to_copy);
|
||||||
|
there_are = stacks->psp_hi.PSP_hi_size - from;
|
||||||
|
if (there_are < to_copy) {
|
||||||
|
pr_err("%s(): user procedure stack overflow, there are 0x%lx "
|
||||||
|
"to copy need 0x%lx, not yet implemented\n",
|
||||||
|
__func__, there_are, to_copy);
|
||||||
|
BUG_ON(true);
|
||||||
|
}
|
||||||
|
if (to_copy > 0) {
|
||||||
|
ret = kvm_copy_user_stack_from_kernel(dst, src, to_copy, false);
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("%s(): procedure stack copying from kernel %px "
|
||||||
|
"to user %px, size 0x%lx failed, error %d\n",
|
||||||
|
__func__, src, dst, to_copy, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
regs->copyed.ps_size = to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy user part of chain stack from kernel back to user */
|
||||||
|
src = (void *)pcsp_lo.PCSP_lo_base;
|
||||||
|
DebugUST("chain stack at kernel from %px, size 0x%x, ind 0x%x, "
|
||||||
|
"pcshtp 0x%x\n",
|
||||||
|
src, pcsp_hi.PCSP_hi_size, pcsp_hi.PCSP_hi_ind, pcshtp);
|
||||||
|
BUG_ON(pcsp_hi.PCSP_hi_ind + PCSHTP_SIGN_EXTEND(pcshtp) >
|
||||||
|
pcsp_hi.PCSP_hi_size);
|
||||||
|
if (stacks->pcsp_hi.PCSP_hi_ind >= stacks->pcsp_hi.PCSP_hi_size) {
|
||||||
|
/* chain stack overflow, need expand */
|
||||||
|
ret = handle_chain_stack_bounds(stacks, regs->trap);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
pr_err("%s(): could not handle process %s (%d) "
|
||||||
|
"chain stack overflow, error %d\n",
|
||||||
|
__func__, current->comm, current->pid, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_copy = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
||||||
|
BUG_ON(to_copy < 0);
|
||||||
|
from = stacks->pcsp_hi.PCSP_hi_ind - to_copy;
|
||||||
|
BUG_ON(from < 0);
|
||||||
|
dst = (void *)stacks->pcsp_lo.PCSP_lo_base + from;
|
||||||
|
BUG_ON(to_copy > pcsp_hi.PCSP_hi_ind + PCSHTP_SIGN_EXTEND(pcshtp));
|
||||||
|
DebugUST("chain stack at user from %px, ind 0x%x, "
|
||||||
|
"pcshtp size to copy 0x%lx\n",
|
||||||
|
dst, stacks->pcsp_hi.PCSP_hi_ind, to_copy);
|
||||||
|
there_are = stacks->pcsp_hi.PCSP_hi_size - from;
|
||||||
|
if (there_are < to_copy) {
|
||||||
|
pr_err("%s(): user chain stack overflow, there are 0x%lx "
|
||||||
|
"to copy need 0x%lx, not yet implemented\n",
|
||||||
|
__func__, there_are, to_copy);
|
||||||
|
BUG_ON(true);
|
||||||
|
}
|
||||||
|
if (to_copy > 0) {
|
||||||
|
ret = kvm_copy_user_stack_from_kernel(dst, src, to_copy, true);
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("%s(): chain stack copying from kernel %px "
|
||||||
|
"to user %px, size 0x%lx failed, error %d\n",
|
||||||
|
__func__, src, dst, to_copy, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
regs->copyed.pcs_size = to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
failed:
|
||||||
|
if (DEBUG_USER_STACKS_MODE)
|
||||||
|
debug_ustacks = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy additional frames injected to the guest kernel stack, but these frames
|
||||||
|
* are for guest user stack and should be copyed from kernel back to the top
|
||||||
|
* of user.
|
||||||
|
*/
|
||||||
|
static __always_inline int
|
||||||
|
kvm_copy_injected_pcs_frames_to_user(pt_regs_t *regs, int frames_num)
|
||||||
|
{
|
||||||
|
e2k_size_t pcs_ind, pcs_size;
|
||||||
|
e2k_addr_t pcs_base;
|
||||||
|
int pcsh_top;
|
||||||
|
e2k_stacks_t *stacks;
|
||||||
|
void __user *dst;
|
||||||
|
void *src;
|
||||||
|
long copyed_frames_size, to_copy, from, there_are, frames_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
BUG_ON(irqs_disabled());
|
||||||
|
|
||||||
|
frames_size = frames_num * SZ_OF_CR;
|
||||||
|
copyed_frames_size = regs->copyed.pcs_injected_frames_size;
|
||||||
|
if (unlikely(copyed_frames_size >= frames_size)) {
|
||||||
|
/* all frames have been already copyed */
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* copyed only part of frames - not implemented case */
|
||||||
|
BUG_ON(copyed_frames_size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
stacks = ®s->stacks;
|
||||||
|
ATOMIC_GET_HW_PCS_SIZES_BASE_TOP(pcs_ind, pcs_size, pcs_base, pcsh_top);
|
||||||
|
|
||||||
|
/* guest user stacks part spilled to kernel should be already copyed */
|
||||||
|
BUG_ON(PCSHTP_SIGN_EXTEND(regs->copyed.pcs_size != stacks->pcshtp));
|
||||||
|
|
||||||
|
src = (void *)(pcs_base + regs->copyed.pcs_size);
|
||||||
|
DebugUST("chain stack at kernel from %px, size 0x%lx + 0x%lx, "
|
||||||
|
"ind 0x%lx, pcsh top 0x%x\n",
|
||||||
|
src, pcs_size, frames_size, pcs_ind, pcsh_top);
|
||||||
|
BUG_ON(regs->copyed.pcs_size + frames_size > pcs_ind + pcsh_top);
|
||||||
|
if (stacks->pcsp_hi.PCSP_hi_ind + frames_size >
|
||||||
|
stacks->pcsp_hi.PCSP_hi_size) {
|
||||||
|
/* user chain stack can overflow, need expand */
|
||||||
|
ret = handle_chain_stack_bounds(stacks, regs->trap);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
pr_err("%s(): could not handle process %s (%d) "
|
||||||
|
"chain stack overflow, error %d\n",
|
||||||
|
__func__, current->comm, current->pid, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_copy = frames_size;
|
||||||
|
BUG_ON(to_copy < 0);
|
||||||
|
from = stacks->pcsp_hi.PCSP_hi_ind;
|
||||||
|
BUG_ON(from < regs->copyed.pcs_size);
|
||||||
|
dst = (void *)stacks->pcsp_lo.PCSP_lo_base + from;
|
||||||
|
DebugUST("chain stack at user from %px, ind 0x%x, "
|
||||||
|
"frames size to copy 0x%lx\n",
|
||||||
|
dst, stacks->pcsp_hi.PCSP_hi_ind, to_copy);
|
||||||
|
there_are = stacks->pcsp_hi.PCSP_hi_size - from;
|
||||||
|
if (there_are < to_copy) {
|
||||||
|
pr_err("%s(): user chain stack overflow, there are 0x%lx "
|
||||||
|
"to copy need 0x%lx, not yet implemented\n",
|
||||||
|
__func__, there_are, to_copy);
|
||||||
|
BUG_ON(true);
|
||||||
|
}
|
||||||
|
if (likely(to_copy > 0)) {
|
||||||
|
ret = kvm_copy_user_stack_from_kernel(dst, src, to_copy, true);
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("%s(): chain stack copying from kernel %px "
|
||||||
|
"to user %px, size 0x%lx failed, error %d\n",
|
||||||
|
__func__, src, dst, to_copy, ret);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
regs->copyed.pcs_injected_frames_size = to_copy;
|
||||||
|
/* increment chain stack pointer */
|
||||||
|
stacks->pcsp_hi.PCSP_hi_ind += to_copy;
|
||||||
|
} else {
|
||||||
|
BUG_ON(true);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
failed:
|
||||||
|
if (DEBUG_USER_STACKS_MODE)
|
||||||
|
debug_ustacks = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user_hw_stacks_prepare - prepare user hardware stacks that have been
|
||||||
|
* SPILLed to kernel back to user space
|
||||||
|
* @stacks - saved user stack registers
|
||||||
|
* @cur_window_q - size of current window in procedure stack,
|
||||||
|
* needed only if @copy_full is not set
|
||||||
|
* @syscall - true if called upon direct system call exit (no signal handlers)
|
||||||
|
*
|
||||||
|
* This does two things:
|
||||||
|
*
|
||||||
|
* 1) It is possible that upon kernel entry pcshtp == 0 in some cases:
|
||||||
|
* - user signal handler had pcshtp==0x20 before return to sigreturn()
|
||||||
|
* - user context had pcshtp==0x20 before return to makecontext_trampoline()
|
||||||
|
* - chain stack underflow happened
|
||||||
|
* So it is possible in sigreturn() and traps, but not in system calls.
|
||||||
|
* If we are using the trick with return to FILL user hardware stacks than
|
||||||
|
* we must have frame in chain stack to return to. So in this case kernel's
|
||||||
|
* chain stack is moved up by one frame (0x20 bytes).
|
||||||
|
* We also fill the new frame with actual user data and update stacks->pcshtp,
|
||||||
|
* this is needed to keep the coherent state where saved stacks->pcshtp values
|
||||||
|
* shows how much data from user space has been spilled to kernel space.
|
||||||
|
*
|
||||||
|
* 2) It is not possible to always FILL all of user data that have been
|
||||||
|
* SPILLed to kernel stacks. So we manually copy the leftovers that can
|
||||||
|
* not be FILLed to user space.
|
||||||
|
* This copy does not update stacks->pshtp and stacks->pcshtp. Main reason
|
||||||
|
* is signals: if a signal arrives after copying then it must see a coherent
|
||||||
|
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
||||||
|
* data from user space has been spilled to kernel space.
|
||||||
|
*/
|
||||||
|
static __always_inline int kvm_user_hw_stacks_prepare(
|
||||||
|
struct e2k_stacks *stacks, pt_regs_t *regs,
|
||||||
|
u64 cur_window_q, enum restore_caller from, int syscall)
|
||||||
|
{
|
||||||
|
e2k_pcshtp_t u_pcshtp = stacks->pcshtp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
BUG_ON(!kvm_trap_user_mode(regs));
|
||||||
|
|
||||||
|
BUG_ON(from & FROM_PV_VCPU_MODE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1) Make sure there is free space in kernel chain stack to return to
|
||||||
|
*/
|
||||||
|
if (!syscall && u_pcshtp == 0) {
|
||||||
|
DebugUST("%s(): PCSHTP is empty\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2) User data copying will be done some later at
|
||||||
|
* kvm_prepare_user_hv_stacks()
|
||||||
|
*/
|
||||||
|
ret = kvm_user_hw_stacks_copy(regs);
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("%s(): copying of hardware stacks failed< error %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
do_exit(SIGKILL);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
kvm_ret_from_fork_prepare_hv_stacks(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
return kvm_user_hw_stacks_copy(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_GUEST_KERNEL
|
||||||
|
/* native guest kernel */
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
||||||
|
{
|
||||||
|
kvm_kernel_hw_stack_frames_copy(dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
kvm_collapse_kernel_ps(dst, src, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
||||||
|
{
|
||||||
|
kvm_collapse_kernel_pcs(dst, src, spilled_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int
|
||||||
|
user_hw_stacks_copy(struct e2k_stacks *stacks,
|
||||||
|
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
||||||
|
{
|
||||||
|
return kvm_user_hw_stacks_copy(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void host_user_hw_stacks_prepare(
|
||||||
|
struct e2k_stacks *stacks, pt_regs_t *regs,
|
||||||
|
u64 cur_window_q, enum restore_caller from, int syscall)
|
||||||
|
{
|
||||||
|
if (regs->sys_num == __NR_e2k_longjmp2) {
|
||||||
|
/* hardware stacks already are prepared */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kvm_user_hw_stacks_prepare(stacks, regs, cur_window_q,
|
||||||
|
from, syscall);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ret_from_fork_prepare_hv_stacks(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
return kvm_ret_from_fork_prepare_hv_stacks(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_KVM_GUEST_KERNEL */
|
||||||
|
|
||||||
|
#endif /* !(_E2K_KVM_GUEST_COPY_HW_STACKS_H) */
|
@ -11,25 +11,6 @@
|
|||||||
#include <asm/stacks.h>
|
#include <asm/stacks.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
|
|
||||||
#undef DEBUG_USER_STACKS_MODE
|
|
||||||
#undef DebugKVMUS
|
|
||||||
#define DEBUG_USER_STACKS_MODE 0
|
|
||||||
#define DebugKVMUS(fmt, args...) \
|
|
||||||
({ \
|
|
||||||
if (DEBUG_USER_STACKS_MODE) \
|
|
||||||
pr_info("%s(): " fmt, __func__, ##args); \
|
|
||||||
})
|
|
||||||
|
|
||||||
extern bool debug_ustacks;
|
|
||||||
#undef DEBUG_USER_STACKS_MODE
|
|
||||||
#undef DebugUST
|
|
||||||
#define DEBUG_USER_STACKS_MODE 0 /* guest user stacks debug mode */
|
|
||||||
#define DebugUST(fmt, args...) \
|
|
||||||
({ \
|
|
||||||
if (debug_ustacks) \
|
|
||||||
pr_info("%s(): " fmt, __func__, ##args); \
|
|
||||||
})
|
|
||||||
|
|
||||||
/* real flush of hardware stacks should be done by host hypercall */
|
/* real flush of hardware stacks should be done by host hypercall */
|
||||||
/* so here nothing to do */
|
/* so here nothing to do */
|
||||||
#ifdef CONFIG_KVM_GUEST_HW_PV
|
#ifdef CONFIG_KVM_GUEST_HW_PV
|
||||||
@ -129,474 +110,8 @@ kvm_preserve_user_hw_stacks_to_copy(e2k_stacks_t *u_stacks,
|
|||||||
/* after copying and therefore are not preserve */
|
/* after copying and therefore are not preserve */
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
kvm_kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
|
||||||
{
|
|
||||||
fast_tagged_memory_copy(dst, src, size,
|
|
||||||
TAGGED_MEM_STORE_REC_OPC |
|
|
||||||
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
|
||||||
TAGGED_MEM_LOAD_REC_OPC |
|
|
||||||
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
kvm_collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
e2k_psp_hi_t k_psp_hi;
|
|
||||||
u64 ps_ind, ps_size;
|
|
||||||
u64 size;
|
|
||||||
|
|
||||||
DebugUST("current host procedure stack index 0x%x, PSHTP 0x%x\n",
|
|
||||||
NATIVE_NV_READ_PSP_HI_REG().PSP_hi_ind,
|
|
||||||
NATIVE_NV_READ_PSHTP_REG().PSHTP_ind);
|
|
||||||
|
|
||||||
KVM_COPY_STACKS_TO_MEMORY();
|
|
||||||
ATOMIC_GET_HW_PS_SIZES(ps_ind, ps_size);
|
|
||||||
|
|
||||||
size = ps_ind - spilled_size;
|
|
||||||
BUG_ON(!IS_ALIGNED(size, ALIGN_PSTACK_TOP_SIZE) || (s64) size < 0);
|
|
||||||
|
|
||||||
kvm_kernel_hw_stack_frames_copy(dst, src, size);
|
|
||||||
|
|
||||||
k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
|
||||||
k_psp_hi.PSP_hi_ind = size;
|
|
||||||
HYPERVISOR_update_psp_hi(k_psp_hi.PSP_hi_half);
|
|
||||||
|
|
||||||
DebugUST("move spilled procedure part from host top %px to "
|
|
||||||
"bottom %px, size 0x%llx\n",
|
|
||||||
src, dst, size);
|
|
||||||
DebugUST("host kernel procedure stack index is now 0x%x, "
|
|
||||||
"guest user PSHTP 0x%llx\n",
|
|
||||||
k_psp_hi.PSP_hi_ind, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
kvm_collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
e2k_pcsp_hi_t k_pcsp_hi;
|
|
||||||
u64 pcs_ind, pcs_size;
|
|
||||||
u64 size;
|
|
||||||
|
|
||||||
DebugUST("current host chain stack index 0x%x, PCSHTP 0x%llx\n",
|
|
||||||
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_ind,
|
|
||||||
NATIVE_READ_PCSHTP_REG_SVALUE());
|
|
||||||
|
|
||||||
KVM_COPY_STACKS_TO_MEMORY();
|
|
||||||
ATOMIC_GET_HW_PCS_SIZES(pcs_ind, pcs_size);
|
|
||||||
|
|
||||||
size = pcs_ind - spilled_size;
|
|
||||||
BUG_ON(!IS_ALIGNED(size, ALIGN_PCSTACK_TOP_SIZE) || (s64) size < 0);
|
|
||||||
|
|
||||||
kvm_kernel_hw_stack_frames_copy(dst, src, size);
|
|
||||||
|
|
||||||
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
k_pcsp_hi.PCSP_hi_ind = size;
|
|
||||||
HYPERVISOR_update_pcsp_hi(k_pcsp_hi.PCSP_hi_half);
|
|
||||||
|
|
||||||
DebugUST("move spilled chain part from host top %px to "
|
|
||||||
"bottom %px, size 0x%llx\n",
|
|
||||||
src, dst, size);
|
|
||||||
DebugUST("host kernel chain stack index is now 0x%x, "
|
|
||||||
"guest user PCSHTP 0x%llx\n",
|
|
||||||
k_pcsp_hi.PCSP_hi_ind, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
copy_stack_page_from_kernel(void __user *dst, void *src, e2k_size_t to_copy,
|
|
||||||
bool is_chain)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = HYPERVISOR_copy_hw_stacks_frames(dst, src, to_copy, is_chain);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
copy_stack_page_to_user(void __user *dst, void *src, e2k_size_t to_copy,
|
|
||||||
bool is_chain)
|
|
||||||
{
|
|
||||||
struct page *page = NULL;
|
|
||||||
unsigned long addr = (unsigned long)dst;
|
|
||||||
void *k_dst;
|
|
||||||
e2k_size_t offset;
|
|
||||||
mm_segment_t seg;
|
|
||||||
unsigned long ts_flag;
|
|
||||||
int npages;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (to_copy == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
DebugUST("started to copy %s stack from kernel stack %px to user %px "
|
|
||||||
"size 0x%lx\n",
|
|
||||||
(is_chain) ? "chain" : "procedure",
|
|
||||||
src, dst, to_copy);
|
|
||||||
seg = get_fs();
|
|
||||||
set_fs(K_USER_DS);
|
|
||||||
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
|
||||||
do {
|
|
||||||
npages = __get_user_pages_fast(addr, 1, 1, &page);
|
|
||||||
if (npages == 1)
|
|
||||||
break;
|
|
||||||
npages = get_user_pages_unlocked(addr, 1, &page, FOLL_WRITE);
|
|
||||||
if (npages == 1)
|
|
||||||
break;
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
set_fs(seg);
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto failed;
|
|
||||||
} while (npages != 1);
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
set_fs(seg);
|
|
||||||
|
|
||||||
offset = addr & ~PAGE_MASK;
|
|
||||||
k_dst = page_address(page) + offset;
|
|
||||||
DebugUST("copy stack frames from kernel %px to user %px, size 0x%lx\n",
|
|
||||||
src, k_dst, to_copy);
|
|
||||||
ret = copy_stack_page_from_kernel(k_dst, src, to_copy, is_chain);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("%s(): copy %s stack to user %px from kernel %px, "
|
|
||||||
"size 0x%lx failed, error %d\n",
|
|
||||||
__func__, (is_chain) ? "chain" : "procedure",
|
|
||||||
src, k_dst, to_copy, ret);
|
|
||||||
goto failed_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
failed_copy:
|
|
||||||
put_page(page);
|
|
||||||
failed:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
kvm_copy_user_stack_from_kernel(void __user *dst, void *src,
|
|
||||||
e2k_size_t to_copy, bool is_chain)
|
|
||||||
{
|
|
||||||
e2k_size_t offset, len;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (to_copy == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
DebugUST("started to copy %s stack from kernel stack %px to user %px "
|
|
||||||
"size 0x%lx\n",
|
|
||||||
(is_chain) ? "chain" : "procedure",
|
|
||||||
src, dst, to_copy);
|
|
||||||
do {
|
|
||||||
offset = (unsigned long)dst & ~PAGE_MASK;
|
|
||||||
len = min(to_copy, PAGE_SIZE - offset);
|
|
||||||
ret = copy_stack_page_to_user(dst, src, len, is_chain);
|
|
||||||
if (ret != 0)
|
|
||||||
goto failed;
|
|
||||||
dst += len;
|
|
||||||
src += len;
|
|
||||||
to_copy -= len;
|
|
||||||
} while (to_copy > 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
failed:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
kvm_user_hw_stacks_copy(pt_regs_t *regs)
|
|
||||||
{
|
|
||||||
e2k_psp_lo_t psp_lo;
|
|
||||||
e2k_psp_hi_t psp_hi;
|
|
||||||
e2k_pshtp_t pshtp;
|
|
||||||
e2k_pcsp_lo_t pcsp_lo;
|
|
||||||
e2k_pcsp_hi_t pcsp_hi;
|
|
||||||
e2k_pcshtp_t pcshtp;
|
|
||||||
e2k_stacks_t *stacks;
|
|
||||||
void __user *dst;
|
|
||||||
void *src;
|
|
||||||
long copyed_ps_size, copyed_pcs_size, to_copy, from, there_are;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (unlikely(irqs_disabled())) {
|
|
||||||
pr_err("%s() called with IRQs disabled PSP: 0x%lx UPSR: 0x%lx "
|
|
||||||
"under UPSR %d\n",
|
|
||||||
__func__, KVM_READ_PSR_REG_VALUE(),
|
|
||||||
KVM_READ_UPSR_REG_VALUE(),
|
|
||||||
kvm_get_vcpu_state()->irqs_under_upsr);
|
|
||||||
local_irq_enable();
|
|
||||||
WARN_ON(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
stacks = ®s->stacks;
|
|
||||||
copyed_ps_size = regs->copyed.ps_size;
|
|
||||||
copyed_pcs_size = regs->copyed.pcs_size;
|
|
||||||
if (unlikely(copyed_ps_size || copyed_pcs_size)) {
|
|
||||||
/* stacks have been already copyed */
|
|
||||||
BUG_ON(copyed_ps_size != GET_PSHTP_MEM_INDEX(stacks->pshtp) &&
|
|
||||||
GET_PSHTP_MEM_INDEX(stacks->pshtp) != 0);
|
|
||||||
BUG_ON(copyed_pcs_size != PCSHTP_SIGN_EXTEND(stacks->pcshtp) &&
|
|
||||||
PCSHTP_SIGN_EXTEND(stacks->pcshtp) != SZ_OF_CR);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = HYPERVISOR_copy_stacks_to_memory();
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("%s(): flush of kernel stacks failed, error %d\n",
|
|
||||||
__func__, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy user part of procedure stack from kernel back to user */
|
|
||||||
ATOMIC_READ_HW_STACKS_REGS(psp_lo.PSP_lo_half, psp_hi.PSP_hi_half,
|
|
||||||
pshtp.PSHTP_reg,
|
|
||||||
pcsp_lo.PCSP_lo_half, pcsp_hi.PCSP_hi_half,
|
|
||||||
pcshtp);
|
|
||||||
src = (void *)psp_lo.PSP_lo_base;
|
|
||||||
DebugUST("procedure stack at kernel from %px, size 0x%x, ind 0x%x, "
|
|
||||||
"pshtp 0x%llx\n",
|
|
||||||
src, psp_hi.PSP_hi_size, psp_hi.PSP_hi_ind, pshtp.PSHTP_reg);
|
|
||||||
BUG_ON(psp_hi.PSP_hi_ind > psp_hi.PSP_hi_size);
|
|
||||||
|
|
||||||
if (stacks->psp_hi.PSP_hi_ind >= stacks->psp_hi.PSP_hi_size) {
|
|
||||||
/* procedure stack overflow, need expand */
|
|
||||||
ret = handle_proc_stack_bounds(stacks, regs->trap);
|
|
||||||
if (unlikely(ret)) {
|
|
||||||
pr_err("%s(): could not handle process %s (%d) "
|
|
||||||
"procedure stack overflow, error %d\n",
|
|
||||||
__func__, current->comm, current->pid, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
to_copy = GET_PSHTP_MEM_INDEX(stacks->pshtp);
|
|
||||||
BUG_ON(to_copy < 0);
|
|
||||||
from = stacks->psp_hi.PSP_hi_ind - to_copy;
|
|
||||||
BUG_ON(from < 0);
|
|
||||||
dst = (void __user *)stacks->psp_lo.PSP_lo_base + from;
|
|
||||||
DebugUST("procedure stack at user from %px, ind 0x%x, "
|
|
||||||
"pshtp size to copy 0x%lx\n",
|
|
||||||
dst, stacks->psp_hi.PSP_hi_ind, to_copy);
|
|
||||||
there_are = stacks->psp_hi.PSP_hi_size - from;
|
|
||||||
if (there_are < to_copy) {
|
|
||||||
pr_err("%s(): user procedure stack overflow, there are 0x%lx "
|
|
||||||
"to copy need 0x%lx, not yet implemented\n",
|
|
||||||
__func__, there_are, to_copy);
|
|
||||||
BUG_ON(true);
|
|
||||||
}
|
|
||||||
if (to_copy > 0) {
|
|
||||||
ret = kvm_copy_user_stack_from_kernel(dst, src, to_copy, false);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("%s(): procedure stack copying from kernel %px "
|
|
||||||
"to user %px, size 0x%lx failed, error %d\n",
|
|
||||||
__func__, src, dst, to_copy, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
regs->copyed.ps_size = to_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy user part of chain stack from kernel back to user */
|
|
||||||
src = (void *)pcsp_lo.PCSP_lo_base;
|
|
||||||
DebugUST("chain stack at kernel from %px, size 0x%x, ind 0x%x, "
|
|
||||||
"pcshtp 0x%x\n",
|
|
||||||
src, pcsp_hi.PCSP_hi_size, pcsp_hi.PCSP_hi_ind, pcshtp);
|
|
||||||
BUG_ON(pcsp_hi.PCSP_hi_ind + PCSHTP_SIGN_EXTEND(pcshtp) >
|
|
||||||
pcsp_hi.PCSP_hi_size);
|
|
||||||
if (stacks->pcsp_hi.PCSP_hi_ind >= stacks->pcsp_hi.PCSP_hi_size) {
|
|
||||||
/* chain stack overflow, need expand */
|
|
||||||
ret = handle_chain_stack_bounds(stacks, regs->trap);
|
|
||||||
if (unlikely(ret)) {
|
|
||||||
pr_err("%s(): could not handle process %s (%d) "
|
|
||||||
"chain stack overflow, error %d\n",
|
|
||||||
__func__, current->comm, current->pid, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
to_copy = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
|
||||||
BUG_ON(to_copy < 0);
|
|
||||||
from = stacks->pcsp_hi.PCSP_hi_ind - to_copy;
|
|
||||||
BUG_ON(from < 0);
|
|
||||||
dst = (void *)stacks->pcsp_lo.PCSP_lo_base + from;
|
|
||||||
BUG_ON(to_copy > pcsp_hi.PCSP_hi_ind + PCSHTP_SIGN_EXTEND(pcshtp));
|
|
||||||
DebugUST("chain stack at user from %px, ind 0x%x, "
|
|
||||||
"pcshtp size to copy 0x%lx\n",
|
|
||||||
dst, stacks->pcsp_hi.PCSP_hi_ind, to_copy);
|
|
||||||
there_are = stacks->pcsp_hi.PCSP_hi_size - from;
|
|
||||||
if (there_are < to_copy) {
|
|
||||||
pr_err("%s(): user chain stack overflow, there are 0x%lx "
|
|
||||||
"to copy need 0x%lx, not yet implemented\n",
|
|
||||||
__func__, there_are, to_copy);
|
|
||||||
BUG_ON(true);
|
|
||||||
}
|
|
||||||
if (to_copy > 0) {
|
|
||||||
ret = kvm_copy_user_stack_from_kernel(dst, src, to_copy, true);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("%s(): chain stack copying from kernel %px "
|
|
||||||
"to user %px, size 0x%lx failed, error %d\n",
|
|
||||||
__func__, src, dst, to_copy, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
regs->copyed.pcs_size = to_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
failed:
|
|
||||||
if (DEBUG_USER_STACKS_MODE)
|
|
||||||
debug_ustacks = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy additional frames injected to the guest kernel stack, but these frames
|
|
||||||
* are for guest user stack and should be copyed from kernel back to the top
|
|
||||||
* of user.
|
|
||||||
*/
|
|
||||||
static __always_inline int
|
|
||||||
kvm_copy_injected_pcs_frames_to_user(pt_regs_t *regs, int frames_num)
|
|
||||||
{
|
|
||||||
e2k_size_t pcs_ind, pcs_size;
|
|
||||||
e2k_addr_t pcs_base;
|
|
||||||
int pcsh_top;
|
|
||||||
e2k_stacks_t *stacks;
|
|
||||||
void __user *dst;
|
|
||||||
void *src;
|
|
||||||
long copyed_frames_size, to_copy, from, there_are, frames_size;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
BUG_ON(irqs_disabled());
|
|
||||||
|
|
||||||
frames_size = frames_num * SZ_OF_CR;
|
|
||||||
copyed_frames_size = regs->copyed.pcs_injected_frames_size;
|
|
||||||
if (unlikely(copyed_frames_size >= frames_size)) {
|
|
||||||
/* all frames have been already copyed */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
/* copyed only part of frames - not implemented case */
|
|
||||||
BUG_ON(copyed_frames_size != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
stacks = ®s->stacks;
|
|
||||||
ATOMIC_GET_HW_PCS_SIZES_BASE_TOP(pcs_ind, pcs_size, pcs_base, pcsh_top);
|
|
||||||
|
|
||||||
/* guest user stacks part spilled to kernel should be already copyed */
|
|
||||||
BUG_ON(PCSHTP_SIGN_EXTEND(regs->copyed.pcs_size != stacks->pcshtp));
|
|
||||||
|
|
||||||
src = (void *)(pcs_base + regs->copyed.pcs_size);
|
|
||||||
DebugUST("chain stack at kernel from %px, size 0x%lx + 0x%lx, "
|
|
||||||
"ind 0x%lx, pcsh top 0x%x\n",
|
|
||||||
src, pcs_size, frames_size, pcs_ind, pcsh_top);
|
|
||||||
BUG_ON(regs->copyed.pcs_size + frames_size > pcs_ind + pcsh_top);
|
|
||||||
if (stacks->pcsp_hi.PCSP_hi_ind + frames_size >
|
|
||||||
stacks->pcsp_hi.PCSP_hi_size) {
|
|
||||||
/* user chain stack can overflow, need expand */
|
|
||||||
ret = handle_chain_stack_bounds(stacks, regs->trap);
|
|
||||||
if (unlikely(ret)) {
|
|
||||||
pr_err("%s(): could not handle process %s (%d) "
|
|
||||||
"chain stack overflow, error %d\n",
|
|
||||||
__func__, current->comm, current->pid, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
to_copy = frames_size;
|
|
||||||
BUG_ON(to_copy < 0);
|
|
||||||
from = stacks->pcsp_hi.PCSP_hi_ind;
|
|
||||||
BUG_ON(from < regs->copyed.pcs_size);
|
|
||||||
dst = (void *)stacks->pcsp_lo.PCSP_lo_base + from;
|
|
||||||
DebugUST("chain stack at user from %px, ind 0x%x, "
|
|
||||||
"frames size to copy 0x%lx\n",
|
|
||||||
dst, stacks->pcsp_hi.PCSP_hi_ind, to_copy);
|
|
||||||
there_are = stacks->pcsp_hi.PCSP_hi_size - from;
|
|
||||||
if (there_are < to_copy) {
|
|
||||||
pr_err("%s(): user chain stack overflow, there are 0x%lx "
|
|
||||||
"to copy need 0x%lx, not yet implemented\n",
|
|
||||||
__func__, there_are, to_copy);
|
|
||||||
BUG_ON(true);
|
|
||||||
}
|
|
||||||
if (likely(to_copy > 0)) {
|
|
||||||
ret = kvm_copy_user_stack_from_kernel(dst, src, to_copy, true);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("%s(): chain stack copying from kernel %px "
|
|
||||||
"to user %px, size 0x%lx failed, error %d\n",
|
|
||||||
__func__, src, dst, to_copy, ret);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
regs->copyed.pcs_injected_frames_size = to_copy;
|
|
||||||
/* increment chain stack pointer */
|
|
||||||
stacks->pcsp_hi.PCSP_hi_ind += to_copy;
|
|
||||||
} else {
|
|
||||||
BUG_ON(true);
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
failed:
|
|
||||||
if (DEBUG_USER_STACKS_MODE)
|
|
||||||
debug_ustacks = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void kvm_get_mm_notifier(thread_info_t *ti, struct mm_struct *mm);
|
extern void kvm_get_mm_notifier(thread_info_t *ti, struct mm_struct *mm);
|
||||||
|
|
||||||
/**
|
|
||||||
* user_hw_stacks_prepare - prepare user hardware stacks that have been
|
|
||||||
* SPILLed to kernel back to user space
|
|
||||||
* @stacks - saved user stack registers
|
|
||||||
* @cur_window_q - size of current window in procedure stack,
|
|
||||||
* needed only if @copy_full is not set
|
|
||||||
* @syscall - true if called upon direct system call exit (no signal handlers)
|
|
||||||
*
|
|
||||||
* This does two things:
|
|
||||||
*
|
|
||||||
* 1) It is possible that upon kernel entry pcshtp == 0 in some cases:
|
|
||||||
* - user signal handler had pcshtp==0x20 before return to sigreturn()
|
|
||||||
* - user context had pcshtp==0x20 before return to makecontext_trampoline()
|
|
||||||
* - chain stack underflow happened
|
|
||||||
* So it is possible in sigreturn() and traps, but not in system calls.
|
|
||||||
* If we are using the trick with return to FILL user hardware stacks than
|
|
||||||
* we must have frame in chain stack to return to. So in this case kernel's
|
|
||||||
* chain stack is moved up by one frame (0x20 bytes).
|
|
||||||
* We also fill the new frame with actual user data and update stacks->pcshtp,
|
|
||||||
* this is needed to keep the coherent state where saved stacks->pcshtp values
|
|
||||||
* shows how much data from user space has been spilled to kernel space.
|
|
||||||
*
|
|
||||||
* 2) It is not possible to always FILL all of user data that have been
|
|
||||||
* SPILLed to kernel stacks. So we manually copy the leftovers that can
|
|
||||||
* not be FILLed to user space.
|
|
||||||
* This copy does not update stacks->pshtp and stacks->pcshtp. Main reason
|
|
||||||
* is signals: if a signal arrives after copying then it must see a coherent
|
|
||||||
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
|
||||||
* data from user space has been spilled to kernel space.
|
|
||||||
*/
|
|
||||||
static __always_inline int kvm_user_hw_stacks_prepare(
|
|
||||||
struct e2k_stacks *stacks, pt_regs_t *regs,
|
|
||||||
u64 cur_window_q, enum restore_caller from, int syscall)
|
|
||||||
{
|
|
||||||
e2k_pcshtp_t u_pcshtp = stacks->pcshtp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
BUG_ON(!kvm_trap_user_mode(regs));
|
|
||||||
|
|
||||||
BUG_ON(from & FROM_PV_VCPU_MODE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 1) Make sure there is free space in kernel chain stack to return to
|
|
||||||
*/
|
|
||||||
if (!syscall && u_pcshtp == 0) {
|
|
||||||
DebugUST("%s(): PCSHTP is empty\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 2) User data copying will be done some later at
|
|
||||||
* kvm_prepare_user_hv_stacks()
|
|
||||||
*/
|
|
||||||
ret = kvm_user_hw_stacks_copy(regs);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("%s(): copying of hardware stacks failed< error %d\n",
|
|
||||||
__func__, ret);
|
|
||||||
do_exit(SIGKILL);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
kvm_ret_from_fork_prepare_hv_stacks(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
return kvm_user_hw_stacks_copy(regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
static __always_inline void
|
||||||
kvm_jump_to_ttable_entry(struct pt_regs *regs, enum restore_caller from)
|
kvm_jump_to_ttable_entry(struct pt_regs *regs, enum restore_caller from)
|
||||||
{
|
{
|
||||||
@ -823,55 +338,12 @@ preserve_user_hw_stacks_to_copy(e2k_stacks_t *u_stacks,
|
|||||||
kvm_preserve_user_hw_stacks_to_copy(u_stacks, cur_stacks);
|
kvm_preserve_user_hw_stacks_to_copy(u_stacks, cur_stacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
|
||||||
{
|
|
||||||
kvm_kernel_hw_stack_frames_copy(dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
kvm_collapse_kernel_ps(dst, src, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
kvm_collapse_kernel_pcs(dst, src, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
user_hw_stacks_copy(struct e2k_stacks *stacks,
|
|
||||||
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
|
||||||
{
|
|
||||||
return kvm_user_hw_stacks_copy(regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void host_user_hw_stacks_prepare(
|
|
||||||
struct e2k_stacks *stacks, pt_regs_t *regs,
|
|
||||||
u64 cur_window_q, enum restore_caller from, int syscall)
|
|
||||||
{
|
|
||||||
if (regs->sys_num == __NR_e2k_longjmp2) {
|
|
||||||
/* hardware stacks already are prepared */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
kvm_user_hw_stacks_prepare(stacks, regs, cur_window_q,
|
|
||||||
from, syscall);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
static __always_inline void
|
||||||
host_exit_to_usermode_loop(struct pt_regs *regs, bool syscall, bool has_signal)
|
host_exit_to_usermode_loop(struct pt_regs *regs, bool syscall, bool has_signal)
|
||||||
{
|
{
|
||||||
/* native & guest kernels cannot be as host */
|
/* native & guest kernels cannot be as host */
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
|
||||||
ret_from_fork_prepare_hv_stacks(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
return kvm_ret_from_fork_prepare_hv_stacks(regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
static __always_inline void
|
||||||
jump_to_ttable_entry(struct pt_regs *regs, enum restore_caller from)
|
jump_to_ttable_entry(struct pt_regs *regs, enum restore_caller from)
|
||||||
{
|
{
|
||||||
@ -885,8 +357,6 @@ virt_cpu_thread_init(struct task_struct *boot_task)
|
|||||||
|
|
||||||
KVM_GET_VCPU_STATE_BASE(vcpu_state_base);
|
KVM_GET_VCPU_STATE_BASE(vcpu_state_base);
|
||||||
task_thread_info(boot_task)->vcpu_state_base = vcpu_state_base;
|
task_thread_info(boot_task)->vcpu_state_base = vcpu_state_base;
|
||||||
if (!IS_HV_GM())
|
|
||||||
kvm_vcpu_boot_thread_init(boot_task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
41
arch/e2k/include/asm/kvm/guest/trace-defs.h
Normal file
41
arch/e2k/include/asm/kvm/guest/trace-defs.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef _E2K_KVM_GUEST_TRACE_DEFS_H_
|
||||||
|
#define _E2K_KVM_GUEST_TRACE_DEFS_H_
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <asm/mmu_types.h>
|
||||||
|
#include <asm/pgtable_def.h>
|
||||||
|
#include <asm/kvm/hypercall.h>
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
trace_kvm_get_gva_spt_translation(e2k_addr_t address,
|
||||||
|
pgdval_t *pgd, pudval_t *pud, pmdval_t *pmd, pteval_t *pte, int *pt_level)
|
||||||
|
{
|
||||||
|
mmu_spt_trans_t spt_trans;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = HYPERVISOR_get_spt_translation(address, &spt_trans);
|
||||||
|
if (unlikely(ret != 0)) {
|
||||||
|
pr_err("%s() : host could not get guest address 0x%lx "
|
||||||
|
"translation at SPTs, error %d\n",
|
||||||
|
__func__, address, ret);
|
||||||
|
*pgd = -1;
|
||||||
|
*pt_level = E2K_PGD_LEVEL_NUM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*pt_level = spt_trans.pt_levels;
|
||||||
|
if (*pt_level <= E2K_PGD_LEVEL_NUM) {
|
||||||
|
*pgd = spt_trans.pgd;
|
||||||
|
}
|
||||||
|
if (*pt_level <= E2K_PUD_LEVEL_NUM) {
|
||||||
|
*pud = spt_trans.pud;
|
||||||
|
}
|
||||||
|
if (*pt_level <= E2K_PMD_LEVEL_NUM) {
|
||||||
|
*pmd = spt_trans.pmd;
|
||||||
|
}
|
||||||
|
if (*pt_level <= E2K_PTE_LEVEL_NUM) {
|
||||||
|
*pte = spt_trans.pte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _E2K_KVM_GUEST_TRACE_DEFS_H_ */
|
308
arch/e2k/include/asm/kvm/guest/trace-hw-stacks.h
Normal file
308
arch/e2k/include/asm/kvm/guest/trace-hw-stacks.h
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
#if !defined(_KVM_GUEST_TRACE_COPY_HW_STACKS_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
|
#define _KVM_GUEST_TRACE_COPY_HW_STACKS_H
|
||||||
|
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
#include <linux/hugetlb.h>
|
||||||
|
|
||||||
|
#include <asm/trace-defs.h>
|
||||||
|
#include <asm/trace_pgtable-v2.h>
|
||||||
|
#include <asm/trace_pgtable-v6.h>
|
||||||
|
#include <asm/pgtable_def.h>
|
||||||
|
#include <asm/kvm/guest/trace-defs.h>
|
||||||
|
|
||||||
|
#undef TRACE_SYSTEM
|
||||||
|
#define TRACE_SYSTEM guest
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
guest_copy_hw_stack,
|
||||||
|
|
||||||
|
TP_PROTO(void *dst, void *src, unsigned long size, bool is_chain),
|
||||||
|
|
||||||
|
TP_ARGS(dst, src, size, is_chain),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( void *, dst )
|
||||||
|
__field( void *, src )
|
||||||
|
__field( u64, size )
|
||||||
|
__field( bool, is_chain )
|
||||||
|
__field( pgdval_t, dst_pgd )
|
||||||
|
__field( pudval_t, dst_pud )
|
||||||
|
__field( pmdval_t, dst_pmd )
|
||||||
|
__field( pteval_t, dst_pte )
|
||||||
|
__field( int, dst_pt_level )
|
||||||
|
__field( pgdval_t, src_pgd )
|
||||||
|
__field( pudval_t, src_pud )
|
||||||
|
__field( pmdval_t, src_pmd )
|
||||||
|
__field( pteval_t, src_pte )
|
||||||
|
__field( int, src_pt_level )
|
||||||
|
__field( pgdval_t, dst_spt_pgd )
|
||||||
|
__field( pudval_t, dst_spt_pud )
|
||||||
|
__field( pmdval_t, dst_spt_pmd )
|
||||||
|
__field( pteval_t, dst_spt_pte )
|
||||||
|
__field( int, dst_spt_level )
|
||||||
|
__field( pgdval_t, src_spt_pgd )
|
||||||
|
__field( pudval_t, src_spt_pud )
|
||||||
|
__field( pmdval_t, src_spt_pmd )
|
||||||
|
__field( pteval_t, src_spt_pte )
|
||||||
|
__field( int, src_spt_level )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dst = dst;
|
||||||
|
__entry->src = src;
|
||||||
|
__entry->size = size;
|
||||||
|
__entry->is_chain = is_chain;
|
||||||
|
|
||||||
|
trace_get_va_translation(current->mm, (e2k_addr_t)dst,
|
||||||
|
&__entry->dst_pgd, &__entry->dst_pud, &__entry->dst_pmd,
|
||||||
|
&__entry->dst_pte, &__entry->dst_pt_level);
|
||||||
|
trace_kvm_get_gva_spt_translation((e2k_addr_t)dst,
|
||||||
|
&__entry->dst_spt_pgd, &__entry->dst_spt_pud,
|
||||||
|
&__entry->dst_spt_pmd, &__entry->dst_spt_pte,
|
||||||
|
&__entry->dst_spt_level);
|
||||||
|
|
||||||
|
trace_get_va_translation(current->mm, (e2k_addr_t)src,
|
||||||
|
&__entry->src_pgd, &__entry->src_pud, &__entry->src_pmd,
|
||||||
|
&__entry->src_pte, &__entry->src_pt_level);
|
||||||
|
trace_kvm_get_gva_spt_translation((e2k_addr_t)src,
|
||||||
|
&__entry->src_spt_pgd, &__entry->src_spt_pud,
|
||||||
|
&__entry->src_spt_pmd, &__entry->src_spt_pte,
|
||||||
|
&__entry->src_spt_level);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("copy %s stack guest user <- guest kernel: dst %px "
|
||||||
|
"src %px size %llx\n"
|
||||||
|
" user guest dst %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" user guest dst spt %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" kernel guest src %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" kernel guest src spt %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s",
|
||||||
|
(__entry->is_chain) ? "chain" : "procedure",
|
||||||
|
__entry->dst,
|
||||||
|
__entry->src,
|
||||||
|
__entry->size,
|
||||||
|
__entry->dst,
|
||||||
|
(__entry->dst_pt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pgd,
|
||||||
|
__entry->dst_pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pud,
|
||||||
|
__entry->dst_pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pmd,
|
||||||
|
__entry->dst_pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pte,
|
||||||
|
__entry->dst_pt_level <= E2K_PTE_LEVEL_NUM),
|
||||||
|
__entry->dst,
|
||||||
|
(__entry->dst_spt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_spt_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_spt_pgd,
|
||||||
|
__entry->dst_spt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->dst_spt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_spt_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_spt_pud,
|
||||||
|
__entry->dst_spt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->dst_spt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_spt_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_spt_pmd,
|
||||||
|
__entry->dst_spt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->dst_spt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->dst_spt_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_spt_pte,
|
||||||
|
__entry->dst_spt_level <= E2K_PTE_LEVEL_NUM),
|
||||||
|
__entry->src,
|
||||||
|
(__entry->src_pt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->src_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pgd,
|
||||||
|
__entry->src_pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->src_pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->src_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pud,
|
||||||
|
__entry->src_pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->src_pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->src_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pmd,
|
||||||
|
__entry->src_pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->src_pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->src_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pte,
|
||||||
|
__entry->src_pt_level <= E2K_PTE_LEVEL_NUM),
|
||||||
|
__entry->src,
|
||||||
|
(__entry->src_spt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->src_spt_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_spt_pgd,
|
||||||
|
__entry->src_spt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->src_spt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->src_spt_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_spt_pud,
|
||||||
|
__entry->src_spt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->src_spt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->src_spt_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_spt_pmd,
|
||||||
|
__entry->src_spt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->src_spt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->src_spt_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_spt_pte,
|
||||||
|
__entry->src_spt_level <= E2K_PTE_LEVEL_NUM)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
guest_proc_stack_frame,
|
||||||
|
|
||||||
|
TP_PROTO(kernel_mem_ps_t *ps_base, kernel_mem_ps_t *ps_frame),
|
||||||
|
|
||||||
|
TP_ARGS(ps_base, ps_frame),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( kernel_mem_ps_t *, ps_base )
|
||||||
|
__field_struct( kernel_mem_ps_t, ps_frame )
|
||||||
|
__field( pgprotval_t, dtlb_entry )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->ps_base = ps_base;
|
||||||
|
__entry->ps_frame = *ps_frame;
|
||||||
|
__entry->dtlb_entry = HYPERVISOR_mmu_probe((e2k_addr_t)ps_base,
|
||||||
|
KVM_MMU_PROBE_ENTRY);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(" %px (dtlb 0x%016lx) : 0x%016lx 0x%016lx",
|
||||||
|
__entry->ps_base, __entry->dtlb_entry,
|
||||||
|
__entry->ps_frame.word_lo,
|
||||||
|
__entry->ps_frame.word_hi)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
guest_chain_stack_frame,
|
||||||
|
|
||||||
|
TP_PROTO(e2k_mem_crs_t *pcs_base, e2k_mem_crs_t *pcs_frame),
|
||||||
|
|
||||||
|
TP_ARGS(pcs_base, pcs_frame),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( e2k_mem_crs_t *, pcs_base )
|
||||||
|
__field_struct( e2k_mem_crs_t, pcs_frame )
|
||||||
|
__field( pgprotval_t, dtlb_entry )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->pcs_base = pcs_base;
|
||||||
|
__entry->pcs_frame = *pcs_frame;
|
||||||
|
__entry->dtlb_entry = HYPERVISOR_mmu_probe((e2k_addr_t)pcs_base,
|
||||||
|
KVM_MMU_PROBE_ENTRY);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(" %px (dtlb 0x%016lx) : 0x%016llx 0x%016llx "
|
||||||
|
"0x%016llx 0x%016llx",
|
||||||
|
__entry->pcs_base, __entry->dtlb_entry,
|
||||||
|
__entry->pcs_frame.cr0_lo.CR0_lo_half,
|
||||||
|
__entry->pcs_frame.cr0_hi.CR0_hi_half,
|
||||||
|
__entry->pcs_frame.cr1_lo.CR1_lo_half,
|
||||||
|
__entry->pcs_frame.cr1_hi.CR1_hi_half)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
guest_va_tlb_state,
|
||||||
|
|
||||||
|
TP_PROTO(e2k_addr_t address),
|
||||||
|
|
||||||
|
TP_ARGS(address),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( e2k_addr_t, address )
|
||||||
|
__field( tlb_tag_t, set0_tag )
|
||||||
|
__field_struct( pte_t, set0_entry )
|
||||||
|
__field( tlb_tag_t, set1_tag )
|
||||||
|
__field_struct( pte_t, set1_entry )
|
||||||
|
__field( tlb_tag_t, set2_tag )
|
||||||
|
__field_struct( pte_t, set2_entry )
|
||||||
|
__field( tlb_tag_t, set3_tag )
|
||||||
|
__field_struct( pte_t, set3_entry )
|
||||||
|
__field( tlb_tag_t, setH_tag )
|
||||||
|
__field_struct( pte_t, setH_entry )
|
||||||
|
__field( u64, dtlb_entry )
|
||||||
|
__field( unsigned long, mmu_pptb )
|
||||||
|
__field( unsigned long, mmu_pid )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->address = address;
|
||||||
|
__entry->set0_tag = HYPERVISOR_get_tlb_set_tag(address, 0, false);
|
||||||
|
pte_val(__entry->set0_entry) =
|
||||||
|
HYPERVISOR_get_tlb_set_entry(address, 0, false);
|
||||||
|
__entry->set1_tag = HYPERVISOR_get_tlb_set_tag(address, 1, false);
|
||||||
|
pte_val(__entry->set1_entry) =
|
||||||
|
HYPERVISOR_get_tlb_set_entry(address, 1, false);
|
||||||
|
__entry->set2_tag = HYPERVISOR_get_tlb_set_tag(address, 2, false);
|
||||||
|
pte_val(__entry->set2_entry) =
|
||||||
|
HYPERVISOR_get_tlb_set_entry(address, 2, false);
|
||||||
|
__entry->set3_tag = HYPERVISOR_get_tlb_set_tag(address, 3, false);
|
||||||
|
pte_val(__entry->set3_entry) =
|
||||||
|
HYPERVISOR_get_tlb_set_entry(address, 3, false);
|
||||||
|
__entry->setH_tag = HYPERVISOR_get_tlb_set_tag(address, 3, true);
|
||||||
|
pte_val(__entry->setH_entry) =
|
||||||
|
HYPERVISOR_get_tlb_set_entry(address, 3, true);
|
||||||
|
__entry->dtlb_entry = HYPERVISOR_mmu_probe(address,
|
||||||
|
KVM_MMU_PROBE_ENTRY);
|
||||||
|
__entry->mmu_pptb = HYPERVISOR_get_host_mmu_pptb();
|
||||||
|
__entry->mmu_pid = HYPERVISOR_get_host_mmu_pid();
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(" 0x%016lx : dtlb 0x%016llx U_PPTB 0x%lx PID 0x%lx\n"
|
||||||
|
" TLB set #0 tag 0x%016lx entry 0x%016lx\n"
|
||||||
|
" TLB set #1 tag 0x%016lx entry 0x%016lx\n"
|
||||||
|
" TLB set #2 tag 0x%016lx entry 0x%016lx\n"
|
||||||
|
" TLB set #3 tag 0x%016lx entry 0x%016lx\n"
|
||||||
|
" TLB set #H tag 0x%016lx entry 0x%016lx",
|
||||||
|
__entry->address, __entry->dtlb_entry,
|
||||||
|
__entry->mmu_pptb, __entry->mmu_pid,
|
||||||
|
__entry->set0_tag, pte_val(__entry->set0_entry),
|
||||||
|
__entry->set1_tag, pte_val(__entry->set1_entry),
|
||||||
|
__entry->set2_tag, pte_val(__entry->set2_entry),
|
||||||
|
__entry->set3_tag, pte_val(__entry->set3_entry),
|
||||||
|
__entry->setH_tag, pte_val(__entry->setH_entry)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif /* _KVM_GUEST_TRACE_COPY_HW_STACKS_H */
|
||||||
|
|
||||||
|
#undef TRACE_INCLUDE_PATH
|
||||||
|
#define TRACE_INCLUDE_PATH ../../arch/e2k/include/asm/kvm/guest
|
||||||
|
#undef TRACE_INCLUDE_FILE
|
||||||
|
#define TRACE_INCLUDE_FILE trace-hw-stacks
|
||||||
|
|
||||||
|
/* This part must be outside protection */
|
||||||
|
#include <trace/define_trace.h>
|
@ -205,8 +205,10 @@ static inline unsigned long generic_hypercall6(unsigned long nr,
|
|||||||
/* time (cycles) */
|
/* time (cycles) */
|
||||||
#define KVM_HCALL_GET_GUEST_RUNNING_TIME 6 /* get running time of guest */
|
#define KVM_HCALL_GET_GUEST_RUNNING_TIME 6 /* get running time of guest */
|
||||||
/* VCPU at cycles */
|
/* VCPU at cycles */
|
||||||
#define KVM_HCALL_GET_VCPU_START_THREAD 8 /* register on host the guest */
|
#define KVM_HCALL_GET_HOST_MMU_PPTB 7 /* get the host MMU PPTB register */
|
||||||
/* kernel VCPU booting thread */
|
/* state */
|
||||||
|
#define KVM_HCALL_GET_TLB_SET_TAG 8 /* get tag of TLB line set */
|
||||||
|
#define KVM_HCALL_GET_TLB_SET_ENTRY 9 /* get entry of TLB line set */
|
||||||
#define KVM_HCALL_UPDATE_PSP_HI 10 /* write updated value to */
|
#define KVM_HCALL_UPDATE_PSP_HI 10 /* write updated value to */
|
||||||
/* PSP_hi register */
|
/* PSP_hi register */
|
||||||
#define KVM_HCALL_UPDATE_PCSP_HI 11 /* write updated value to */
|
#define KVM_HCALL_UPDATE_PCSP_HI 11 /* write updated value to */
|
||||||
@ -215,6 +217,8 @@ static inline unsigned long generic_hypercall6(unsigned long nr,
|
|||||||
/* guest as task */
|
/* guest as task */
|
||||||
#define KVM_HCALL_UPDATE_WD_PSIZE 13 /* write updated psize field */
|
#define KVM_HCALL_UPDATE_WD_PSIZE 13 /* write updated psize field */
|
||||||
/* to the WD register */
|
/* to the WD register */
|
||||||
|
#define KVM_HCALL_GET_HOST_MMU_PID 14 /* get the host MMU PID register */
|
||||||
|
/* state */
|
||||||
#define KVM_HCALL_MOVE_TAGGED_DATA 15 /* move quad value from to */
|
#define KVM_HCALL_MOVE_TAGGED_DATA 15 /* move quad value from to */
|
||||||
#define KVM_HCALL_UNFREEZE_TRAPS 16 /* unfreeze TIRs & trap */
|
#define KVM_HCALL_UNFREEZE_TRAPS 16 /* unfreeze TIRs & trap */
|
||||||
/* cellar */
|
/* cellar */
|
||||||
@ -303,9 +307,27 @@ HYPERVISOR_get_guest_running_time(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long
|
static inline unsigned long
|
||||||
HYPERVISOR_get_vcpu_start_thread(void)
|
HYPERVISOR_get_tlb_set_tag(e2k_addr_t va, int set_no, bool huge)
|
||||||
{
|
{
|
||||||
return light_hypercall0(KVM_HCALL_GET_VCPU_START_THREAD);
|
return light_hypercall3(KVM_HCALL_GET_TLB_SET_TAG, va, set_no, huge);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
HYPERVISOR_get_tlb_set_entry(e2k_addr_t va, int set_no, bool huge)
|
||||||
|
{
|
||||||
|
return light_hypercall3(KVM_HCALL_GET_TLB_SET_ENTRY, va, set_no, huge);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
HYPERVISOR_get_host_mmu_pptb(void)
|
||||||
|
{
|
||||||
|
return light_hypercall0(KVM_HCALL_GET_HOST_MMU_PPTB);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
HYPERVISOR_get_host_mmu_pid(void)
|
||||||
|
{
|
||||||
|
return light_hypercall0(KVM_HCALL_GET_HOST_MMU_PID);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long
|
static inline unsigned long
|
||||||
@ -611,6 +633,8 @@ HYPERVISOR_switch_to_expanded_guest_chain_stack(long delta_size,
|
|||||||
/* in page tables and flush tlb */
|
/* in page tables and flush tlb */
|
||||||
#define KVM_HCALL_SYNC_ADDR_RANGE 135 /* sync ptes in page */
|
#define KVM_HCALL_SYNC_ADDR_RANGE 135 /* sync ptes in page */
|
||||||
/* tables without flushing tlb */
|
/* tables without flushing tlb */
|
||||||
|
#define KVM_HCALL_GET_SPT_TRANSLATION 137 /* get full translation of guest */
|
||||||
|
/* address at shadow PTs */
|
||||||
#define KVM_HCALL_RECOVERY_FAULTED_TAGGED_STORE 141
|
#define KVM_HCALL_RECOVERY_FAULTED_TAGGED_STORE 141
|
||||||
/* recovery faulted store */
|
/* recovery faulted store */
|
||||||
/* tagged value operations */
|
/* tagged value operations */
|
||||||
@ -1252,10 +1276,11 @@ HYPERVISOR_guest_csd_lock_try_wait(void *lock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long
|
static inline unsigned long
|
||||||
HYPERVISOR_pt_atomic_update(unsigned long gpa, void __user *old_gpte,
|
HYPERVISOR_pt_atomic_update(int gmmid_nr, unsigned long gpa,
|
||||||
unsigned atomic_op, unsigned long prot_mask)
|
void __user *old_gpte,
|
||||||
|
unsigned atomic_op, unsigned long prot_mask)
|
||||||
{
|
{
|
||||||
return generic_hypercall4(KVM_HCALL_PT_ATOMIC_UPDATE,
|
return generic_hypercall5(KVM_HCALL_PT_ATOMIC_UPDATE, (int)gmmid_nr,
|
||||||
gpa, (unsigned long)old_gpte, atomic_op, prot_mask);
|
gpa, (unsigned long)old_gpte, atomic_op, prot_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1400,6 +1425,28 @@ HYPERVISOR_host_printk(char *msg, int size)
|
|||||||
return generic_hypercall2(KVM_HCALL_HOST_PRINTK, (unsigned long)msg,
|
return generic_hypercall2(KVM_HCALL_HOST_PRINTK, (unsigned long)msg,
|
||||||
(unsigned long)size);
|
(unsigned long)size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The guest virtual address info of full track of translation
|
||||||
|
* at the host shadow PTs
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct mmu_spt_trans {
|
||||||
|
int pt_levels; /* the last significant level of PT */
|
||||||
|
unsigned long pgd;
|
||||||
|
unsigned long pud;
|
||||||
|
unsigned long pmd;
|
||||||
|
unsigned long pte;
|
||||||
|
} mmu_spt_trans_t;
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
HYPERVISOR_get_spt_translation(e2k_addr_t address,
|
||||||
|
mmu_spt_trans_t __user *trans_info)
|
||||||
|
{
|
||||||
|
return generic_hypercall2(KVM_HCALL_GET_SPT_TRANSLATION, address,
|
||||||
|
(unsigned long)trans_info);
|
||||||
|
}
|
||||||
|
|
||||||
static inline unsigned long
|
static inline unsigned long
|
||||||
HYPERVISOR_print_guest_kernel_ptes(e2k_addr_t address)
|
HYPERVISOR_print_guest_kernel_ptes(e2k_addr_t address)
|
||||||
{
|
{
|
||||||
|
@ -124,6 +124,16 @@ static inline void reset_spt_gpa_fault(struct kvm_vcpu *vcpu)
|
|||||||
vcpu->arch.mmu.spt_gpa_fault = false;
|
vcpu->arch.mmu.spt_gpa_fault = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned long get_mmu_u_pptb_reg(void)
|
||||||
|
{
|
||||||
|
return NATIVE_READ_MMU_U_PPTB_REG();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long get_mmu_pid_reg(void)
|
||||||
|
{
|
||||||
|
return NATIVE_READ_MMU_PID_REG();
|
||||||
|
}
|
||||||
|
|
||||||
static inline hpa_t
|
static inline hpa_t
|
||||||
kvm_get_gp_phys_root(struct kvm_vcpu *vcpu)
|
kvm_get_gp_phys_root(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
@ -286,6 +296,11 @@ static inline struct kvm_mmu_page *page_header(hpa_t shadow_page)
|
|||||||
return (struct kvm_mmu_page *)page_private(page);
|
return (struct kvm_mmu_page *)page_private(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void kvm_get_spt_translation(struct kvm_vcpu *vcpu, e2k_addr_t address,
|
||||||
|
pgdval_t *pgd, pudval_t *pud, pmdval_t *pmd,
|
||||||
|
pteval_t *pte, int *pt_level);
|
||||||
|
extern unsigned long kvm_get_gva_to_hva(struct kvm_vcpu *vcpu, gva_t gva);
|
||||||
|
|
||||||
static inline gpa_t kvm_hva_to_gpa(struct kvm *kvm, unsigned long hva)
|
static inline gpa_t kvm_hva_to_gpa(struct kvm *kvm, unsigned long hva)
|
||||||
{
|
{
|
||||||
struct kvm_memslots *slots;
|
struct kvm_memslots *slots;
|
||||||
|
@ -31,8 +31,8 @@ struct kvm_page_track_notifier_node {
|
|||||||
* @new: the data was written to the address.
|
* @new: the data was written to the address.
|
||||||
* @bytes: the written length.
|
* @bytes: the written length.
|
||||||
*/
|
*/
|
||||||
void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
|
void (*track_write)(struct kvm_vcpu *vcpu, struct gmm_struct *gmm,
|
||||||
int bytes);
|
gpa_t gpa, const u8 *new, int bytes);
|
||||||
/*
|
/*
|
||||||
* It is called when memory slot is being moved or removed
|
* It is called when memory slot is being moved or removed
|
||||||
* users can drop write-protection for the pages in that memory slot
|
* users can drop write-protection for the pages in that memory slot
|
||||||
@ -68,8 +68,8 @@ kvm_page_track_register_notifier(struct kvm *kvm,
|
|||||||
void
|
void
|
||||||
kvm_page_track_unregister_notifier(struct kvm *kvm,
|
kvm_page_track_unregister_notifier(struct kvm *kvm,
|
||||||
struct kvm_page_track_notifier_node *n);
|
struct kvm_page_track_notifier_node *n);
|
||||||
void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
|
void kvm_page_track_write(struct kvm_vcpu *vcpu, struct gmm_struct *gmm,
|
||||||
int bytes);
|
gpa_t gpa, const u8 *new, int bytes);
|
||||||
void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot);
|
void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot);
|
||||||
#else /* ! CONFIG_KVM_HV_MMU */
|
#else /* ! CONFIG_KVM_HV_MMU */
|
||||||
static inline void kvm_page_track_init(struct kvm *kvm)
|
static inline void kvm_page_track_init(struct kvm *kvm)
|
||||||
|
@ -15,16 +15,6 @@
|
|||||||
#include <asm/kvm/page.h>
|
#include <asm/kvm/page.h>
|
||||||
#include <asm/kvm/switch.h>
|
#include <asm/kvm/switch.h>
|
||||||
|
|
||||||
#undef DEBUG_KVM_GUEST_STACKS_MODE
|
|
||||||
#undef DebugGUST
|
|
||||||
#define DEBUG_KVM_GUEST_STACKS_MODE 0 /* guest user stacks */
|
|
||||||
/* copy debug */
|
|
||||||
#define DebugGUST(fmt, args...) \
|
|
||||||
({ \
|
|
||||||
if (DEBUG_KVM_GUEST_STACKS_MODE) \
|
|
||||||
pr_info("%s(): " fmt, __func__, ##args); \
|
|
||||||
})
|
|
||||||
|
|
||||||
extern void kvm_clear_host_thread_info(thread_info_t *ti);
|
extern void kvm_clear_host_thread_info(thread_info_t *ti);
|
||||||
extern gthread_info_t *create_guest_start_thread_info(struct kvm_vcpu *vcpu);
|
extern gthread_info_t *create_guest_start_thread_info(struct kvm_vcpu *vcpu);
|
||||||
extern int kvm_resume_vm_thread(void);
|
extern int kvm_resume_vm_thread(void);
|
||||||
@ -233,416 +223,6 @@ void go2guest(long fn, bool priv_guest);
|
|||||||
INIT_HOST_VCPU_STATE_GREG_COPY(__ti, vcpu); \
|
INIT_HOST_VCPU_STATE_GREG_COPY(__ti, vcpu); \
|
||||||
})
|
})
|
||||||
|
|
||||||
static inline void
|
|
||||||
prepare_pv_vcpu_inject_stacks(struct kvm_vcpu *vcpu, pt_regs_t *regs)
|
|
||||||
{
|
|
||||||
e2k_stacks_t *stacks, *g_stacks;
|
|
||||||
gthread_info_t *gti = pv_vcpu_get_gti(vcpu);
|
|
||||||
|
|
||||||
if (regs->g_stacks_valid) {
|
|
||||||
/* already prepared */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* all stacks at empty state, because of guest user recursion */
|
|
||||||
/* of trap/system calls can not be */
|
|
||||||
g_stacks = ®s->g_stacks;
|
|
||||||
g_stacks->usd_lo = gti->g_usd_lo;
|
|
||||||
g_stacks->usd_hi = gti->g_usd_hi;
|
|
||||||
g_stacks->top = gti->g_sbr.SBR_base;
|
|
||||||
g_stacks->psp_lo = gti->g_psp_lo;
|
|
||||||
g_stacks->psp_hi = gti->g_psp_hi;
|
|
||||||
g_stacks->pcsp_lo = gti->g_pcsp_lo;
|
|
||||||
g_stacks->pcsp_hi = gti->g_pcsp_hi;
|
|
||||||
|
|
||||||
/* pshtp & pcshtp from guest user stack real state upon trap/syscall */
|
|
||||||
stacks = ®s->stacks;
|
|
||||||
g_stacks->pshtp = stacks->pshtp;
|
|
||||||
g_stacks->pcshtp = stacks->pcshtp;
|
|
||||||
|
|
||||||
regs->g_stacks_valid = true;
|
|
||||||
regs->g_stacks_active = false;
|
|
||||||
regs->need_inject = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef EMULATE_EMPTY_CHAIN_STACK /* only to debug */
|
|
||||||
|
|
||||||
#ifdef EMULATE_EMPTY_CHAIN_STACK
|
|
||||||
static __always_inline void
|
|
||||||
pv_vcpu_emulate_empty_chain_staks(struct kvm_vcpu *vcpu, pt_regs_t *regs,
|
|
||||||
e2k_stacks_t *stacks, bool guest_user)
|
|
||||||
{
|
|
||||||
e2k_pcshtp_t pcshtp;
|
|
||||||
unsigned long flags;
|
|
||||||
e2k_pcsp_lo_t g_pcsp_lo, k_pcsp_lo;
|
|
||||||
e2k_pcsp_hi_t g_pcsp_hi, k_pcsp_hi;
|
|
||||||
e2k_mem_crs_t __user *g_cframe;
|
|
||||||
e2k_mem_crs_t *k_crs;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pcshtp = stacks->pcshtp;
|
|
||||||
if (!(guest_user && pcshtp <= 0x40))
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_pcsp_lo = regs->stacks.pcsp_lo;
|
|
||||||
g_pcsp_hi = regs->stacks.pcsp_hi;
|
|
||||||
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
NATIVE_FLUSHC;
|
|
||||||
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
k_pcsp_lo = NATIVE_NV_READ_PCSP_LO_REG();
|
|
||||||
BUG_ON(AS(k_pcsp_hi).ind != pcshtp);
|
|
||||||
|
|
||||||
k_crs = (e2k_mem_crs_t *) AS(k_pcsp_lo).base;
|
|
||||||
g_cframe = (e2k_mem_crs_t __user *) (AS(g_pcsp_lo).base +
|
|
||||||
AS(g_pcsp_hi).ind - pcshtp);
|
|
||||||
ret = user_hw_stack_frames_copy(g_cframe, k_crs, pcshtp, regs,
|
|
||||||
k_pcsp_hi.PCSP_hi_ind, true);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("%s(): copy to user stack failed\n", __func__);
|
|
||||||
BUG_ON(true);
|
|
||||||
}
|
|
||||||
k_pcsp_hi.PCSP_hi_ind -= pcshtp;
|
|
||||||
pcshtp = 0;
|
|
||||||
regs->stacks.pcshtp = pcshtp;
|
|
||||||
stacks->pcshtp = pcshtp;
|
|
||||||
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
}
|
|
||||||
#else /* !EMULATE_EMPTY_CHAIN_STACK */
|
|
||||||
static __always_inline void
|
|
||||||
pv_vcpu_emulate_empty_chain_staks(struct kvm_vcpu *vcpu, pt_regs_t *regs,
|
|
||||||
e2k_stacks_t *stacks, bool guest_user)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif /* EMULATE_EMPTY_CHAIN_STACK */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pv_vcpu_user_hw_stacks_copy - check size of user hardware stacks that have
|
|
||||||
* been SPILLed to kernel back to guest space
|
|
||||||
* @regs - saved guest user stack registers
|
|
||||||
* @cur_window_q - size of current window in procedure stack
|
|
||||||
*
|
|
||||||
* All guest user's stacks part were already copied to guest kernel stacks,
|
|
||||||
* so it need only check that it was full size and nothing to copy here
|
|
||||||
*/
|
|
||||||
static __always_inline int
|
|
||||||
pv_vcpu_user_hw_stacks_copy(pt_regs_t *regs, e2k_stacks_t *stacks,
|
|
||||||
u64 cur_window_q, bool guest_user)
|
|
||||||
{
|
|
||||||
e2k_psp_lo_t g_psp_lo = stacks->psp_lo,
|
|
||||||
k_psp_lo = current_thread_info()->k_psp_lo;
|
|
||||||
e2k_psp_hi_t g_psp_hi = stacks->psp_hi;
|
|
||||||
e2k_pcsp_lo_t g_pcsp_lo = stacks->pcsp_lo,
|
|
||||||
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
|
||||||
e2k_pcsp_hi_t g_pcsp_hi = stacks->pcsp_hi;
|
|
||||||
s64 g_pshtp_size, g_pcshtp_size, ps_copy_size, pcs_copy_size;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
DebugUST("guest kernel chain state: base 0x%llx ind 0x%x size 0x%x\n",
|
|
||||||
g_pcsp_lo.PCSP_lo_base, g_pcsp_hi.PCSP_hi_ind,
|
|
||||||
g_pcsp_hi.PCSP_hi_size);
|
|
||||||
DebugUST("guest kernel proc state: base 0x%llx ind 0x%x size 0x%x\n",
|
|
||||||
g_psp_lo.PSP_lo_base, g_psp_hi.PSP_hi_ind,
|
|
||||||
g_psp_hi.PSP_hi_size);
|
|
||||||
g_pshtp_size = GET_PSHTP_MEM_INDEX(stacks->pshtp);
|
|
||||||
g_pcshtp_size = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
|
||||||
DebugUST("guest kernel chain stack PCSHTP 0x%llx, "
|
|
||||||
"proc stack PSHTP 0x%llx cur window 0x%llx\n",
|
|
||||||
g_pcshtp_size, g_pshtp_size, cur_window_q);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: the current implementation of the guest user signal handler
|
|
||||||
* injection uses direct copying to guest hardware stacks.
|
|
||||||
* It is bad decision, needs to be corrected
|
|
||||||
KVM_BUG_ON(is_paging(current_thread_info()->vcpu) &&
|
|
||||||
(g_psp_lo.PSP_lo_base < GUEST_TASK_SIZE ||
|
|
||||||
g_pcsp_lo.PCSP_lo_base < GUEST_TASK_SIZE));
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate size of user's part to copy from kernel stacks
|
|
||||||
* into guest kernel stacks
|
|
||||||
*/
|
|
||||||
pcs_copy_size = get_pcs_copy_size(g_pcshtp_size);
|
|
||||||
ps_copy_size = get_ps_copy_size(cur_window_q, g_pshtp_size);
|
|
||||||
/* Make sure there is enough space in CF for the FILL */
|
|
||||||
BUG_ON((E2K_MAXCR_q - 4) * 16 < E2K_CF_MAX_FILL);
|
|
||||||
DebugUST("to copy chain stack 0x%llx, proc stack 0x%llx\n",
|
|
||||||
pcs_copy_size, ps_copy_size);
|
|
||||||
|
|
||||||
if (likely(pcs_copy_size <= 0 && ps_copy_size <= 0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (unlikely(pcs_copy_size > 0)) {
|
|
||||||
e2k_pcsp_hi_t k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
void __user *dst;
|
|
||||||
void *src;
|
|
||||||
|
|
||||||
/* Since SPILL'ed guest user data will be copyed to guest */
|
|
||||||
/* kernel stacks then cannot be any overflow of user's */
|
|
||||||
/* hardware stack. */
|
|
||||||
if (unlikely(AS(g_pcsp_hi).ind > AS(g_pcsp_hi).size)) {
|
|
||||||
pr_err("%s(): guest kernel chain stack overflow "
|
|
||||||
"(out of memory?): ind 0x%x size 0x%x\n",
|
|
||||||
__func__, g_pcsp_hi.PCSP_hi_ind,
|
|
||||||
g_pcsp_hi.PCSP_hi_size);
|
|
||||||
KVM_BUG_ON(true);
|
|
||||||
}
|
|
||||||
dst = (void __user *)(g_pcsp_lo.PCSP_lo_base +
|
|
||||||
g_pcsp_hi.PCSP_hi_ind);
|
|
||||||
if (!guest_user) {
|
|
||||||
/* stack index has been incremented on PCSHTP */
|
|
||||||
dst -= g_pcshtp_size;
|
|
||||||
}
|
|
||||||
src = (void *)k_pcsp_lo.PCSP_lo_base;
|
|
||||||
|
|
||||||
ret = user_hw_stack_frames_copy(dst, src, pcs_copy_size, regs,
|
|
||||||
k_pcsp_hi.PCSP_hi_ind, true);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
if (guest_user) {
|
|
||||||
g_pcsp_hi.PCSP_hi_ind += pcs_copy_size;
|
|
||||||
stacks->pcsp_hi = g_pcsp_hi;
|
|
||||||
DebugGUST("guest user chain stack frames copied from "
|
|
||||||
"host %px to guest kernel from %px size 0x%llx "
|
|
||||||
"PCSP.ind 0x%x\n",
|
|
||||||
src, dst, pcs_copy_size, g_pcsp_hi.PCSP_hi_ind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(ps_copy_size > 0)) {
|
|
||||||
e2k_psp_hi_t k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
|
||||||
void __user *dst;
|
|
||||||
void *src;
|
|
||||||
|
|
||||||
/* Since SPILL'ed guest user data will be copyed to guest */
|
|
||||||
/* kernel stacks then cannot be any overflow of user's */
|
|
||||||
/* hardware stack. */
|
|
||||||
if (unlikely(AS(g_psp_hi).ind > AS(g_psp_hi).size)) {
|
|
||||||
pr_err("%s(): guest kernel proc stack overflow "
|
|
||||||
"(out of memory?): ind 0x%x size 0x%x\n",
|
|
||||||
__func__, g_psp_hi.PSP_hi_ind,
|
|
||||||
g_psp_hi.PSP_hi_size);
|
|
||||||
KVM_BUG_ON(true);
|
|
||||||
}
|
|
||||||
dst = (void __user *)(g_psp_lo.PSP_lo_base +
|
|
||||||
g_psp_hi.PSP_hi_ind);
|
|
||||||
if (!guest_user) {
|
|
||||||
/* stack index has been incremented on PSHTP */
|
|
||||||
dst -= g_pshtp_size;
|
|
||||||
}
|
|
||||||
src = (void *)k_psp_lo.PSP_lo_base;
|
|
||||||
|
|
||||||
ret = user_hw_stack_frames_copy(dst, src, ps_copy_size, regs,
|
|
||||||
k_psp_hi.PSP_hi_ind, false);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
if (guest_user) {
|
|
||||||
g_psp_hi.PSP_hi_ind += ps_copy_size;
|
|
||||||
stacks->psp_hi = g_psp_hi;
|
|
||||||
DebugGUST("guest user proc stack frames copied from "
|
|
||||||
"host %px to guest kernel from %px size 0x%llx "
|
|
||||||
"PSP.ind 0x%x\n",
|
|
||||||
src, dst, ps_copy_size, g_psp_hi.PSP_hi_ind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pv_vcpu_user_hw_stacks_prepare - prepare guest user hardware stacks
|
|
||||||
that have been SPILLed to kernel back
|
|
||||||
to guest user space
|
|
||||||
* @regs - saved guest user stack registers
|
|
||||||
* @cur_window_q - size of current window in procedure stack
|
|
||||||
* @syscall - true if called upon direct system call exit (no signal handlers)
|
|
||||||
*
|
|
||||||
* This does two things:
|
|
||||||
*
|
|
||||||
* 1) It is possible that upon kernel entry pcshtp == 0 in some cases:
|
|
||||||
* - user signal handler had pcshtp==0x20 before return to sigreturn()
|
|
||||||
* - user context had pcshtp==0x20 before return to makecontext_trampoline()
|
|
||||||
* - chain stack underflow happened
|
|
||||||
* So it is possible in sigreturn() and traps, but not in system calls.
|
|
||||||
* If we are using the trick with return to FILL user hardware stacks than
|
|
||||||
* we must have frame in chain stack to return to. So in this case kernel's
|
|
||||||
* chain stack is moved up by one frame (0x20 bytes).
|
|
||||||
* We also fill the new frame with actual user data and update stacks->pcshtp,
|
|
||||||
* this is needed to keep the coherent state where saved stacks->pcshtp values
|
|
||||||
* shows how much data from user space has been spilled to kernel space.
|
|
||||||
*
|
|
||||||
* 2) It is not possible to always FILL all of user data that have been
|
|
||||||
* SPILLed to kernel stacks. So we manually copy the leftovers that can
|
|
||||||
* not be FILLed to user space.
|
|
||||||
* This copy does not update stacks->pshtp and stacks->pcshtp. Main reason
|
|
||||||
* is signals: if a signal arrives after copying then it must see a coherent
|
|
||||||
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
|
||||||
* data from user space has been spilled to kernel space.
|
|
||||||
*/
|
|
||||||
static __always_inline void
|
|
||||||
pv_vcpu_user_hw_stacks_prepare(struct kvm_vcpu *vcpu, pt_regs_t *regs,
|
|
||||||
u64 cur_window_q, enum restore_caller from, int syscall)
|
|
||||||
{
|
|
||||||
e2k_stacks_t *stacks;
|
|
||||||
e2k_pcshtp_t pcshtp;
|
|
||||||
bool guest_user;
|
|
||||||
bool paging = is_paging(vcpu);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (likely(paging)) {
|
|
||||||
guest_user = !!(syscall || !pv_vcpu_trap_on_guest_kernel(regs));
|
|
||||||
} else {
|
|
||||||
guest_user = false;
|
|
||||||
}
|
|
||||||
if (guest_user) {
|
|
||||||
if (from & FROM_PV_VCPU_MODE) {
|
|
||||||
/* all preparation has been made */
|
|
||||||
/* by host & guest handler */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* trap on/syscall from guest user, so regs keeps user */
|
|
||||||
/* registers state and it need use guest kernel stacks */
|
|
||||||
/* in empty state to handle this trap/syscall */
|
|
||||||
if (!regs->g_stacks_valid) {
|
|
||||||
prepare_pv_vcpu_inject_stacks(vcpu, regs);
|
|
||||||
}
|
|
||||||
stacks = ®s->g_stacks;
|
|
||||||
} else {
|
|
||||||
/* trap on guest kernel, so regs already points to guest */
|
|
||||||
/* kernel stacks and trap will be handled by host */
|
|
||||||
/* same as other user's processes traps */
|
|
||||||
stacks = ®s->stacks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only to debug on simulator : pcshtp == 0 */
|
|
||||||
pv_vcpu_emulate_empty_chain_staks(vcpu, regs, stacks, guest_user);
|
|
||||||
|
|
||||||
pcshtp = stacks->pcshtp;
|
|
||||||
DebugUST("guest kernel chain stack state: base 0x%llx ind 0x%x "
|
|
||||||
"size 0x%x\n",
|
|
||||||
stacks->pcsp_lo.PCSP_lo_base,
|
|
||||||
stacks->pcsp_hi.PCSP_hi_ind,
|
|
||||||
stacks->pcsp_hi.PCSP_hi_size);
|
|
||||||
DebugUST("host kernel chain stack state: base 0x%llx ind 0x%x "
|
|
||||||
"size 0x%x\n",
|
|
||||||
NATIVE_NV_READ_PCSP_LO_REG().PCSP_lo_base,
|
|
||||||
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_ind,
|
|
||||||
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_size);
|
|
||||||
DebugUST("guest kernel chain stack size to fill PCSHTP 0x%x\n",
|
|
||||||
pcshtp);
|
|
||||||
/*
|
|
||||||
* 1) Make sure there is free space in kernel chain stack to return to
|
|
||||||
*/
|
|
||||||
if (!syscall && pcshtp == 0 && !guest_user) {
|
|
||||||
unsigned long flags;
|
|
||||||
e2k_pcsp_lo_t g_pcsp_lo = stacks->pcsp_lo,
|
|
||||||
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
|
||||||
e2k_pcsp_hi_t g_pcsp_hi = stacks->pcsp_hi, k_pcsp_hi;
|
|
||||||
e2k_mem_crs_t __user *g_cframe;
|
|
||||||
e2k_mem_crs_t *k_crs;
|
|
||||||
int ret = -EINVAL;
|
|
||||||
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
NATIVE_FLUSHC;
|
|
||||||
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
BUG_ON(AS(k_pcsp_hi).ind);
|
|
||||||
AS(k_pcsp_hi).ind += SZ_OF_CR;
|
|
||||||
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
|
||||||
|
|
||||||
k_crs = (e2k_mem_crs_t *) AS(k_pcsp_lo).base;
|
|
||||||
g_cframe = (e2k_mem_crs_t __user *) (AS(g_pcsp_lo).base +
|
|
||||||
AS(g_pcsp_hi).ind);
|
|
||||||
if ((u64) g_cframe > (u64) AS(g_pcsp_lo).base) {
|
|
||||||
ret = __copy_user_to_current_hw_stack(k_crs,
|
|
||||||
g_cframe - 1, sizeof(*k_crs), regs, true);
|
|
||||||
}
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
|
|
||||||
/* Can happen if application returns until runs out of
|
|
||||||
* chain stack or there is no free memory for stacks.
|
|
||||||
* There is no user stack to return to - die. */
|
|
||||||
if (ret) {
|
|
||||||
E2K_LMS_HALT_OK;
|
|
||||||
pr_err("%s(): SIGKILL. %s\n",
|
|
||||||
__func__,
|
|
||||||
(ret == -EINVAL) ?
|
|
||||||
"tried to return to kernel"
|
|
||||||
:
|
|
||||||
"ran into Out-of-Memory on user stacks");
|
|
||||||
force_sig(SIGKILL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DebugUST("copy guest user chain frame from %px to kernel "
|
|
||||||
"bottom from %px\n",
|
|
||||||
g_cframe - 1, k_crs);
|
|
||||||
|
|
||||||
if (AS(g_pcsp_hi).ind < SZ_OF_CR) {
|
|
||||||
pr_err("%s(): guest kernel chain stack underflow\n",
|
|
||||||
__func__);
|
|
||||||
KVM_BUG_ON(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcshtp = SZ_OF_CR;
|
|
||||||
stacks->pcshtp = pcshtp;
|
|
||||||
DebugUST("guest kernel chain stack to FILL PCSHTP "
|
|
||||||
"set to 0x%x\n",
|
|
||||||
stacks->pcshtp);
|
|
||||||
} else if (!syscall && pcshtp == 0 && guest_user) {
|
|
||||||
e2k_pcsp_hi_t k_pcsp_hi;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/* set flag for unconditional injection to do not copy */
|
|
||||||
/* from guest user space */
|
|
||||||
regs->need_inject = true;
|
|
||||||
|
|
||||||
/* reserve one bottom frames for trampoline */
|
|
||||||
/* the guest handler replaces guest user trapped frame */
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
NATIVE_FLUSHC;
|
|
||||||
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
BUG_ON(k_pcsp_hi.PCSP_hi_ind);
|
|
||||||
k_pcsp_hi.PCSP_hi_ind += 1 * SZ_OF_CR;
|
|
||||||
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 2) Copy user data that cannot be FILLed
|
|
||||||
*/
|
|
||||||
ret = pv_vcpu_user_hw_stacks_copy(regs, stacks, cur_window_q,
|
|
||||||
guest_user);
|
|
||||||
if (unlikely(ret))
|
|
||||||
do_exit(SIGKILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Same as for native kernel without virtualization support */
|
|
||||||
static __always_inline int
|
|
||||||
user_hw_stacks_copy(struct e2k_stacks *stacks,
|
|
||||||
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
|
||||||
{
|
|
||||||
return native_user_hw_stacks_copy(stacks, regs, cur_window_q, copy_full);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
host_user_hw_stacks_prepare(struct e2k_stacks *stacks, pt_regs_t *regs,
|
|
||||||
u64 cur_window_q, enum restore_caller from, int syscall)
|
|
||||||
{
|
|
||||||
struct kvm_vcpu *vcpu;
|
|
||||||
|
|
||||||
if (likely(!kvm_test_intc_emul_flag(regs))) {
|
|
||||||
/* trap on/syscall from host user processes */
|
|
||||||
return native_user_hw_stacks_prepare(stacks, regs,
|
|
||||||
cur_window_q, from, syscall);
|
|
||||||
}
|
|
||||||
|
|
||||||
vcpu = current_thread_info()->vcpu;
|
|
||||||
KVM_BUG_ON(vcpu == NULL);
|
|
||||||
pv_vcpu_user_hw_stacks_prepare(vcpu, regs, cur_window_q, from, syscall);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
static __always_inline void
|
||||||
host_exit_to_usermode_loop(struct pt_regs *regs, bool syscall, bool has_signal)
|
host_exit_to_usermode_loop(struct pt_regs *regs, bool syscall, bool has_signal)
|
||||||
{
|
{
|
||||||
|
@ -101,8 +101,7 @@ native_guest_syscall_enter(struct pt_regs *regs)
|
|||||||
#ifdef CONFIG_VIRTUALIZATION
|
#ifdef CONFIG_VIRTUALIZATION
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For interceptions just switch actual registers with saved values
|
* For interceptions just switch actual registers with saved values in 'sw_ctxt'.
|
||||||
* in 'sw_ctxt'.
|
|
||||||
*
|
*
|
||||||
* For hypercalls:
|
* For hypercalls:
|
||||||
* 1) Enter hypercall.
|
* 1) Enter hypercall.
|
||||||
@ -128,8 +127,7 @@ static inline void kvm_switch_stack_regs(struct kvm_sw_cpu_context *sw_ctxt,
|
|||||||
AW(sbr) = NATIVE_NV_READ_SBR_REG_VALUE();
|
AW(sbr) = NATIVE_NV_READ_SBR_REG_VALUE();
|
||||||
}
|
}
|
||||||
|
|
||||||
NATIVE_NV_WRITE_USBR_USD_REG(sw_ctxt->sbr,
|
NATIVE_NV_WRITE_USBR_USD_REG(sw_ctxt->sbr, sw_ctxt->usd_hi, sw_ctxt->usd_lo);
|
||||||
sw_ctxt->usd_hi, sw_ctxt->usd_lo);
|
|
||||||
|
|
||||||
if (ctxt_save) {
|
if (ctxt_save) {
|
||||||
KVM_BUG_ON(sw_ctxt->saved.valid);
|
KVM_BUG_ON(sw_ctxt->saved.valid);
|
||||||
@ -225,14 +223,27 @@ static inline void kvm_switch_mmu_regs(struct kvm_sw_cpu_context *sw_ctxt,
|
|||||||
static inline void kvm_switch_to_guest_mmu_pid(struct kvm_vcpu *vcpu)
|
static inline void kvm_switch_to_guest_mmu_pid(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
mm_context_t *gmm_context;
|
mm_context_t *gmm_context;
|
||||||
|
unsigned long mask;
|
||||||
|
|
||||||
gmm_context = pv_vcpu_get_gmm_context(vcpu);
|
gmm_context = pv_vcpu_get_gmm_context(vcpu);
|
||||||
reload_mmu_pid(gmm_context, smp_processor_id());
|
mask = get_mmu_pid(gmm_context, smp_processor_id());
|
||||||
|
reload_context_mask(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long kvm_get_guest_mmu_pid(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
mm_context_t *gmm_context;
|
||||||
|
|
||||||
|
gmm_context = pv_vcpu_get_gmm_context(vcpu);
|
||||||
|
return gmm_context->cpumsk[smp_processor_id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void kvm_switch_to_host_mmu_pid(struct mm_struct *mm)
|
static inline void kvm_switch_to_host_mmu_pid(struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
reload_context(mm, smp_processor_id());
|
unsigned long mask;
|
||||||
|
|
||||||
|
mask = get_mmu_context(mm, smp_processor_id());
|
||||||
|
reload_context_mask(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void kvm_switch_debug_regs(struct kvm_sw_cpu_context *sw_ctxt,
|
static inline void kvm_switch_debug_regs(struct kvm_sw_cpu_context *sw_ctxt,
|
||||||
@ -479,6 +490,8 @@ static inline void host_guest_enter(struct thread_info *ti,
|
|||||||
if (flags & DEBUG_REGS_SWITCH)
|
if (flags & DEBUG_REGS_SWITCH)
|
||||||
kvm_switch_debug_regs(sw_ctxt, true);
|
kvm_switch_debug_regs(sw_ctxt, true);
|
||||||
|
|
||||||
|
KVM_BUG_ON(vcpu->is_hv && !NATIVE_READ_MMU_US_CL_D());
|
||||||
|
|
||||||
/* Switch data stack after all function calls */
|
/* Switch data stack after all function calls */
|
||||||
if (flags & USD_CONTEXT_SWITCH) {
|
if (flags & USD_CONTEXT_SWITCH) {
|
||||||
if (!(flags & FROM_HYPERCALL_SWITCH) || !vcpu->is_hv) {
|
if (!(flags & FROM_HYPERCALL_SWITCH) || !vcpu->is_hv) {
|
||||||
@ -506,6 +519,8 @@ static inline void host_guest_enter_light(struct thread_info *ti,
|
|||||||
|
|
||||||
kvm_switch_cu_regs(sw_ctxt);
|
kvm_switch_cu_regs(sw_ctxt);
|
||||||
|
|
||||||
|
KVM_BUG_ON(vcpu->is_hv && !NATIVE_READ_MMU_US_CL_D());
|
||||||
|
|
||||||
/* Switch data stack after all function calls */
|
/* Switch data stack after all function calls */
|
||||||
if (!from_sdisp) {
|
if (!from_sdisp) {
|
||||||
if (!vcpu->is_hv) {
|
if (!vcpu->is_hv) {
|
||||||
@ -513,6 +528,7 @@ static inline void host_guest_enter_light(struct thread_info *ti,
|
|||||||
} else {
|
} else {
|
||||||
/* restore saved source pointers of host stack */
|
/* restore saved source pointers of host stack */
|
||||||
kvm_switch_stack_regs(sw_ctxt, false, true);
|
kvm_switch_stack_regs(sw_ctxt, false, true);
|
||||||
|
kvm_switch_clw_regs(sw_ctxt, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,6 +556,8 @@ static inline void host_guest_exit(struct thread_info *ti,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KVM_BUG_ON(vcpu->is_hv && !NATIVE_READ_MMU_US_CL_D());
|
||||||
|
|
||||||
if (flags & FROM_HYPERCALL_SWITCH) {
|
if (flags & FROM_HYPERCALL_SWITCH) {
|
||||||
/*
|
/*
|
||||||
* Hypercalls - both hardware and software virtualization
|
* Hypercalls - both hardware and software virtualization
|
||||||
@ -649,6 +667,8 @@ static inline void host_guest_exit_light(struct thread_info *ti,
|
|||||||
KVM_BUG_ON(sw_ctxt->in_hypercall);
|
KVM_BUG_ON(sw_ctxt->in_hypercall);
|
||||||
sw_ctxt->in_hypercall = true;
|
sw_ctxt->in_hypercall = true;
|
||||||
|
|
||||||
|
KVM_BUG_ON(vcpu->is_hv && !NATIVE_READ_MMU_US_CL_D());
|
||||||
|
|
||||||
HOST_SAVE_KERNEL_GREGS_AS_LIGHT(ti);
|
HOST_SAVE_KERNEL_GREGS_AS_LIGHT(ti);
|
||||||
ONLY_SET_KERNEL_GREGS(ti);
|
ONLY_SET_KERNEL_GREGS(ti);
|
||||||
|
|
||||||
|
23
arch/e2k/include/asm/kvm/trace-defs.h
Normal file
23
arch/e2k/include/asm/kvm/trace-defs.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef _E2K_KVM_TRACE_DEFS_H_
|
||||||
|
#define _E2K_KVM_TRACE_DEFS_H_
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <asm/mmu_types.h>
|
||||||
|
#include <asm/pgtable_def.h>
|
||||||
|
#include <asm/kvm/mmu.h>
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
trace_kvm_get_va_translation(struct kvm_vcpu *vcpu, e2k_addr_t address,
|
||||||
|
pgdval_t *pgd, pudval_t *pud, pmdval_t *pmd, pteval_t *pte, int *pt_level)
|
||||||
|
{
|
||||||
|
kvm_get_spt_translation(vcpu, address, pgd, pud, pmd, pte, pt_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
trace_kvm_get_gva_to_hva(struct kvm_vcpu *vcpu, gva_t gva)
|
||||||
|
{
|
||||||
|
return kvm_get_gva_to_hva(vcpu, gva);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _E2K_KVM_TRACE_DEFS_H_ */
|
367
arch/e2k/include/asm/kvm/trace-hw-stacks.h
Normal file
367
arch/e2k/include/asm/kvm/trace-hw-stacks.h
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
#if !defined(_KVM_TRACE_COPY_HW_STACKS_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
|
#define _KVM_TRACE_COPY_HW_STACKS_H
|
||||||
|
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
#include <linux/hugetlb.h>
|
||||||
|
|
||||||
|
#include <asm/trace-defs.h>
|
||||||
|
#include <asm/trace_pgtable-v2.h>
|
||||||
|
#include <asm/trace_pgtable-v6.h>
|
||||||
|
#include <asm/pgtable_def.h>
|
||||||
|
#include <asm/kvm/trace-defs.h>
|
||||||
|
|
||||||
|
#undef TRACE_SYSTEM
|
||||||
|
#define TRACE_SYSTEM host
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_HOST_MODE
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
host_copy_hw_stack,
|
||||||
|
|
||||||
|
TP_PROTO(void *dst, void *src, unsigned long size, bool is_chain),
|
||||||
|
|
||||||
|
TP_ARGS(dst, src, size, is_chain),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field_struct(struct kvm_vcpu *, vcpu )
|
||||||
|
__field( void *, dst )
|
||||||
|
__field( void *, src )
|
||||||
|
__field( u64, size )
|
||||||
|
__field( bool, is_chain )
|
||||||
|
__field( pgdval_t, dst_pgd )
|
||||||
|
__field( pudval_t, dst_pud )
|
||||||
|
__field( pmdval_t, dst_pmd )
|
||||||
|
__field( pteval_t, dst_pte )
|
||||||
|
__field( int, dst_pt_level )
|
||||||
|
__field( pgdval_t, hva_pgd )
|
||||||
|
__field( pudval_t, hva_pud )
|
||||||
|
__field( pmdval_t, hva_pmd )
|
||||||
|
__field( pteval_t, hva_pte )
|
||||||
|
__field( int, hva_pt_level )
|
||||||
|
__field( unsigned long, hva )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
unsigned long hva;
|
||||||
|
|
||||||
|
__entry->vcpu = current_thread_info()->vcpu;
|
||||||
|
__entry->dst = dst;
|
||||||
|
__entry->src = src;
|
||||||
|
__entry->size = size;
|
||||||
|
__entry->is_chain = is_chain;
|
||||||
|
|
||||||
|
trace_kvm_get_va_translation(__entry->vcpu, (e2k_addr_t)dst,
|
||||||
|
&__entry->dst_pgd, &__entry->dst_pud, &__entry->dst_pmd,
|
||||||
|
&__entry->dst_pte, &__entry->dst_pt_level);
|
||||||
|
|
||||||
|
hva = trace_kvm_get_gva_to_hva(__entry->vcpu, (gva_t)dst);
|
||||||
|
__entry->hva = hva;
|
||||||
|
|
||||||
|
trace_get_va_translation(current->mm, (e2k_addr_t)hva,
|
||||||
|
&__entry->hva_pgd, &__entry->hva_pud, &__entry->hva_pmd,
|
||||||
|
&__entry->hva_pte, &__entry->hva_pt_level);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("VCPU #%d copy %s stack kernel guest <- kernel host: dst %px "
|
||||||
|
"src %px size %llx\n"
|
||||||
|
" kernel guest dst GVA %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" kernel host dst HVA %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s",
|
||||||
|
__entry->vcpu->vcpu_id,
|
||||||
|
(__entry->is_chain) ? "chain" : "procedure",
|
||||||
|
__entry->dst,
|
||||||
|
__entry->src,
|
||||||
|
__entry->size,
|
||||||
|
__entry->dst,
|
||||||
|
(__entry->dst_pt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pgd,
|
||||||
|
__entry->dst_pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pud,
|
||||||
|
__entry->dst_pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pmd,
|
||||||
|
__entry->dst_pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pte,
|
||||||
|
__entry->dst_pt_level <= E2K_PTE_LEVEL_NUM),
|
||||||
|
(void *)__entry->hva,
|
||||||
|
(__entry->hva_pt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->hva_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->hva_pgd,
|
||||||
|
__entry->hva_pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->hva_pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->hva_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->hva_pud,
|
||||||
|
__entry->hva_pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->hva_pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->hva_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->hva_pmd,
|
||||||
|
__entry->hva_pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->hva_pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->hva_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->hva_pte,
|
||||||
|
__entry->hva_pt_level <= E2K_PTE_LEVEL_NUM)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
host_proc_stack_frame,
|
||||||
|
|
||||||
|
TP_PROTO(kernel_mem_ps_t *ps_base, kernel_mem_ps_t *ps_frame),
|
||||||
|
|
||||||
|
TP_ARGS(ps_base, ps_frame),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( kernel_mem_ps_t *, ps_base )
|
||||||
|
__field_struct( kernel_mem_ps_t, ps_frame )
|
||||||
|
__field( pgprotval_t, dtlb_entry )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->ps_base = ps_base;
|
||||||
|
__entry->ps_frame = *ps_frame;
|
||||||
|
__entry->dtlb_entry = get_MMU_DTLB_ENTRY((e2k_addr_t)ps_base);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(" %px (dtlb 0x%016lx) : 0x%016lx 0x%016lx",
|
||||||
|
__entry->ps_base, __entry->dtlb_entry,
|
||||||
|
__entry->ps_frame.word_lo, __entry->ps_frame.word_hi)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
host_chain_stack_frame,
|
||||||
|
|
||||||
|
TP_PROTO(e2k_mem_crs_t *pcs_base, e2k_mem_crs_t *pcs_frame),
|
||||||
|
|
||||||
|
TP_ARGS(pcs_base, pcs_frame),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( e2k_mem_crs_t *, pcs_base )
|
||||||
|
__field_struct( e2k_mem_crs_t, pcs_frame )
|
||||||
|
__field( pgprotval_t, dtlb_entry )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->pcs_base = pcs_base;
|
||||||
|
__entry->pcs_frame = *pcs_frame;
|
||||||
|
__entry->dtlb_entry = get_MMU_DTLB_ENTRY((e2k_addr_t)pcs_base);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(" %px (dtlb 0x%016lx) : 0x%016llx 0x%016llx "
|
||||||
|
"0x%016llx 0x%016llx",
|
||||||
|
__entry->pcs_base, __entry->dtlb_entry,
|
||||||
|
__entry->pcs_frame.cr0_lo.CR0_lo_half,
|
||||||
|
__entry->pcs_frame.cr0_hi.CR0_hi_half,
|
||||||
|
__entry->pcs_frame.cr1_lo.CR1_lo_half,
|
||||||
|
__entry->pcs_frame.cr1_hi.CR1_hi_half)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
host_copy_hva_area,
|
||||||
|
|
||||||
|
TP_PROTO(void *dst, void *src, unsigned long size),
|
||||||
|
|
||||||
|
TP_ARGS(dst, src, size),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( void *, dst )
|
||||||
|
__field( void *, src )
|
||||||
|
__field( u64, size )
|
||||||
|
__field( pgdval_t, dst_pgd )
|
||||||
|
__field( pudval_t, dst_pud )
|
||||||
|
__field( pmdval_t, dst_pmd )
|
||||||
|
__field( pteval_t, dst_pte )
|
||||||
|
__field( int, dst_pt_level )
|
||||||
|
__field( pgdval_t, src_pgd )
|
||||||
|
__field( pudval_t, src_pud )
|
||||||
|
__field( pmdval_t, src_pmd )
|
||||||
|
__field( pteval_t, src_pte )
|
||||||
|
__field( int, src_pt_level )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dst = dst;
|
||||||
|
__entry->src = src;
|
||||||
|
__entry->size = size;
|
||||||
|
|
||||||
|
trace_get_va_translation(current->mm, (e2k_addr_t)dst,
|
||||||
|
&__entry->dst_pgd, &__entry->dst_pud, &__entry->dst_pmd,
|
||||||
|
&__entry->dst_pte, &__entry->dst_pt_level);
|
||||||
|
trace_get_va_translation(current->mm, (e2k_addr_t)src,
|
||||||
|
&__entry->src_pgd, &__entry->src_pud, &__entry->src_pmd,
|
||||||
|
&__entry->src_pte, &__entry->src_pt_level);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("copy area user guest <- kernel guest: dst %px "
|
||||||
|
"src %px size %llx\n"
|
||||||
|
" kernel guest dst HVA %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" kernel guest dst HVA %px : pgd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pud 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pmd 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s\n"
|
||||||
|
" pte 0x%016lx : %s\n"
|
||||||
|
" Access mode: %s%s",
|
||||||
|
__entry->dst,
|
||||||
|
__entry->src,
|
||||||
|
__entry->size,
|
||||||
|
__entry->dst,
|
||||||
|
(__entry->dst_pt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pgd,
|
||||||
|
__entry->dst_pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pud,
|
||||||
|
__entry->dst_pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pmd,
|
||||||
|
__entry->dst_pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->dst_pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->dst_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->dst_pte,
|
||||||
|
__entry->dst_pt_level <= E2K_PTE_LEVEL_NUM),
|
||||||
|
__entry->src,
|
||||||
|
(__entry->src_pt_level <= E2K_PGD_LEVEL_NUM) ?
|
||||||
|
__entry->src_pgd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pgd,
|
||||||
|
__entry->src_pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
|
(__entry->src_pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
|
__entry->src_pud : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pud,
|
||||||
|
__entry->src_pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
|
(__entry->src_pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
|
__entry->src_pmd : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pmd,
|
||||||
|
__entry->src_pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
|
(__entry->src_pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
|
__entry->src_pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->src_pte,
|
||||||
|
__entry->src_pt_level <= E2K_PTE_LEVEL_NUM)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
host_hva_area_line,
|
||||||
|
|
||||||
|
TP_PROTO(u64 *hva_base, u64 size),
|
||||||
|
|
||||||
|
TP_ARGS(hva_base, size),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( u64 *, hva_base )
|
||||||
|
__array( u64, hva_line, 4 )
|
||||||
|
__field( u64, size )
|
||||||
|
__field( pgprotval_t, dtlb_entry )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->hva_base = hva_base;
|
||||||
|
__entry->hva_line[0] =
|
||||||
|
(size >= 1 * sizeof(u64)) ? hva_base[0] : -1;
|
||||||
|
__entry->hva_line[1] =
|
||||||
|
(size >= 2 * sizeof(u64)) ? hva_base[1] : -1;
|
||||||
|
__entry->hva_line[2] =
|
||||||
|
(size >= 3 * sizeof(u64)) ? hva_base[2] : -1;
|
||||||
|
__entry->hva_line[3] =
|
||||||
|
(size >= 4 * sizeof(u64)) ? hva_base[3] : -1;
|
||||||
|
__entry->size = size;
|
||||||
|
__entry->dtlb_entry = get_MMU_DTLB_ENTRY((e2k_addr_t)hva_base);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(" %px (dtlb 0x%016lx) : 0x%016llx 0x%016llx "
|
||||||
|
"0x%016llx 0x%016llx",
|
||||||
|
__entry->hva_base, __entry->dtlb_entry,
|
||||||
|
__entry->hva_line[0],
|
||||||
|
__entry->hva_line[1],
|
||||||
|
__entry->hva_line[2],
|
||||||
|
__entry->hva_line[3]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
#else /* !CONFIG_KVM_HOST_MODE */
|
||||||
|
|
||||||
|
static inline bool trace_host_copy_hw_stack_enabled(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
trace_host_copy_hw_stack(void *dst, void *src, unsigned long size, bool is_chain)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool trace_host_proc_stack_frame_enabled(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
trace_host_proc_stack_frame(kernel_mem_ps_t *ps_base, kernel_mem_ps_t *ps_frame)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool trace_host_chain_stack_frame_enabled(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
trace_host_chain_stack_frame(e2k_mem_crs_t *pcs_base, e2k_mem_crs_t *pcs_frame)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool trace_host_copy_hva_area_enabled(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
trace_host_copy_hva_area(void *dst, void *src, unsigned long size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool trace_host_hva_area_line_enabled(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
trace_host_hva_area_line(u64 *hva_base, u64 size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_KVM_HOST_MODE */
|
||||||
|
|
||||||
|
#endif /* _KVM_TRACE_COPY_HW_STACKS_H */
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_HOST_MODE
|
||||||
|
#undef TRACE_INCLUDE_PATH
|
||||||
|
#define TRACE_INCLUDE_PATH ../../arch/e2k/include/asm/kvm
|
||||||
|
#undef TRACE_INCLUDE_FILE
|
||||||
|
#define TRACE_INCLUDE_FILE trace-hw-stacks
|
||||||
|
|
||||||
|
/* This part must be outside protection */
|
||||||
|
#include <trace/define_trace.h>
|
||||||
|
#endif /* CONFIG_KVM_HOST_MODE */
|
@ -1090,6 +1090,79 @@ TRACE_EVENT(
|
|||||||
TP_printk("Light hypercall exit: %llu\n", __entry->ret)
|
TP_printk("Light hypercall exit: %llu\n", __entry->ret)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
guest_switch_to,
|
||||||
|
|
||||||
|
TP_PROTO(struct kvm_vcpu *vcpu, int gpid_from, int gmmid_from,
|
||||||
|
int gpid_to, int gmmid_to, struct sw_regs *next_gsw),
|
||||||
|
|
||||||
|
TP_ARGS(vcpu, gpid_from, gmmid_from, gpid_to, gmmid_to, next_gsw),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( int, vcpu_id )
|
||||||
|
__field( int, gpid_from )
|
||||||
|
__field( int, gmmid_from )
|
||||||
|
__field( int, gpid_to )
|
||||||
|
__field( int, gmmid_to )
|
||||||
|
__field( e2k_addr_t, top )
|
||||||
|
__field_struct( e2k_usd_lo_t, usd_lo )
|
||||||
|
__field_struct( e2k_usd_hi_t, usd_hi )
|
||||||
|
__field_struct( e2k_psp_lo_t, psp_lo )
|
||||||
|
__field_struct( e2k_psp_hi_t, psp_hi )
|
||||||
|
__field_struct( e2k_pcsp_lo_t, pcsp_lo )
|
||||||
|
__field_struct( e2k_pcsp_hi_t, pcsp_hi )
|
||||||
|
__field( pgprotval_t, u_pptb )
|
||||||
|
__field( gva_t, u_vptb )
|
||||||
|
__field( hpa_t, root )
|
||||||
|
__field( u64, mmu_pptb )
|
||||||
|
__field( u64, mmu_pid )
|
||||||
|
__field( u64, ctxt_pid )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->vcpu_id = vcpu->vcpu_id;
|
||||||
|
__entry->gpid_from = gpid_from;
|
||||||
|
__entry->gmmid_from = gmmid_from;
|
||||||
|
__entry->gpid_to = gpid_to;
|
||||||
|
__entry->gmmid_to = gmmid_to;
|
||||||
|
__entry->top = next_gsw->top;
|
||||||
|
__entry->usd_lo = next_gsw->usd_lo;
|
||||||
|
__entry->usd_hi = next_gsw->usd_hi;
|
||||||
|
__entry->psp_lo = next_gsw->psp_lo;
|
||||||
|
__entry->psp_hi = next_gsw->psp_hi;
|
||||||
|
__entry->pcsp_lo = next_gsw->pcsp_lo;
|
||||||
|
__entry->pcsp_hi = next_gsw->pcsp_hi;
|
||||||
|
__entry->u_pptb = vcpu->arch.mmu.get_vcpu_u_pptb(vcpu);
|
||||||
|
__entry->u_vptb = vcpu->arch.mmu.get_vcpu_sh_u_vptb(vcpu);
|
||||||
|
__entry->root = kvm_get_space_type_spt_u_root(vcpu);
|
||||||
|
__entry->mmu_pptb = get_mmu_u_pptb_reg();
|
||||||
|
__entry->mmu_pid = get_mmu_pid_reg();
|
||||||
|
__entry->ctxt_pid = kvm_get_guest_mmu_pid(vcpu);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("VCPU #%d: switch from gpid #%d gmm #%d to gpid #%d gmm #%d\n"
|
||||||
|
" USD: base 0x%llx size 0x%x top at 0x%lx\n"
|
||||||
|
" PSP: base 0x%llx ind 0x%x size 0x%x\n"
|
||||||
|
" PCSP: base 0x%llx ind 0x%x size 0x%x\n"
|
||||||
|
" MMU: u_pptb 0x%lx u_vptb 0x%lx sh_pptb 0x%llx\n"
|
||||||
|
" mmu_upptb 0x%llx mmu_pid 0x%llx ctxt pid 0x%llx",
|
||||||
|
__entry->vcpu_id,
|
||||||
|
__entry->gpid_from, __entry->gmmid_from,
|
||||||
|
__entry->gpid_to, __entry->gmmid_to,
|
||||||
|
__entry->usd_lo.USD_lo_base,
|
||||||
|
__entry->usd_hi.USD_hi_size,
|
||||||
|
__entry->top,
|
||||||
|
__entry->psp_lo.PSP_lo_base,
|
||||||
|
__entry->psp_hi.PSP_hi_ind,
|
||||||
|
__entry->psp_hi.PSP_hi_size,
|
||||||
|
__entry->pcsp_lo.PCSP_lo_base,
|
||||||
|
__entry->pcsp_hi.PCSP_hi_ind,
|
||||||
|
__entry->pcsp_hi.PCSP_hi_size,
|
||||||
|
__entry->u_pptb, __entry->u_vptb, __entry->root,
|
||||||
|
__entry->mmu_pptb, __entry->mmu_pid, __entry->ctxt_pid
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
TRACE_EVENT(
|
TRACE_EVENT(
|
||||||
vcpu_put,
|
vcpu_put,
|
||||||
|
|
||||||
|
@ -368,6 +368,42 @@ TRACE_EVENT(
|
|||||||
TP_printk("pmirr#%d val 0x%llx\n", __entry->pmirr, __entry->val)
|
TP_printk("pmirr#%d val 0x%llx\n", __entry->pmirr, __entry->val)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
save_pnmirr,
|
||||||
|
|
||||||
|
TP_PROTO(u32 val),
|
||||||
|
|
||||||
|
TP_ARGS(val),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( u32, val )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->val = val;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("pnmirr val 0x%x\n", __entry->val)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(
|
||||||
|
restore_pnmirr,
|
||||||
|
|
||||||
|
TP_PROTO(u32 val),
|
||||||
|
|
||||||
|
TP_ARGS(val),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( u32, val )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->val = val;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("pnmirr val 0x%x\n", __entry->val)
|
||||||
|
);
|
||||||
|
|
||||||
TRACE_EVENT(
|
TRACE_EVENT(
|
||||||
save_cir,
|
save_cir,
|
||||||
|
|
||||||
|
@ -744,6 +744,7 @@ typedef struct kvm_sw_cpu_context {
|
|||||||
u64 rpr_hi;
|
u64 rpr_hi;
|
||||||
u64 tcd;
|
u64 tcd;
|
||||||
|
|
||||||
|
#ifdef CONFIG_CLW_ENABLE
|
||||||
mmu_reg_t us_cl_d;
|
mmu_reg_t us_cl_d;
|
||||||
clw_reg_t us_cl_b;
|
clw_reg_t us_cl_b;
|
||||||
clw_reg_t us_cl_up;
|
clw_reg_t us_cl_up;
|
||||||
@ -751,6 +752,7 @@ typedef struct kvm_sw_cpu_context {
|
|||||||
clw_reg_t us_cl_m1;
|
clw_reg_t us_cl_m1;
|
||||||
clw_reg_t us_cl_m2;
|
clw_reg_t us_cl_m2;
|
||||||
clw_reg_t us_cl_m3;
|
clw_reg_t us_cl_m3;
|
||||||
|
#endif
|
||||||
} kvm_sw_cpu_context_t;
|
} kvm_sw_cpu_context_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -39,10 +39,13 @@ enum {
|
|||||||
CPU_HWBUG_SPURIOUS_EXC_DATA_DEBUG,
|
CPU_HWBUG_SPURIOUS_EXC_DATA_DEBUG,
|
||||||
CPU_HWBUG_TLB_FLUSH_L1D,
|
CPU_HWBUG_TLB_FLUSH_L1D,
|
||||||
CPU_HWBUG_GUEST_ASYNC_PM,
|
CPU_HWBUG_GUEST_ASYNC_PM,
|
||||||
|
CPU_HWBUG_E16C_SLEEP,
|
||||||
CPU_HWBUG_L1I_STOPS_WORKING,
|
CPU_HWBUG_L1I_STOPS_WORKING,
|
||||||
CPU_HWBUG_CLW_STALE_L1_ENTRY,
|
CPU_HWBUG_CLW_STALE_L1_ENTRY,
|
||||||
CPU_HWBUG_C3_WAIT_MA_C,
|
CPU_HWBUG_C3_WAIT_MA_C,
|
||||||
CPU_HWBUG_VIRT_SCLKM3_INTC,
|
CPU_HWBUG_VIRT_SCLKM3_INTC,
|
||||||
|
CPU_HWBUG_VIRT_PSIZE_INTERCEPTION,
|
||||||
|
CPU_HWBUG_USD_ALIGNMENT,
|
||||||
CPU_FEAT_WC_PCI_PREFETCH,
|
CPU_FEAT_WC_PCI_PREFETCH,
|
||||||
CPU_FEAT_FLUSH_DC_IC,
|
CPU_FEAT_FLUSH_DC_IC,
|
||||||
CPU_FEAT_EPIC,
|
CPU_FEAT_EPIC,
|
||||||
@ -52,7 +55,6 @@ enum {
|
|||||||
CPU_FEAT_ISET_V3,
|
CPU_FEAT_ISET_V3,
|
||||||
CPU_FEAT_ISET_V5,
|
CPU_FEAT_ISET_V5,
|
||||||
CPU_FEAT_ISET_V6,
|
CPU_FEAT_ISET_V6,
|
||||||
CPU_HWBUG_E16C_SLEEP,
|
|
||||||
NR_CPU_FEATURES
|
NR_CPU_FEATURES
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -298,7 +300,8 @@ CPUHAS(CPU_HWBUG_DMA_AT_APIC_ADDR,
|
|||||||
false,
|
false,
|
||||||
cpu == IDR_ES2_DSP_MDL);
|
cpu == IDR_ES2_DSP_MDL);
|
||||||
/* #88644 - data profiling events are lost if overflow happens
|
/* #88644 - data profiling events are lost if overflow happens
|
||||||
* under closed NM interrupts.
|
* under closed NM interrupts; also DDMCR writing does not clear
|
||||||
|
* pending exc_data_debug exceptions.
|
||||||
* Workaround - disable data monitor profiling in kernel. */
|
* Workaround - disable data monitor profiling in kernel. */
|
||||||
CPUHAS(CPU_HWBUG_KERNEL_DATA_MONITOR,
|
CPUHAS(CPU_HWBUG_KERNEL_DATA_MONITOR,
|
||||||
!IS_ENABLED(CONFIG_CPU_ES2) && !IS_ENABLED(CONFIG_CPU_E2S) &&
|
!IS_ENABLED(CONFIG_CPU_ES2) && !IS_ENABLED(CONFIG_CPU_E2S) &&
|
||||||
@ -491,6 +494,22 @@ CPUHAS(CPU_HWBUG_VIRT_SCLKM3_INTC,
|
|||||||
cpu == IDR_E16C_MDL && revision == 0 ||
|
cpu == IDR_E16C_MDL && revision == 0 ||
|
||||||
cpu == IDR_E12C_MDL && revision == 0 ||
|
cpu == IDR_E12C_MDL && revision == 0 ||
|
||||||
cpu == IDR_E2C3_MDL && revision == 0);
|
cpu == IDR_E2C3_MDL && revision == 0);
|
||||||
|
/* #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_E2K_MACHINE),
|
||||||
|
IS_ENABLED(CONFIG_CPU_E16C) || IS_ENABLED(CONFIG_CPU_E2C3),
|
||||||
|
(cpu == IDR_E16C_MDL || 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_E16C) &&
|
||||||
|
!IS_ENABLED(CONFIG_CPU_E2C3),
|
||||||
|
!IS_ENABLED(CONFIG_CPU_E12C),
|
||||||
|
cpu == IDR_E16C_MDL && revision <= 1 ||
|
||||||
|
cpu == IDR_E2C3_MDL && revision <= 1);
|
||||||
/* Rely on IDR instead of iset version to choose between APIC and EPIC.
|
/* 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
|
* 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. */
|
* EPIC based on what hardware guest *thinks* it's being executed on. */
|
||||||
|
@ -733,6 +733,7 @@ typedef unsigned long clw_reg_t;
|
|||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
|
u32 half_word[2];
|
||||||
struct { /* structure of register */
|
struct { /* structure of register */
|
||||||
u32 user : 1; /* [ 0: 0] */
|
u32 user : 1; /* [ 0: 0] */
|
||||||
u32 system : 1; /* [ 1: 1] */
|
u32 system : 1; /* [ 1: 1] */
|
||||||
@ -755,7 +756,10 @@ typedef union {
|
|||||||
u64 unus9 : 4;
|
u64 unus9 : 4;
|
||||||
u64 b3 : 8;
|
u64 b3 : 8;
|
||||||
u64 unu10 : 4;
|
u64 unu10 : 4;
|
||||||
u64 unu11 : 16;
|
u64 unu11 : 1;
|
||||||
|
u64 m0 : 1;
|
||||||
|
u64 m1 : 1;
|
||||||
|
u64 unu12 : 13;
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
|
@ -29,8 +29,8 @@ typedef struct { pgprotval_t pgprot; } pgprot_t;
|
|||||||
#define pgprot_val(x) ((x).pgprot)
|
#define pgprot_val(x) ((x).pgprot)
|
||||||
|
|
||||||
#define __pte(x) ((pte_t) { (x) } )
|
#define __pte(x) ((pte_t) { (x) } )
|
||||||
#define __pmd(x) ((pmd_t) { (x) } )
|
|
||||||
#define __pud(x) ((pud_t) { (x) } )
|
#define __pud(x) ((pud_t) { (x) } )
|
||||||
|
#define __pmd(x) ((pmd_t) { (x) } )
|
||||||
#define __pgd(x) ((pgd_t) { (x) } )
|
#define __pgd(x) ((pgd_t) { (x) } )
|
||||||
#define __pgprot(x) ((pgprot_t) { (x) } )
|
#define __pgprot(x) ((pgprot_t) { (x) } )
|
||||||
|
|
||||||
|
@ -246,19 +246,12 @@ native_read_TIR_hi_reg(void)
|
|||||||
NATIVE_SET_DSREG_OPEN(usd.lo, USD_lo_value)
|
NATIVE_SET_DSREG_OPEN(usd.lo, USD_lo_value)
|
||||||
#define NATIVE_NV_WRITE_USD_HI_REG_VALUE(USD_hi_value) \
|
#define NATIVE_NV_WRITE_USD_HI_REG_VALUE(USD_hi_value) \
|
||||||
NATIVE_SET_DSREG_OPEN(usd.hi, USD_hi_value)
|
NATIVE_SET_DSREG_OPEN(usd.hi, USD_hi_value)
|
||||||
#define NATIVE_NV_WRITE_USD_REG_VALUE(USD_hi_value, USD_lo_value) \
|
|
||||||
({ \
|
|
||||||
NATIVE_NV_WRITE_USD_HI_REG_VALUE(USD_hi_value); \
|
|
||||||
NATIVE_NV_WRITE_USD_LO_REG_VALUE(USD_lo_value); \
|
|
||||||
})
|
|
||||||
#define NATIVE_NV_WRITE_USD_REG(USD_hi, USD_lo) \
|
|
||||||
({ \
|
|
||||||
NATIVE_NV_WRITE_USD_REG_VALUE(USD_hi.USD_hi_half, USD_lo.USD_lo_half); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define NATIVE_NV_WRITE_USBR_USD_REG_VALUE(usbr, usd_hi, usd_lo) \
|
#define NATIVE_NV_WRITE_USBR_USD_REG_VALUE(usbr, usd_hi, usd_lo) \
|
||||||
do { \
|
do { \
|
||||||
NATIVE_NV_WRITE_USBR_REG_VALUE(usbr); \
|
NATIVE_NV_WRITE_USBR_REG_VALUE(usbr); \
|
||||||
|
if (cpu_has(CPU_HWBUG_USD_ALIGNMENT)) \
|
||||||
|
NATIVE_NV_WRITE_USD_LO_REG_VALUE(usd_lo); \
|
||||||
NATIVE_NV_WRITE_USD_HI_REG_VALUE(usd_hi); \
|
NATIVE_NV_WRITE_USD_HI_REG_VALUE(usd_hi); \
|
||||||
NATIVE_NV_WRITE_USD_LO_REG_VALUE(usd_lo); \
|
NATIVE_NV_WRITE_USD_LO_REG_VALUE(usd_lo); \
|
||||||
} while (0)
|
} while (0)
|
||||||
@ -266,6 +259,8 @@ do { \
|
|||||||
#define NATIVE_NV_WRITE_USBR_USD_REG(usbr, usd_hi, usd_lo) \
|
#define NATIVE_NV_WRITE_USBR_USD_REG(usbr, usd_hi, usd_lo) \
|
||||||
do { \
|
do { \
|
||||||
NATIVE_NV_WRITE_USBR_REG(usbr); \
|
NATIVE_NV_WRITE_USBR_REG(usbr); \
|
||||||
|
if (cpu_has(CPU_HWBUG_USD_ALIGNMENT)) \
|
||||||
|
NATIVE_NV_WRITE_USD_LO_REG(usd_lo); \
|
||||||
NATIVE_NV_WRITE_USD_HI_REG(usd_hi); \
|
NATIVE_NV_WRITE_USD_HI_REG(usd_hi); \
|
||||||
NATIVE_NV_WRITE_USD_LO_REG(usd_lo); \
|
NATIVE_NV_WRITE_USD_LO_REG(usd_lo); \
|
||||||
} while (0)
|
} while (0)
|
||||||
@ -429,8 +424,10 @@ extern void native_write_SCLKM2_reg_value(unsigned long reg_value);
|
|||||||
NATIVE_SET_SREG_CLOSED_NOEXC(dibcr, DIBCR_value, 4)
|
NATIVE_SET_SREG_CLOSED_NOEXC(dibcr, DIBCR_value, 4)
|
||||||
#define NATIVE_WRITE_DIBSR_REG_VALUE(DIBSR_value) \
|
#define NATIVE_WRITE_DIBSR_REG_VALUE(DIBSR_value) \
|
||||||
NATIVE_SET_SREG_CLOSED_NOEXC(dibsr, DIBSR_value, 4)
|
NATIVE_SET_SREG_CLOSED_NOEXC(dibsr, DIBSR_value, 4)
|
||||||
|
/* 6 cycles delay guarantess that all counting
|
||||||
|
* is stopped and %dibsr is updated accordingly. */
|
||||||
#define NATIVE_WRITE_DIMCR_REG_VALUE(DIMCR_value) \
|
#define NATIVE_WRITE_DIMCR_REG_VALUE(DIMCR_value) \
|
||||||
NATIVE_SET_DSREG_CLOSED_NOEXC(dimcr, DIMCR_value, 4)
|
NATIVE_SET_DSREG_CLOSED_NOEXC(dimcr, DIMCR_value, 5)
|
||||||
#define NATIVE_WRITE_DIBAR0_REG_VALUE(DIBAR0_value) \
|
#define NATIVE_WRITE_DIBAR0_REG_VALUE(DIBAR0_value) \
|
||||||
NATIVE_SET_DSREG_CLOSED_NOEXC(dibar0, DIBAR0_value, 4)
|
NATIVE_SET_DSREG_CLOSED_NOEXC(dibar0, DIBAR0_value, 4)
|
||||||
#define NATIVE_WRITE_DIBAR1_REG_VALUE(DIBAR1_value) \
|
#define NATIVE_WRITE_DIBAR1_REG_VALUE(DIBAR1_value) \
|
||||||
|
@ -242,46 +242,44 @@ native_flush_ICACHE_all(void)
|
|||||||
/*
|
/*
|
||||||
* native MMU DEBUG registers access
|
* native MMU DEBUG registers access
|
||||||
*/
|
*/
|
||||||
#define NATIVE_READ_MMU_DEBUG_REG(reg_mnemonic) \
|
|
||||||
NATIVE_GET_MMUREG(reg_mnemonic)
|
|
||||||
#define NATIVE_WRITE_MMU_DEBUG_REG(reg_mnemonic, reg_value) \
|
|
||||||
NATIVE_SET_MMUREG(reg_mnemonic, reg_value)
|
|
||||||
#define NATIVE_READ_DDBAR0_REG_VALUE() \
|
#define NATIVE_READ_DDBAR0_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddbar0)
|
NATIVE_GET_MMUREG(ddbar0)
|
||||||
#define NATIVE_READ_DDBAR1_REG_VALUE() \
|
#define NATIVE_READ_DDBAR1_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddbar1)
|
NATIVE_GET_MMUREG(ddbar1)
|
||||||
#define NATIVE_READ_DDBAR2_REG_VALUE() \
|
#define NATIVE_READ_DDBAR2_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddbar2)
|
NATIVE_GET_MMUREG(ddbar2)
|
||||||
#define NATIVE_READ_DDBAR3_REG_VALUE() \
|
#define NATIVE_READ_DDBAR3_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddbar3)
|
NATIVE_GET_MMUREG(ddbar3)
|
||||||
#define NATIVE_READ_DDBCR_REG_VALUE() \
|
#define NATIVE_READ_DDBCR_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddbcr)
|
NATIVE_GET_MMUREG(ddbcr)
|
||||||
#define NATIVE_READ_DDBSR_REG_VALUE() \
|
#define NATIVE_READ_DDBSR_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddbsr)
|
NATIVE_GET_MMUREG(ddbsr)
|
||||||
#define NATIVE_READ_DDMAR0_REG_VALUE() \
|
#define NATIVE_READ_DDMAR0_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddmar0)
|
NATIVE_GET_MMUREG(ddmar0)
|
||||||
#define NATIVE_READ_DDMAR1_REG_VALUE() \
|
#define NATIVE_READ_DDMAR1_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddmar1)
|
NATIVE_GET_MMUREG(ddmar1)
|
||||||
#define NATIVE_READ_DDMCR_REG_VALUE() \
|
#define NATIVE_READ_DDMCR_REG_VALUE() \
|
||||||
NATIVE_READ_MMU_DEBUG_REG(ddmcr)
|
NATIVE_GET_MMUREG(ddmcr)
|
||||||
#define NATIVE_WRITE_DDBAR0_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDBAR0_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddbar0, value)
|
NATIVE_SET_MMUREG(ddbar0, value)
|
||||||
#define NATIVE_WRITE_DDBAR1_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDBAR1_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddbar1, value)
|
NATIVE_SET_MMUREG(ddbar1, value)
|
||||||
#define NATIVE_WRITE_DDBAR2_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDBAR2_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddbar2, value)
|
NATIVE_SET_MMUREG(ddbar2, value)
|
||||||
#define NATIVE_WRITE_DDBAR3_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDBAR3_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddbar3, value)
|
NATIVE_SET_MMUREG(ddbar3, value)
|
||||||
#define NATIVE_WRITE_DDBCR_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDBCR_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddbcr, value)
|
NATIVE_SET_MMUREG(ddbcr, value)
|
||||||
#define NATIVE_WRITE_DDBSR_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDBSR_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddbsr, value)
|
NATIVE_SET_MMUREG(ddbsr, value)
|
||||||
#define NATIVE_WRITE_DDMAR0_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDMAR0_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddmar0, value)
|
NATIVE_SET_MMUREG(ddmar0, value)
|
||||||
#define NATIVE_WRITE_DDMAR1_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDMAR1_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddmar1, value)
|
NATIVE_SET_MMUREG(ddmar1, value)
|
||||||
|
/* 4 cycles delay guarantess that all counting
|
||||||
|
* is stopped and %ddbsr is updated accordingly. */
|
||||||
#define NATIVE_WRITE_DDMCR_REG_VALUE(value) \
|
#define NATIVE_WRITE_DDMCR_REG_VALUE(value) \
|
||||||
NATIVE_WRITE_MMU_DEBUG_REG(ddmcr, value)
|
NATIVE_SET_MMUREG_CLOSED(ddmcr, value, 3)
|
||||||
|
|
||||||
#define NATIVE_READ_DDBAR0_REG() \
|
#define NATIVE_READ_DDBAR0_REG() \
|
||||||
NATIVE_READ_DDBAR0_REG_VALUE()
|
NATIVE_READ_DDBAR0_REG_VALUE()
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
#ifndef _ASM_E2K_PERF_EVENT_H
|
#pragma once
|
||||||
#define _ASM_E2K_PERF_EVENT_H
|
|
||||||
|
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
|
#include <asm/cpu_regs.h>
|
||||||
|
#include <asm/perf_event_types.h>
|
||||||
#include <asm/process.h>
|
#include <asm/process.h>
|
||||||
#include <asm/regs_state.h>
|
#include <asm/regs_state.h>
|
||||||
|
|
||||||
static inline void set_perf_event_pending(void) {}
|
static inline void set_perf_event_pending(void) {}
|
||||||
static inline void clear_perf_event_pending(void) {}
|
static inline void clear_perf_event_pending(void) {}
|
||||||
|
|
||||||
#define PERF_EVENT_INDEX_OFFSET 0
|
void perf_data_overflow_handle(struct pt_regs *);
|
||||||
|
void perf_instr_overflow_handle(struct pt_regs *);
|
||||||
int perf_data_overflow_handle(struct pt_regs *);
|
|
||||||
int perf_instr_overflow_handle(struct pt_regs *);
|
|
||||||
void dimtp_overflow(struct perf_event *event);
|
void dimtp_overflow(struct perf_event *event);
|
||||||
|
|
||||||
#define perf_arch_fetch_caller_regs perf_arch_fetch_caller_regs
|
#define perf_arch_fetch_caller_regs perf_arch_fetch_caller_regs
|
||||||
@ -22,35 +21,81 @@ static __always_inline void perf_arch_fetch_caller_regs(struct pt_regs *regs,
|
|||||||
WARN_ON_ONCE(instruction_pointer(regs) != ip);
|
WARN_ON_ONCE(instruction_pointer(regs) != ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ARCH_PERFMON_EVENT_MASK 0xffff
|
static inline e2k_dimcr_t dimcr_pause(void)
|
||||||
#define ARCH_PERFMON_OS (1 << 16)
|
{
|
||||||
#define ARCH_PERFMON_USR (1 << 17)
|
e2k_dimcr_t dimcr, dimcr_old;
|
||||||
#define ARCH_PERFMON_ENABLED (1 << 18)
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop counting for more precise group counting and also
|
||||||
|
* to avoid races when one counter overflows while another
|
||||||
|
* is being handled.
|
||||||
|
*
|
||||||
|
* Writing %dimcr also clears other pending exc_instr_debug
|
||||||
|
*/
|
||||||
|
dimcr = READ_DIMCR_REG();
|
||||||
|
dimcr_old = dimcr;
|
||||||
|
AS(dimcr)[0].user = 0;
|
||||||
|
AS(dimcr)[0].system = 0;
|
||||||
|
AS(dimcr)[1].user = 0;
|
||||||
|
AS(dimcr)[1].system = 0;
|
||||||
|
WRITE_DIMCR_REG(dimcr);
|
||||||
|
|
||||||
DECLARE_PER_CPU(struct perf_event * [4], cpu_events);
|
return dimcr_old;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
static inline e2k_ddmcr_t ddmcr_pause(void)
|
||||||
* Bitmask for perf_monitors_used
|
{
|
||||||
*
|
e2k_ddmcr_t ddmcr, ddmcr_old;
|
||||||
* DIM0 has all counters from DIM1 and some more. So events for
|
|
||||||
* DIM1 are marked with DIM0_DIM1, and the actual used monitor
|
|
||||||
* will be determined at runtime.
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
_DDM0 = 0,
|
|
||||||
_DDM1,
|
|
||||||
_DIM0,
|
|
||||||
_DIM1,
|
|
||||||
_DDM0_DDM1,
|
|
||||||
_DIM0_DIM1,
|
|
||||||
MAX_HW_MONITORS
|
|
||||||
};
|
|
||||||
#define DDM0 (1 << _DDM0)
|
|
||||||
#define DDM1 (1 << _DDM1)
|
|
||||||
#define DIM0 (1 << _DIM0)
|
|
||||||
#define DIM1 (1 << _DIM1)
|
|
||||||
#define DDM0_DDM1 (1 << _DDM0_DDM1)
|
|
||||||
#define DIM0_DIM1 (1 << _DIM0_DIM1)
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop counting for more precise group counting and also
|
||||||
|
* to avoid races when one counter overflows while another
|
||||||
|
* is being handled.
|
||||||
|
*
|
||||||
|
* Writing %ddmcr also clears other pending exc_data_debug
|
||||||
|
*/
|
||||||
|
ddmcr = READ_DDMCR_REG();
|
||||||
|
ddmcr_old = ddmcr;
|
||||||
|
AS(ddmcr)[0].user = 0;
|
||||||
|
AS(ddmcr)[0].system = 0;
|
||||||
|
AS(ddmcr)[1].user = 0;
|
||||||
|
AS(ddmcr)[1].system = 0;
|
||||||
|
WRITE_DDMCR_REG(ddmcr);
|
||||||
|
|
||||||
|
return ddmcr_old;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
|
extern void dimcr_continue(e2k_dimcr_t dimcr_old);
|
||||||
|
extern void ddmcr_continue(e2k_ddmcr_t ddmcr_old);
|
||||||
|
#else
|
||||||
|
static inline void dimcr_continue(e2k_dimcr_t dimcr_old)
|
||||||
|
{
|
||||||
|
e2k_dimcr_t dimcr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restart counting
|
||||||
|
*/
|
||||||
|
dimcr = READ_DIMCR_REG();
|
||||||
|
AS(dimcr)[0].user = AS(dimcr_old)[0].user;
|
||||||
|
AS(dimcr)[0].system = AS(dimcr_old)[0].system;
|
||||||
|
AS(dimcr)[1].user = AS(dimcr_old)[1].user;
|
||||||
|
AS(dimcr)[1].system = AS(dimcr_old)[1].system;
|
||||||
|
WRITE_DIMCR_REG(dimcr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ddmcr_continue(e2k_ddmcr_t ddmcr_old)
|
||||||
|
{
|
||||||
|
e2k_ddmcr_t ddmcr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restart counting
|
||||||
|
*/
|
||||||
|
ddmcr = READ_DDMCR_REG();
|
||||||
|
AS(ddmcr)[0].user = AS(ddmcr_old)[0].user;
|
||||||
|
AS(ddmcr)[0].system = AS(ddmcr_old)[0].system;
|
||||||
|
AS(ddmcr)[1].user = AS(ddmcr_old)[1].user;
|
||||||
|
AS(ddmcr)[1].system = AS(ddmcr_old)[1].system;
|
||||||
|
WRITE_DDMCR_REG(ddmcr);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
46
arch/e2k/include/asm/perf_event_types.h
Normal file
46
arch/e2k/include/asm/perf_event_types.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <linux/percpu.h>
|
||||||
|
#include <asm/ptrace.h>
|
||||||
|
|
||||||
|
#define PERF_EVENT_INDEX_OFFSET 0
|
||||||
|
|
||||||
|
#define ARCH_PERFMON_EVENT_MASK 0xffff
|
||||||
|
#define ARCH_PERFMON_OS (1 << 16)
|
||||||
|
#define ARCH_PERFMON_USR (1 << 17)
|
||||||
|
#define ARCH_PERFMON_ENABLED (1 << 18)
|
||||||
|
|
||||||
|
DECLARE_PER_CPU(struct perf_event * [4], cpu_events);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
|
DECLARE_PER_CPU(u8, perf_monitors_used);
|
||||||
|
DECLARE_PER_CPU(u8, perf_bps_used);
|
||||||
|
# define perf_read_monitors_used() __this_cpu_read(perf_monitors_used)
|
||||||
|
# define perf_read_bps_used() __this_cpu_read(perf_bps_used)
|
||||||
|
#else /* ! CONFIG_PERF_EVENTS */
|
||||||
|
# define perf_read_monitors_used() 0
|
||||||
|
# define perf_read_bps_used() 0
|
||||||
|
#endif /* CONFIG_PERF_EVENTS */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bitmask for perf_monitors_used
|
||||||
|
*
|
||||||
|
* DIM0 has all counters from DIM1 and some more. So events for
|
||||||
|
* DIM1 are marked with DIM0_DIM1, and the actual used monitor
|
||||||
|
* will be determined at runtime.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
_DDM0 = 0,
|
||||||
|
_DDM1,
|
||||||
|
_DIM0,
|
||||||
|
_DIM1,
|
||||||
|
_DDM0_DDM1,
|
||||||
|
_DIM0_DIM1,
|
||||||
|
MAX_HW_MONITORS
|
||||||
|
};
|
||||||
|
#define DDM0 (1 << _DDM0)
|
||||||
|
#define DDM1 (1 << _DDM1)
|
||||||
|
#define DIM0 (1 << _DIM0)
|
||||||
|
#define DIM1 (1 << _DIM1)
|
||||||
|
#define DDM0_DDM1 (1 << _DDM0_DDM1)
|
||||||
|
#define DIM0_DIM1 (1 << _DIM0_DIM1)
|
@ -82,7 +82,7 @@ struct e2k_uncore {
|
|||||||
struct e2k_uncore_valid_events *valid_events;
|
struct e2k_uncore_valid_events *valid_events;
|
||||||
int (*validate_event)(struct e2k_uncore *, struct hw_perf_event *);
|
int (*validate_event)(struct e2k_uncore *, struct hw_perf_event *);
|
||||||
u64 (*get_event)(struct hw_perf_event *);
|
u64 (*get_event)(struct hw_perf_event *);
|
||||||
int (*add_event)(struct perf_event *);
|
int (*add_event)(struct e2k_uncore *, struct perf_event *);
|
||||||
|
|
||||||
struct e2k_uncore_reg_ops *reg_ops;
|
struct e2k_uncore_reg_ops *reg_ops;
|
||||||
struct pmu pmu;
|
struct pmu pmu;
|
||||||
|
@ -27,24 +27,6 @@
|
|||||||
#include <asm/e2k_debug.h>
|
#include <asm/e2k_debug.h>
|
||||||
#include <asm/kvm/uaccess.h> /* host mode support */
|
#include <asm/kvm/uaccess.h> /* host mode support */
|
||||||
|
|
||||||
#undef DEBUG_PV_UST_MODE
|
|
||||||
#undef DebugUST
|
|
||||||
#define DEBUG_PV_UST_MODE 0 /* guest user stacks debug */
|
|
||||||
#define DebugUST(fmt, args...) \
|
|
||||||
({ \
|
|
||||||
if (debug_guest_ust) \
|
|
||||||
pr_info("%s(): " fmt, __func__, ##args); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#undef DEBUG_PV_SYSCALL_MODE
|
|
||||||
#define DEBUG_PV_SYSCALL_MODE 0 /* syscall injection debugging */
|
|
||||||
|
|
||||||
#if DEBUG_PV_UST_MODE || DEBUG_PV_SYSCALL_MODE
|
|
||||||
extern bool debug_guest_ust;
|
|
||||||
#else
|
|
||||||
#define debug_guest_ust false
|
|
||||||
#endif /* DEBUG_PV_UST_MODE || DEBUG_PV_SYSCALL_MODE */
|
|
||||||
|
|
||||||
#undef DEBUG_SS_MODE
|
#undef DEBUG_SS_MODE
|
||||||
#undef DebugSS
|
#undef DebugSS
|
||||||
#define DEBUG_SS_MODE 0 /* stack switching */
|
#define DEBUG_SS_MODE 0 /* stack switching */
|
||||||
@ -818,12 +800,10 @@ static inline void free_virt_task_struct(struct task_struct *task)
|
|||||||
{
|
{
|
||||||
/* virtual machines is not supported */
|
/* virtual machines is not supported */
|
||||||
}
|
}
|
||||||
#else /* CONFIG_VIRTUALIZATION */
|
#elif defined(CONFIG_KVM_HOST_MODE)
|
||||||
/* It is native host kernel with virtualization support */
|
/* It is native host kernel with virtualization support */
|
||||||
/* or paravirtualized host and guest */
|
/* or paravirtualized host and guest */
|
||||||
/* or native guest kernel
|
|
||||||
#include <asm/kvm/process.h>
|
#include <asm/kvm/process.h>
|
||||||
*/
|
|
||||||
#endif /* ! CONFIG_VIRTUALIZATION */
|
#endif /* ! CONFIG_VIRTUALIZATION */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -852,119 +832,6 @@ native_preserve_user_hw_stacks_to_copy(e2k_stacks_t *u_stacks,
|
|||||||
u_stacks->pcshtp = cur_stacks->pcshtp;
|
u_stacks->pcshtp = cur_stacks->pcshtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
native_kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
|
||||||
{
|
|
||||||
void *dst_tail;
|
|
||||||
const void *src_tail;
|
|
||||||
u64 copied;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Kernel does not use FP registers so do not copy them.
|
|
||||||
* This only applies to CPUs before V5 instruction set
|
|
||||||
* (since V5 FP registers become general-purpose QP registers).
|
|
||||||
*/
|
|
||||||
if (cpu_has(CPU_FEAT_QPREG)) {
|
|
||||||
#pragma loop count (10)
|
|
||||||
for (i = 0; i < size / 64; i++)
|
|
||||||
E2K_TAGGED_MEMMOVE_64(&dst[8 * i], &src[8 * i]);
|
|
||||||
|
|
||||||
copied = round_down(size, 64);
|
|
||||||
dst_tail = (void *) dst + copied;
|
|
||||||
src_tail = (void *) src + copied;
|
|
||||||
} else {
|
|
||||||
#pragma loop count (5)
|
|
||||||
for (i = 0; i < size / 128; i++)
|
|
||||||
E2K_TAGGED_MEMMOVE_128_RF_V2(&dst[16 * i],
|
|
||||||
&src[16 * i]);
|
|
||||||
|
|
||||||
copied = round_down(size, 128);
|
|
||||||
dst_tail = (void *) dst + copied;
|
|
||||||
src_tail = (void *) src + copied;
|
|
||||||
|
|
||||||
if (size & 64) {
|
|
||||||
E2K_TAGGED_MEMMOVE_64(dst_tail, src_tail);
|
|
||||||
dst_tail += 64;
|
|
||||||
src_tail += 64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size & 32)
|
|
||||||
E2K_TAGGED_MEMMOVE_32(dst_tail, src_tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
native_collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
e2k_pcsp_hi_t k_pcsp_hi;
|
|
||||||
u64 size;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
DebugUST("current host chain stack index 0x%x, PCSHTP 0x%llx\n",
|
|
||||||
NATIVE_NV_READ_PCSP_HI_REG().PCSP_hi_ind,
|
|
||||||
NATIVE_READ_PCSHTP_REG_SVALUE());
|
|
||||||
|
|
||||||
NATIVE_FLUSHC;
|
|
||||||
k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
|
|
||||||
size = k_pcsp_hi.PCSP_hi_ind - spilled_size;
|
|
||||||
BUG_ON(!IS_ALIGNED(size, ALIGN_PCSTACK_TOP_SIZE) || (s64) size < 0);
|
|
||||||
#pragma loop count (2)
|
|
||||||
for (i = 0; i < size / 32; i++) {
|
|
||||||
u64 v0, v1, v2, v3;
|
|
||||||
|
|
||||||
v0 = src[4 * i];
|
|
||||||
v1 = src[4 * i + 1];
|
|
||||||
v2 = src[4 * i + 2];
|
|
||||||
v3 = src[4 * i + 3];
|
|
||||||
dst[4 * i] = v0;
|
|
||||||
dst[4 * i + 1] = v1;
|
|
||||||
dst[4 * i + 2] = v2;
|
|
||||||
dst[4 * i + 3] = v3;
|
|
||||||
}
|
|
||||||
|
|
||||||
k_pcsp_hi.PCSP_hi_ind -= spilled_size;
|
|
||||||
NATIVE_NV_NOIRQ_WRITE_PCSP_HI_REG(k_pcsp_hi);
|
|
||||||
|
|
||||||
DebugUST("move spilled chain part from host top %px to "
|
|
||||||
"bottom %px, size 0x%llx\n",
|
|
||||||
src, dst, size);
|
|
||||||
DebugUST("host kernel chain stack index is now 0x%x, "
|
|
||||||
"guest user PCSHTP 0x%llx\n",
|
|
||||||
k_pcsp_hi.PCSP_hi_ind, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
native_collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
e2k_psp_hi_t k_psp_hi;
|
|
||||||
u64 size;
|
|
||||||
|
|
||||||
DebugUST("current host procedure stack index 0x%x, PSHTP 0x%x\n",
|
|
||||||
NATIVE_NV_READ_PSP_HI_REG().PSP_hi_ind,
|
|
||||||
NATIVE_NV_READ_PSHTP_REG().PSHTP_ind);
|
|
||||||
|
|
||||||
NATIVE_FLUSHR;
|
|
||||||
k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
|
||||||
|
|
||||||
size = k_psp_hi.PSP_hi_ind - spilled_size;
|
|
||||||
BUG_ON(!IS_ALIGNED(size, ALIGN_PSTACK_TOP_SIZE) || (s64) size < 0);
|
|
||||||
|
|
||||||
prefetchw_range(src, size);
|
|
||||||
native_kernel_hw_stack_frames_copy(dst, src, size);
|
|
||||||
|
|
||||||
k_psp_hi.PSP_hi_ind -= spilled_size;
|
|
||||||
NATIVE_NV_NOIRQ_WRITE_PSP_HI_REG(k_psp_hi);
|
|
||||||
|
|
||||||
DebugUST("move spilled procedure part from host top %px to "
|
|
||||||
"bottom %px, size 0x%llx\n",
|
|
||||||
src, dst, size);
|
|
||||||
DebugUST("host kernel procedure stack index is now 0x%x, "
|
|
||||||
"guest user PSHTP 0x%llx\n",
|
|
||||||
k_psp_hi.PSP_hi_ind, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find_in_u_pcs_list - find frame offset from old_u_pcs_list
|
* find_in_u_pcs_list - find frame offset from old_u_pcs_list
|
||||||
* @frame - frame to search
|
* @frame - frame to search
|
||||||
@ -1093,22 +960,6 @@ preserve_user_hw_stacks_to_copy(e2k_stacks_t *u_stacks,
|
|||||||
native_preserve_user_hw_stacks_to_copy(u_stacks, cur_stacks);
|
native_preserve_user_hw_stacks_to_copy(u_stacks, cur_stacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
kernel_hw_stack_frames_copy(u64 *dst, const u64 *src, unsigned long size)
|
|
||||||
{
|
|
||||||
native_kernel_hw_stack_frames_copy(dst, src, size);
|
|
||||||
}
|
|
||||||
static __always_inline void
|
|
||||||
collapse_kernel_pcs(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
native_collapse_kernel_pcs(dst, src, spilled_size);
|
|
||||||
}
|
|
||||||
static __always_inline void
|
|
||||||
collapse_kernel_ps(u64 *dst, const u64 *src, u64 spilled_size)
|
|
||||||
{
|
|
||||||
native_collapse_kernel_ps(dst, src, spilled_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(CONFIG_VIRTUALIZATION)
|
#if !defined(CONFIG_VIRTUALIZATION)
|
||||||
/* native kernel without virtualization support */
|
/* native kernel without virtualization support */
|
||||||
#define do_map_user_hard_stack_to_kernel(node, kstart, ubase, size) \
|
#define do_map_user_hard_stack_to_kernel(node, kstart, ubase, size) \
|
||||||
@ -1164,104 +1015,6 @@ copy_spilled_user_stacks(struct e2k_stacks *child_stacks,
|
|||||||
#error "Undefined virtualization mode"
|
#error "Undefined virtualization mode"
|
||||||
#endif /* CONFIG_PARAVIRT_GUEST */
|
#endif /* CONFIG_PARAVIRT_GUEST */
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy hardware stack from user to *current* kernel stack.
|
|
||||||
* One has to be careful to avoid hardware FILL of this stack.
|
|
||||||
*/
|
|
||||||
static inline int __copy_user_to_current_hw_stack(void *dst, void __user *src,
|
|
||||||
unsigned long size, const pt_regs_t *regs, bool chain)
|
|
||||||
{
|
|
||||||
unsigned long min_flt, maj_flt, ts_flag;
|
|
||||||
|
|
||||||
if (likely(!host_test_intc_emul_mode(regs))) {
|
|
||||||
if (!__range_ok((unsigned long __force) src, size,
|
|
||||||
PAGE_OFFSET))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Every page fault here has a chance of FILL'ing the frame
|
|
||||||
* that is being copied, in which case we repeat the copy.
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
min_flt = READ_ONCE(current->min_flt);
|
|
||||||
maj_flt = READ_ONCE(current->maj_flt);
|
|
||||||
|
|
||||||
if (chain)
|
|
||||||
E2K_FLUSHC;
|
|
||||||
else
|
|
||||||
E2K_FLUSHR;
|
|
||||||
|
|
||||||
SET_USR_PFAULT("$.recovery_memcpy_fault");
|
|
||||||
fast_tagged_memory_copy_from_user(dst, src, size, regs,
|
|
||||||
TAGGED_MEM_STORE_REC_OPC |
|
|
||||||
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
|
||||||
TAGGED_MEM_LOAD_REC_OPC |
|
|
||||||
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
|
||||||
true);
|
|
||||||
if (RESTORE_USR_PFAULT) {
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
} while (unlikely(min_flt != READ_ONCE(current->min_flt) ||
|
|
||||||
maj_flt != READ_ONCE(current->maj_flt)));
|
|
||||||
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline int copy_user_to_current_hw_stack(void *dst, void __user *src,
|
|
||||||
unsigned long size, pt_regs_t *regs, bool chain)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
ret = __copy_user_to_current_hw_stack(dst, src, size, regs, chain);
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int copy_e2k_stack_from_user(void *dst, void __user *src,
|
|
||||||
unsigned long size, pt_regs_t *regs)
|
|
||||||
{
|
|
||||||
unsigned long ts_flag;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (likely(!host_test_intc_emul_mode(regs))) {
|
|
||||||
if (!__range_ok((unsigned long __force) src, size, PAGE_OFFSET))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
|
||||||
ret = host_copy_from_user_with_tags(dst, src, size, regs);
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
|
|
||||||
return (ret) ? -EFAULT : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int copy_e2k_stack_to_user(void __user *dst, void *src,
|
|
||||||
unsigned long size, pt_regs_t *regs)
|
|
||||||
{
|
|
||||||
unsigned long ts_flag;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (likely(!host_test_intc_emul_mode(regs))) {
|
|
||||||
if (!__range_ok((unsigned long __force) dst, size, PAGE_OFFSET))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
|
||||||
ret = host_copy_to_user_with_tags(dst, src, size, regs);
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
|
|
||||||
return (ret) ? -EFAULT : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DECLARE_PER_CPU(void *, reserve_hw_stacks);
|
DECLARE_PER_CPU(void *, reserve_hw_stacks);
|
||||||
static inline int on_reserve_stacks(void)
|
static inline int on_reserve_stacks(void)
|
||||||
{
|
{
|
||||||
@ -1278,134 +1031,6 @@ static inline int on_reserve_stacks(void)
|
|||||||
KERNEL_PC_STACK_SIZE;
|
KERNEL_PC_STACK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
user_hw_stack_frames_copy(void __user *dst, void *src, unsigned long copy_size,
|
|
||||||
const pt_regs_t *regs, unsigned long hw_stack_ind, bool is_pcsp)
|
|
||||||
{
|
|
||||||
unsigned long ts_flag;
|
|
||||||
|
|
||||||
if (unlikely(hw_stack_ind < copy_size)) {
|
|
||||||
unsigned long flags;
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
if (is_pcsp) {
|
|
||||||
E2K_FLUSHC;
|
|
||||||
} else {
|
|
||||||
E2K_FLUSHR;
|
|
||||||
}
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
SET_USR_PFAULT("$.recovery_memcpy_fault");
|
|
||||||
|
|
||||||
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
|
||||||
fast_tagged_memory_copy_to_user(dst, src, copy_size, regs,
|
|
||||||
TAGGED_MEM_STORE_REC_OPC |
|
|
||||||
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT,
|
|
||||||
TAGGED_MEM_LOAD_REC_OPC |
|
|
||||||
MAS_BYPASS_L1_CACHE << LDST_REC_OPC_MAS_SHIFT, true);
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
|
|
||||||
if (RESTORE_USR_PFAULT) {
|
|
||||||
pr_err("process %s (%d) %s stack could not be copied "
|
|
||||||
"from %px to %px size 0x%lx (out of memory?)\n",
|
|
||||||
current->comm, current->pid,
|
|
||||||
(is_pcsp) ? "chain" : "procedure",
|
|
||||||
src, dst, copy_size);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
DebugUST("copying guest %s stack spilled to host from %px "
|
|
||||||
"to guest kernel stack from %px, size 0x%lx\n",
|
|
||||||
(is_pcsp) ? "chain" : "procedure", src, dst, copy_size);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int
|
|
||||||
user_crs_frames_copy(e2k_mem_crs_t __user *u_frame, pt_regs_t *regs,
|
|
||||||
e2k_mem_crs_t *crs)
|
|
||||||
{
|
|
||||||
unsigned long ts_flag;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ts_flag = set_ts_flag(TS_KERNEL_SYSCALL);
|
|
||||||
ret = host_copy_to_user(u_frame, crs, sizeof(*crs), regs);
|
|
||||||
clear_ts_flag(ts_flag);
|
|
||||||
if (unlikely(ret))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline u64 get_wsz(enum restore_caller from)
|
|
||||||
{
|
|
||||||
return NATIVE_READ_WD_REG().size >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int user_psp_stack_copy(e2k_psp_lo_t u_psp_lo,
|
|
||||||
e2k_psp_hi_t u_psp_hi, s64 u_pshtp_size,
|
|
||||||
e2k_psp_lo_t k_psp_lo, e2k_psp_hi_t k_psp_hi,
|
|
||||||
unsigned long copy_size, const pt_regs_t *regs)
|
|
||||||
{
|
|
||||||
void __user *dst;
|
|
||||||
void *src;
|
|
||||||
|
|
||||||
dst = (void __user *) (AS(u_psp_lo).base + AS(u_psp_hi).ind -
|
|
||||||
u_pshtp_size);
|
|
||||||
src = (void *) AS(k_psp_lo).base;
|
|
||||||
|
|
||||||
return user_hw_stack_frames_copy(dst, src, copy_size,
|
|
||||||
regs, k_psp_hi.PSP_hi_ind, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline int user_pcsp_stack_copy(e2k_pcsp_lo_t u_pcsp_lo,
|
|
||||||
e2k_pcsp_hi_t u_pcsp_hi, s64 u_pcshtp_size,
|
|
||||||
e2k_pcsp_lo_t k_pcsp_lo, e2k_pcsp_hi_t k_pcsp_hi,
|
|
||||||
unsigned long copy_size, const pt_regs_t *regs)
|
|
||||||
{
|
|
||||||
void __user *dst;
|
|
||||||
void *src;
|
|
||||||
|
|
||||||
dst = (void __user *)(AS(u_pcsp_lo).base + AS(u_pcsp_hi).ind -
|
|
||||||
u_pcshtp_size);
|
|
||||||
src = (void *) AS(k_pcsp_lo).base;
|
|
||||||
|
|
||||||
return user_hw_stack_frames_copy(dst, src, copy_size,
|
|
||||||
regs, k_pcsp_hi.PCSP_hi_ind, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline u64 get_ps_clear_size(u64 cur_window_q,
|
|
||||||
e2k_pshtp_t pshtp)
|
|
||||||
{
|
|
||||||
s64 u_pshtp_size_q;
|
|
||||||
|
|
||||||
u_pshtp_size_q = GET_PSHTP_Q_INDEX(pshtp);
|
|
||||||
if (u_pshtp_size_q > E2K_MAXSR - cur_window_q)
|
|
||||||
u_pshtp_size_q = E2K_MAXSR - cur_window_q;
|
|
||||||
|
|
||||||
return E2K_MAXSR - (cur_window_q + u_pshtp_size_q);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline s64 get_ps_copy_size(u64 cur_window_q, s64 u_pshtp_size)
|
|
||||||
{
|
|
||||||
return u_pshtp_size - (E2K_MAXSR - cur_window_q) * EXT_4_NR_SZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_HAS_FILL_INSTRUCTION
|
|
||||||
# define E2K_CF_MAX_FILL (E2K_CF_MAX_FILL_FILLC_q * 0x10)
|
|
||||||
#else
|
|
||||||
extern int cf_max_fill_return;
|
|
||||||
# define E2K_CF_MAX_FILL cf_max_fill_return
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static __always_inline s64 get_pcs_copy_size(s64 u_pcshtp_size)
|
|
||||||
{
|
|
||||||
/* Before v6 it was possible to fill no more than 16 registers.
|
|
||||||
* Since E2K_MAXCR_q is much bigger than 16 we can be sure that
|
|
||||||
* there is enough space in CF for the FILL, so there is no
|
|
||||||
* need to take into account space taken by current window. */
|
|
||||||
return u_pcshtp_size - E2K_CF_MAX_FILL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
/* This function is used to fixup ret_stack, so make sure it itself
|
/* This function is used to fixup ret_stack, so make sure it itself
|
||||||
* does not rely on correct values in ret_stack by using "notrace". */
|
* does not rely on correct values in ret_stack by using "notrace". */
|
||||||
@ -1427,352 +1052,14 @@ static inline void apply_graph_tracer_delta(unsigned long delta)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
extern int user_hw_stacks_copy_full(struct e2k_stacks *stacks,
|
||||||
* user_hw_stacks_copy - copy user hardware stacks that have been
|
pt_regs_t *regs, e2k_mem_crs_t *crs);
|
||||||
* SPILLed to kernel back to user space
|
|
||||||
* @stacks - saved user stack registers
|
|
||||||
* @cur_window_q - size of current window in procedure stack,
|
|
||||||
* needed only if @copy_full is not set
|
|
||||||
* @copy_full - set if want to copy _all_ of SPILLed stacks
|
|
||||||
*
|
|
||||||
* This does not update stacks->pshtp and stacks->pcshtp. Main reason is
|
|
||||||
* signals: if a signal arrives after copying then it must see a coherent
|
|
||||||
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
|
||||||
* data from user space is spilled to kernel space.
|
|
||||||
*/
|
|
||||||
static __always_inline int
|
|
||||||
native_user_hw_stacks_copy(struct e2k_stacks *stacks,
|
|
||||||
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
|
||||||
{
|
|
||||||
trap_pt_regs_t *trap = regs->trap;
|
|
||||||
e2k_psp_lo_t u_psp_lo = stacks->psp_lo,
|
|
||||||
k_psp_lo = current_thread_info()->k_psp_lo;
|
|
||||||
e2k_psp_hi_t u_psp_hi = stacks->psp_hi;
|
|
||||||
e2k_pcsp_lo_t u_pcsp_lo = stacks->pcsp_lo,
|
|
||||||
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
|
||||||
e2k_pcsp_hi_t u_pcsp_hi = stacks->pcsp_hi;
|
|
||||||
s64 u_pshtp_size, u_pcshtp_size, ps_copy_size, pcs_copy_size;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
u_pshtp_size = GET_PSHTP_MEM_INDEX(stacks->pshtp);
|
|
||||||
u_pcshtp_size = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy user's part from kernel stacks into user stacks
|
|
||||||
* Update user's stack registers
|
|
||||||
*/
|
|
||||||
if (copy_full) {
|
|
||||||
pcs_copy_size = u_pcshtp_size;
|
|
||||||
ps_copy_size = u_pshtp_size;
|
|
||||||
} else {
|
|
||||||
pcs_copy_size = get_pcs_copy_size(u_pcshtp_size);
|
|
||||||
ps_copy_size = get_ps_copy_size(cur_window_q, u_pshtp_size);
|
|
||||||
|
|
||||||
/* Make sure there is enough space in CF for the FILL */
|
|
||||||
BUG_ON((E2K_MAXCR_q - 4) * 16 < E2K_CF_MAX_FILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(pcs_copy_size <= 0 && ps_copy_size <= 0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (unlikely(pcs_copy_size > 0)) {
|
|
||||||
e2k_pcsp_hi_t k_pcsp_hi = NATIVE_NV_READ_PCSP_HI_REG();
|
|
||||||
|
|
||||||
/* Since not all user data has been SPILL'ed it is possible
|
|
||||||
* that we have already overflown user's hardware stack. */
|
|
||||||
if (unlikely(AS(u_pcsp_hi).ind > AS(u_pcsp_hi).size)) {
|
|
||||||
ret = handle_chain_stack_bounds(stacks, trap);
|
|
||||||
if (unlikely(ret)) {
|
|
||||||
pr_warning("process %s (%d) chain stack overflow (out of memory?)\n",
|
|
||||||
current->comm, current->pid);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
u_pcsp_lo = stacks->pcsp_lo;
|
|
||||||
u_pcsp_hi = stacks->pcsp_hi;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = user_pcsp_stack_copy(u_pcsp_lo, u_pcsp_hi, u_pcshtp_size,
|
|
||||||
k_pcsp_lo, k_pcsp_hi, pcs_copy_size, regs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(ps_copy_size > 0)) {
|
|
||||||
e2k_psp_hi_t k_psp_hi = NATIVE_NV_READ_PSP_HI_REG();
|
|
||||||
|
|
||||||
/* Since not all user data has been SPILL'ed it is possible
|
|
||||||
* that we have already overflowed user's hardware stack. */
|
|
||||||
if (unlikely(AS(u_psp_hi).ind > AS(u_psp_hi).size)) {
|
|
||||||
ret = handle_proc_stack_bounds(stacks, trap);
|
|
||||||
if (unlikely(ret)) {
|
|
||||||
pr_warning("process %s (%d) procedure stack overflow (out of memory?)\n",
|
|
||||||
current->comm, current->pid);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
u_psp_lo = stacks->psp_lo;
|
|
||||||
u_psp_hi = stacks->psp_hi;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = user_psp_stack_copy(u_psp_lo, u_psp_hi, u_pshtp_size,
|
|
||||||
k_psp_lo, k_psp_hi, ps_copy_size, regs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void collapse_kernel_hw_stacks(struct e2k_stacks *stacks)
|
|
||||||
{
|
|
||||||
e2k_pcsp_lo_t k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
|
||||||
e2k_psp_lo_t k_psp_lo = current_thread_info()->k_psp_lo;
|
|
||||||
unsigned long flags, spilled_pc_size, spilled_p_size;
|
|
||||||
e2k_pshtp_t pshtp = stacks->pshtp;
|
|
||||||
u64 *dst;
|
|
||||||
const u64 *src;
|
|
||||||
|
|
||||||
spilled_pc_size = PCSHTP_SIGN_EXTEND(stacks->pcshtp);
|
|
||||||
spilled_p_size = GET_PSHTP_MEM_INDEX(pshtp);
|
|
||||||
DebugUST("guest user spilled to host kernel stack part: chain 0x%lx "
|
|
||||||
"procedure 0x%lx\n",
|
|
||||||
spilled_pc_size, spilled_p_size);
|
|
||||||
/* When user tries to return from the last user frame
|
|
||||||
* we will have pcshtp = pcsp_hi.ind = 0. But situation
|
|
||||||
* with pcsp_hi.ind != 0 and pcshtp = 0 is impossible. */
|
|
||||||
if (WARN_ON_ONCE(spilled_pc_size < SZ_OF_CR &&
|
|
||||||
AS(stacks->pcsp_hi).ind != 0))
|
|
||||||
do_exit(SIGKILL);
|
|
||||||
|
|
||||||
/* Keep the last user frame (see user_hw_stacks_copy_full()) */
|
|
||||||
if (spilled_pc_size >= SZ_OF_CR) {
|
|
||||||
spilled_pc_size -= SZ_OF_CR;
|
|
||||||
DebugUST("Keep the prev user chain frame, so spilled chain "
|
|
||||||
"size is now 0x%lx\n",
|
|
||||||
spilled_pc_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
|
|
||||||
if (spilled_pc_size) {
|
|
||||||
dst = (u64 *) AS(k_pcsp_lo).base;
|
|
||||||
src = (u64 *) (AS(k_pcsp_lo).base + spilled_pc_size);
|
|
||||||
collapse_kernel_pcs(dst, src, spilled_pc_size);
|
|
||||||
|
|
||||||
stacks->pcshtp = SZ_OF_CR;
|
|
||||||
|
|
||||||
apply_graph_tracer_delta(-spilled_pc_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spilled_p_size) {
|
|
||||||
dst = (u64 *) AS(k_psp_lo).base;
|
|
||||||
src = (u64 *) (AS(k_psp_lo).base + spilled_p_size);
|
|
||||||
collapse_kernel_ps(dst, src, spilled_p_size);
|
|
||||||
|
|
||||||
AS(pshtp).ind = 0;
|
|
||||||
stacks->pshtp = pshtp;
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* user_hw_stacks_prepare - prepare user hardware stacks that have been
|
|
||||||
* SPILLed to kernel back to user space
|
|
||||||
* @stacks - saved user stack registers
|
|
||||||
* @cur_window_q - size of current window in procedure stack,
|
|
||||||
* needed only if @copy_full is not set
|
|
||||||
* @syscall - true if called upon direct system call exit (no signal handlers)
|
|
||||||
*
|
|
||||||
* This does two things:
|
|
||||||
*
|
|
||||||
* 1) It is possible that upon kernel entry pcshtp == 0 in some cases:
|
|
||||||
* - user signal handler had pcshtp==0x20 before return to sigreturn()
|
|
||||||
* - user context had pcshtp==0x20 before return to makecontext_trampoline()
|
|
||||||
* - chain stack underflow happened
|
|
||||||
* So it is possible in sigreturn() and traps, but not in system calls.
|
|
||||||
* If we are using the trick with return to FILL user hardware stacks than
|
|
||||||
* we must have frame in chain stack to return to. So in this case kernel's
|
|
||||||
* chain stack is moved up by one frame (0x20 bytes).
|
|
||||||
* We also fill the new frame with actual user data and update stacks->pcshtp,
|
|
||||||
* this is needed to keep the coherent state where saved stacks->pcshtp values
|
|
||||||
* shows how much data from user space has been spilled to kernel space.
|
|
||||||
*
|
|
||||||
* 2) It is not possible to always FILL all of user data that have been
|
|
||||||
* SPILLed to kernel stacks. So we manually copy the leftovers that can
|
|
||||||
* not be FILLed to user space.
|
|
||||||
* This copy does not update stacks->pshtp and stacks->pcshtp. Main reason
|
|
||||||
* is signals: if a signal arrives after copying then it must see a coherent
|
|
||||||
* state where saved stacks->pshtp and stacks->pcshtp values show how much
|
|
||||||
* data from user space has been spilled to kernel space.
|
|
||||||
*/
|
|
||||||
static __always_inline void native_user_hw_stacks_prepare(
|
|
||||||
struct e2k_stacks *stacks, pt_regs_t *regs,
|
|
||||||
u64 cur_window_q, enum restore_caller from, int syscall)
|
|
||||||
{
|
|
||||||
e2k_pcshtp_t u_pcshtp = stacks->pcshtp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
BUG_ON(from & FROM_PV_VCPU_MODE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 1) Make sure there is free space in kernel chain stack to return to
|
|
||||||
*/
|
|
||||||
if (!syscall && u_pcshtp == 0) {
|
|
||||||
unsigned long flags;
|
|
||||||
e2k_pcsp_lo_t u_pcsp_lo = stacks->pcsp_lo,
|
|
||||||
k_pcsp_lo = current_thread_info()->k_pcsp_lo;
|
|
||||||
e2k_pcsp_hi_t u_pcsp_hi = stacks->pcsp_hi, k_pcsp_hi;
|
|
||||||
e2k_mem_crs_t __user *u_cframe;
|
|
||||||
e2k_mem_crs_t *k_crs;
|
|
||||||
u64 u_cbase;
|
|
||||||
int ret = -EINVAL;
|
|
||||||
|
|
||||||
raw_all_irq_save(flags);
|
|
||||||
E2K_FLUSHC;
|
|
||||||
k_pcsp_hi = READ_PCSP_HI_REG();
|
|
||||||
BUG_ON(AS(k_pcsp_hi).ind);
|
|
||||||
AS(k_pcsp_hi).ind += SZ_OF_CR;
|
|
||||||
WRITE_PCSP_HI_REG(k_pcsp_hi);
|
|
||||||
|
|
||||||
k_crs = (e2k_mem_crs_t *) AS(k_pcsp_lo).base;
|
|
||||||
u_cframe = (e2k_mem_crs_t __user *) (AS(u_pcsp_lo).base +
|
|
||||||
AS(u_pcsp_hi).ind);
|
|
||||||
u_cbase = ((from & FROM_RETURN_PV_VCPU_TRAP) ||
|
|
||||||
host_test_intc_emul_mode(regs)) ?
|
|
||||||
u_pcsp_lo.PCSP_lo_base :
|
|
||||||
(u64) CURRENT_PCS_BASE();
|
|
||||||
if ((u64) u_cframe > u_cbase) {
|
|
||||||
ret = __copy_user_to_current_hw_stack(k_crs,
|
|
||||||
u_cframe - 1, sizeof(*k_crs), regs, true);
|
|
||||||
}
|
|
||||||
raw_all_irq_restore(flags);
|
|
||||||
|
|
||||||
/* Can happen if application returns until runs out of
|
|
||||||
* chain stack or there is no free memory for stacks.
|
|
||||||
* There is no user stack to return to - die. */
|
|
||||||
if (ret) {
|
|
||||||
E2K_LMS_HALT_OK;
|
|
||||||
SIGDEBUG_PRINT("SIGKILL. %s\n",
|
|
||||||
(ret == -EINVAL) ? "tried to return to kernel" :
|
|
||||||
"ran into Out-of-Memory on user stacks");
|
|
||||||
force_sig(SIGKILL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS(u_pcsp_hi).ind < SZ_OF_CR) {
|
|
||||||
update_pcsp_regs(AS(u_pcsp_lo).base,
|
|
||||||
&u_pcsp_lo, &u_pcsp_hi);
|
|
||||||
stacks->pcsp_lo = u_pcsp_lo;
|
|
||||||
stacks->pcsp_hi = u_pcsp_hi;
|
|
||||||
BUG_ON(AS(u_pcsp_hi).ind < SZ_OF_CR);
|
|
||||||
}
|
|
||||||
|
|
||||||
u_pcshtp = SZ_OF_CR;
|
|
||||||
stacks->pcshtp = u_pcshtp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 2) Copy user data that cannot be FILLed
|
|
||||||
*/
|
|
||||||
ret = native_user_hw_stacks_copy(stacks, regs, cur_window_q, false);
|
|
||||||
if (unlikely(ret))
|
|
||||||
do_exit(SIGKILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_VIRTUALIZATION
|
|
||||||
/* native kernel without virtualization support */
|
|
||||||
static __always_inline int
|
|
||||||
user_hw_stacks_copy(struct e2k_stacks *stacks,
|
|
||||||
pt_regs_t *regs, u64 cur_window_q, bool copy_full)
|
|
||||||
{
|
|
||||||
return native_user_hw_stacks_copy(stacks, regs, cur_window_q, copy_full);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void
|
|
||||||
host_user_hw_stacks_prepare(struct e2k_stacks *stacks, pt_regs_t *regs,
|
|
||||||
u64 cur_window_q, enum restore_caller from, int syscall)
|
|
||||||
{
|
|
||||||
native_user_hw_stacks_prepare(stacks, regs, cur_window_q,
|
|
||||||
from, syscall);
|
|
||||||
}
|
|
||||||
#elif defined(CONFIG_KVM_GUEST_KERNEL)
|
|
||||||
/* It is native guest kernel (without paravirtualization) */
|
|
||||||
#include <asm/kvm/guest/process.h>
|
|
||||||
#elif defined(CONFIG_PARAVIRT_GUEST)
|
|
||||||
/* It is paravirtualized kernel (host and guest) */
|
|
||||||
#include <asm/paravirt/process.h>
|
|
||||||
#elif defined(CONFIG_KVM_HOST_MODE)
|
|
||||||
/* It is host kernel with virtualization support */
|
|
||||||
#include <asm/kvm/process.h>
|
|
||||||
#else /* unknow mode */
|
|
||||||
#error "unknown virtualization mode"
|
|
||||||
#endif /* !CONFIG_VIRTUALIZATION */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* user_hw_stacks_copy_full - copy part of user stacks that was SPILLed
|
|
||||||
* into kernel back to user stacks.
|
|
||||||
* @stacks - saved user stack registers
|
|
||||||
* @regs - pt_regs pointer
|
|
||||||
* @crs - last frame to copy
|
|
||||||
*
|
|
||||||
* If @crs is not NULL then the frame pointed to by it will also be copied
|
|
||||||
* to userspace. Note that 'stacks->pcsp_hi.ind' is _not_ updated after
|
|
||||||
* copying since it would leave stack in inconsistent state (with two
|
|
||||||
* copies of the same @crs frame), this is left to the caller. *
|
|
||||||
*
|
|
||||||
* Inlining this reduces the amount of memory to copy in
|
|
||||||
* collapse_kernel_hw_stacks().
|
|
||||||
*/
|
|
||||||
static inline int user_hw_stacks_copy_full(struct e2k_stacks *stacks,
|
|
||||||
pt_regs_t *regs, e2k_mem_crs_t *crs)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy part of user stacks that were SPILLed into kernel stacks
|
|
||||||
*/
|
|
||||||
ret = user_hw_stacks_copy(stacks, regs, 0, true);
|
|
||||||
if (unlikely(ret))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Nothing to FILL so remove the resulting hole from kernel stacks.
|
|
||||||
*
|
|
||||||
* IMPORTANT: there is always at least one user frame at the top of
|
|
||||||
* kernel stack - the one that issued a system call (in case of an
|
|
||||||
* exception we uphold this rule manually, see user_hw_stacks_prepare())
|
|
||||||
* We keep this ABI and _always_ leave space for one user frame,
|
|
||||||
* this way we can later FILL using return trick (otherwise there
|
|
||||||
* would be no space in chain stack for the trick).
|
|
||||||
*/
|
|
||||||
collapse_kernel_hw_stacks(stacks);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy saved %cr registers
|
|
||||||
*
|
|
||||||
* Caller must take care of filling of resulting hole
|
|
||||||
* (last user frame from pcshtp == SZ_OF_CR).
|
|
||||||
*/
|
|
||||||
if (crs) {
|
|
||||||
e2k_mem_crs_t __user *u_frame;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
u_frame = (void __user *) (AS(stacks->pcsp_lo).base +
|
|
||||||
AS(stacks->pcsp_hi).ind);
|
|
||||||
ret = user_crs_frames_copy(u_frame, regs, ®s->crs);
|
|
||||||
if (unlikely(ret))
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern e2k_addr_t get_nested_kernel_IP(pt_regs_t *regs, int n);
|
extern e2k_addr_t get_nested_kernel_IP(pt_regs_t *regs, int n);
|
||||||
extern unsigned long remap_e2k_stack(unsigned long addr,
|
extern unsigned long remap_e2k_stack(unsigned long addr,
|
||||||
unsigned long old_size, unsigned long new_size, bool after);
|
unsigned long old_size, unsigned long new_size, bool after);
|
||||||
|
|
||||||
extern int find_cui_by_ip(unsigned long ip);
|
extern int find_cui_by_ip(unsigned long ip);
|
||||||
|
|
||||||
#endif /* _E2K_PROCESS_H */
|
#endif /* _E2K_PROCESS_H */
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
#include <asm/monitors.h>
|
#include <asm/monitors.h>
|
||||||
#include <asm/mmu.h>
|
#include <asm/mmu.h>
|
||||||
#include <asm/mmu_regs.h>
|
#include <asm/mmu_regs.h>
|
||||||
|
#include <asm/perf_event_types.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/p2v/boot_head.h>
|
#include <asm/p2v/boot_head.h>
|
||||||
@ -219,14 +220,6 @@ do { \
|
|||||||
NATIVE_DO_SAVE_MONITOR_COUNTERS(sw_regs); \
|
NATIVE_DO_SAVE_MONITOR_COUNTERS(sw_regs); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
|
||||||
* When we use monitor registers, we count monitor events for the whole system,
|
|
||||||
* so DIMAR0, DIMAR1, DDMAR0 and DDMAR1 registers are not depend on process and
|
|
||||||
* need not be saved while process switching. DIMCR and DDMCR registers are not
|
|
||||||
* depend on process too, but they should be saved while process switching,
|
|
||||||
* because they are used to determine monitoring start moment during monitor
|
|
||||||
* events counting for a process.
|
|
||||||
*/
|
|
||||||
static inline void native_save_user_only_regs(struct sw_regs *sw_regs)
|
static inline void native_save_user_only_regs(struct sw_regs *sw_regs)
|
||||||
{
|
{
|
||||||
if (machine.save_dimtp)
|
if (machine.save_dimtp)
|
||||||
@ -946,27 +939,46 @@ static inline void native_restore_user_only_regs(struct sw_regs *sw_regs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PERF_EVENTS
|
|
||||||
DECLARE_PER_CPU(u8, perf_monitors_used);
|
|
||||||
DECLARE_PER_CPU(u8, perf_bps_used);
|
|
||||||
# define is_perf_using_monitors __this_cpu_read(perf_monitors_used)
|
|
||||||
# define is_perf_using_bps __this_cpu_read(perf_bps_used)
|
|
||||||
#else /* ! CONFIG_PERF_EVENTS */
|
|
||||||
#define is_perf_using_monitors false
|
|
||||||
#define is_perf_using_bps false
|
|
||||||
#endif /* CONFIG_PERF_EVENTS */
|
|
||||||
|
|
||||||
static inline void native_clear_user_only_regs(void)
|
static inline void native_clear_user_only_regs(void)
|
||||||
{
|
{
|
||||||
if (!is_perf_using_bps) {
|
u8 monitors_used = perf_read_monitors_used();
|
||||||
|
u8 bps_used = perf_read_bps_used();
|
||||||
|
if (!bps_used) {
|
||||||
NATIVE_WRITE_DIBCR_REG_VALUE(0);
|
NATIVE_WRITE_DIBCR_REG_VALUE(0);
|
||||||
NATIVE_WRITE_DDBCR_REG_VALUE(0);
|
NATIVE_WRITE_DDBCR_REG_VALUE(0);
|
||||||
}
|
}
|
||||||
if (!MONITORING_IS_ACTIVE && !is_perf_using_monitors) {
|
if (!MONITORING_IS_ACTIVE) {
|
||||||
NATIVE_WRITE_DIMCR_REG_VALUE(0);
|
if (!monitors_used) {
|
||||||
NATIVE_WRITE_DIBSR_REG_VALUE(0);
|
NATIVE_WRITE_DIMCR_REG_VALUE(0);
|
||||||
NATIVE_WRITE_DDMCR_REG_VALUE(0);
|
NATIVE_WRITE_DIBSR_REG_VALUE(0);
|
||||||
NATIVE_WRITE_DDBSR_REG_VALUE(0);
|
NATIVE_WRITE_DDMCR_REG_VALUE(0);
|
||||||
|
NATIVE_WRITE_DDBSR_REG_VALUE(0);
|
||||||
|
} else {
|
||||||
|
e2k_dimcr_t dimcr = NATIVE_READ_DIMCR_REG();
|
||||||
|
e2k_ddmcr_t ddmcr = NATIVE_READ_DDMCR_REG();
|
||||||
|
e2k_dibsr_t dibsr = NATIVE_READ_DIBSR_REG();
|
||||||
|
e2k_ddbsr_t ddbsr = NATIVE_READ_DDBSR_REG();
|
||||||
|
if (!(monitors_used & DIM0)) {
|
||||||
|
dimcr.half_word[0] = 0;
|
||||||
|
dibsr.m0 = 0;
|
||||||
|
}
|
||||||
|
if (!(monitors_used & DIM1)) {
|
||||||
|
dimcr.half_word[1] = 0;
|
||||||
|
dibsr.m1 = 0;
|
||||||
|
}
|
||||||
|
if (!(monitors_used & DDM0)) {
|
||||||
|
ddmcr.half_word[0] = 0;
|
||||||
|
ddbsr.m0 = 0;
|
||||||
|
}
|
||||||
|
if (!(monitors_used & DDM1)) {
|
||||||
|
ddmcr.half_word[1] = 0;
|
||||||
|
ddbsr.m1 = 0;
|
||||||
|
}
|
||||||
|
NATIVE_WRITE_DIMCR_REG(dimcr);
|
||||||
|
NATIVE_WRITE_DDMCR_REG(ddmcr);
|
||||||
|
NATIVE_WRITE_DIBSR_REG(dibsr);
|
||||||
|
NATIVE_WRITE_DDBSR_REG(ddbsr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1299,7 +1311,6 @@ native_set_current_thread_info(struct thread_info *thread,
|
|||||||
set_osgd_task_struct(task);
|
set_osgd_task_struct(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
set_current_thread_info(struct thread_info *thread, struct task_struct *task)
|
set_current_thread_info(struct thread_info *thread, struct task_struct *task)
|
||||||
{
|
{
|
||||||
@ -1308,5 +1319,21 @@ set_current_thread_info(struct thread_info *thread, struct task_struct *task)
|
|||||||
set_osgd_task_struct(task);
|
set_osgd_task_struct(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SAVE_PSYSCALL_RVAL(regs, _rval, _rval1, _rval2, _rv1_tag, \
|
||||||
|
_rv2_tag, _return_desk) \
|
||||||
|
({ \
|
||||||
|
(regs)->sys_rval = (_rval); \
|
||||||
|
(regs)->rval1 = (_rval1); \
|
||||||
|
(regs)->rval2 = (_rval2); \
|
||||||
|
(regs)->rv1_tag = (_rv1_tag); \
|
||||||
|
(regs)->rv2_tag = (_rv2_tag); \
|
||||||
|
(regs)->return_desk = (_return_desk); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define SAVE_SYSCALL_RVAL(regs, rval) \
|
||||||
|
({ \
|
||||||
|
(regs)->sys_rval = (rval); \
|
||||||
|
})
|
||||||
|
|
||||||
#endif /* _E2K_REGS_STATE_H */
|
#endif /* _E2K_REGS_STATE_H */
|
||||||
|
|
||||||
|
@ -204,6 +204,18 @@
|
|||||||
#define SIC_mc_ch 0x400
|
#define SIC_mc_ch 0x400
|
||||||
#define SIC_mc_status 0x44c
|
#define SIC_mc_status 0x44c
|
||||||
|
|
||||||
|
#define SIC_mc_opmb 0x424
|
||||||
|
#define SIC_mc0_opmb 0x414
|
||||||
|
#define SIC_mc1_opmb 0x454
|
||||||
|
#define SIC_mc2_opmb 0x494
|
||||||
|
#define SIC_mc3_opmb 0x4d4
|
||||||
|
|
||||||
|
#define SIC_mc_cfg 0x418
|
||||||
|
#define SIC_mc0_cfg 0x418
|
||||||
|
#define SIC_mc1_cfg 0x458
|
||||||
|
#define SIC_mc2_cfg 0x498
|
||||||
|
#define SIC_mc3_cfg 0x4d8
|
||||||
|
|
||||||
/* HMU */
|
/* HMU */
|
||||||
#define SIC_hmu_mic 0xd00
|
#define SIC_hmu_mic 0xd00
|
||||||
#define SIC_hmu0_int 0xd40
|
#define SIC_hmu0_int 0xd40
|
||||||
@ -1085,6 +1097,79 @@ typedef union e2k_mc_ecc_struct { /* Structure word */
|
|||||||
#define E2K_MC_ECC_secnt fields.secnt /* single error counter */
|
#define E2K_MC_ECC_secnt fields.secnt /* single error counter */
|
||||||
#define E2K_MC_ECC_reg word
|
#define E2K_MC_ECC_reg word
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read/Write MCX_OPMb (X={0, 1, 2, 3}) registers
|
||||||
|
* ! only for P1 processor type !
|
||||||
|
*/
|
||||||
|
typedef unsigned int e2k_mc_opmb_t; /* single word (32 bits) */
|
||||||
|
typedef struct e2k_mc_opmb_fields {
|
||||||
|
e2k_mc_opmb_t ct0 : 3; /* [0:2] */
|
||||||
|
e2k_mc_opmb_t ct1 : 3; /* [3:5] */
|
||||||
|
e2k_mc_opmb_t pbm0 : 2; /* [6:7] */
|
||||||
|
e2k_mc_opmb_t pbm1 : 2; /* [8:9] */
|
||||||
|
e2k_mc_opmb_t rm : 1; /* [10] */
|
||||||
|
e2k_mc_opmb_t rdodt : 1; /* [11] */
|
||||||
|
e2k_mc_opmb_t wrodt : 1; /* [12] */
|
||||||
|
e2k_mc_opmb_t bl8int : 1; /* [13] */
|
||||||
|
e2k_mc_opmb_t mi_fast : 1; /* [14] */
|
||||||
|
e2k_mc_opmb_t mt : 1; /* [15] */
|
||||||
|
e2k_mc_opmb_t il : 1; /* [16] */
|
||||||
|
e2k_mc_opmb_t rcven_del : 2; /* [17:18] */
|
||||||
|
e2k_mc_opmb_t mc_ps : 1; /* [19] */
|
||||||
|
e2k_mc_opmb_t arp_en : 1; /* [20] */
|
||||||
|
e2k_mc_opmb_t flt_brop : 1; /* [21] */
|
||||||
|
e2k_mc_opmb_t flt_rdpr : 1; /* [22] */
|
||||||
|
e2k_mc_opmb_t flt_blk : 1; /* [23] */
|
||||||
|
e2k_mc_opmb_t parerr : 1; /* [24] */
|
||||||
|
e2k_mc_opmb_t cmdpack : 1; /* [25] */
|
||||||
|
e2k_mc_opmb_t sldwr : 1; /* [26] */
|
||||||
|
e2k_mc_opmb_t sldrd : 1; /* [27] */
|
||||||
|
e2k_mc_opmb_t mirr : 1; /* [28] */
|
||||||
|
e2k_mc_opmb_t twrwr : 2; /* [29:30] */
|
||||||
|
e2k_mc_opmb_t mcln : 1; /* [31] */
|
||||||
|
} e2k_mc_opmb_fields_t;
|
||||||
|
typedef union e2k_mc_opmb_struct { /* Structure word */
|
||||||
|
e2k_mc_opmb_fields_t fields; /* as fields */
|
||||||
|
e2k_mc_opmb_t word; /* as entire register */
|
||||||
|
} e2k_mc_opmb_struct_t;
|
||||||
|
|
||||||
|
#define E2K_MC_OPMB_pbm0 fields.pbm0 /* physycal bank map slot 0 */
|
||||||
|
#define E2K_MC_OPMB_pbm1 fields.pbm1 /* physycal bank map slot 1 */
|
||||||
|
#define E2K_MC_OPMB_reg word
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read/Write MCX_CFG (X={0, 1, 2, 3}) registers
|
||||||
|
* P9, E2C3, E12 and E16 processor type
|
||||||
|
*/
|
||||||
|
typedef unsigned int e2k_mc_cfg_t; /* single word (32 bits) */
|
||||||
|
typedef struct e2k_mc_cfg_fields {
|
||||||
|
e2k_mc_cfg_t ct0 : 3; /* [0:2] */
|
||||||
|
e2k_mc_cfg_t ct1 : 3; /* [3:5] */
|
||||||
|
e2k_mc_cfg_t pbm0 : 2; /* [6:7] */
|
||||||
|
e2k_mc_cfg_t pbm1 : 2; /* [8:9] */
|
||||||
|
e2k_mc_cfg_t rm : 1; /* [10] */
|
||||||
|
e2k_mc_cfg_t reserve1 : 2; /* [11:12] */
|
||||||
|
e2k_mc_cfg_t mirr : 1; /* [13] */
|
||||||
|
e2k_mc_cfg_t sf : 3; /* [14:16] */
|
||||||
|
e2k_mc_cfg_t mt : 1; /* [17] */
|
||||||
|
e2k_mc_cfg_t cs8 : 1; /* [18] */
|
||||||
|
e2k_mc_cfg_t ptrr_mode : 2; /* [19:20] */
|
||||||
|
e2k_mc_cfg_t mcrc : 1; /* [21] */
|
||||||
|
e2k_mc_cfg_t odt_ext : 2; /* [22:23] */
|
||||||
|
e2k_mc_cfg_t pbswap : 1; /* [24] */
|
||||||
|
e2k_mc_cfg_t dqw : 2; /* [25:26] */
|
||||||
|
e2k_mc_cfg_t pda_sel : 5; /* [27:31] */
|
||||||
|
} e2k_mc_cfg_fields_t;
|
||||||
|
typedef union e2k_mc_cfg_struct { /* Structure word */
|
||||||
|
e2k_mc_cfg_fields_t fields; /* as fields */
|
||||||
|
e2k_mc_cfg_t word; /* as entire register */
|
||||||
|
} e2k_mc_cfg_struct_t;
|
||||||
|
|
||||||
|
#define E2K_MC_CFG_pbm0 fields.pbm0 /* physycal bank map slot 0 */
|
||||||
|
#define E2K_MC_CFG_pbm1 fields.pbm1 /* physycal bank map slot 1 */
|
||||||
|
#define E2K_MC_CFG_reg word
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read/Write IPCC_CSRX (X={1, 2, 3}) registers
|
* Read/Write IPCC_CSRX (X={1, 2, 3}) registers
|
||||||
*/
|
*/
|
||||||
|
@ -77,6 +77,9 @@ boot_sic_write_node_nbsr_reg(int node_id, int reg_offset, unsigned int reg_val)
|
|||||||
unsigned int sic_get_mc_ecc(int node, int num);
|
unsigned int sic_get_mc_ecc(int node, int num);
|
||||||
void sic_set_mc_ecc(int node, int num, unsigned int reg_value);
|
void sic_set_mc_ecc(int node, int num, unsigned int reg_value);
|
||||||
|
|
||||||
|
unsigned int sic_get_mc_opmb(int node, int num);
|
||||||
|
unsigned int sic_get_mc_cfg(int node, int num);
|
||||||
|
|
||||||
unsigned int sic_get_ipcc_csr(int node, int num);
|
unsigned int sic_get_ipcc_csr(int node, int num);
|
||||||
void sic_set_ipcc_csr(int node, int num, unsigned int val);
|
void sic_set_ipcc_csr(int node, int num, unsigned int val);
|
||||||
|
|
||||||
|
@ -135,7 +135,8 @@ do { \
|
|||||||
/* Reserve 64 bytes for kernel per C calling convention */ \
|
/* Reserve 64 bytes for kernel per C calling convention */ \
|
||||||
u64 used_dstack_size = round_up(64, E2K_ALIGN_STACK); \
|
u64 used_dstack_size = round_up(64, E2K_ALIGN_STACK); \
|
||||||
\
|
\
|
||||||
sbr = (u64)thread_info_task(ti)->stack + KERNEL_C_STACK_SIZE; \
|
sbr = (u64)thread_info_task(ti)->stack + KERNEL_C_STACK_SIZE + \
|
||||||
|
KERNEL_C_STACK_OFFSET; \
|
||||||
AW(usd_lo) = AW((ti)->k_usd_lo); \
|
AW(usd_lo) = AW((ti)->k_usd_lo); \
|
||||||
AW(usd_hi) = AW((ti)->k_usd_hi); \
|
AW(usd_hi) = AW((ti)->k_usd_hi); \
|
||||||
AS(usd_lo).base -= used_dstack_size; \
|
AS(usd_lo).base -= used_dstack_size; \
|
||||||
|
@ -241,6 +241,7 @@ typedef struct thread_info {
|
|||||||
/* hypercall */
|
/* hypercall */
|
||||||
/* End of flags only for virtualization support */
|
/* End of flags only for virtualization support */
|
||||||
#define TIF_SYSCALL_TRACEPOINT 30 /* syscall tracepoint instrumentation */
|
#define TIF_SYSCALL_TRACEPOINT 30 /* syscall tracepoint instrumentation */
|
||||||
|
#define TIF_NAPI_WORK 31 /* napi_wq_worker() is running */
|
||||||
|
|
||||||
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
|
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
|
||||||
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
|
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
|
||||||
@ -266,6 +267,7 @@ typedef struct thread_info {
|
|||||||
#define _TIF_LIGHT_HYPERCALL (1 << TIF_LIGHT_HYPERCALL)
|
#define _TIF_LIGHT_HYPERCALL (1 << TIF_LIGHT_HYPERCALL)
|
||||||
#define _TIF_GENERIC_HYPERCALL (1 << TIF_GENERIC_HYPERCALL)
|
#define _TIF_GENERIC_HYPERCALL (1 << TIF_GENERIC_HYPERCALL)
|
||||||
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
|
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
|
||||||
|
#define _TIF_NAPI_WORK (1 << TIF_NAPI_WORK)
|
||||||
|
|
||||||
#define _TIF_WORK_SYSCALL_TRACE (_TIF_SYSCALL_TRACE | \
|
#define _TIF_WORK_SYSCALL_TRACE (_TIF_SYSCALL_TRACE | \
|
||||||
_TIF_KERNEL_TRACE | \
|
_TIF_KERNEL_TRACE | \
|
||||||
|
@ -104,4 +104,50 @@ get_MMU_phys_addr(e2k_addr_t virt_addr)
|
|||||||
return __probe_entry(GET_MMU_PHYS_ADDR(virt_addr));
|
return __probe_entry(GET_MMU_PHYS_ADDR(virt_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
typedef struct tlb_set_state {
|
||||||
|
tlb_tag_t tlb_tag;
|
||||||
|
pte_t tlb_entry;
|
||||||
|
} tlb_set_state_t;
|
||||||
|
|
||||||
|
typedef struct tlb_line_state {
|
||||||
|
e2k_addr_t va;
|
||||||
|
bool huge;
|
||||||
|
tlb_set_state_t sets[NATIVE_TLB_SETS_NUM];
|
||||||
|
} tlb_line_state_t;
|
||||||
|
|
||||||
|
static inline tlb_tag_t
|
||||||
|
get_va_tlb_set_tag(e2k_addr_t addr, int set_no, bool large_page)
|
||||||
|
{
|
||||||
|
return read_DTLB_va_tag_reg(addr, set_no, large_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pte_t
|
||||||
|
get_va_tlb_set_entry(e2k_addr_t addr, int set_no, bool large_page)
|
||||||
|
{
|
||||||
|
pte_t tlb_entry;
|
||||||
|
|
||||||
|
pte_val(tlb_entry) = read_DTLB_va_entry_reg(addr, set_no, large_page);
|
||||||
|
return tlb_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
get_va_tlb_state(tlb_line_state_t *tlb, e2k_addr_t addr, bool large_page)
|
||||||
|
{
|
||||||
|
tlb_set_state_t *set_state;
|
||||||
|
int set_no;
|
||||||
|
|
||||||
|
tlb->va = addr;
|
||||||
|
tlb->huge = large_page;
|
||||||
|
|
||||||
|
for (set_no = 0; set_no < NATIVE_TLB_SETS_NUM; set_no++) {
|
||||||
|
set_state = &tlb->sets[set_no];
|
||||||
|
tlb_tag_t tlb_tag;
|
||||||
|
pte_t tlb_entry;
|
||||||
|
tlb_tag = get_va_tlb_set_tag(addr, set_no, large_page);
|
||||||
|
tlb_entry = get_va_tlb_set_entry(addr, set_no, large_page);
|
||||||
|
set_state->tlb_tag = tlb_tag;
|
||||||
|
set_state->tlb_entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !_E2K_TLB_REGS_ACCESS_H_ */
|
||||||
|
115
arch/e2k/include/asm/trace-defs.h
Normal file
115
arch/e2k/include/asm/trace-defs.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#ifndef _E2K_TRACE_DEFS_H_
|
||||||
|
#define _E2K_TRACE_DEFS_H_
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <asm/mmu_types.h>
|
||||||
|
#include <asm/pgtable_def.h>
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
trace_get_va_translation(struct mm_struct *mm, e2k_addr_t address,
|
||||||
|
pgdval_t *pgd, pudval_t *pud, pmdval_t *pmd, pteval_t *pte, int *pt_level)
|
||||||
|
{
|
||||||
|
pgd_t *pgdp;
|
||||||
|
pud_t *pudp;
|
||||||
|
pmd_t *pmdp;
|
||||||
|
pte_t *ptep;
|
||||||
|
|
||||||
|
if (likely(address < TASK_SIZE)) {
|
||||||
|
pgdp = pgd_offset(mm, address);
|
||||||
|
|
||||||
|
*pgd = pgd_val(*pgdp);
|
||||||
|
*pt_level = E2K_PGD_LEVEL_NUM;
|
||||||
|
|
||||||
|
if (!pgd_huge(*pgdp) && !pgd_none(*pgdp) && !pgd_bad(*pgdp)) {
|
||||||
|
pudp = pud_offset(pgdp, address);
|
||||||
|
|
||||||
|
*pud = pud_val(*pudp);
|
||||||
|
*pt_level = E2K_PUD_LEVEL_NUM;
|
||||||
|
|
||||||
|
if (!pud_huge(*pudp) && !pud_none(*pudp) &&
|
||||||
|
!pud_bad(*pudp)) {
|
||||||
|
pmdp = pmd_offset(pudp, address);
|
||||||
|
|
||||||
|
*pmd = pmd_val(*pmdp);
|
||||||
|
*pt_level = E2K_PMD_LEVEL_NUM;
|
||||||
|
|
||||||
|
if (!pmd_huge(*pmdp) && !pmd_none(*pmdp) &&
|
||||||
|
!pmd_bad(*pmdp)) {
|
||||||
|
ptep = pte_offset_map(pmdp, address);
|
||||||
|
|
||||||
|
*pte = pte_val(*ptep);
|
||||||
|
*pt_level = E2K_PTE_LEVEL_NUM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pgdp = pgd_offset_k(address);
|
||||||
|
*pgd = pgd_val(*pgdp);
|
||||||
|
*pt_level = E2K_PGD_LEVEL_NUM;
|
||||||
|
|
||||||
|
if (!kernel_pgd_huge(*pgdp) && !pgd_none(*pgdp) && !pgd_bad(*pgdp)) {
|
||||||
|
pudp = pud_offset(pgdp, address);
|
||||||
|
*pud = pud_val(*pudp);
|
||||||
|
*pt_level = E2K_PUD_LEVEL_NUM;
|
||||||
|
|
||||||
|
if (!kernel_pud_huge(*pudp) && !pud_none(*pudp) &&
|
||||||
|
!pud_bad(*pudp)) {
|
||||||
|
pmdp = pmd_offset(pudp, address);
|
||||||
|
*pmd = pmd_val(*pmdp);
|
||||||
|
*pt_level = E2K_PMD_LEVEL_NUM;
|
||||||
|
|
||||||
|
if (!kernel_pmd_huge(*pmdp) && !pmd_none(*pmdp) &&
|
||||||
|
!pmd_bad(*pmdp)) {
|
||||||
|
ptep = pte_offset_kernel(pmdp, address);
|
||||||
|
*pte = pte_val(*ptep);
|
||||||
|
*pt_level = E2K_PTE_LEVEL_NUM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save DTLB entries.
|
||||||
|
*
|
||||||
|
* Do not access not existing entries to avoid
|
||||||
|
* creating "empty" records in DTLB for no reason.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
trace_get_dtlb_translation(struct mm_struct *mm, e2k_addr_t address,
|
||||||
|
u64 *dtlb_pgd, u64 *dtlb_pud, u64 *dtlb_pmd, u64 *dtlb_pte, int pt_level)
|
||||||
|
{
|
||||||
|
*dtlb_pgd = get_MMU_DTLB_ENTRY(address);
|
||||||
|
|
||||||
|
if (pt_level <= E2K_PUD_LEVEL_NUM)
|
||||||
|
*dtlb_pud = get_MMU_DTLB_ENTRY(pud_virt_offset(address));
|
||||||
|
|
||||||
|
if (pt_level <= E2K_PMD_LEVEL_NUM)
|
||||||
|
*dtlb_pmd = get_MMU_DTLB_ENTRY(pmd_virt_offset(address));
|
||||||
|
|
||||||
|
if (pt_level <= E2K_PTE_LEVEL_NUM)
|
||||||
|
*dtlb_pte = get_MMU_DTLB_ENTRY(pte_virt_offset(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define mmu_print_pt_flags(entry, print, mmu_pt_v6) \
|
||||||
|
(mmu_pt_v6) ? E2K_TRACE_PRINT_PT_V6_FLAGS(entry, print) \
|
||||||
|
: \
|
||||||
|
E2K_TRACE_PRINT_PT_V2_FLAGS(entry, print)
|
||||||
|
#define print_pt_flags(entry, print) \
|
||||||
|
mmu_print_pt_flags(entry, print, MMU_IS_PT_V6())
|
||||||
|
|
||||||
|
#define E2K_TRACE_PRINT_PT_FLAGS(entry, print) print_pt_flags(entry, print)
|
||||||
|
|
||||||
|
|
||||||
|
#define mmu_print_dtlb_entry(entry, mmu_dtlb_v6) \
|
||||||
|
((mmu_dtlb_v6) ? E2K_TRACE_PRINT_DTLB_ENTRY_V2(entry) \
|
||||||
|
: \
|
||||||
|
E2K_TRACE_PRINT_DTLB_ENTRY_V6(entry))
|
||||||
|
#define print_dtlb_entry(entry) \
|
||||||
|
mmu_print_dtlb_entry(entry, MMU_IS_DTLB_V6())
|
||||||
|
|
||||||
|
#define E2K_TRACE_PRINT_DTLB(entry) print_dtlb_entry(entry)
|
||||||
|
|
||||||
|
#endif /* _E2K_TRACE_DEFS_H_ */
|
@ -15,6 +15,7 @@
|
|||||||
#include <asm/trace-mmu-dtlb-v2.h>
|
#include <asm/trace-mmu-dtlb-v2.h>
|
||||||
#include <asm/trace-mmu-dtlb-v6.h>
|
#include <asm/trace-mmu-dtlb-v6.h>
|
||||||
#include <asm/trap_def.h>
|
#include <asm/trap_def.h>
|
||||||
|
#include <asm/trace-defs.h>
|
||||||
|
|
||||||
#define E2K_TC_TYPE_STORE (1ULL << 17)
|
#define E2K_TC_TYPE_STORE (1ULL << 17)
|
||||||
#define E2K_TC_TYPE_S_F (1ULL << 19)
|
#define E2K_TC_TYPE_S_F (1ULL << 19)
|
||||||
@ -125,25 +126,6 @@ TRACE_EVENT(
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
#define mmu_print_pt_flags(entry, print, mmu_pt_v6) \
|
|
||||||
(mmu_pt_v6) ? E2K_TRACE_PRINT_PT_V6_FLAGS(entry, print) \
|
|
||||||
: \
|
|
||||||
E2K_TRACE_PRINT_PT_V2_FLAGS(entry, print)
|
|
||||||
#define print_pt_flags(entry, print) \
|
|
||||||
mmu_print_pt_flags(entry, print, MMU_IS_PT_V6())
|
|
||||||
|
|
||||||
#define E2K_TRACE_PRINT_PT_FLAGS(entry, print) print_pt_flags(entry, print)
|
|
||||||
|
|
||||||
|
|
||||||
#define mmu_print_dtlb_entry(entry, mmu_dtlb_v6) \
|
|
||||||
((mmu_dtlb_v6) ? E2K_TRACE_PRINT_DTLB_ENTRY_V2(entry) \
|
|
||||||
: \
|
|
||||||
E2K_TRACE_PRINT_DTLB_ENTRY_V6(entry))
|
|
||||||
#define print_dtlb_entry(entry) \
|
|
||||||
mmu_print_dtlb_entry(entry, MMU_IS_DTLB_V6())
|
|
||||||
|
|
||||||
#define E2K_TRACE_PRINT_DTLB(entry) print_dtlb_entry(entry)
|
|
||||||
|
|
||||||
TRACE_EVENT(
|
TRACE_EVENT(
|
||||||
unhandled_page_fault,
|
unhandled_page_fault,
|
||||||
|
|
||||||
@ -157,91 +139,19 @@ TRACE_EVENT(
|
|||||||
__field( u64, dtlb_pud )
|
__field( u64, dtlb_pud )
|
||||||
__field( u64, dtlb_pmd )
|
__field( u64, dtlb_pmd )
|
||||||
__field( u64, dtlb_pte )
|
__field( u64, dtlb_pte )
|
||||||
__field( u64, pgd )
|
__field( pgdval_t, pgd )
|
||||||
__field( u64, pud )
|
__field( pudval_t, pud )
|
||||||
__field( u64, pmd )
|
__field( pmdval_t, pmd )
|
||||||
__field( u64, pte )
|
__field( pteval_t, pte )
|
||||||
__field( int, pt_level )
|
__field( int, pt_level )
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
pgd_t *pgdp;
|
|
||||||
pud_t *pudp;
|
|
||||||
pmd_t *pmdp;
|
|
||||||
pte_t *ptep;
|
|
||||||
|
|
||||||
__entry->address = address;
|
__entry->address = address;
|
||||||
|
|
||||||
/*
|
trace_get_va_translation(current->mm, address,
|
||||||
* Save page table entries
|
&__entry->pgd, &__entry->pud, &__entry->pmd,
|
||||||
*/
|
&__entry->pte, &__entry->pt_level);
|
||||||
__entry->pt_level = 0;
|
|
||||||
|
|
||||||
if (address < TASK_SIZE) {
|
|
||||||
struct mm_struct *mm = current->mm;
|
|
||||||
|
|
||||||
pgdp = pgd_offset(mm, address);
|
|
||||||
|
|
||||||
__entry->pgd = pgd_val(*pgdp);
|
|
||||||
__entry->pt_level = 1;
|
|
||||||
|
|
||||||
if (!pgd_huge(*pgdp) && !pgd_none(*pgdp) &&
|
|
||||||
!pgd_bad(*pgdp)) {
|
|
||||||
pudp = pud_offset(pgdp, address);
|
|
||||||
|
|
||||||
__entry->pud = pud_val(*pudp);
|
|
||||||
__entry->pt_level = 2;
|
|
||||||
|
|
||||||
if (!pud_huge(*pudp) && !pud_none(*pudp) &&
|
|
||||||
!pud_bad(*pudp)) {
|
|
||||||
pmdp = pmd_offset(pudp, address);
|
|
||||||
|
|
||||||
__entry->pmd = pmd_val(*pmdp);
|
|
||||||
__entry->pt_level = 3;
|
|
||||||
|
|
||||||
if (!pmd_huge(*pmdp) &&
|
|
||||||
!pmd_none(*pmdp) &&
|
|
||||||
!pmd_bad(*pmdp)) {
|
|
||||||
ptep = pte_offset_map(pmdp,
|
|
||||||
address);
|
|
||||||
|
|
||||||
__entry->pte = pte_val(*ptep);
|
|
||||||
__entry->pt_level = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pgdp = pgd_offset_k(address);
|
|
||||||
|
|
||||||
__entry->pgd = pgd_val(*pgdp);
|
|
||||||
__entry->pt_level = 1;
|
|
||||||
|
|
||||||
if (!kernel_pgd_huge(*pgdp) &&
|
|
||||||
!pgd_none(*pgdp) && !pgd_bad(*pgdp)) {
|
|
||||||
pudp = pud_offset(pgdp, address);
|
|
||||||
|
|
||||||
__entry->pud = pud_val(*pudp);
|
|
||||||
__entry->pt_level = 2;
|
|
||||||
|
|
||||||
if (!kernel_pud_huge(*pudp) &&
|
|
||||||
!pud_none(*pudp) && !pud_bad(*pudp)) {
|
|
||||||
pmdp = pmd_offset(pudp, address);
|
|
||||||
|
|
||||||
__entry->pmd = pmd_val(*pmdp);
|
|
||||||
__entry->pt_level = 3;
|
|
||||||
|
|
||||||
if (!kernel_pmd_huge(*pmdp) &&
|
|
||||||
!pmd_none(*pmdp) &&
|
|
||||||
!pmd_bad(*pmdp)) {
|
|
||||||
ptep = pte_offset_kernel(pmdp,
|
|
||||||
address);
|
|
||||||
|
|
||||||
__entry->pte = pte_val(*ptep);
|
|
||||||
__entry->pt_level = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save DTLB entries.
|
* Save DTLB entries.
|
||||||
@ -249,30 +159,21 @@ TRACE_EVENT(
|
|||||||
* Do not access not existing entries to avoid
|
* Do not access not existing entries to avoid
|
||||||
* creating "empty" records in DTLB for no reason.
|
* creating "empty" records in DTLB for no reason.
|
||||||
*/
|
*/
|
||||||
__entry->dtlb_entry = get_MMU_DTLB_ENTRY(address);
|
trace_get_dtlb_translation(current->mm, address,
|
||||||
|
&__entry->dtlb_entry, &__entry->dtlb_pud,
|
||||||
if (__entry->pt_level >= 2)
|
&__entry->dtlb_pmd, &__entry->dtlb_pte,
|
||||||
__entry->dtlb_pud = get_MMU_DTLB_ENTRY(
|
__entry->pt_level);
|
||||||
pud_virt_offset(address));
|
|
||||||
|
|
||||||
if (__entry->pt_level >= 3)
|
|
||||||
__entry->dtlb_pmd = get_MMU_DTLB_ENTRY(
|
|
||||||
pmd_virt_offset(address));
|
|
||||||
|
|
||||||
if (__entry->pt_level >= 4)
|
|
||||||
__entry->dtlb_pte = get_MMU_DTLB_ENTRY(
|
|
||||||
pte_virt_offset(address));
|
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("\n"
|
TP_printk("\n"
|
||||||
"Page table for address 0x%lx (all f's are returned if the entry has not been read)\n"
|
"Page table for address 0x%lx (all f's are returned if the entry has not been read)\n"
|
||||||
" pgd 0x%llx: %s\n"
|
" pgd 0x%lx: %s\n"
|
||||||
" Access mode: %s%s\n"
|
" Access mode: %s%s\n"
|
||||||
" pud 0x%llx: %s\n"
|
" pud 0x%lx: %s\n"
|
||||||
" Access mode: %s%s\n"
|
" Access mode: %s%s\n"
|
||||||
" pmd 0x%llx: %s\n"
|
" pmd 0x%lx: %s\n"
|
||||||
" Access mode: %s%s\n"
|
" Access mode: %s%s\n"
|
||||||
" pte 0x%llx: %s\n"
|
" pte 0x%lx: %s\n"
|
||||||
" Access mode: %s%s\n"
|
" Access mode: %s%s\n"
|
||||||
"Probed DTLB entries:\n"
|
"Probed DTLB entries:\n"
|
||||||
" pud address entry 0x%llx: %s\n"
|
" pud address entry 0x%llx: %s\n"
|
||||||
@ -281,22 +182,26 @@ TRACE_EVENT(
|
|||||||
" address entry 0x%llx: %s"
|
" address entry 0x%llx: %s"
|
||||||
,
|
,
|
||||||
__entry->address,
|
__entry->address,
|
||||||
(__entry->pt_level >= 1) ? __entry->pgd : -1ULL,
|
(__entry->pt_level <= E2K_PGD_LEVEL_NUM) ? __entry->pgd : -1UL,
|
||||||
E2K_TRACE_PRINT_PT_FLAGS(__entry->pgd, __entry->pt_level >= 1),
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->pgd,
|
||||||
(__entry->pt_level >= 2) ? __entry->pud : -1ULL,
|
__entry->pt_level <= E2K_PGD_LEVEL_NUM),
|
||||||
E2K_TRACE_PRINT_PT_FLAGS(__entry->pud, __entry->pt_level >= 2),
|
(__entry->pt_level <= E2K_PUD_LEVEL_NUM) ? __entry->pud : -1UL,
|
||||||
(__entry->pt_level >= 3) ? __entry->pmd : -1ULL,
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->pud,
|
||||||
E2K_TRACE_PRINT_PT_FLAGS(__entry->pmd, __entry->pt_level >= 3),
|
__entry->pt_level <= E2K_PUD_LEVEL_NUM),
|
||||||
(__entry->pt_level >= 4) ? __entry->pte : -1ULL,
|
(__entry->pt_level <= E2K_PMD_LEVEL_NUM) ? __entry->pmd : -1UL,
|
||||||
E2K_TRACE_PRINT_PT_FLAGS(__entry->pte, __entry->pt_level >= 4),
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->pmd,
|
||||||
(__entry->pt_level >= 2) ? __entry->dtlb_pud : -1ULL,
|
__entry->pt_level <= E2K_PMD_LEVEL_NUM),
|
||||||
(__entry->pt_level >= 2) ?
|
(__entry->pt_level <= E2K_PTE_LEVEL_NUM) ? __entry->pte : -1UL,
|
||||||
|
E2K_TRACE_PRINT_PT_FLAGS(__entry->pte,
|
||||||
|
__entry->pt_level <= E2K_PTE_LEVEL_NUM),
|
||||||
|
(__entry->pt_level <= E2K_PUD_LEVEL_NUM) ? __entry->dtlb_pud : -1ULL,
|
||||||
|
(__entry->pt_level <= E2K_PUD_LEVEL_NUM) ?
|
||||||
E2K_TRACE_PRINT_DTLB(__entry->dtlb_pud) : "(not read)",
|
E2K_TRACE_PRINT_DTLB(__entry->dtlb_pud) : "(not read)",
|
||||||
(__entry->pt_level >= 3) ? __entry->dtlb_pmd : -1ULL,
|
(__entry->pt_level <= E2K_PMD_LEVEL_NUM) ? __entry->dtlb_pmd : -1ULL,
|
||||||
(__entry->pt_level >= 3) ?
|
(__entry->pt_level <= E2K_PMD_LEVEL_NUM) ?
|
||||||
E2K_TRACE_PRINT_DTLB(__entry->dtlb_pmd) : "(not read)",
|
E2K_TRACE_PRINT_DTLB(__entry->dtlb_pmd) : "(not read)",
|
||||||
(__entry->pt_level >= 4) ? __entry->dtlb_pte : -1ULL,
|
(__entry->pt_level <= E2K_PTE_LEVEL_NUM) ? __entry->dtlb_pte : -1ULL,
|
||||||
(__entry->pt_level >= 4) ?
|
(__entry->pt_level <= E2K_PTE_LEVEL_NUM) ?
|
||||||
E2K_TRACE_PRINT_DTLB(__entry->dtlb_pte) : "(not read)",
|
E2K_TRACE_PRINT_DTLB(__entry->dtlb_pte) : "(not read)",
|
||||||
__entry->dtlb_entry,
|
__entry->dtlb_entry,
|
||||||
E2K_TRACE_PRINT_DTLB(__entry->dtlb_entry))
|
E2K_TRACE_PRINT_DTLB(__entry->dtlb_entry))
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
#undef TRACE_SYSTEM
|
|
||||||
#define TRACE_SYSTEM e2k
|
|
||||||
|
|
||||||
#if !defined(_TRACE_E2K_PGTABLE_V2_H) || defined(TRACE_HEADER_MULTI_READ)
|
#if !defined(_TRACE_E2K_PGTABLE_V2_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
#define _TRACE_E2K_PGTABLE_V2_H
|
#define _TRACE_E2K_PGTABLE_V2_H
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
#undef TRACE_SYSTEM
|
|
||||||
#define TRACE_SYSTEM e2k
|
|
||||||
|
|
||||||
#if !defined(_TRACE_E2K_PGTABLE_V6_H) || defined(TRACE_HEADER_MULTI_READ)
|
#if !defined(_TRACE_E2K_PGTABLE_V6_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
#define _TRACE_E2K_PGTABLE_V6_H
|
#define _TRACE_E2K_PGTABLE_V6_H
|
||||||
|
|
||||||
|
@ -146,9 +146,10 @@
|
|||||||
.macro HANDLER_TRAMPOLINE ctprN, scallN, fn, wbsL
|
.macro HANDLER_TRAMPOLINE ctprN, scallN, fn, wbsL
|
||||||
/* Force load OSGD->GD. Alternative is to use non-0 CUI for kernel */
|
/* Force load OSGD->GD. Alternative is to use non-0 CUI for kernel */
|
||||||
{
|
{
|
||||||
nop
|
|
||||||
sdisp \ctprN, \scallN
|
sdisp \ctprN, \scallN
|
||||||
}
|
}
|
||||||
|
/* CPU_HWBUG_VIRT_PSIZE_INTERCEPTION */
|
||||||
|
{ nop } { nop } { nop } { nop }
|
||||||
call \ctprN, wbs=\wbsL
|
call \ctprN, wbs=\wbsL
|
||||||
disp \ctprN, \fn
|
disp \ctprN, \fn
|
||||||
SWITCH_HW_STACKS_FROM_USER()
|
SWITCH_HW_STACKS_FROM_USER()
|
||||||
|
@ -249,7 +249,8 @@ is_kernel_data_stack_bounds(bool on_kernel, e2k_usd_lo_t usd_lo)
|
|||||||
#include <asm/kvm/trap_table.h>
|
#include <asm/kvm/trap_table.h>
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
static inline void init_pt_regs_for_syscall(struct pt_regs *regs)
|
__always_inline /* For CPU_HWBUG_VIRT_PSIZE_INTERCEPTION */
|
||||||
|
static void init_pt_regs_for_syscall(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
regs->next = NULL;
|
regs->next = NULL;
|
||||||
regs->trap = NULL;
|
regs->trap = NULL;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#define __ARCH_WANT_NEW_STAT
|
#define __ARCH_WANT_NEW_STAT
|
||||||
#define __ARCH_WANT_SYS_ALARM
|
#define __ARCH_WANT_SYS_ALARM
|
||||||
#define __ARCH_WANT_SYS_CLONE
|
#define __ARCH_WANT_SYS_CLONE
|
||||||
|
#define __ARCH_WANT_SYS_CLONE3
|
||||||
#define __ARCH_WANT_SYS_FORK
|
#define __ARCH_WANT_SYS_FORK
|
||||||
#define __ARCH_WANT_SYS_GETHOSTNAME
|
#define __ARCH_WANT_SYS_GETHOSTNAME
|
||||||
#define __ARCH_WANT_SYS_IPC
|
#define __ARCH_WANT_SYS_IPC
|
||||||
|
@ -222,25 +222,10 @@
|
|||||||
#define __NR_stat64 195
|
#define __NR_stat64 195
|
||||||
#define __NR_lstat64 196
|
#define __NR_lstat64 196
|
||||||
#define __NR_fstat64 197
|
#define __NR_fstat64 197
|
||||||
#define __NR_lchown32 198
|
|
||||||
#define __NR_getuid32 199
|
|
||||||
#define __NR_getgid32 200
|
|
||||||
#define __NR_geteuid32 201
|
|
||||||
#define __NR_getegid32 202
|
|
||||||
#define __NR_setreuid32 203
|
|
||||||
#define __NR_setregid32 204
|
|
||||||
#define __NR_pidfd_send_signal 205
|
#define __NR_pidfd_send_signal 205
|
||||||
#define __NR_pidfd_open 206
|
#define __NR_pidfd_open 206
|
||||||
#define __NR_fchown32 207
|
|
||||||
#define __NR_setresuid32 208
|
|
||||||
#define __NR_getresuid32 209
|
|
||||||
#define __NR_setresgid32 210
|
|
||||||
#define __NR_getresgid32 211
|
|
||||||
#define __NR_chown32 212
|
|
||||||
#define __NR_setuid32 213
|
|
||||||
#define __NR_setgid32 214
|
|
||||||
#define __NR_setfsuid32 215
|
|
||||||
#define __NR_setfsgid32 216
|
|
||||||
#define __NR_pivot_root 217
|
#define __NR_pivot_root 217
|
||||||
#define __NR_mincore 218
|
#define __NR_mincore 218
|
||||||
#define __NR_madvise 219
|
#define __NR_madvise 219
|
||||||
|
@ -701,6 +701,9 @@ struct sk_buff {
|
|||||||
};
|
};
|
||||||
struct rb_node rbnode; /* used in netem, ip4 defrag, and tcp stack */
|
struct rb_node rbnode; /* used in netem, ip4 defrag, and tcp stack */
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
#ifdef CONFIG_MCST
|
||||||
|
struct list_head napi_skb_list;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
Loading…
Reference in New Issue
Block a user