android_kernel_modules_leno.../debug_tools/vtunedk/src/cpumon.c

445 lines
12 KiB
C

/*COPYRIGHT**
Copyright (C) 2005-2014 Intel Corporation. All Rights Reserved.
This file is part of SEP Development Kit
SEP Development Kit is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
SEP Development Kit is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SEP Development Kit; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
**COPYRIGHT*/
/*
* CVS_Id="$Id$"
*/
#include "lwpmudrv_defines.h"
#include <linux/version.h>
#include <linux/interrupt.h>
#if defined(DRV_EM64T)
#include <asm/desc.h>
#endif
#include "lwpmudrv_types.h"
#include "rise_errors.h"
#include "lwpmudrv_ecb.h"
#if defined(DRV_IA32) || defined(DRV_EM64T)
#include "apic.h"
#endif
#include "lwpmudrv.h"
#include "control.h"
#include "utility.h"
#include "cpumon.h"
#include "pmi.h"
#if defined DRV_USE_NMI
#include <linux/ptrace.h>
#include <asm/nmi.h>
#include <linux/notifier.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
static int
cpumon_NMI_Handler(unsigned int cpu, struct pt_regs *regs)
{
PMI_Interrupt_Handler(regs);
return NMI_HANDLED;
}
#define EBS_NMI_CALLBACK cpumon_NMI_Handler
#define SET_NMI_CALLBACK(type,func,flags,name) register_nmi_handler((type),(func),(flags),(name))
#define UNSET_NMI_CALLBACK(type,name) unregister_nmi_handler((type),(name))
#endif
#endif // DRV_USE_NMI
/*
* CPU Monitoring Functionality
*/
/*
* General per-processor initialization
*/
#if defined(DRV_IA32) && !defined(DRV_USE_NMI)
typedef union {
unsigned long long u64[1];
unsigned short int u16[4];
} local_handler_t;
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Save_Cpu(param)
*
* @param param unused parameter
*
* @return None No return needed
*
* @brief Save the old handler for restoration when done
*
*/
static void
cpumon_Save_Cpu (
PVOID parm
)
{
unsigned long eflags;
U64 *idt_base;
CPU_STATE pcpu;
preempt_disable();
pcpu = &pcb[CONTROL_THIS_CPU()];
preempt_enable();
SYS_Local_Irq_Save(eflags);
CPU_STATE_idt_base(pcpu) = idt_base = SYS_Get_IDT_Base();
// save original perf. vector
CPU_STATE_saved_ih(pcpu) = idt_base[CPU_PERF_VECTOR];
SEP_PRINT_DEBUG("saved_ih is 0x%llx\n", CPU_STATE_saved_ih(pcpu));
SYS_Local_Irq_Restore(eflags);
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Init_Cpu(param)
*
* @param param unused parameter
*
* @return None No return needed
*
* @brief Set up the interrupt handler.
*
*/
static VOID
cpumon_Init_Cpu (
PVOID parm
)
{
unsigned long eflags;
U64 *idt_base;
CPU_STATE pcpu;
local_handler_t lhandler;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
unsigned long cr0_value;
#endif
preempt_disable();
pcpu = &pcb[CONTROL_THIS_CPU()];
preempt_enable();
SYS_Local_Irq_Save(eflags);
idt_base = CPU_STATE_idt_base(pcpu);
// install perf. handler
// These are the necessary steps to have an ISR entry
// Note the changes in the data written
lhandler.u64[0] = (unsigned long)SYS_Perfvec_Handler;
lhandler.u16[3] = lhandler.u16[1];
lhandler.u16[1] = SYS_Get_cs();
lhandler.u16[2] = 0xee00;
// From 3.10 kernel, the IDT memory has been moved to a read-only location
// which is controlled by the bit 16 in the CR0 register.
// The write protection should be temporarily released to update the IDT.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
cr0_value = read_cr0();
write_cr0(cr0_value & ~X86_CR0_WP);
#endif
idt_base[CPU_PERF_VECTOR] = lhandler.u64[0];
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
write_cr0(cr0_value);
#endif
SYS_Local_Irq_Restore(eflags);
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Destroy_Cpu(param)
*
* @param param unused parameter
*
* @return None No return needed
*
* @brief Restore the old handler
* @brief Finish clean up of the apic
*
*/
static VOID
cpumon_Destroy_Cpu (
PVOID ctx
)
{
unsigned long eflags;
unsigned long long *idt_base;
CPU_STATE pcpu;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
unsigned long cr0_value;
#endif
preempt_disable();
pcpu = &pcb[CONTROL_THIS_CPU()];
preempt_enable();
SYS_Local_Irq_Save(eflags);
// restore perf. vector (to a safe stub pointer)
idt_base = SYS_Get_IDT_Base();
APIC_Disable_PMI();
// From 3.10 kernel, the IDT memory has been moved to a read-only location
// which is controlled by the bit 16 in the CR0 register.
// The write protection should be temporarily released to update the IDT.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
cr0_value = read_cr0();
write_cr0(cr0_value & ~X86_CR0_WP);
#endif
idt_base[CPU_PERF_VECTOR] = CPU_STATE_saved_ih(pcpu);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
write_cr0(cr0_value);
#endif
SYS_Local_Irq_Restore(eflags);
return;
}
#endif
#if defined(DRV_EM64T) && !defined(DRV_USE_NMI)
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Set_IDT_Func(idt, func)
*
* @param GATE_STRUCT* - address of the idt vector
* @param PVOID - function to set in IDT
*
* @return None No return needed
*
* @brief Set up the interrupt handler.
* @brief Save the old handler for restoration when done
*
*/
static VOID
cpumon_Set_IDT_Func (
GATE_STRUCT *idt,
PVOID func
)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
_set_gate(&idt[CPU_PERF_VECTOR], GATE_INTERRUPT, (unsigned long) func, 3, 0);
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
unsigned long cr0_value;
#endif
GATE_STRUCT local;
// _set_gate() cannot be used because the IDT table is not exported.
pack_gate(&local, GATE_INTERRUPT, (unsigned long)func, 3, 0, __KERNEL_CS);
// From 3.10 kernel, the IDT memory has been moved to a read-only location
// which is controlled by the bit 16 in the CR0 register.
// The write protection should be temporarily released to update the IDT.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
cr0_value = read_cr0();
write_cr0(cr0_value & ~X86_CR0_WP);
#endif
write_idt_entry((idt), CPU_PERF_VECTOR, &local);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
write_cr0(cr0_value);
#endif
#endif
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Save_Cpu(param)
*
* @param param - Unused, set up to enable parallel calls
*
* @return None No return needed
*
* @brief Set up the interrupt handler.
* @brief Save the old handler for restoration when done
*
*/
static VOID
cpumon_Save_Cpu (
PVOID parm
)
{
unsigned long eflags;
IDTGDT_DESC idt_base;
CPU_STATE pcpu = &pcb[CONTROL_THIS_CPU()];
GATE_STRUCT old_gate;
GATE_STRUCT *idt;
SYS_Local_Irq_Save(eflags);
SYS_Get_IDT_Base((PVOID*)&idt_base);
idt = idt_base.idtgdt_base;
CPU_STATE_idt_base(pcpu) = idt;
memcpy (&old_gate, &idt[CPU_PERF_VECTOR], 16);
CPU_STATE_saved_ih(pcpu) = (PVOID) ((((U64) old_gate.offset_high) << 32) |
(((U64) old_gate.offset_middle) << 16) |
((U64) old_gate.offset_low));
SEP_PRINT_DEBUG("saved_ih is 0x%llx\n", CPU_STATE_saved_ih(pcpu));
SYS_Local_Irq_Restore(eflags);
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Init_Cpu(param)
*
* @param param unused parameter
*
* @return None No return needed
*
* @brief Set up the interrupt handler.
*
*/
static VOID
cpumon_Init_Cpu (
PVOID parm
)
{
unsigned long eflags;
CPU_STATE pcpu = &pcb[CONTROL_THIS_CPU()];
GATE_STRUCT *idt;
SYS_Local_Irq_Save(eflags);
idt = CPU_STATE_idt_base(pcpu);
cpumon_Set_IDT_Func(idt, SYS_Perfvec_Handler);
SYS_Local_Irq_Restore(eflags);
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn void cpumon_Destroy_Cpu(param)
*
* @param param unused parameter
*
* @return None No return needed
*
* @brief Restore the old handler
* @brief Finish clean up of the apic
*
*/
static VOID
cpumon_Destroy_Cpu (
PVOID ctx
)
{
unsigned long eflags;
CPU_STATE pcpu = &pcb[CONTROL_THIS_CPU()];
GATE_STRUCT *idt;
SYS_Local_Irq_Save(eflags);
APIC_Disable_PMI();
idt = CPU_STATE_idt_base(pcpu);
cpumon_Set_IDT_Func(idt, CPU_STATE_saved_ih(pcpu));
SYS_Local_Irq_Restore(eflags);
return;
}
#endif
#if defined(DRV_IA32) || defined(DRV_EM64T)
/* ------------------------------------------------------------------------- */
/*!
* @fn extern void CPUMON_Install_Cpuhools(void)
*
* @param None
*
* @return None No return needed
*
* @brief set up the interrupt handler (on a per-processor basis)
* @brief Initialize the APIC in two phases (current CPU, then others)
*
*/
extern VOID
CPUMON_Install_Cpuhooks (
void
)
{
S32 me = 0;
PVOID linear = NULL;
#ifndef DRV_USE_NMI
CONTROL_Invoke_Parallel(cpumon_Save_Cpu, (PVOID)(size_t)me);
CONTROL_Invoke_Parallel(cpumon_Init_Cpu, (PVOID)(size_t)me);
#endif
APIC_Init(&linear);
CONTROL_Invoke_Parallel(APIC_Init, &linear);
CONTROL_Invoke_Parallel(APIC_Install_Interrupt_Handler, (PVOID)(size_t)me);
#ifdef DRV_USE_NMI
SET_NMI_CALLBACK(NMI_LOCAL, EBS_NMI_CALLBACK, NMI_FLAG_FIRST, "sep_pmi");
#endif
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn extern void CPUMON_Remove_Cpuhools(void)
*
* @param None
*
* @return None No return needed
*
* @brief De-Initialize the APIC in phases
* @brief clean up the interrupt handler (on a per-processor basis)
*
*/
extern VOID
CPUMON_Remove_Cpuhooks (
void
)
{
int i;
#ifndef DRV_USE_NMI
unsigned long eflags;
SYS_Local_Irq_Save(eflags);
cpumon_Destroy_Cpu((PVOID)(size_t)0);
SYS_Local_Irq_Restore(eflags);
CONTROL_Invoke_Parallel_XS(cpumon_Destroy_Cpu,
(PVOID)(size_t)0);
#else
UNSET_NMI_CALLBACK(NMI_LOCAL, "sep_pmi");
#endif
// de-initialize APIC
APIC_Unmap(CPU_STATE_apic_linear_addr(&pcb[0]));
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
APIC_Deinit_Phase1(i);
}
return;
}
#endif /* defined(DRV_IA32) || defined(DRV_EM64T) */