| /* |
| * Copyright 2019 The Hafnium Authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "../../src/arch/aarch64/hypervisor/perfmon.h" |
| |
| #include "primary_with_secondary.h" |
| #include "sysregs.h" |
| #include "util.h" |
| |
| /* |
| * TODO(b/132394973): Devise a way to test exhaustively read/write behavior to |
| * all debug registers that does not involve a separate service per register, |
| * because creating a new test/VM for every instance becomes too slow. |
| * This needs proper trap support as a starting point. |
| */ |
| |
| TEST(perfmon, secondary_pmccfiltr_el0) |
| { |
| struct hf_vcpu_run_return run_res; |
| struct mailbox_buffers mb = set_up_mailbox(); |
| |
| SERVICE_SELECT(SERVICE_VM0, "perfmon_secondary_pmccfiltr_el0", mb.send); |
| |
| run_res = hf_vcpu_run(SERVICE_VM0, 0); |
| EXPECT_EQ(run_res.code, HF_VCPU_RUN_ABORTED); |
| } |
| |
| TEST(perfmon, secondary_pmcr_el0) |
| { |
| struct hf_vcpu_run_return run_res; |
| struct mailbox_buffers mb = set_up_mailbox(); |
| |
| SERVICE_SELECT(SERVICE_VM0, "perfmon_secondary_pmcr_el0", mb.send); |
| |
| run_res = hf_vcpu_run(SERVICE_VM0, 0); |
| EXPECT_EQ(run_res.code, HF_VCPU_RUN_ABORTED); |
| } |
| |
| TEST(perfmon, secondary_pmintenset_el1) |
| { |
| struct hf_vcpu_run_return run_res; |
| struct mailbox_buffers mb = set_up_mailbox(); |
| |
| SERVICE_SELECT(SERVICE_VM0, "perfmon_secondary_pmintenset_el1", |
| mb.send); |
| |
| run_res = hf_vcpu_run(SERVICE_VM0, 0); |
| EXPECT_EQ(run_res.code, HF_VCPU_RUN_ABORTED); |
| } |
| |
| /** |
| * Attempts to access performance monitor registers for read, without validating |
| * their value. |
| */ |
| TEST(perfmon, primary_basic) |
| { |
| EXPECT_EQ(hf_vm_get_id(), HF_PRIMARY_VM_ID); |
| |
| TRY_READ(PMCEID0_EL0); |
| TRY_READ(PMCEID1_EL0); |
| TRY_READ(PMCCFILTR_EL0); |
| TRY_READ(PMCR_EL0); |
| } |
| |
| /** |
| * Tests a few performance counter registers for read and write, and checks that |
| * the expected value is written/read. |
| */ |
| TEST(perfmon, primary_read_write) |
| { |
| EXPECT_EQ(hf_vm_get_id(), HF_PRIMARY_VM_ID); |
| |
| TRY_WRITE_READ(PMCCNTR_EL0, 0xaaaa); |
| |
| write_msr(PMINTENCLR_EL1, 0xffff); |
| CHECK_READ(PMINTENSET_EL1, 0); |
| |
| /* Bits set in PMINTENSET_EL1 can be read in PMINTENCLR_EL1. */ |
| write_msr(PMINTENSET_EL1, 0xf); |
| CHECK_READ(PMINTENCLR_EL1, 0xf); |
| |
| /* Writes to PMINTENSET_EL1 do not clear already set bits. */ |
| write_msr(PMINTENSET_EL1, 0xf0); |
| CHECK_READ(PMINTENCLR_EL1, 0xff); |
| } |
| |
| /** |
| * Attempts to read all performance counters supported by the current CPU |
| * configuration. |
| */ |
| /* NOLINTNEXTLINE(readability-function-size) */ |
| TEST(perfmon, primary_counters) |
| { |
| uintreg_t pmcr_el0 = read_msr(PMCR_EL0); |
| uintreg_t perf_mon_count = GET_PMCR_EL0_N(pmcr_el0); |
| |
| EXPECT_EQ(hf_vm_get_id(), HF_PRIMARY_VM_ID); |
| |
| if (perf_mon_count == 0) { |
| return; |
| } |
| |
| switch (perf_mon_count - 1) { |
| default: |
| FAIL("More performance monitor registers than supported."); |
| case 30: |
| TRY_READ(PMEVCNTR30_EL0); |
| TRY_WRITE_READ(PMEVTYPER30_EL0, 0x1); |
| /* fallthrough */ |
| case 29: |
| TRY_READ(PMEVCNTR29_EL0); |
| TRY_WRITE_READ(PMEVTYPER29_EL0, 0x1); |
| /* fallthrough */ |
| case 28: |
| TRY_READ(PMEVCNTR28_EL0); |
| TRY_WRITE_READ(PMEVTYPER28_EL0, 0x1); |
| /* fallthrough */ |
| case 27: |
| TRY_READ(PMEVCNTR27_EL0); |
| TRY_WRITE_READ(PMEVTYPER27_EL0, 0x1); |
| /* fallthrough */ |
| case 26: |
| TRY_READ(PMEVCNTR26_EL0); |
| TRY_WRITE_READ(PMEVTYPER26_EL0, 0x1); |
| /* fallthrough */ |
| case 25: |
| TRY_READ(PMEVCNTR25_EL0); |
| TRY_WRITE_READ(PMEVTYPER25_EL0, 0x1); |
| /* fallthrough */ |
| case 24: |
| TRY_READ(PMEVCNTR24_EL0); |
| TRY_WRITE_READ(PMEVTYPER24_EL0, 0x1); |
| /* fallthrough */ |
| case 23: |
| TRY_READ(PMEVCNTR23_EL0); |
| TRY_WRITE_READ(PMEVTYPER23_EL0, 0x1); |
| /* fallthrough */ |
| case 22: |
| TRY_READ(PMEVCNTR22_EL0); |
| TRY_WRITE_READ(PMEVTYPER22_EL0, 0x1); |
| /* fallthrough */ |
| case 21: |
| TRY_READ(PMEVCNTR21_EL0); |
| TRY_WRITE_READ(PMEVTYPER21_EL0, 0x1); |
| /* fallthrough */ |
| case 20: |
| TRY_READ(PMEVCNTR20_EL0); |
| TRY_WRITE_READ(PMEVTYPER20_EL0, 0x1); |
| /* fallthrough */ |
| case 19: |
| TRY_READ(PMEVCNTR19_EL0); |
| TRY_WRITE_READ(PMEVTYPER19_EL0, 0x1); |
| /* fallthrough */ |
| case 18: |
| TRY_READ(PMEVCNTR18_EL0); |
| TRY_WRITE_READ(PMEVTYPER18_EL0, 0x1); |
| /* fallthrough */ |
| case 17: |
| TRY_READ(PMEVCNTR17_EL0); |
| TRY_WRITE_READ(PMEVTYPER17_EL0, 0x1); |
| /* fallthrough */ |
| case 16: |
| TRY_READ(PMEVCNTR16_EL0); |
| TRY_WRITE_READ(PMEVTYPER16_EL0, 0x1); |
| /* fallthrough */ |
| case 15: |
| TRY_READ(PMEVCNTR15_EL0); |
| TRY_WRITE_READ(PMEVTYPER15_EL0, 0x1); |
| /* fallthrough */ |
| case 14: |
| TRY_READ(PMEVCNTR14_EL0); |
| TRY_WRITE_READ(PMEVTYPER14_EL0, 0x1); |
| /* fallthrough */ |
| case 13: |
| TRY_READ(PMEVCNTR13_EL0); |
| TRY_WRITE_READ(PMEVTYPER13_EL0, 0x1); |
| /* fallthrough */ |
| case 12: |
| TRY_READ(PMEVCNTR12_EL0); |
| TRY_WRITE_READ(PMEVTYPER12_EL0, 0x1); |
| /* fallthrough */ |
| case 11: |
| TRY_READ(PMEVCNTR11_EL0); |
| TRY_WRITE_READ(PMEVTYPER11_EL0, 0x1); |
| /* fallthrough */ |
| case 10: |
| TRY_READ(PMEVCNTR10_EL0); |
| TRY_WRITE_READ(PMEVTYPER10_EL0, 0x1); |
| /* fallthrough */ |
| case 9: |
| TRY_READ(PMEVCNTR9_EL0); |
| TRY_WRITE_READ(PMEVTYPER9_EL0, 0x1); |
| /* fallthrough */ |
| case 8: |
| TRY_READ(PMEVCNTR8_EL0); |
| TRY_WRITE_READ(PMEVTYPER8_EL0, 0x1); |
| /* fallthrough */ |
| case 7: |
| TRY_READ(PMEVCNTR7_EL0); |
| TRY_WRITE_READ(PMEVTYPER7_EL0, 0x1); |
| /* fallthrough */ |
| case 6: |
| TRY_READ(PMEVCNTR6_EL0); |
| TRY_WRITE_READ(PMEVTYPER6_EL0, 0x1); |
| /* fallthrough */ |
| case 5: |
| TRY_READ(PMEVCNTR5_EL0); |
| TRY_WRITE_READ(PMEVTYPER5_EL0, 0x1); |
| /* fallthrough */ |
| case 4: |
| TRY_READ(PMEVCNTR4_EL0); |
| TRY_WRITE_READ(PMEVTYPER4_EL0, 0x1); |
| /* fallthrough */ |
| case 3: |
| TRY_READ(PMEVCNTR3_EL0); |
| TRY_WRITE_READ(PMEVTYPER3_EL0, 0x1); |
| /* fallthrough */ |
| case 2: |
| TRY_READ(PMEVCNTR2_EL0); |
| TRY_WRITE_READ(PMEVTYPER2_EL0, 0x1); |
| /* fallthrough */ |
| case 1: |
| TRY_READ(PMEVCNTR1_EL0); |
| TRY_WRITE_READ(PMEVTYPER1_EL0, 0x1); |
| /* fallthrough */ |
| case 0: |
| TRY_READ(PMEVCNTR0_EL0); |
| TRY_WRITE_READ(PMEVTYPER0_EL0, 0x1); |
| break; |
| } |
| } |