1 // © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #include <assert.h>
6 #include <hyptypes.h>
7
8 #include <hypregisters.h>
9
10 #include <compiler.h>
11 #include <irq.h>
12 #include <log.h>
13 #include <panic.h>
14 #include <preempt.h>
15 #include <thread.h>
16 #include <trace.h>
17 #if defined(INTERFACE_VCPU)
18 #include <vcpu.h>
19 #endif
20
21 #include <asm/barrier.h>
22 #include <asm/system_registers.h>
23
24 #include "event_handlers.h"
25
26 #if defined(INTERFACE_VCPU)
27
28 // The module to trap and emulate the read accesses to the cluster register. We
29 // currently only emulate IMP_CLUSTERIDR_EL1 and treat the rest as RAZ.
30
31 // Before reading any cluster registers we need to apply the DSU SCLK
32 // gating erratum (2,313,941) workaround, which is executing a dummy
33 // cache maintenance operation instruction immediately prior to
34 // accessing the register.
35 static inline void
platform_dsu_apply_sclk_gating_erratum_workaround(void)36 platform_dsu_apply_sclk_gating_erratum_workaround(void) REQUIRE_PREEMPT_DISABLED
37 {
38 register_t dummy = 0;
39
40 assert_preempt_disabled();
41 __asm__ volatile("DC CIVAC, %[VA]; "
42 : "+m"(asm_ordering)
43 : [VA] "r"(&dummy));
44 }
45
46 static inline IMP_CLUSTERIDR_EL1_t
register_CLUSTERIDR_EL1_read(void)47 register_CLUSTERIDR_EL1_read(void)
48 {
49 IMP_CLUSTERIDR_EL1_t val;
50
51 preempt_disable();
52 platform_dsu_apply_sclk_gating_erratum_workaround();
53 val = register_IMP_CLUSTERIDR_EL1_read_ordered(&asm_ordering);
54 preempt_enable();
55
56 return val;
57 }
58
59 vcpu_trap_result_t
arm_dsu_handle_vcpu_trap_sysreg_read(ESR_EL2_ISS_MSR_MRS_t iss)60 arm_dsu_handle_vcpu_trap_sysreg_read(ESR_EL2_ISS_MSR_MRS_t iss)
61 {
62 register_t val = 0ULL; // Default action is RAZ
63 vcpu_trap_result_t ret = VCPU_TRAP_RESULT_EMULATED;
64 thread_t *thread = thread_get_self();
65
66 // Assert this is a read
67 assert(ESR_EL2_ISS_MSR_MRS_get_Direction(&iss));
68
69 uint8_t reg_num = ESR_EL2_ISS_MSR_MRS_get_Rt(&iss);
70
71 // Remove the fields that are not used in the comparison
72 ESR_EL2_ISS_MSR_MRS_t temp_iss = iss;
73 ESR_EL2_ISS_MSR_MRS_set_Rt(&temp_iss, 0U);
74 ESR_EL2_ISS_MSR_MRS_set_Direction(&temp_iss, false);
75
76 switch (ESR_EL2_ISS_MSR_MRS_raw(temp_iss)) {
77 case ISS_MRS_MSR_IMP_CLUSTERIDR_EL1: {
78 IMP_CLUSTERIDR_EL1_t clusteridr =
79 register_CLUSTERIDR_EL1_read();
80 val = IMP_CLUSTERIDR_EL1_raw(clusteridr);
81 break;
82 }
83 default: {
84 uint8_t crn, crm;
85
86 crn = ESR_EL2_ISS_MSR_MRS_get_CRn(&iss);
87 crm = ESR_EL2_ISS_MSR_MRS_get_CRm(&iss);
88
89 if ((crn == 15U) && ((crm == 3U) || (crm == 4U))) {
90 TRACE_AND_LOG(DEBUG, WARN,
91 "Emulated RAZ for cluster register: ISS "
92 "{:#x}",
93 ESR_EL2_ISS_MSR_MRS_raw(iss));
94 } else {
95 ret = VCPU_TRAP_RESULT_UNHANDLED;
96 }
97 break;
98 }
99 }
100
101 // Update the thread's register
102 if (ret == VCPU_TRAP_RESULT_EMULATED) {
103 vcpu_gpr_write(thread, reg_num, val);
104 }
105
106 return ret;
107 }
108
109 #endif
110