@@ -54,6 +54,15 @@ static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
5454 return res .a0 ;
5555}
5656
57+ static uint64_t psci_system_off2 (uint64_t type , uint64_t cookie )
58+ {
59+ struct arm_smccc_res res ;
60+
61+ smccc_hvc (PSCI_1_3_FN64_SYSTEM_OFF2 , type , cookie , 0 , 0 , 0 , 0 , 0 , & res );
62+
63+ return res .a0 ;
64+ }
65+
5766static uint64_t psci_features (uint32_t func_id )
5867{
5968 struct arm_smccc_res res ;
@@ -188,11 +197,94 @@ static void host_test_system_suspend(void)
188197 kvm_vm_free (vm );
189198}
190199
200+ static void guest_test_system_off2 (void )
201+ {
202+ uint64_t ret ;
203+
204+ /* assert that SYSTEM_OFF2 is discoverable */
205+ GUEST_ASSERT (psci_features (PSCI_1_3_FN_SYSTEM_OFF2 ) &
206+ PSCI_1_3_OFF_TYPE_HIBERNATE_OFF );
207+ GUEST_ASSERT (psci_features (PSCI_1_3_FN64_SYSTEM_OFF2 ) &
208+ PSCI_1_3_OFF_TYPE_HIBERNATE_OFF );
209+
210+ /* With non-zero 'cookie' field, it should fail */
211+ ret = psci_system_off2 (PSCI_1_3_OFF_TYPE_HIBERNATE_OFF , 1 );
212+ GUEST_ASSERT (ret == PSCI_RET_INVALID_PARAMS );
213+
214+ /*
215+ * This would normally never return, so KVM sets the return value
216+ * to PSCI_RET_INTERNAL_FAILURE. The test case *does* return, so
217+ * that it can test both values for HIBERNATE_OFF.
218+ */
219+ ret = psci_system_off2 (PSCI_1_3_OFF_TYPE_HIBERNATE_OFF , 0 );
220+ GUEST_ASSERT (ret == PSCI_RET_INTERNAL_FAILURE );
221+
222+ /*
223+ * Revision F.b of the PSCI v1.3 specification documents zero as an
224+ * alias for HIBERNATE_OFF, since that's the value used in earlier
225+ * revisions of the spec and some implementations in the field.
226+ */
227+ ret = psci_system_off2 (0 , 1 );
228+ GUEST_ASSERT (ret == PSCI_RET_INVALID_PARAMS );
229+
230+ ret = psci_system_off2 (0 , 0 );
231+ GUEST_ASSERT (ret == PSCI_RET_INTERNAL_FAILURE );
232+
233+ GUEST_DONE ();
234+ }
235+
236+ static void host_test_system_off2 (void )
237+ {
238+ struct kvm_vcpu * source , * target ;
239+ struct kvm_mp_state mps ;
240+ uint64_t psci_version = 0 ;
241+ int nr_shutdowns = 0 ;
242+ struct kvm_run * run ;
243+ struct ucall uc ;
244+
245+ setup_vm (guest_test_system_off2 , & source , & target );
246+
247+ vcpu_get_reg (target , KVM_REG_ARM_PSCI_VERSION , & psci_version );
248+
249+ TEST_ASSERT (psci_version >= PSCI_VERSION (1 , 3 ),
250+ "Unexpected PSCI version %lu.%lu" ,
251+ PSCI_VERSION_MAJOR (psci_version ),
252+ PSCI_VERSION_MINOR (psci_version ));
253+
254+ vcpu_power_off (target );
255+ run = source -> run ;
256+
257+ enter_guest (source );
258+ while (run -> exit_reason == KVM_EXIT_SYSTEM_EVENT ) {
259+ TEST_ASSERT (run -> system_event .type == KVM_SYSTEM_EVENT_SHUTDOWN ,
260+ "Unhandled system event: %u (expected: %u)" ,
261+ run -> system_event .type , KVM_SYSTEM_EVENT_SHUTDOWN );
262+ TEST_ASSERT (run -> system_event .ndata >= 1 ,
263+ "Unexpected amount of system event data: %u (expected, >= 1)" ,
264+ run -> system_event .ndata );
265+ TEST_ASSERT (run -> system_event .data [0 ] & KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 ,
266+ "PSCI_OFF2 flag not set. Flags %llu (expected %llu)" ,
267+ run -> system_event .data [0 ], KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 );
268+
269+ nr_shutdowns ++ ;
270+
271+ /* Restart the vCPU */
272+ mps .mp_state = KVM_MP_STATE_RUNNABLE ;
273+ vcpu_mp_state_set (source , & mps );
274+
275+ enter_guest (source );
276+ }
277+
278+ TEST_ASSERT (get_ucall (source , & uc ) == UCALL_DONE , "Guest did not exit cleanly" );
279+ TEST_ASSERT (nr_shutdowns == 2 , "Two shutdown events were expected, but saw %d" , nr_shutdowns );
280+ }
281+
191282int main (void )
192283{
193284 TEST_REQUIRE (kvm_has_cap (KVM_CAP_ARM_SYSTEM_SUSPEND ));
194285
195286 host_test_cpu_on ();
196287 host_test_system_suspend ();
288+ host_test_system_off2 ();
197289 return 0 ;
198290}
0 commit comments