diff --git a/hypervisor/Makefile b/hypervisor/Makefile index 00fc1195e..2e20dbac3 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -218,7 +218,7 @@ endif C_OBJS := $(patsubst %.c,$(HV_OBJDIR)/%.o,$(C_SRCS)) ifneq ($(CONFIG_RELEASE),y) C_OBJS += $(patsubst %.c,$(HV_OBJDIR)/%.o,$(D_SRCS)) -CFLAGS += -DHV_DEBUG +CFLAGS += -DHV_DEBUG -DPROFILING_ON endif S_OBJS := $(patsubst %.S,$(HV_OBJDIR)/%.o,$(S_SRCS)) diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index 050541b66..33bc107cc 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -485,6 +485,7 @@ static void bsp_boot_post(void) init_iommu(); timer_init(); + profiling_setup(); setup_notification(); setup_posted_intr_notification(); ptdev_init(); @@ -564,7 +565,7 @@ static void cpu_secondary_post(void) interrupt_init(get_cpu_id()); timer_init(); - + profiling_setup(); /* Wait for boot processor to signal all secondary cores to continue */ wait_sync_change(&pcpu_sync, 0UL); diff --git a/hypervisor/arch/x86/guest/vmcall.c b/hypervisor/arch/x86/guest/vmcall.c index 4a0e83741..7827de5fd 100644 --- a/hypervisor/arch/x86/guest/vmcall.c +++ b/hypervisor/arch/x86/guest/vmcall.c @@ -173,6 +173,10 @@ int vmcall_vmexit_handler(struct vcpu *vcpu) case HC_SETUP_HV_NPK_LOG: ret = hcall_setup_hv_npk_log(vm, param1); break; + + case HC_PROFILING_OPS: + ret = hcall_profiling_ops(vm, param1, param2); + break; #endif case HC_WORLD_SWITCH: diff --git a/hypervisor/arch/x86/irq.c b/hypervisor/arch/x86/irq.c index 58002e28f..b250d33aa 100644 --- a/hypervisor/arch/x86/irq.c +++ b/hypervisor/arch/x86/irq.c @@ -26,6 +26,7 @@ static struct static_mapping_table irq_static_mappings[NR_STATIC_MAPPINGS] = { {TIMER_IRQ, VECTOR_TIMER}, {NOTIFY_IRQ, VECTOR_NOTIFY_VCPU}, {POSTED_INTR_NOTIFY_IRQ, VECTOR_POSTED_INTR}, + {PMI_IRQ, VECTOR_PMI}, }; /* @@ -96,7 +97,7 @@ uint32_t alloc_irq_vector(uint32_t irq) } desc = &irq_desc_array[irq]; - + if (desc->vector != VECTOR_INVALID) { if (vector_to_irq[desc->vector] == irq) { /* statically binded */ @@ -121,7 +122,7 @@ uint32_t alloc_irq_vector(uint32_t irq) } } vr = (vr > VECTOR_DYNAMIC_END) ? VECTOR_INVALID : vr; - + spinlock_irqrestore_release(&irq_alloc_spinlock, rflags); } diff --git a/hypervisor/common/hv_main.c b/hypervisor/common/hv_main.c index 05aa01a91..4d6ee72d6 100644 --- a/hypervisor/common/hv_main.c +++ b/hypervisor/common/hv_main.c @@ -70,6 +70,8 @@ void vcpu_thread(struct vcpu *vcpu) #endif TRACE_2L(TRACE_VM_ENTER, 0UL, 0UL); + profiling_vmenter_handler(vcpu); + /* Restore guest TSC_AUX */ if (vcpu->launched) { cpu_msr_write(MSR_IA32_TSC_AUX, @@ -109,6 +111,8 @@ void vcpu_thread(struct vcpu *vcpu) #endif TRACE_2L(TRACE_VM_EXIT, basic_exit_reason, vcpu_get_rip(vcpu)); + + profiling_vmexit_handler(vcpu, basic_exit_reason); } while (1); } diff --git a/hypervisor/debug/hypercall.c b/hypervisor/debug/hypercall.c new file mode 100644 index 000000000..9543bbbd6 --- /dev/null +++ b/hypervisor/debug/hypercall.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#ifdef PROFILING_ON +/** + *@pre Pointer vm shall point to VM0 + */ +int32_t hcall_profiling_ops(struct vm *vm, uint64_t cmd, uint64_t param) +{ + int32_t ret; + switch (cmd) { + case PROFILING_MSR_OPS: + ret = profiling_msr_ops_all_cpus(vm, param); + break; + case PROFILING_GET_VMINFO: + ret = profiling_vm_list_info(vm, param); + break; + case PROFILING_GET_VERSION: + ret = profiling_get_version_info(vm, param); + break; + case PROFILING_GET_CONTROL_SWITCH: + ret = profiling_get_control(vm, param); + break; + case PROFILING_SET_CONTROL_SWITCH: + ret = profiling_set_control(vm, param); + break; + case PROFILING_CONFIG_PMI: + ret = profiling_configure_pmi(vm, param); + break; + case PROFILING_CONFIG_VMSWITCH: + ret = profiling_configure_vmsw(vm, param); + break; + case PROFILING_GET_PCPUID: + ret = profiling_get_pcpu_id(vm, param); + break; + default: + pr_err("%s: invalid profiling command %llu\n", __func__, cmd); + ret = -1; + break; + } + return ret; +} +#endif diff --git a/hypervisor/debug/profiling.c b/hypervisor/debug/profiling.c new file mode 100644 index 000000000..9634c405f --- /dev/null +++ b/hypervisor/debug/profiling.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifdef PROFILING_ON + +#include + +#define ACRN_DBG_PROFILING 5U + +#define LVT_PERFCTR_BIT_MASK 0x10000U + +static uint32_t profiling_pmi_irq = IRQ_INVALID; + +static void profiling_initialize_vmsw(void) +{ + /* to be implemented */ +} + +/* + * Configure the PMU's for sep/socwatch profiling. + */ +static void profiling_initialize_pmi(void) +{ + /* to be implemented */ +} + +/* + * Enable all the Performance Monitoring Control registers. + */ +static void profiling_enable_pmu(void) +{ + /* to be implemented */ +} + +/* + * Disable all Performance Monitoring Control registers + */ +static void profiling_disable_pmu(void) +{ + /* to be implemented */ +} + +/* + * Performs MSR operations - read, write and clear + */ +static void profiling_handle_msrops(void) +{ + /* to be implemented */ +} + +/* + * Interrupt handler for performance monitoring interrupts + */ +static void profiling_pmi_handler(__unused unsigned int irq, __unused void *data) +{ + /* to be implemented */ +} + +/* + * Performs MSR operations on all the CPU's + */ + int32_t profiling_msr_ops_all_cpus(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented + * call to smp_call_function profiling_ipi_handler + */ + return 0; +} + +/* + * Generate VM info list + */ +int32_t profiling_vm_list_info(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented */ + return 0; +} + +/* + * Sep/socwatch profiling version + */ +int32_t profiling_get_version_info(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented */ + return 0; +} + +/* + * Gets type of profiling - sep/socwatch + */ +int32_t profiling_get_control(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented */ + return 0; +} + +/* + * Update the profiling type based on control switch + */ +int32_t profiling_set_control(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented */ + return 0; +} + +/* + * Configure PMI on all cpus + */ +int32_t profiling_configure_pmi(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented + * call to smp_call_function profiling_ipi_handler + */ + return 0; +} + +/* + * Configure for VM-switch data on all cpus + */ +int32_t profiling_configure_vmsw(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented + * call to smp_call_function profiling_ipi_handler + */ + return 0; +} + +/* + * Get the physical cpu id + */ +int32_t profiling_get_pcpu_id(__unused struct vm *vm, __unused uint64_t addr) +{ + /* to be implemented */ + return 0; +} + +/* + * IPI interrupt handler function + */ +void profiling_ipi_handler(__unused void *data) +{ + switch (get_cpu_var(profiling_info.ipi_cmd)) { + case IPI_PMU_START: + profiling_enable_pmu(); + break; + case IPI_PMU_STOP: + profiling_disable_pmu(); + break; + case IPI_MSR_OP: + profiling_handle_msrops(); + break; + case IPI_PMU_CONFIG: + profiling_initialize_pmi(); + break; + case IPI_VMSW_CONFIG: + profiling_initialize_vmsw(); + break; + default: + pr_err("%s: unknown IPI command %d on cpu %d", + __func__, get_cpu_var(profiling_info.ipi_cmd), get_cpu_id()); + break; + } + get_cpu_var(profiling_info.ipi_cmd) = IPI_UNKNOWN; +} + +/* + * Save the VCPU info on vmenter + */ +void profiling_vmenter_handler(__unused struct vcpu *vcpu) +{ + /* to be implemented */ +} + +/* + * Save the VCPU info on vmexit + */ +void profiling_vmexit_handler(__unused struct vcpu *vcpu, __unused uint64_t exit_reason) +{ + if (exit_reason == VMX_EXIT_REASON_EXTERNAL_INTERRUPT) { + /* to be implemented */ + } else { + /* to be implemented */ + } +} + +/* + * Setup PMI irq vector + */ +void profiling_setup(void) +{ + uint16_t cpu; + int32_t retval; + dev_dbg(ACRN_DBG_PROFILING, "%s: entering", __func__); + cpu = get_cpu_id(); + /* support PMI notification, VM0 will register all CPU */ + if ((cpu == BOOT_CPU_ID) && (profiling_pmi_irq == IRQ_INVALID)) { + pr_info("%s: calling request_irq", __func__); + retval = request_irq(PMI_IRQ, + profiling_pmi_handler, NULL, IRQF_NONE); + if (retval < 0) { + pr_err("Failed to add PMI isr"); + return; + } + profiling_pmi_irq = (uint32_t)retval; + } + + msr_write(MSR_IA32_EXT_APIC_LVT_PMI, + VECTOR_PMI | LVT_PERFCTR_BIT_MASK); + + dev_dbg(ACRN_DBG_PROFILING, "%s: exiting", __func__); +} + +#endif diff --git a/hypervisor/include/arch/x86/irq.h b/hypervisor/include/arch/x86/irq.h index f1746675c..d22494fd0 100644 --- a/hypervisor/include/arch/x86/irq.h +++ b/hypervisor/include/arch/x86/irq.h @@ -23,6 +23,7 @@ #define VECTOR_VIRT_IRQ_VHM 0xF7U #define VECTOR_SPURIOUS 0xFFU #define VECTOR_HYPERVISOR_CALLBACK_VHM 0xF3U +#define VECTOR_PMI 0xF4U /* the maximum number of msi entry is 2048 according to PCI * local bus specification @@ -34,10 +35,11 @@ #define NR_IRQS 256U #define IRQ_INVALID 0xffffffffU -#define NR_STATIC_MAPPINGS (3U) +#define NR_STATIC_MAPPINGS (4U) #define TIMER_IRQ (NR_IRQS - 1U) #define NOTIFY_IRQ (NR_IRQS - 2U) #define POSTED_INTR_NOTIFY_IRQ (NR_IRQS - 3U) +#define PMI_IRQ (NR_IRQS - 4U) #define DEFAULT_DEST_MODE IOAPIC_RTE_DESTLOG #define DEFAULT_DELIVERY_MODE IOAPIC_RTE_DELLOPRI diff --git a/hypervisor/include/arch/x86/per_cpu.h b/hypervisor/include/arch/x86/per_cpu.h index aaca1cf7b..67407004d 100644 --- a/hypervisor/include/arch/x86/per_cpu.h +++ b/hypervisor/include/arch/x86/per_cpu.h @@ -17,6 +17,7 @@ #include #include #include "arch/x86/guest/instr_emul.h" +#include struct per_cpu_region { /* vmxon_region MUST be 4KB-aligned */ @@ -50,6 +51,9 @@ struct per_cpu_region { uint32_t lapic_id; uint32_t lapic_ldr; struct smp_call_info_data smp_call_info; +#ifdef PROFILING_ON + struct profiling_info_wrapper profiling_info; +#endif } __aligned(CPU_PAGE_SIZE); /* per_cpu_region size aligned with CPU_PAGE_SIZE */ extern struct per_cpu_region *per_cpu_data_base_ptr; diff --git a/hypervisor/include/common/hypercall.h b/hypervisor/include/common/hypercall.h index 6dd6af4c9..1c0da2237 100644 --- a/hypervisor/include/common/hypercall.h +++ b/hypervisor/include/common/hypercall.h @@ -46,7 +46,7 @@ int32_t hcall_sos_offline_cpu(struct vm *vm, uint64_t lapicid); * @param param guest physical memory address. The api version returned * will be copied to this gpa * - * @pre Pointer vm shall point to VM0 + * @pre Pointer vm shall point to VM0 * @return 0 on success, non-zero on error. */ int32_t hcall_get_api_version(struct vm *vm, uint64_t param); @@ -407,6 +407,19 @@ int32_t hcall_setup_sbuf(struct vm *vm, uint64_t param); */ int32_t hcall_setup_hv_npk_log(struct vm *vm, uint64_t param); +/** + * @brief Execute profiling operation + * + * @param vm Pointer to VM data structure + * @param cmd profiling command to be executed + * @param cmd profiling command to be executed + * @param param guest physical address. This gpa points to + * data structure required by each command + * + * @return 0 on success, non-zero on error. + */ +int32_t hcall_profiling_ops(struct vm *vm, uint64_t cmd, uint64_t param); + /** * @brief Get VCPU Power state. * diff --git a/hypervisor/include/debug/profiling.h b/hypervisor/include/debug/profiling.h new file mode 100644 index 000000000..6c21b7bcd --- /dev/null +++ b/hypervisor/include/debug/profiling.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 int32_tel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PROFILING_H +#define PROFILING_H + +#ifdef PROFILING_ON + +#include + +void profiling_vmenter_handler(struct vcpu *vcpu); +void profiling_vmexit_handler(struct vcpu *vcpu, uint64_t exit_reason); +void profiling_setup(void); + +#else + +static inline void profiling_vmenter_handler(__unused struct vcpu *vcpu) {} +static inline void profiling_vmexit_handler(__unused struct vcpu *vcpu, + __unused uint64_t exit_reason) {} +static inline void profiling_setup(void) {} + +static inline int32_t hcall_profiling_ops(__unused struct vm *vm, + __unused uint64_t cmd, __unused uint64_t param) +{ + return -ENODEV; +} + +#endif + +#endif /* PROFILING_H */ diff --git a/hypervisor/include/debug/profiling_internal.h b/hypervisor/include/debug/profiling_internal.h new file mode 100644 index 000000000..0cacb64b0 --- /dev/null +++ b/hypervisor/include/debug/profiling_internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 int32_tel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PROFILING_INTERNAL_H +#define PROFILING_INTERNAL_H + +#ifdef PROFILING_ON + + typedef enum IPI_COMMANDS { + IPI_MSR_OP = 0, + IPI_PMU_CONFIG, + IPI_PMU_START, + IPI_PMU_STOP, + IPI_VMSW_CONFIG, + IPI_UNKNOWN, +} ipi_commands; + /* + * Wrapper containing SEP sampling/profiling related data structures + */ +struct profiling_info_wrapper { + ipi_commands ipi_cmd; +}; + +int32_t profiling_get_version_info(struct vm *vm, uint64_t addr); +int32_t profiling_get_pcpu_id(struct vm *vm, uint64_t addr); +int32_t profiling_msr_ops_all_cpus(struct vm *vm, uint64_t addr); +int32_t profiling_vm_list_info(struct vm *vm, uint64_t addr); +int32_t profiling_get_control(struct vm *vm, uint64_t addr); +int32_t profiling_set_control(struct vm *vm, uint64_t addr); +int32_t profiling_configure_pmi(struct vm *vm, uint64_t addr); +int32_t profiling_configure_vmsw(struct vm *vm, uint64_t addr); +void profiling_ipi_handler(void *data); + +#endif + +#endif /* PROFILING_INTERNAL_H */ diff --git a/hypervisor/include/hv_debug.h b/hypervisor/include/hv_debug.h index 010eb63c0..4a7fcfcc4 100644 --- a/hypervisor/include/hv_debug.h +++ b/hypervisor/include/hv_debug.h @@ -13,5 +13,6 @@ #include #include #include +#include #endif /* HV_DEBUG_H */ diff --git a/hypervisor/include/public/acrn_hv_defs.h b/hypervisor/include/public/acrn_hv_defs.h index 04e0e1533..85928fdf0 100644 --- a/hypervisor/include/public/acrn_hv_defs.h +++ b/hypervisor/include/public/acrn_hv_defs.h @@ -72,6 +72,7 @@ #define HC_ID_DBG_BASE 0x60UL #define HC_SETUP_SBUF BASE_HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00UL) #define HC_SETUP_HV_NPK_LOG BASE_HC_ID(HC_ID, HC_ID_DBG_BASE + 0x01UL) +#define HC_PROFILING_OPS BASE_HC_ID(HC_ID, HC_ID_DBG_BASE + 0x02UL) /* Trusty */ #define HC_ID_TRUSTY_BASE 0x70UL @@ -317,4 +318,15 @@ struct trusty_boot_param { * @} */ +enum profiling_cmd_type { + PROFILING_MSR_OPS = 0U, + PROFILING_GET_VMINFO, + PROFILING_GET_VERSION, + PROFILING_GET_CONTROL_SWITCH, + PROFILING_SET_CONTROL_SWITCH, + PROFILING_CONFIG_PMI, + PROFILING_CONFIG_VMSWITCH, + PROFILING_GET_PCPUID +}; + #endif /* ACRN_HV_DEFS_H */