mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
xen/arm: zynqmp: implement zynqmp_eemi
zynqmp_eemi uses the defined functions and structs to decide whether to make a call to the firmware, or to simply return a predefined value. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Signed-off-by: Stefano Stabellini <stefanos@xilinx.com>
This commit is contained in:
1 parent
1417b6a8ad
commit
0dd0734a44
1 file changed
+167
-1
@@ -17,11 +17,177 @@
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||