*/
#include <asm/regs.h>
+#include <xen/sched.h>
+#include <asm/smccc.h>
#include <asm/platforms/xilinx-zynqmp-eemi.h>
+/*
+ * EEMI firmware API:
+ * https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf
+ *
+ * Power domain node_ids identify the area of effect of the power
+ * management operations. They are the first parameter passed to power
+ * management EEMI calls.
+ *
+ * Reset IDs identify the area of effect of a reset operation. They are
+ * the first parameter passed to reset EEMI calls.
+ *
+ * For now, let the hardware domain access to all power domain nodes and
+ * all reset lines. In the future, we'll check for ownership of
+ * resources by specific virtual machines.
+ */
+static inline bool domain_has_node_access(struct domain *d, uint32_t nodeid)
+{
+ return is_hardware_domain(d);
+}
+
+static inline bool domain_has_reset_access(struct domain *d, uint32_t rst)
+{
+ return is_hardware_domain(d);
+}
+
bool zynqmp_eemi(struct cpu_user_regs *regs)
{
- return false;
+ struct arm_smccc_res res;
+ uint32_t fid = get_user_reg(regs, 0);
+ uint32_t nodeid = get_user_reg(regs, 1);
+ unsigned int pm_fn = fid & 0xFFFF;
+ enum pm_ret_status ret;
+
+ switch ( fid )
+ {
+ /* Mandatory SMC32 functions. */
+ case ARM_SMCCC_CALL_COUNT_FID(SIP):
+ case ARM_SMCCC_CALL_UID_FID(SIP):
+ case ARM_SMCCC_REVISION_FID(SIP):
+ goto forward_to_fw;
+ /*
+ * We can't allow CPUs to suspend without Xen knowing about it.
+ * We accept but ignore the request and wait for the guest to issue
+ * a WFI or PSCI call which Xen will trap and act accordingly upon.
+ */
+ case EEMI_FID(PM_SELF_SUSPEND):
+ ret = XST_PM_SUCCESS;
+ goto done;
+
+ case EEMI_FID(PM_GET_NODE_STATUS):
+ /* API for PUs. */
+ case EEMI_FID(PM_REQ_SUSPEND):
+ case EEMI_FID(PM_FORCE_POWERDOWN):
+ case EEMI_FID(PM_ABORT_SUSPEND):
+ case EEMI_FID(PM_REQ_WAKEUP):
+ case EEMI_FID(PM_SET_WAKEUP_SOURCE):
+ /* API for slaves. */
+ case EEMI_FID(PM_REQ_NODE):
+ case EEMI_FID(PM_RELEASE_NODE):
+ case EEMI_FID(PM_SET_REQUIREMENT):
+ case EEMI_FID(PM_SET_MAX_LATENCY):
+ if ( !domain_has_node_access(current->domain, nodeid) )
+ {
+ gprintk(XENLOG_WARNING,
+ "zynqmp-pm: fn=%u No access to node %u\n", pm_fn, nodeid);
+ ret = XST_PM_NO_ACCESS;
+ goto done;
+ }
+ goto forward_to_fw;
+
+ case EEMI_FID(PM_RESET_ASSERT):
+ case EEMI_FID(PM_RESET_GET_STATUS):
+ if ( !domain_has_reset_access(current->domain, nodeid) )
+ {
+ gprintk(XENLOG_WARNING,
+ "zynqmp-pm: fn=%u No access to reset %u\n", pm_fn, nodeid);
+ ret = XST_PM_NO_ACCESS;
+ goto done;
+ }
+ goto forward_to_fw;
+
+ /* These calls are safe and always allowed. */
+ case EEMI_FID(PM_GET_TRUSTZONE_VERSION):
+ case EEMI_FID(PM_GET_API_VERSION):
+ case EEMI_FID(PM_GET_CHIPID):
+ goto forward_to_fw;
+
+ /* No MMIO access is allowed from non-secure domains */
+ case EEMI_FID(PM_MMIO_WRITE):
+ case EEMI_FID(PM_MMIO_READ):
+ gprintk(XENLOG_WARNING,
+ "zynqmp-pm: fn=%u No MMIO access to %u\n", pm_fn, nodeid);
+ ret = XST_PM_NO_ACCESS;
+ goto done;
+
+ /* Exclusive to the hardware domain. */
+ case EEMI_FID(PM_INIT):
+ case EEMI_FID(PM_SET_CONFIGURATION):
+ case EEMI_FID(PM_FPGA_LOAD):
+ case EEMI_FID(PM_FPGA_GET_STATUS):
+ case EEMI_FID(PM_SECURE_SHA):
+ case EEMI_FID(PM_SECURE_RSA):
+ case EEMI_FID(PM_PINCTRL_SET_FUNCTION):
+ case EEMI_FID(PM_PINCTRL_REQUEST):
+ case EEMI_FID(PM_PINCTRL_RELEASE):
+ case EEMI_FID(PM_PINCTRL_GET_FUNCTION):
+ case EEMI_FID(PM_PINCTRL_CONFIG_PARAM_GET):
+ case EEMI_FID(PM_PINCTRL_CONFIG_PARAM_SET):
+ case EEMI_FID(PM_IOCTL):
+ case EEMI_FID(PM_QUERY_DATA):
+ case EEMI_FID(PM_CLOCK_ENABLE):
+ case EEMI_FID(PM_CLOCK_DISABLE):
+ case EEMI_FID(PM_CLOCK_GETSTATE):
+ case EEMI_FID(PM_CLOCK_GETDIVIDER):
+ case EEMI_FID(PM_CLOCK_SETDIVIDER):
+ case EEMI_FID(PM_CLOCK_SETRATE):
+ case EEMI_FID(PM_CLOCK_GETRATE):
+ case EEMI_FID(PM_CLOCK_SETPARENT):
+ case EEMI_FID(PM_CLOCK_GETPARENT):
+ if ( !is_hardware_domain(current->domain) )
+ {
+ gprintk(XENLOG_WARNING, "eemi: fn=%u No access", pm_fn);
+ ret = XST_PM_NO_ACCESS;
+ goto done;
+ }
+ goto forward_to_fw;
+
+ /* These calls are never allowed. */
+ case EEMI_FID(PM_SYSTEM_SHUTDOWN):
+ ret = XST_PM_NO_ACCESS;
+ goto done;
+
+ default:
+ gprintk(XENLOG_WARNING, "zynqmp-pm: Unhandled PM Call: %u\n", fid);
+ return false;
+ }
+
+forward_to_fw:
+ /*
+ * ZynqMP firmware calls (EEMI) take an argument that specifies the
+ * area of effect of the function called. Specifically, node ids for
+ * power management functions and reset ids for reset functions.
+ *
+ * The code above checks if a virtual machine has access rights over
+ * the node id, reset id, etc. Now that the check has been done, we
+ * can forward the whole command to firmware without additional
+ * parameters checks.
+ */
+ arm_smccc_1_1_smc(get_user_reg(regs, 0),
+ get_user_reg(regs, 1),
+ get_user_reg(regs, 2),
+ get_user_reg(regs, 3),
+ get_user_reg(regs, 4),
+ get_user_reg(regs, 5),
+ get_user_reg(regs, 6),
+ get_user_reg(regs, 7),
+ &res);
+
+ set_user_reg(regs, 0, res.a0);
+ set_user_reg(regs, 1, res.a1);
+ set_user_reg(regs, 2, res.a2);
+ set_user_reg(regs, 3, res.a3);
+ return true;
+
+done:
+ set_user_reg(regs, 0, ret);
+ return true;
}
/*