Skip to content

Commit 7ccd615

Browse files
committed
Merge branch kvm-arm64/psci-1.3 into kvmarm/next
* kvm-arm64/psci-1.3: : PSCI v1.3 support, courtesy of David Woodhouse : : Bump KVM's PSCI implementation up to v1.3, with the added bonus of : implementing the SYSTEM_OFF2 call. Like other system-scoped PSCI calls, : this gets relayed to userspace for further processing with a new : KVM_SYSTEM_EVENT_SHUTDOWN flag. : : As an added bonus, implement client-side support for hibernation with : the SYSTEM_OFF2 call. arm64: Use SYSTEM_OFF2 PSCI call to power off for hibernate KVM: arm64: nvhe: Pass through PSCI v1.3 SYSTEM_OFF2 call KVM: selftests: Add test for PSCI SYSTEM_OFF2 KVM: arm64: Add support for PSCI v1.2 and v1.3 KVM: arm64: Add PSCI v1.3 SYSTEM_OFF2 function for hibernation firmware/psci: Add definitions for PSCI v1.3 specification Signed-off-by: Oliver Upton <[email protected]>
2 parents 2865463 + 3e251af commit 7ccd615

File tree

10 files changed

+212
-3
lines changed

10 files changed

+212
-3
lines changed

Documentation/virt/kvm/api.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6855,6 +6855,10 @@ the first `ndata` items (possibly zero) of the data array are valid.
68556855
the guest issued a SYSTEM_RESET2 call according to v1.1 of the PSCI
68566856
specification.
68576857

6858+
- for arm64, data[0] is set to KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2
6859+
if the guest issued a SYSTEM_OFF2 call according to v1.3 of the PSCI
6860+
specification.
6861+
68586862
- for RISC-V, data[0] is set to the value of the second argument of the
68596863
``sbi_system_reset`` call.
68606864

@@ -6888,6 +6892,12 @@ either:
68886892
- Deny the guest request to suspend the VM. See ARM DEN0022D.b 5.19.2
68896893
"Caller responsibilities" for possible return values.
68906894

6895+
Hibernation using the PSCI SYSTEM_OFF2 call is enabled when PSCI v1.3
6896+
is enabled. If a guest invokes the PSCI SYSTEM_OFF2 function, KVM will
6897+
exit to userspace with the KVM_SYSTEM_EVENT_SHUTDOWN event type and with
6898+
data[0] set to KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2. The only
6899+
supported hibernate type for the SYSTEM_OFF2 function is HIBERNATE_OFF.
6900+
68916901
::
68926902

68936903
/* KVM_EXIT_IOAPIC_EOI */

arch/arm64/include/uapi/asm/kvm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,12 @@ enum {
484484
*/
485485
#define KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2 (1ULL << 0)
486486

487+
/*
488+
* Shutdown caused by a PSCI v1.3 SYSTEM_OFF2 call.
489+
* Valid only when the system event has a type of KVM_SYSTEM_EVENT_SHUTDOWN.
490+
*/
491+
#define KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 (1ULL << 0)
492+
487493
/* run->fail_entry.hardware_entry_failure_reason codes. */
488494
#define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0)
489495

arch/arm64/kvm/hyp/nvhe/psci-relay.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_
265265
case PSCI_1_0_FN_PSCI_FEATURES:
266266
case PSCI_1_0_FN_SET_SUSPEND_MODE:
267267
case PSCI_1_1_FN64_SYSTEM_RESET2:
268+
case PSCI_1_3_FN_SYSTEM_OFF2:
269+
case PSCI_1_3_FN64_SYSTEM_OFF2:
268270
return psci_forward(host_ctxt);
269271
case PSCI_1_0_FN64_SYSTEM_SUSPEND:
270272
return psci_system_suspend(func_id, host_ctxt);

arch/arm64/kvm/hypercalls.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
575575
case KVM_ARM_PSCI_0_2:
576576
case KVM_ARM_PSCI_1_0:
577577
case KVM_ARM_PSCI_1_1:
578+
case KVM_ARM_PSCI_1_2:
579+
case KVM_ARM_PSCI_1_3:
578580
if (!wants_02)
579581
return -EINVAL;
580582
vcpu->kvm->arch.psci_version = val;

arch/arm64/kvm/psci.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@ static void kvm_psci_system_off(struct kvm_vcpu *vcpu)
194194
kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN, 0);
195195
}
196196

197+
static void kvm_psci_system_off2(struct kvm_vcpu *vcpu)
198+
{
199+
kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN,
200+
KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2);
201+
}
202+
197203
static void kvm_psci_system_reset(struct kvm_vcpu *vcpu)
198204
{
199205
kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET, 0);
@@ -322,7 +328,7 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
322328

323329
switch(psci_fn) {
324330
case PSCI_0_2_FN_PSCI_VERSION:
325-
val = minor == 0 ? KVM_ARM_PSCI_1_0 : KVM_ARM_PSCI_1_1;
331+
val = PSCI_VERSION(1, minor);
326332
break;
327333
case PSCI_1_0_FN_PSCI_FEATURES:
328334
arg = smccc_get_arg1(vcpu);
@@ -358,6 +364,11 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
358364
if (minor >= 1)
359365
val = 0;
360366
break;
367+
case PSCI_1_3_FN_SYSTEM_OFF2:
368+
case PSCI_1_3_FN64_SYSTEM_OFF2:
369+
if (minor >= 3)
370+
val = PSCI_1_3_OFF_TYPE_HIBERNATE_OFF;
371+
break;
361372
}
362373
break;
363374
case PSCI_1_0_FN_SYSTEM_SUSPEND:
@@ -392,6 +403,33 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
392403
break;
393404
}
394405
break;
406+
case PSCI_1_3_FN_SYSTEM_OFF2:
407+
kvm_psci_narrow_to_32bit(vcpu);
408+
fallthrough;
409+
case PSCI_1_3_FN64_SYSTEM_OFF2:
410+
if (minor < 3)
411+
break;
412+
413+
arg = smccc_get_arg1(vcpu);
414+
/*
415+
* SYSTEM_OFF2 defaults to HIBERNATE_OFF if arg1 is zero. arg2
416+
* must be zero.
417+
*/
418+
if ((arg && arg != PSCI_1_3_OFF_TYPE_HIBERNATE_OFF) ||
419+
smccc_get_arg2(vcpu) != 0) {
420+
val = PSCI_RET_INVALID_PARAMS;
421+
break;
422+
}
423+
kvm_psci_system_off2(vcpu);
424+
/*
425+
* We shouldn't be going back to the guest after receiving a
426+
* SYSTEM_OFF2 request. Preload a return value of
427+
* INTERNAL_FAILURE should userspace ignore the exit and resume
428+
* the vCPU.
429+
*/
430+
val = PSCI_RET_INTERNAL_FAILURE;
431+
ret = 0;
432+
break;
395433
default:
396434
return kvm_psci_0_2_call(vcpu);
397435
}
@@ -449,6 +487,10 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
449487
}
450488

451489
switch (version) {
490+
case KVM_ARM_PSCI_1_3:
491+
return kvm_psci_1_x_call(vcpu, 3);
492+
case KVM_ARM_PSCI_1_2:
493+
return kvm_psci_1_x_call(vcpu, 2);
452494
case KVM_ARM_PSCI_1_1:
453495
return kvm_psci_1_x_call(vcpu, 1);
454496
case KVM_ARM_PSCI_1_0:

drivers/firmware/psci/psci.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct psci_0_1_function_ids get_psci_0_1_function_ids(void)
7878

7979
static u32 psci_cpu_suspend_feature;
8080
static bool psci_system_reset2_supported;
81+
static bool psci_system_off2_hibernate_supported;
8182

8283
static inline bool psci_has_ext_power_state(void)
8384
{
@@ -333,6 +334,36 @@ static void psci_sys_poweroff(void)
333334
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
334335
}
335336

337+
#ifdef CONFIG_HIBERNATION
338+
static int psci_sys_hibernate(struct sys_off_data *data)
339+
{
340+
/*
341+
* If no hibernate type is specified SYSTEM_OFF2 defaults to selecting
342+
* HIBERNATE_OFF.
343+
*
344+
* There are hypervisors in the wild that do not align with the spec and
345+
* reject calls that explicitly provide a hibernate type. For
346+
* compatibility with these nonstandard implementations, pass 0 as the
347+
* type.
348+
*/
349+
if (system_entering_hibernation())
350+
invoke_psci_fn(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2), 0, 0, 0);
351+
return NOTIFY_DONE;
352+
}
353+
354+
static int __init psci_hibernate_init(void)
355+
{
356+
if (psci_system_off2_hibernate_supported) {
357+
/* Higher priority than EFI shutdown, but only for hibernate */
358+
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
359+
SYS_OFF_PRIO_FIRMWARE + 2,
360+
psci_sys_hibernate, NULL);
361+
}
362+
return 0;
363+
}
364+
subsys_initcall(psci_hibernate_init);
365+
#endif
366+
336367
static int psci_features(u32 psci_func_id)
337368
{
338369
return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
@@ -364,6 +395,7 @@ static const struct {
364395
PSCI_ID_NATIVE(1_1, SYSTEM_RESET2),
365396
PSCI_ID(1_1, MEM_PROTECT),
366397
PSCI_ID_NATIVE(1_1, MEM_PROTECT_CHECK_RANGE),
398+
PSCI_ID_NATIVE(1_3, SYSTEM_OFF2),
367399
};
368400

369401
static int psci_debugfs_read(struct seq_file *s, void *data)
@@ -525,6 +557,18 @@ static void __init psci_init_system_reset2(void)
525557
psci_system_reset2_supported = true;
526558
}
527559

560+
static void __init psci_init_system_off2(void)
561+
{
562+
int ret;
563+
564+
ret = psci_features(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2));
565+
if (ret < 0)
566+
return;
567+
568+
if (ret & PSCI_1_3_OFF_TYPE_HIBERNATE_OFF)
569+
psci_system_off2_hibernate_supported = true;
570+
}
571+
528572
static void __init psci_init_system_suspend(void)
529573
{
530574
int ret;
@@ -655,6 +699,7 @@ static int __init psci_probe(void)
655699
psci_init_cpu_suspend();
656700
psci_init_system_suspend();
657701
psci_init_system_reset2();
702+
psci_init_system_off2();
658703
kvm_init_hyp_services();
659704
}
660705

include/kvm/arm_psci.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
#define KVM_ARM_PSCI_0_2 PSCI_VERSION(0, 2)
1515
#define KVM_ARM_PSCI_1_0 PSCI_VERSION(1, 0)
1616
#define KVM_ARM_PSCI_1_1 PSCI_VERSION(1, 1)
17+
#define KVM_ARM_PSCI_1_2 PSCI_VERSION(1, 2)
18+
#define KVM_ARM_PSCI_1_3 PSCI_VERSION(1, 3)
1719

18-
#define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_1
20+
#define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_3
1921

2022
static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
2123
{

include/uapi/linux/psci.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18)
6060
#define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19)
6161
#define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20)
62+
#define PSCI_1_3_FN_SYSTEM_OFF2 PSCI_0_2_FN(21)
6263

6364
#define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12)
6465
#define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13)
@@ -68,6 +69,7 @@
6869

6970
#define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18)
7071
#define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20)
72+
#define PSCI_1_3_FN64_SYSTEM_OFF2 PSCI_0_2_FN64(21)
7173

7274
/* PSCI v0.2 power state encoding for CPU_SUSPEND function */
7375
#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff
@@ -100,6 +102,9 @@
100102
#define PSCI_1_1_RESET_TYPE_SYSTEM_WARM_RESET 0
101103
#define PSCI_1_1_RESET_TYPE_VENDOR_START 0x80000000U
102104

105+
/* PSCI v1.3 hibernate type for SYSTEM_OFF2 */
106+
#define PSCI_1_3_OFF_TYPE_HIBERNATE_OFF BIT(0)
107+
103108
/* PSCI version decoding (independent of PSCI version) */
104109
#define PSCI_VERSION_MAJOR_SHIFT 16
105110
#define PSCI_VERSION_MINOR_MASK \

kernel/power/hibernate.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,8 +685,11 @@ static void power_down(void)
685685
}
686686
fallthrough;
687687
case HIBERNATION_SHUTDOWN:
688-
if (kernel_can_power_off())
688+
if (kernel_can_power_off()) {
689+
entering_platform_hibernation = true;
689690
kernel_power_off();
691+
entering_platform_hibernation = false;
692+
}
690693
break;
691694
}
692695
kernel_halt();

tools/testing/selftests/kvm/aarch64/psci_test.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
5766
static 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+
191282
int 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

Comments
 (0)