1 // Copyright 2017 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include <dev/psci.h>
8 
9 #include <arch/arm64/smccc.h>
10 #include <inttypes.h>
11 #include <pdev/driver.h>
12 #include <zircon/boot/driver-config.h>
13 #include <string.h>
14 
15 static uint64_t shutdown_args[3] = { 0, 0, 0 };
16 static uint64_t reboot_args[3] = { 0, 0, 0 };
17 static uint64_t reboot_bootloader_args[3] = { 0, 0, 0 };
18 static uint64_t reboot_recovery_args[3] = { 0, 0, 0 };
19 
psci_smc_call(uint32_t function,uint64_t arg0,uint64_t arg1,uint64_t arg2)20 static uint64_t psci_smc_call(uint32_t function, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
21     return arm_smccc_smc(function, arg0, arg1, arg2, 0, 0, 0, 0).x0;
22 }
23 
psci_hvc_call(uint32_t function,uint64_t arg0,uint64_t arg1,uint64_t arg2)24 static uint64_t psci_hvc_call(uint32_t function, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
25     return arm_smccc_hvc(function, arg0, arg1, arg2, 0, 0, 0, 0).x0;
26 }
27 
28 #if PSCI_USE_HVC
29 psci_call_proc do_psci_call = psci_hvc_call;
30 #else
31 psci_call_proc do_psci_call = psci_smc_call;
32 #endif
33 
psci_system_off()34 void psci_system_off() {
35     do_psci_call(PSCI64_SYSTEM_OFF, shutdown_args[0], shutdown_args[1], shutdown_args[2]);
36 }
37 
psci_system_reset(enum reboot_flags flags)38 void psci_system_reset(enum reboot_flags flags) {
39     uint64_t* args = reboot_args;
40 
41     if (flags == REBOOT_BOOTLOADER) {
42         args = reboot_bootloader_args;
43     } else if (flags == REBOOT_RECOVERY) {
44         args = reboot_recovery_args;
45     }
46 
47     do_psci_call(PSCI64_SYSTEM_RESET, args[0], args[1], args[2]);
48 }
49 
arm_psci_init(const void * driver_data,uint32_t length)50 static void arm_psci_init(const void* driver_data, uint32_t length) {
51 #if 0
52     // TODO: restore this after everyone is updated to new bootloaders
53     ASSERT(length >= sizeof(dcfg_arm_psci_driver_t));
54 #else
55     ASSERT(length >= sizeof(dcfg_arm_psci_driver_t) - sizeof(reboot_recovery_args));
56 #endif
57 
58     auto driver = static_cast<const dcfg_arm_psci_driver_t*>(driver_data);
59 
60     do_psci_call = driver->use_hvc ? psci_hvc_call : psci_smc_call;
61     memcpy(shutdown_args, driver->shutdown_args, sizeof(shutdown_args));
62     memcpy(reboot_args, driver->reboot_args, sizeof(reboot_args));
63     memcpy(reboot_bootloader_args, driver->reboot_bootloader_args, sizeof(reboot_bootloader_args));
64 
65     // TODO: remove this check after everyone is updated to new bootloaders
66     if (length >= sizeof(dcfg_arm_psci_driver_t)) {
67         memcpy(reboot_recovery_args, driver->reboot_recovery_args, sizeof(reboot_recovery_args));
68     }
69 }
70 
71 LK_PDEV_INIT(arm_psci_init, KDRV_ARM_PSCI, arm_psci_init, LK_INIT_LEVEL_PLATFORM_EARLY);
72 
73 #include <lib/console.h>
74 
cmd_psci(int argc,const cmd_args * argv,uint32_t flags)75 static int cmd_psci(int argc, const cmd_args *argv, uint32_t flags) {
76     if (argc < 2) {
77         printf("not enough arguments\n");
78         printf("%s function [arg0] [arg1] [arg2]\n", argv[0].str);
79         return -1;
80     }
81 
82     uint32_t function = static_cast<uint32_t>(argv[1].u);
83     uint64_t arg0 = (argc >= 3) ? argv[2].u : 0;
84     uint64_t arg1 = (argc >= 4) ? argv[3].u : 0;
85     uint64_t arg2 = (argc >= 5) ? argv[4].u : 0;
86 
87     uint64_t ret = do_psci_call(function, arg0, arg1, arg2);
88     printf("do_psci_call returned %" PRIu64 "\n", ret);
89     return 0;
90 }
91 
92 STATIC_COMMAND_START
93 STATIC_COMMAND_MASKED("psci", "execute PSCI command", &cmd_psci, CMD_AVAIL_ALWAYS)
94 STATIC_COMMAND_END(psci);
95