#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <uvm/uvm_extern.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/hypervisor.h>
#include <machine/pte.h>
#include <machine/sparc64.h>
#define SPARC64_IPI_RETRIES 10000
void sun4u_send_ipi(int, void (*)(void), u_int64_t, u_int64_t);
void sun4u_broadcast_ipi(void (*)(void), u_int64_t, u_int64_t);
void sun4v_send_ipi(int, void (*)(void), u_int64_t, u_int64_t);
void sun4v_broadcast_ipi(void (*)(void), u_int64_t, u_int64_t);
void sun4u_ipi_tlb_page_demap(void);
void sun4u_ipi_tlb_context_demap(void);
void sun4v_ipi_tlb_page_demap(void);
void sun4v_ipi_tlb_context_demap(void);
void ipi_softint(void);
void
sparc64_send_ipi(int itid, void (*func)(void), u_int64_t arg0, u_int64_t arg1)
{
if (CPU_ISSUN4V)
sun4v_send_ipi(itid, func, arg0, arg1);
else
sun4u_send_ipi(itid, func, arg0, arg1);
}
void
sun4u_send_ipi(int itid, void (*func)(void), u_int64_t arg0, u_int64_t arg1)
{
int i, j, shift = 0;
KASSERT((u_int64_t)func > MAXINTNUM);
if (((getver() & VER_IMPL) >> VER_IMPL_SHIFT) == IMPL_JALAPENO)
shift = (itid & 0x3) * 2;
if (ldxa(0, ASR_IDSR) & (IDSR_BUSY << shift)) {
__asm volatile("ta 1; nop");
}
for (i = 0; i < SPARC64_IPI_RETRIES; i++) {
u_int64_t s = intr_disable();
stxa(IDDR_0H, ASI_INTERRUPT_DISPATCH, (u_int64_t)func);
stxa(IDDR_1H, ASI_INTERRUPT_DISPATCH, arg0);
stxa(IDDR_2H, ASI_INTERRUPT_DISPATCH, arg1);
stxa(IDCR(itid), ASI_INTERRUPT_DISPATCH, 0);
membar_sync();
for (j = 0; j < 1000000; j++) {
if (ldxa(0, ASR_IDSR) & (IDSR_BUSY << shift))
continue;
else
break;
}
intr_restore(s);
if (j == 1000000)
break;
if ((ldxa(0, ASR_IDSR) & (IDSR_NACK << shift)) == 0)
return;
}
#if 1
if (db_active || panicstr != NULL)
printf("ipi_send: couldn't send ipi to module %u\n", itid);
else
panic("ipi_send: couldn't send ipi");
#else
__asm volatile("ta 1; nop" : :);
#endif
}
void
sun4v_send_ipi(int itid, void (*func)(void), u_int64_t arg0, u_int64_t arg1)
{
struct cpu_info *ci = curcpu();
int err, i, s;
s = splhigh();
stha(ci->ci_cpuset, ASI_PHYS_CACHED, itid);
stxa(ci->ci_mondo, ASI_PHYS_CACHED, (vaddr_t)func);
stxa(ci->ci_mondo + 8, ASI_PHYS_CACHED, arg0);
stxa(ci->ci_mondo + 16, ASI_PHYS_CACHED, arg1);
for (i = 0; i < SPARC64_IPI_RETRIES; i++) {
err = hv_cpu_mondo_send(1, ci->ci_cpuset, ci->ci_mondo);
if (err != H_EWOULDBLOCK)
break;
delay(10);
}
splx(s);
if (err != H_EOK)
panic("Unable to send mondo %llx to cpu %d: %d",
(u_int64_t)func, itid, err);
}
void
sparc64_broadcast_ipi(void (*func)(void), u_int64_t arg0, u_int64_t arg1)
{
if (CPU_ISSUN4V)
sun4v_broadcast_ipi(func, arg0, arg1);
else
sun4u_broadcast_ipi(func, arg0, arg1);
}
void
sun4u_broadcast_ipi(void (*func)(void), u_int64_t arg0, u_int64_t arg1)
{
struct cpu_info *ci;
for (ci = cpus; ci != NULL; ci = ci->ci_next) {
if (ci->ci_cpuid == cpu_number())
continue;
if ((ci->ci_flags & CPUF_RUNNING) == 0)
continue;
sun4u_send_ipi(ci->ci_itid, func, arg0, arg1);
}
}
void
sun4v_broadcast_ipi(void (*func)(void), u_int64_t arg0, u_int64_t arg1)
{
struct cpu_info *ci = curcpu();
paddr_t cpuset = ci->ci_cpuset;
int err, i, s, ncpus = 0;
s = splhigh();
for (ci = cpus; ci != NULL; ci = ci->ci_next) {
if (ci->ci_cpuid == cpu_number())
continue;
if ((ci->ci_flags & CPUF_RUNNING) == 0)
continue;
stha(cpuset, ASI_PHYS_CACHED, ci->ci_itid);
cpuset += sizeof(int16_t);
ncpus++;
}
if (ncpus == 0) {
splx(s);
return;
}
ci = curcpu();
stxa(ci->ci_mondo, ASI_PHYS_CACHED, (vaddr_t)func);
stxa(ci->ci_mondo + 8, ASI_PHYS_CACHED, arg0);
stxa(ci->ci_mondo + 16, ASI_PHYS_CACHED, arg1);
for (i = 0; i < SPARC64_IPI_RETRIES; i++) {
err = hv_cpu_mondo_send(ncpus, ci->ci_cpuset, ci->ci_mondo);
if (err != H_EWOULDBLOCK)
break;
delay(10);
}
splx(s);
if (err != H_EOK)
panic("Unable to broadcast mondo %llx: %d",
(u_int64_t)func, err);
}
void
smp_tlb_flush_pte(vaddr_t va, uint64_t ctx)
{
(*sp_tlb_flush_pte)(va, ctx);
if (db_active)
return;
if (CPU_ISSUN4V)
sun4v_broadcast_ipi(sun4v_ipi_tlb_page_demap, va, ctx);
else
sun4u_broadcast_ipi(sun4u_ipi_tlb_page_demap, va, ctx);
}
void
smp_tlb_flush_ctx(uint64_t ctx)
{
(*sp_tlb_flush_ctx)(ctx);
if (db_active)
return;
if (CPU_ISSUN4V)
sun4v_broadcast_ipi(sun4v_ipi_tlb_context_demap, ctx, 0);
else
sun4u_broadcast_ipi(sun4u_ipi_tlb_context_demap, ctx, 0);
}
void
cpu_unidle(struct cpu_info *ci)
{
if (ci == curcpu() || db_active || ((ci->ci_flags & CPUF_RUNNING) == 0))
return;
if (CPU_ISSUN4V)
sun4v_send_ipi(ci->ci_itid, ipi_softint, 1 << IPL_SOFTINT, 0);
else
sun4u_send_ipi(ci->ci_itid, ipi_softint, 1 << IPL_SOFTINT, 0);
}