@@ -54,6 +54,15 @@ static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
54
54
return res .a0 ;
55
55
}
56
56
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
+
57
66
static uint64_t psci_features (uint32_t func_id )
58
67
{
59
68
struct arm_smccc_res res ;
@@ -188,11 +197,94 @@ static void host_test_system_suspend(void)
188
197
kvm_vm_free (vm );
189
198
}
190
199
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
+
191
282
int main (void )
192
283
{
193
284
TEST_REQUIRE (kvm_has_cap (KVM_CAP_ARM_SYSTEM_SUSPEND ));
194
285
195
286
host_test_cpu_on ();
196
287
host_test_system_suspend ();
288
+ host_test_system_off2 ();
197
289
return 0 ;
198
290
}
0 commit comments