1 /*
2 * Copyright 2022 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "hf/arch/irq.h"
10 #include "hf/arch/types.h"
11 #include "hf/arch/vm/interrupts_gicv3.h"
12 #include "hf/arch/vm/timer.h"
13
14 #include "hf/ffa.h"
15
16 #include "vmapi/hf/call.h"
17
18 #include "ffa_endpoints.h"
19 #include "ffa_secure_partitions.h"
20 #include "gicv3.h"
21 #include "msr.h"
22 #include "partition_services.h"
23 #include "test/hftest.h"
24 #include "test/vmapi/ffa.h"
25
26 #define TEST_SP_PREEMPTED_BY_NS_INTERRUPT_LOOP_COUNT UINT64_C(1000000)
27
SET_UP(interrupts)28 SET_UP(interrupts)
29 {
30 gicv3_system_setup();
31 }
32
TEAR_DOWN(interrupts)33 TEAR_DOWN(interrupts)
34 {
35 EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED);
36 }
37
setup_physical_timer(void)38 static void setup_physical_timer(void)
39 {
40 interrupt_enable(PHYSICAL_TIMER_IRQ, true);
41 interrupt_set_priority(PHYSICAL_TIMER_IRQ, 0x80);
42 interrupt_set_edge_triggered(PHYSICAL_TIMER_IRQ, true);
43 interrupt_set_priority_mask(0xff);
44 arch_irq_enable();
45 }
46
start_physical_timer(uint32_t ns)47 static void start_physical_timer(uint32_t ns)
48 {
49 /*
50 * Check that no (SGI or PPI) interrupts are active or pending to start
51 * with.
52 */
53 EXPECT_EQ(io_read32_array(GICD_ISPENDR, 0), 0);
54 EXPECT_EQ(io_read32(GICR_ISPENDR0), 0);
55 EXPECT_EQ(io_read32_array(GICD_ISACTIVER, 0), 0);
56 EXPECT_EQ(io_read32(GICR_ISACTIVER0), 0);
57
58 HFTEST_LOG("Starting timer\n");
59 /* Set physical timer for 20 ms and enable. */
60 write_msr(CNTP_TVAL_EL0, ns_to_ticks(ns));
61 write_msr(CNTP_CTL_EL0, CNTx_CTL_ENABLE_MASK);
62 }
63
check_physical_timer_interrupt_serviced(void)64 static void check_physical_timer_interrupt_serviced(void)
65 {
66 /* Waiting for interrupt to be serviced in normal world. */
67 while (last_interrupt_id == 0) {
68 EXPECT_EQ(io_read32_array(GICD_ISPENDR, 0), 0);
69 EXPECT_EQ(io_read32(GICR_ISPENDR0), 0);
70 EXPECT_EQ(io_read32_array(GICD_ISACTIVER, 0), 0);
71 EXPECT_EQ(io_read32(GICR_ISACTIVER0), 0);
72 }
73
74 /* Check that we got the interrupt. */
75 HFTEST_LOG("Checking for interrupt\n");
76 EXPECT_EQ(last_interrupt_id, PHYSICAL_TIMER_IRQ);
77
78 /* Check timer status. */
79 EXPECT_EQ(read_msr(CNTP_CTL_EL0),
80 CNTx_CTL_ISTS_MASK | CNTx_CTL_ENABLE_MASK);
81
82 /* There should again be no pending or active interrupts. */
83 EXPECT_EQ(io_read32_array(GICD_ISPENDR, 0), 0);
84 EXPECT_EQ(io_read32(GICR_ISPENDR0), 0);
85 EXPECT_EQ(io_read32_array(GICD_ISACTIVER, 0), 0);
86 EXPECT_EQ(io_read32(GICR_ISACTIVER0), 0);
87 }
88
89 /**
90 * This test arms a timer in the normal world and emits a direct request to a
91 * secure partition to query for a busy loop. The timer physical PPI NS
92 * interrupt traps to Hafnium/SPMC which saves the currently running SP vCPU
93 * state and returns to the normal world. The latter traps to the irq handler
94 * and handles the timer interrupt. The SP vCPU is then resumed again through
95 * FFA_RUN which completes the busy loop and returns a sucess direct msg
96 * response.
97 */
TEST(interrupts,sp_preempted_by_ns_interrupt)98 TEST(interrupts, sp_preempted_by_ns_interrupt)
99 {
100 struct ffa_value res;
101 struct mailbox_buffers mb = set_up_mailbox();
102 struct ffa_partition_info *receiver_info = service2(mb.recv);
103 const ffa_id_t receiver_id = receiver_info->vm_id;
104
105 setup_physical_timer();
106 start_physical_timer(20000000);
107
108 /* Send direct request to query SP to wait in a busy loop. */
109 res = sp_busy_loop_cmd_send(
110 hf_vm_get_id(), receiver_id,
111 TEST_SP_PREEMPTED_BY_NS_INTERRUPT_LOOP_COUNT);
112
113 /* SP is pre-empted by the non-secure timer interrupt. */
114 EXPECT_EQ(res.func, FFA_INTERRUPT_32);
115
116 /* VM id/vCPU index are passed through arg1. */
117 EXPECT_EQ(res.arg1, ffa_vm_vcpu(receiver_id, 0));
118
119 check_physical_timer_interrupt_serviced();
120
121 /* Resume the SP to complete the busy loop and return with success. */
122 res = ffa_run(ffa_vm_id(res), ffa_vcpu_index(res));
123 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
124 EXPECT_EQ(res.arg3, SP_SUCCESS);
125 }
126
127 /**
128 * This test arms a timer in the normal world and emits a direct request to a
129 * secure partition to query for a busy loop. The timer physical PPI NS
130 * interrupt traps to Hafnium/SPMC at S-EL2 as FIQ. SPMC injects a managed
131 * exit vIRQ interrupt(as requested by the secure partition through the
132 * `managed-exit-virq` field in its manifest). Further, SPMC resumes the SP
133 * causing it to run its interrupt handler. SP sends a managed exit (direct
134 * message) response to the normal world. The latter traps to the irq handler
135 * and handles the timer interrupt. The SP vCPU is then resumed again through
136 * a special direct message request which completes the busy loop and returns
137 * a success direct msg response.
138 */
TEST(interrupts,sp_managed_exit)139 TEST(interrupts, sp_managed_exit)
140 {
141 struct ffa_value res;
142 ffa_id_t own_id = hf_vm_get_id();
143 struct mailbox_buffers mb = set_up_mailbox();
144 struct ffa_partition_info *receiver_info = service1(mb.recv);
145 const ffa_id_t receiver_id = receiver_info->vm_id;
146
147 setup_physical_timer();
148
149 /* Enable SP to handle managed exit. */
150 res = sp_virtual_interrupt_cmd_send(own_id, receiver_id,
151 HF_MANAGED_EXIT_INTID, true,
152 INTERRUPT_TYPE_IRQ);
153
154 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
155 EXPECT_EQ(sp_resp(res), SP_SUCCESS);
156
157 start_physical_timer(20000000);
158
159 /* Send direct request to query SP to wait in a busy loop. */
160 res = sp_busy_loop_cmd_send(
161 hf_vm_get_id(), receiver_id,
162 TEST_SP_PREEMPTED_BY_NS_INTERRUPT_LOOP_COUNT);
163
164 /* Expect a managed exit response from SP. */
165 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
166 EXPECT_EQ(sp_resp(res), HF_MANAGED_EXIT_INTID);
167
168 check_physical_timer_interrupt_serviced();
169
170 /* Resume the SP to complete the busy loop and return with success. */
171 res = sp_resume_after_managed_exit_send(own_id, receiver_id);
172 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
173 EXPECT_EQ(res.arg3, SP_SUCCESS);
174 }
175