diff --git a/hypervisor/debug/profiling.c b/hypervisor/debug/profiling.c index 9634c405f..8a4fc08be 100644 --- a/hypervisor/debug/profiling.c +++ b/hypervisor/debug/profiling.c @@ -3,15 +3,19 @@ * * SPDX-License-Identifier: BSD-3-Clause */ - #ifdef PROFILING_ON #include #define ACRN_DBG_PROFILING 5U +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 + #define LVT_PERFCTR_BIT_MASK 0x10000U +static uint64_t sep_collection_switch; + static uint32_t profiling_pmi_irq = IRQ_INVALID; static void profiling_initialize_vmsw(void) @@ -62,7 +66,8 @@ static void profiling_pmi_handler(__unused unsigned int irq, __unused void *data /* * Performs MSR operations on all the CPU's */ - int32_t profiling_msr_ops_all_cpus(__unused struct vm *vm, __unused uint64_t addr) + +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 @@ -73,27 +78,138 @@ static void profiling_pmi_handler(__unused unsigned int irq, __unused void *data /* * Generate VM info list */ -int32_t profiling_vm_list_info(__unused struct vm *vm, __unused uint64_t addr) +int32_t profiling_vm_list_info(struct vm *vm, uint64_t addr) { - /* to be implemented */ + struct vm *tmp_vm; + struct vcpu *vcpu; + int32_t vm_idx; + uint16_t i, j; + struct profiling_vm_info_list vm_info_list; + + (void)memset((void *)&vm_info_list, 0U, sizeof(vm_info_list)); + + dev_dbg(ACRN_DBG_PROFILING, "%s: entering", __func__); + + if (copy_from_gpa(vm, &vm_info_list, addr, sizeof(vm_info_list)) != 0) { + pr_err("%s: Unable to copy addr from vm\n", __func__); + return -EINVAL; + } + + vm_idx = 0; + vm_info_list.vm_list[vm_idx].vm_id_num = -1; + (void)memcpy_s((void *)vm_info_list.vm_list[vm_idx].vm_name, 4U, "VMM\0", 4U); + for (i = 0U; i < phys_cpu_num; i++) { + vm_info_list.vm_list[vm_idx].cpu_map[i].vcpu_id = (int32_t)i; + vm_info_list.vm_list[vm_idx].cpu_map[i].pcpu_id = (int32_t)i; + vm_info_list.vm_list[vm_idx].cpu_map[i].apic_id + = (int32_t)per_cpu(lapic_id, i); + } + vm_info_list.vm_list[vm_idx].num_vcpus = (int32_t)i; + vm_info_list.num_vms = 1; + + for (j = 0U; j < CONFIG_MAX_VM_NUM; j++) { + tmp_vm = get_vm_from_vmid(j); + if (tmp_vm == NULL) { + break; + } + vm_info_list.num_vms++; + vm_idx++; + + vm_info_list.vm_list[vm_idx].vm_id_num = (int32_t)tmp_vm->vm_id; + (void)memcpy_s((void *)vm_info_list.vm_list[vm_idx].guid, + 16U, tmp_vm->GUID, 16U); + snprintf(vm_info_list.vm_list[vm_idx].vm_name, 16U, "vm_%d", + tmp_vm->vm_id, 16U); + vm_info_list.vm_list[vm_idx].num_vcpus = 0; + i = 0U; + foreach_vcpu(i, tmp_vm, vcpu) { + vm_info_list.vm_list[vm_idx].cpu_map[i].vcpu_id + = (int32_t)vcpu->vcpu_id; + vm_info_list.vm_list[vm_idx].cpu_map[i].pcpu_id + = (int32_t)vcpu->pcpu_id; + vm_info_list.vm_list[vm_idx].cpu_map[i].apic_id = 0; + vm_info_list.vm_list[vm_idx].num_vcpus++; + } + } + + if (copy_to_gpa(vm, &vm_info_list, addr, sizeof(vm_info_list)) != 0) { + pr_err("%s: Unable to copy addr to vm\n", __func__); + return -EINVAL; + } + + dev_dbg(ACRN_DBG_PROFILING, "%s: exiting", __func__); return 0; } /* * Sep/socwatch profiling version */ -int32_t profiling_get_version_info(__unused struct vm *vm, __unused uint64_t addr) +int32_t profiling_get_version_info(struct vm *vm, uint64_t addr) { - /* to be implemented */ + struct profiling_version_info ver_info; + + (void)memset((void *)&ver_info, 0U, sizeof(ver_info)); + + dev_dbg(ACRN_DBG_PROFILING, "%s: entering", __func__); + + if (copy_from_gpa(vm, &ver_info, addr, sizeof(ver_info)) != 0) { + pr_err("%s: Unable to copy addr from vm\n", __func__); + return -EINVAL; + } + + ver_info.major = MAJOR_VERSION; + ver_info.minor = MINOR_VERSION; + ver_info.supported_features = (int64_t) + ((1U << (uint64_t)CORE_PMU_SAMPLING) | + (1U << (uint64_t)CORE_PMU_COUNTING) | + (1U << (uint64_t)LBR_PMU_SAMPLING) | + (1U << (uint64_t)VM_SWITCH_TRACING)); + + if (copy_to_gpa(vm, &ver_info, addr, sizeof(ver_info)) != 0) { + pr_err("%s: Unable to copy addr to vm\n", __func__); + return -EINVAL; + } + + dev_dbg(ACRN_DBG_PROFILING, "%s: exiting", __func__); + return 0; } /* * Gets type of profiling - sep/socwatch */ -int32_t profiling_get_control(__unused struct vm *vm, __unused uint64_t addr) +int32_t profiling_get_control(struct vm *vm, uint64_t addr) { - /* to be implemented */ + struct profiling_control prof_control; + + (void)memset((void *)&prof_control, 0U, sizeof(prof_control)); + + dev_dbg(ACRN_DBG_PROFILING, "%s: entering", __func__); + + if (copy_from_gpa(vm, &prof_control, addr, sizeof(prof_control)) != 0) { + pr_err("%s: Unable to copy addr from vm\n", __func__); + return -EINVAL; + } + + switch (prof_control.collector_id) { + case COLLECT_PROFILE_DATA: + prof_control.switches = sep_collection_switch; + break; + case COLLECT_POWER_DATA: + break; + default: + pr_err("%s: unknown collector %d", + __func__, prof_control.collector_id); + break; + } + + if (copy_to_gpa(vm, &prof_control, addr, sizeof(prof_control)) != 0) { + pr_err("%s: Unable to copy addr to vm\n", __func__); + return -EINVAL; + } + + dev_dbg(ACRN_DBG_PROFILING, "%s: exiting", __func__); + return 0; } @@ -131,9 +247,29 @@ int32_t profiling_configure_vmsw(__unused struct vm *vm, __unused uint64_t addr) /* * Get the physical cpu id */ -int32_t profiling_get_pcpu_id(__unused struct vm *vm, __unused uint64_t addr) +int32_t profiling_get_pcpu_id(struct vm *vm, uint64_t addr) { - /* to be implemented */ + struct profiling_pcpuid pcpuid; + + (void)memset((void *)&pcpuid, 0U, sizeof(pcpuid)); + + dev_dbg(ACRN_DBG_PROFILING, "%s: entering", __func__); + + if (copy_from_gpa(vm, &pcpuid, addr, sizeof(pcpuid)) != 0) { + pr_err("%s: Unable to copy addr from vm\n", __func__); + return -EINVAL; + } + + cpuid_subleaf(pcpuid.leaf, pcpuid.subleaf, &pcpuid.eax, + &pcpuid.ebx, &pcpuid.ecx, &pcpuid.edx); + + if (copy_to_gpa(vm, &pcpuid, addr, sizeof(pcpuid)) != 0) { + pr_err("%s: Unable to copy param to vm\n", __func__); + return -EINVAL; + } + + dev_dbg(ACRN_DBG_PROFILING, "%s: exiting", __func__); + return 0; } diff --git a/hypervisor/include/debug/profiling_internal.h b/hypervisor/include/debug/profiling_internal.h index 0cacb64b0..01667bdeb 100644 --- a/hypervisor/include/debug/profiling_internal.h +++ b/hypervisor/include/debug/profiling_internal.h @@ -9,7 +9,13 @@ #ifdef PROFILING_ON - typedef enum IPI_COMMANDS { +#define MAX_NR_VCPUS 8 +#define MAX_NR_VMS 6 + +#define COLLECT_PROFILE_DATA 0 +#define COLLECT_POWER_DATA 1 + +typedef enum IPI_COMMANDS { IPI_MSR_OP = 0, IPI_PMU_CONFIG, IPI_PMU_START, @@ -17,7 +23,59 @@ IPI_VMSW_CONFIG, IPI_UNKNOWN, } ipi_commands; - /* + +typedef enum PROFILING_SEP_FEATURE { + CORE_PMU_SAMPLING = 0, + CORE_PMU_COUNTING, + PEBS_PMU_SAMPLING, + LBR_PMU_SAMPLING, + UNCORE_PMU_SAMPLING, + VM_SWITCH_TRACING, + MAX_SEP_FEATURE_ID +} profiling_sep_feature; + +struct profiling_version_info { + int32_t major; + int32_t minor; + int64_t supported_features; + int64_t reserved; +}; + +struct profiling_pcpuid { + uint32_t leaf; + uint32_t subleaf; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +struct profiling_control { + int32_t collector_id; + int32_t reserved; + uint64_t switches; +}; + +struct profiling_vcpu_pcpu_map { + int32_t vcpu_id; + int32_t pcpu_id; + int32_t apic_id; +}; + +struct profiling_vm_info { + int32_t vm_id_num; + unsigned char guid[16]; + char vm_name[16]; + int32_t num_vcpus; + struct profiling_vcpu_pcpu_map cpu_map[MAX_NR_VCPUS]; +}; + +struct profiling_vm_info_list { + int32_t num_vms; + struct profiling_vm_info vm_list[MAX_NR_VMS]; +}; + +/* * Wrapper containing SEP sampling/profiling related data structures */ struct profiling_info_wrapper {