1 /*
2 * xen/arch/arm/platforms/xilinx-zynqmp-eemi.c
3 *
4 * Xilinx ZynqMP EEMI API
5 *
6 * Copyright (c) 2018 Xilinx Inc.
7 * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms and conditions of the GNU General Public
11 * License, version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19 #include <asm/regs.h>
20 #include <xen/sched.h>
21 #include <asm/smccc.h>
22 #include <asm/platforms/xilinx-zynqmp-eemi.h>
23
24 /*
25 * EEMI firmware API:
26 * https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf
27 *
28 * IPI firmware API:
29 * https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.h
30 *
31 * Power domain node_ids identify the area of effect of the power
32 * management operations. They are the first parameter passed to power
33 * management EEMI calls.
34 *
35 * Reset IDs identify the area of effect of a reset operation. They are
36 * the first parameter passed to reset EEMI calls.
37 *
38 * For now, let the hardware domain access to all power domain nodes and
39 * all reset lines. In the future, we'll check for ownership of
40 * resources by specific virtual machines.
41 */
domain_has_node_access(struct domain * d,uint32_t nodeid)42 static inline bool domain_has_node_access(struct domain *d, uint32_t nodeid)
43 {
44 return is_hardware_domain(d);
45 }
46
domain_has_reset_access(struct domain * d,uint32_t rst)47 static inline bool domain_has_reset_access(struct domain *d, uint32_t rst)
48 {
49 return is_hardware_domain(d);
50 }
51
zynqmp_eemi(struct cpu_user_regs * regs)52 bool zynqmp_eemi(struct cpu_user_regs *regs)
53 {
54 struct arm_smccc_res res;
55 uint32_t fid = get_user_reg(regs, 0);
56 uint32_t nodeid = get_user_reg(regs, 1);
57 unsigned int pm_fn = fid & 0xFFFF;
58 enum pm_ret_status ret;
59
60 switch ( fid )
61 {
62 /* Mandatory SMC32 functions. */
63 case ARM_SMCCC_CALL_COUNT_FID(SIP):
64 case ARM_SMCCC_CALL_UID_FID(SIP):
65 case ARM_SMCCC_REVISION_FID(SIP):
66 goto forward_to_fw;
67 /*
68 * We can't allow CPUs to suspend without Xen knowing about it.
69 * We accept but ignore the request and wait for the guest to issue
70 * a WFI or PSCI call which Xen will trap and act accordingly upon.
71 */
72 case EEMI_FID(PM_SELF_SUSPEND):
73 ret = XST_PM_SUCCESS;
74 goto done;
75
76 case EEMI_FID(PM_GET_NODE_STATUS):
77 /* API for PUs. */
78 case EEMI_FID(PM_REQ_SUSPEND):
79 case EEMI_FID(PM_FORCE_POWERDOWN):
80 case EEMI_FID(PM_ABORT_SUSPEND):
81 case EEMI_FID(PM_REQ_WAKEUP):
82 case EEMI_FID(PM_SET_WAKEUP_SOURCE):
83 /* API for slaves. */
84 case EEMI_FID(PM_REQ_NODE):
85 case EEMI_FID(PM_RELEASE_NODE):
86 case EEMI_FID(PM_SET_REQUIREMENT):
87 case EEMI_FID(PM_SET_MAX_LATENCY):
88 if ( !domain_has_node_access(current->domain, nodeid) )
89 {
90 gprintk(XENLOG_WARNING,
91 "zynqmp-pm: fn=%u No access to node %u\n", pm_fn, nodeid);
92 ret = XST_PM_NO_ACCESS;
93 goto done;
94 }
95 goto forward_to_fw;
96
97 case EEMI_FID(PM_RESET_ASSERT):
98 case EEMI_FID(PM_RESET_GET_STATUS):
99 if ( !domain_has_reset_access(current->domain, nodeid) )
100 {
101 gprintk(XENLOG_WARNING,
102 "zynqmp-pm: fn=%u No access to reset %u\n", pm_fn, nodeid);
103 ret = XST_PM_NO_ACCESS;
104 goto done;
105 }
106 goto forward_to_fw;
107
108 /* These calls are safe and always allowed. */
109 case EEMI_FID(PM_GET_TRUSTZONE_VERSION):
110 case EEMI_FID(PM_GET_API_VERSION):
111 case EEMI_FID(PM_GET_CHIPID):
112 goto forward_to_fw;
113
114 /* No MMIO access is allowed from non-secure domains */
115 case EEMI_FID(PM_MMIO_WRITE):
116 case EEMI_FID(PM_MMIO_READ):
117 gprintk(XENLOG_WARNING,
118 "zynqmp-pm: fn=%u No MMIO access to %u\n", pm_fn, nodeid);
119 ret = XST_PM_NO_ACCESS;
120 goto done;
121
122 /* Exclusive to the hardware domain. */
123 case EEMI_FID(PM_INIT):
124 case EEMI_FID(PM_SET_CONFIGURATION):
125 case EEMI_FID(PM_FPGA_LOAD):
126 case EEMI_FID(PM_FPGA_GET_STATUS):
127 case EEMI_FID(PM_SECURE_SHA):
128 case EEMI_FID(PM_SECURE_RSA):
129 case EEMI_FID(PM_PINCTRL_SET_FUNCTION):
130 case EEMI_FID(PM_PINCTRL_REQUEST):
131 case EEMI_FID(PM_PINCTRL_RELEASE):
132 case EEMI_FID(PM_PINCTRL_GET_FUNCTION):
133 case EEMI_FID(PM_PINCTRL_CONFIG_PARAM_GET):
134 case EEMI_FID(PM_PINCTRL_CONFIG_PARAM_SET):
135 case EEMI_FID(PM_IOCTL):
136 case EEMI_FID(PM_QUERY_DATA):
137 case EEMI_FID(PM_CLOCK_ENABLE):
138 case EEMI_FID(PM_CLOCK_DISABLE):
139 case EEMI_FID(PM_CLOCK_GETSTATE):
140 case EEMI_FID(PM_CLOCK_GETDIVIDER):
141 case EEMI_FID(PM_CLOCK_SETDIVIDER):
142 case EEMI_FID(PM_CLOCK_SETRATE):
143 case EEMI_FID(PM_CLOCK_GETRATE):
144 case EEMI_FID(PM_CLOCK_SETPARENT):
145 case EEMI_FID(PM_CLOCK_GETPARENT):
146 if ( !is_hardware_domain(current->domain) )
147 {
148 gprintk(XENLOG_WARNING, "eemi: fn=%u No access", pm_fn);
149 ret = XST_PM_NO_ACCESS;
150 goto done;
151 }
152 goto forward_to_fw;
153
154 /* These calls are never allowed. */
155 case EEMI_FID(PM_SYSTEM_SHUTDOWN):
156 ret = XST_PM_NO_ACCESS;
157 goto done;
158
159 case IPI_MAILBOX_FID(IPI_MAILBOX_OPEN):
160 case IPI_MAILBOX_FID(IPI_MAILBOX_RELEASE):
161 case IPI_MAILBOX_FID(IPI_MAILBOX_STATUS_ENQUIRY):
162 case IPI_MAILBOX_FID(IPI_MAILBOX_NOTIFY):
163 case IPI_MAILBOX_FID(IPI_MAILBOX_ACK):
164 case IPI_MAILBOX_FID(IPI_MAILBOX_ENABLE_IRQ):
165 case IPI_MAILBOX_FID(IPI_MAILBOX_DISABLE_IRQ):
166 if ( !is_hardware_domain(current->domain) )
167 {
168 gprintk(XENLOG_WARNING, "IPI mailbox: fn=%u No access", pm_fn);
169 ret = XST_PM_NO_ACCESS;
170 goto done;
171 }
172 goto forward_to_fw;
173
174 default:
175 gprintk(XENLOG_WARNING, "zynqmp-pm: Unhandled PM Call: %u\n", fid);
176 return false;
177 }
178
179 forward_to_fw:
180 /*
181 * ZynqMP firmware calls (EEMI) take an argument that specifies the
182 * area of effect of the function called. Specifically, node ids for
183 * power management functions and reset ids for reset functions.
184 *
185 * The code above checks if a virtual machine has access rights over
186 * the node id, reset id, etc. Now that the check has been done, we
187 * can forward the whole command to firmware without additional
188 * parameters checks.
189 */
190 arm_smccc_1_1_smc(get_user_reg(regs, 0),
191 get_user_reg(regs, 1),
192 get_user_reg(regs, 2),
193 get_user_reg(regs, 3),
194 get_user_reg(regs, 4),
195 get_user_reg(regs, 5),
196 get_user_reg(regs, 6),
197 get_user_reg(regs, 7),
198 &res);
199
200 set_user_reg(regs, 0, res.a0);
201 set_user_reg(regs, 1, res.a1);
202 set_user_reg(regs, 2, res.a2);
203 set_user_reg(regs, 3, res.a3);
204 return true;
205
206 done:
207 set_user_reg(regs, 0, ret);
208 return true;
209 }
210
211 /*
212 * Local variables:
213 * mode: C
214 * c-file-style: "BSD"
215 * c-basic-offset: 4
216 * indent-tabs-mode: nil
217 * End:
218 */
219