1/**
2 * \file
3 * \brief Bidirectional IPI (inter-processor interrupt) signaling implementation
4 */
5
6/*
7 * Copyright (c) 2009, 2010, 2011, 2012, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <barrelfish/barrelfish.h>
16#include <arch/x86/barrelfish/ipi_notify.h>
17#include <if/monitor_defs.h>
18
19static void ipi_alloc_notify_reply(struct monitor_binding *b, uintptr_t st,
20                                   struct capref notify_cap, errval_t err)
21{
22    struct ipi_notify *uc = (void *)st;
23    uc->my_notify_cap = notify_cap;
24    assert(uc->cont.handler != NULL);
25    uc->cont.handler(uc->cont.st, err, uc);
26}
27
28errval_t ipi_notify_init(struct ipi_notify *rn, struct capref rmt_notify_cap,
29                         struct capref my_notify_cap, struct capref ep,
30                         struct lmp_endpoint *iep)
31{
32    rn->rmt_notify_cap = rmt_notify_cap;
33    rn->my_notify_cap = my_notify_cap;
34    rn->ep = ep;
35    rn->iep = iep;
36    return SYS_ERR_OK;
37}
38
39/**
40 * \brief Set remote notify cap.
41 *
42 * \param notify        Cap to notify the remote side.
43 */
44errval_t ipi_notify_set(struct ipi_notify *rn, struct capref notify)
45{
46    rn->rmt_notify_cap = notify;
47    return SYS_ERR_OK;
48}
49
50static void ipi_alloc_notify_try_request(void* arg) {
51    struct ipi_notify* uc = arg;
52    struct monitor_binding* b = get_monitor_binding();
53
54    errval_t err = b->tx_vtbl.ipi_alloc_notify_request(b, NOP_CONT, uc->ep, (uintptr_t)uc);
55
56    if (err_is_fail(err)) {
57        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
58            b->register_send(b, b->waitset, MKCONT(ipi_alloc_notify_try_request, uc));
59        } else {
60            USER_PANIC_ERR(err, "ipi_notify_alloc fail");
61        }
62    }
63}
64
65/**
66 * \brief Initialise a new IPI notify channel
67 *
68 * \param uc Storage for channel state
69 */
70errval_t ipi_notify_alloc(struct ipi_notify *uc,
71                          struct ipi_alloc_continuation cont)
72{
73    // Allocate receive endpoint
74    // use minimum-sized endpoint buffer, as we don't care about its contents
75    errval_t err = endpoint_create(LMP_RECV_LENGTH, &uc->ep, &uc->iep);
76    assert(err_is_ok(err));
77
78    // Initialize the rest
79    uc->cont = cont;
80
81    ipi_alloc_notify_try_request(uc);
82
83    return SYS_ERR_OK;
84}
85
86static void ipi_notify_handler(void *arg)
87{
88    struct ipi_notify *uc = arg;
89    struct lmp_recv_buf dummy = { .buflen = 0 };
90
91    // Consume the endpoint message
92    errval_t err = lmp_endpoint_recv(uc->iep, &dummy, NULL);
93    assert(err_is_ok(err));
94
95    // Call user's closure
96    uc->closure.handler(uc->closure.arg);
97}
98
99/**
100 * \brief Register an event handler to be notified when messages can be received
101 *
102 * In the future, call the closure on the given waitset when it is likely that
103 * a message can be received on the channel. A channel may only be registered
104 * with a single receive event handler on a single waitset at any one time.
105 *
106 * \param uc IPI channel
107 * \param ws Waitset
108 * \param closure Event handler
109 */
110errval_t ipi_notify_register(struct ipi_notify *uc,
111                             struct waitset *ws,
112                             struct event_closure closure)
113{
114    struct event_closure cl = {
115        .handler = ipi_notify_handler,
116        .arg = uc
117    };
118
119    uc->closure = closure;
120    assert(uc->iep != NULL);
121    return lmp_endpoint_register(uc->iep, ws, cl);
122}
123
124/// Destroy the local state associated with a given channel
125void ipi_notify_destroy(struct ipi_notify *uc)
126{
127    lmp_endpoint_free(uc->iep);
128    cap_destroy(uc->ep);
129    cap_destroy(uc->my_notify_cap);
130    cap_destroy(uc->rmt_notify_cap);
131}
132
133/// Initialise the IPI channel driver
134void ipi_init(void)
135{
136    struct monitor_binding *mcb = get_monitor_binding();
137
138    mcb->rx_vtbl.ipi_alloc_notify_reply = ipi_alloc_notify_reply;
139}
140