xref: /kvm-unit-tests/lib/arm/psci.c (revision 201b9e8bdc84c6436dd53b45d93a60c681b92719)
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 
psci_invoke_none(unsigned int function_id,unsigned long arg0,unsigned long arg1,unsigned long arg2,unsigned long arg3,unsigned long arg4,unsigned long arg5,unsigned long arg6,unsigned long arg7,unsigned long arg8,unsigned long arg9,unsigned long arg10,struct smccc_result * result)15 static int psci_invoke_none(unsigned int function_id, unsigned long arg0,
16 			    unsigned long arg1, unsigned long arg2,
17 			    unsigned long arg3, unsigned long arg4,
18 			    unsigned long arg5, unsigned long arg6,
19 			    unsigned long arg7, unsigned long arg8,
20 			    unsigned long arg9, unsigned long arg10,
21 			    struct smccc_result *result)
22 {
23 	printf("No PSCI method configured! Can't invoke...\n");
24 	return PSCI_RET_NOT_PRESENT;
25 }
26 
27 smccc_invoke_fn psci_invoke_fn = psci_invoke_none;
28 
psci_invoke(unsigned int function_id,unsigned long arg0,unsigned long arg1,unsigned long arg2)29 int psci_invoke(unsigned int function_id, unsigned long arg0,
30 		unsigned long arg1, unsigned long arg2)
31 {
32 	return psci_invoke_fn(function_id, arg0, arg1, arg2, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
33 }
34 
psci_cpu_on(unsigned long cpuid,unsigned long entry_point)35 int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
36 {
37 #ifdef __arm__
38 	return psci_invoke(PSCI_0_2_FN_CPU_ON, cpuid, entry_point, 0);
39 #else
40 	return psci_invoke(PSCI_0_2_FN64_CPU_ON, cpuid, entry_point, 0);
41 #endif
42 }
43 
44 extern void secondary_entry(void);
cpu_psci_cpu_boot(unsigned int cpu)45 int cpu_psci_cpu_boot(unsigned int cpu)
46 {
47 	int err = psci_cpu_on(cpus[cpu], __pa(secondary_entry));
48 	if (err)
49 		printf("failed to boot CPU%d (%d)\n", cpu, err);
50 	return err;
51 }
52 
cpu_psci_cpu_die(void)53 void cpu_psci_cpu_die(void)
54 {
55 	int err = psci_invoke(PSCI_0_2_FN_CPU_OFF, 0, 0, 0);
56 	printf("CPU%d unable to power off (error = %d)\n", smp_processor_id(), err);
57 }
58 
psci_system_reset(void)59 void psci_system_reset(void)
60 {
61 	psci_invoke(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
62 }
63 
psci_system_off(void)64 void psci_system_off(void)
65 {
66 	int err = psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
67 	printf("CPU%d unable to do system off (error = %d)\n", smp_processor_id(), err);
68 }
69 
psci_set_conduit_fdt(void)70 static void psci_set_conduit_fdt(void)
71 {
72 	const void *fdt = dt_fdt();
73 	const struct fdt_property *method;
74 	int node, len;
75 
76 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,psci-0.2");
77 	assert_msg(node >= 0, "PSCI v0.2 compatibility required");
78 
79 	method = fdt_get_property(fdt, node, "method", &len);
80 	assert(method != NULL && len == 4);
81 
82 	if (strcmp(method->data, "hvc") == 0)
83 		psci_invoke_fn = arm_smccc_hvc;
84 	else if (strcmp(method->data, "smc") == 0)
85 		psci_invoke_fn = arm_smccc_smc;
86 	else
87 		assert_msg(false, "Unknown PSCI conduit: %s", method->data);
88 }
89 
90 #ifdef CONFIG_EFI
91 
92 #include <acpi.h>
93 
psci_set_conduit_acpi(void)94 static void psci_set_conduit_acpi(void)
95 {
96 	struct acpi_table_fadt *fadt = find_acpi_table_addr(FACP_SIGNATURE);
97 
98 	assert_msg(fadt, "Unable to find ACPI FADT");
99 	assert_msg(fadt->arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT,
100 		   "PSCI is not supported in this platform");
101 
102 	if (fadt->arm_boot_flags & ACPI_FADT_PSCI_USE_HVC)
103 		psci_invoke_fn = arm_smccc_hvc;
104 	else
105 		psci_invoke_fn = arm_smccc_smc;
106 }
107 
108 #else
109 
psci_set_conduit_acpi(void)110 static void psci_set_conduit_acpi(void)
111 {
112 	assert_msg(false, "ACPI not available");
113 }
114 
115 #endif
116 
psci_set_conduit(void)117 void psci_set_conduit(void)
118 {
119 	if (dt_available())
120 		psci_set_conduit_fdt();
121 	else
122 		psci_set_conduit_acpi();
123 }
124