xref: /kvm-unit-tests/lib/arm/psci.c (revision bff097a8fe181928a8164f9a2f5f146480c6aefc)
1 /*
2  * PSCI API
3  * From arch/arm[64]/kernel/psci.c
4  *
5  * Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com>
6  *
7  * This work is licensed under the terms of the GNU LGPL, version 2.
8  */
9 #include <devicetree.h>
10 #include <asm/psci.h>
11 #include <asm/setup.h>
12 #include <asm/page.h>
13 #include <asm/smp.h>
14 
15 static int psci_invoke_none(unsigned int function_id, unsigned long arg0,
16 			    unsigned long arg1, unsigned long arg2)
17 {
18 	printf("No PSCI method configured! Can't invoke...\n");
19 	return PSCI_RET_NOT_PRESENT;
20 }
21 
22 psci_invoke_fn psci_invoke = psci_invoke_none;
23 
24 int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
25 {
26 #ifdef __arm__
27 	return psci_invoke(PSCI_0_2_FN_CPU_ON, cpuid, entry_point, 0);
28 #else
29 	return psci_invoke(PSCI_0_2_FN64_CPU_ON, cpuid, entry_point, 0);
30 #endif
31 }
32 
33 extern void secondary_entry(void);
34 int cpu_psci_cpu_boot(unsigned int cpu)
35 {
36 	int err = psci_cpu_on(cpus[cpu], __pa(secondary_entry));
37 	if (err)
38 		printf("failed to boot CPU%d (%d)\n", cpu, err);
39 	return err;
40 }
41 
42 void cpu_psci_cpu_die(void)
43 {
44 	int err = psci_invoke(PSCI_0_2_FN_CPU_OFF, 0, 0, 0);
45 	printf("CPU%d unable to power off (error = %d)\n", smp_processor_id(), err);
46 }
47 
48 void psci_system_reset(void)
49 {
50 	psci_invoke(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
51 }
52 
53 void psci_system_off(void)
54 {
55 	int err = psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
56 	printf("CPU%d unable to do system off (error = %d)\n", smp_processor_id(), err);
57 }
58 
59 static void psci_set_conduit_fdt(void)
60 {
61 	const void *fdt = dt_fdt();
62 	const struct fdt_property *method;
63 	int node, len;
64 
65 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,psci-0.2");
66 	assert_msg(node >= 0, "PSCI v0.2 compatibility required");
67 
68 	method = fdt_get_property(fdt, node, "method", &len);
69 	assert(method != NULL && len == 4);
70 
71 	if (strcmp(method->data, "hvc") == 0)
72 		psci_invoke = psci_invoke_hvc;
73 	else if (strcmp(method->data, "smc") == 0)
74 		psci_invoke = psci_invoke_smc;
75 	else
76 		assert_msg(false, "Unknown PSCI conduit: %s", method->data);
77 }
78 
79 #ifdef CONFIG_EFI
80 
81 #include <acpi.h>
82 
83 static void psci_set_conduit_acpi(void)
84 {
85 	struct acpi_table_fadt *fadt = find_acpi_table_addr(FACP_SIGNATURE);
86 
87 	assert_msg(fadt, "Unable to find ACPI FADT");
88 	assert_msg(fadt->arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT,
89 		   "PSCI is not supported in this platform");
90 
91 	if (fadt->arm_boot_flags & ACPI_FADT_PSCI_USE_HVC)
92 		psci_invoke = psci_invoke_hvc;
93 	else
94 		psci_invoke = psci_invoke_smc;
95 }
96 
97 #else
98 
99 static void psci_set_conduit_acpi(void)
100 {
101 	assert_msg(false, "ACPI not available");
102 }
103 
104 #endif
105 
106 void psci_set_conduit(void)
107 {
108 	if (dt_available())
109 		psci_set_conduit_fdt();
110 	else
111 		psci_set_conduit_acpi();
112 }
113