/*
* Copyright 2012 Michael Ellerman, IBM Corporation.
* Copyright 2012 Benjamin Herrenschmidt, IBM Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/kvm_host.h>
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/anon_inodes.h>
#include <asm/uaccess.h>
#include <asm/kvm_book3s.h>
#include <asm/kvm_ppc.h>
#include <asm/hvcall.h>
#include <asm/xics.h>
#include <asm/debug.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "book3s_xics.h"
#if 1
#define XICS_DBG(fmt...) do { } while (0)
#else
#define XICS_DBG(fmt...) trace_printk(fmt)
#endif
#define ENABLE_REALMODE true
#define DEBUG_REALMODE false
/*
* LOCKING
* =======
*
* Each ICS has a mutex protecting the information about the IRQ
* sources and avoiding simultaneous deliveries if the same interrupt.
*
* ICP operations are done via a single compare & swap transaction
* (most ICP state fits in the union kvmppc_icp_state)
*/
/*
* TODO
* ====
*
* - To speed up resends, keep a bitmap of "resend" set bits in the
* ICS
*
* - Speed up server# -> ICP lookup (array ? hash table ?)
*
* - Make ICS lockless as well, or at least a per-interrupt lock or hashed
* locks array to improve scalability
*/
/* -- ICS routines -- */
static void icp_deliver_irq(struct kvmppc_xics *xics, struct kvmppc_icp *icp,
u32 new_irq);
static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level,
bool report_status)
{
struct ics_irq_state *state;
struct kvmppc_ics *ics;
u16 src;
XICS_DBG("ics deliver %#x (level: %d)\n", irq, level);
ics = kvmppc_xics_find_ics(xics, irq, &src);
if (!ics) {
XICS_DBG("ics_deliver_irq: IRQ 0x%06x not found !\n", irq);
return -EINVAL;
}
state = &ics->irq_state[src];
if (!state->exists)
return -EINVAL;
if (report_status)
return state->asserted;
/*
* We set state->asserted locklessly. This should be fine as
* we are the only setter, thus concurrent access is undefined
* to begin with.
*/
if (level == KVM_INTERRUPT_SET_LEVEL)
state->asserted = 1;
else if (level == KVM_INTERRUPT_UNSET) {
state->asserted = 0;
return 0;
}
/* Attempt delivery */
icp_deliver_irq(xics, NULL, irq);
return state->asserted;
}
static void ics_check_resend(struct kvmppc_xics *xics, struct kvmppc_ics *ics,
struct kvmppc_icp *icp)
{
int i;
mutex_lock(&ics->lock);
for (i = 0; i < KVMPPC_XICS_IRQ_PER_ICS; i++) {
struct ics_irq_state *state = &ics->irq_state[i];
if (!state->resend)
continue;
XICS_DBG("resend %#x prio %#x\n", state->number,
state->priority);
mutex_unlock(&ics->lock);
icp_deliver_irq(xics, icp, state->n