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