/*
 * Event channel port operations.
 *
 * Copyright (c) 2003-2006, K A Fraser.
 *
 * This source code is licensed under the GNU General Public License,
 * Version 2 or later.  See the file COPYING for more details.
 */

#include "event_channel.h"

#include <xen/init.h>
#include <xen/lib.h>
#include <xen/errno.h>
#include <xen/sched.h>

#include <asm/guest_atomics.h>

static void cf_check evtchn_2l_set_pending(
    struct vcpu *v, struct evtchn *evtchn)
{
    struct domain *d = v->domain;
    unsigned int port = evtchn->port;

    /*
     * The following bit operations must happen in strict order.
     * NB. On x86, the atomic bit operations also act as memory barriers.
     * There is therefore sufficiently strict ordering for this architecture --
     * others may require explicit memory barriers.
     */

    if ( guest_test_and_set_bit(d, port, &shared_info(d, evtchn_pending)) )
        return;

    if ( !guest_test_bit(d, port, &shared_info(d, evtchn_mask)) &&
         !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d),
                                 &vcpu_info(v, evtchn_pending_sel)) )
    {
        vcpu_mark_events_pending(v);
    }

    evtchn_check_pollers(d, port);
}

static void cf_check evtchn_2l_clear_pending(
    struct domain *d, struct evtchn *evtchn)
{
    guest_clear_bit(d, evtchn->port, &shared_info(d, evtchn_pending));
}

static void cf_check evtchn_2l_unmask(
    struct domain *d, struct evtchn *evtchn)
{
    struct vcpu *v = d->vcpu[evtchn->notify_vcpu_id];
    unsigned int port = evtchn->port;

    /*
     * These operations must happen in strict order. Based on
     * evtchn_2l_set_pending() above.
     */
    if ( guest_test_and_clear_bit(d, port, &shared_info(d, evtchn_mask)) &&
         guest_test_bit(d, port, &shared_info(d, evtchn_pending)) &&
         !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d),
                                 &vcpu_info(v, evtchn_pending_sel)) )
    {
        vcpu_mark_events_pending(v);
    }
}

static bool cf_check evtchn_2l_is_pending(
    const struct domain *d, const struct evtchn *evtchn)
{
    evtchn_port_t port = evtchn->port;
    unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d);

    ASSERT(port < max_ports);
    return (port < max_ports &&
            guest_test_bit(d, port, &shared_info(d, evtchn_pending)));
}

static bool cf_check evtchn_2l_is_masked(
    const struct domain *d, const struct evtchn *evtchn)
{
    evtchn_port_t port = evtchn->port;
    unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d);

    ASSERT(port < max_ports);
    return (port >= max_ports ||
            guest_test_bit(d, port, &shared_info(d, evtchn_mask)));
}

static void cf_check evtchn_2l_print_state(
    struct domain *d, const struct evtchn *evtchn)
{
    struct vcpu *v = d->vcpu[evtchn->notify_vcpu_id];

    printk("%d", !!test_bit(evtchn->port / BITS_PER_EVTCHN_WORD(d),
                            &vcpu_info(v, evtchn_pending_sel)));
}

static const struct evtchn_port_ops evtchn_port_ops_2l =
{
    .set_pending   = evtchn_2l_set_pending,
    .clear_pending = evtchn_2l_clear_pending,
    .unmask        = evtchn_2l_unmask,
    .is_pending    = evtchn_2l_is_pending,
    .is_masked     = evtchn_2l_is_masked,
    .print_state   = evtchn_2l_print_state,
};

void evtchn_2l_init(struct domain *d)
{
    d->evtchn_port_ops = &evtchn_port_ops_2l;
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */
