/* * Copyright (c) 2012, ARM Limited. All rights reserved. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of ARM nor the names of its * contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. */ #include "ipi.h" #include "misc.h" #include "virt_helpers.h" #include "bakery.h" #include "hyp_vmmap.h" #include "gic_registers.h" extern void gic_send_ipi(unsigned, unsigned); /* * Set of flags used by the interrupt handling code * to distinguish between IPIs sent by the big-little * code and the payload software. * TODO: Assumes only one cpu will send an IPI at a * time rather than multiple cpus sending the same * IPI to each other at the same time from within the * HYP mode. */ static unsigned lock_ipi_check; static unsigned hyp_ipi_check[16]; /* * Returns the id of the first IPI that is not pending on * our cpu interface or the first IPI that is pending but * was not generated by us. Returns 16 if no such IPI is * found */ static unsigned get_free_ipi(void) { unsigned shift, cpu_if_bit, cpu_id = read_cpuid(), cluster_id = read_clusterid(); int ctr; cpu_if_bit = 1 << get_cpuif(cluster_id, cpu_id); /* Find the register offset */ for (ctr = 3; ctr >= 0; ctr--) /* Check whether IPI has already been generated by us */ for (shift = 0; shift < 4; shift++) { if (read32 (GIC_ID_PHY_BASE + GICD_SPENDSGIR + (ctr << 2)) & (cpu_if_bit << (shift << 3))) continue; return (ctr << 2) + shift; } return MAX_IPI; } /* * Given a mask of destination cpus and what the IPI is supposed * todo, select an unused IPI and send it. */ unsigned send_hyp_ipi(unsigned cpuif_mask, unsigned type) { unsigned rc = TRUE; unsigned ipi_no = 0; /* * First choose a non-pending IPI to avoid a clash with the OS. */ ipi_no = get_free_ipi(); if (ipi_no == MAX_IPI) { rc = FALSE; return rc; } /* * For this IPI set the mask in our global variable. We do it, payload software * does not. But, first check whether any earlier IPIs have already been acked */ while (TRUE) { spin_lock(&lock_ipi_check); if (hyp_ipi_check[ipi_no] & 0xff) { spin_unlock(&lock_ipi_check); } else { hyp_ipi_check[ipi_no] = (type << 8) | cpuif_mask; dsb(); spin_unlock(&lock_ipi_check); break; } }; /* Send the IPI to the cpu_mask */ gic_send_ipi(cpuif_mask, ipi_no); return rc; } /* * Given a received IPI, check whether it is a HYP IPI and return * the type if so else 0 */ unsigned get_hyp_ipi(unsigned cpu_if, unsigned ipi_no) { unsigned type = 0; spin_lock(&lock_ipi_check); /* * If this IPI was sent by the big-little code then our cpu_if bit must have * been set in the ipi_check flag. Reset the bit and indicate that its an * internal IPI. */ if (hyp_ipi_check[ipi_no] & (1 << cpu_if)) { type = hyp_ipi_check[ipi_no] >> 8; hyp_ipi_check[ipi_no] &= ~(1 << cpu_if); dsb(); } spin_unlock(&lock_ipi_check); return type; }