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