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