1/**
2 * \file
3 * \brief Arch-specific inter-monitor communication
4 */
5
6/*
7 * Copyright (c) 2009, 2010, 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 <inttypes.h>
16#include "monitor.h"
17#include <trace/trace.h>
18
19struct bind_monitor_reply_state {
20    struct intermon_msg_queue_elem elem;
21    struct intermon_binding *orig_binding;
22    errval_t err;
23};
24
25static void send_bind_monitor_reply(struct intermon_binding *b, errval_t err);
26
27static void send_bind_monitor_reply_cont(struct intermon_binding *b,
28                                         struct intermon_msg_queue_elem *e)
29{
30    struct bind_monitor_reply_state *st = (struct bind_monitor_reply_state *)e;
31    send_bind_monitor_reply(st->orig_binding, st->err);
32    free(st);
33}
34
35static void send_bind_monitor_reply(struct intermon_binding *b, errval_t err)
36{
37    errval_t err2 = b->tx_vtbl.bind_monitor_reply(b, NOP_CONT, err);
38    if (err_is_fail(err2)) {
39        if (err_no(err2) == FLOUNDER_ERR_TX_BUSY) {
40            struct intermon_state *is = b->st;
41            struct bind_monitor_reply_state *st = malloc(sizeof(*st));
42            assert(st != NULL);
43
44            st->orig_binding = b;
45            st->elem.cont = send_bind_monitor_reply_cont;
46            st->err = err;
47
48            err2 = intermon_enqueue_send(b, &is->queue,
49                                         get_default_waitset(), &st->elem.queue);
50            assert(err_is_ok(err2));
51
52        } else {
53            DEBUG_ERR(err2, "reply failed");
54        }
55    }
56}
57
58/**
59 * \brief A monitor receives request to setup a connection
60 * with another newly booted monitor from a third monitor
61 */
62static void bind_monitor_request(struct intermon_binding *b,
63                                 coreid_t core_id,
64                                 intermon_caprep_t caprep)
65{
66    errval_t err;
67
68    /* Create the cap */
69    struct capability cap_raw;
70    caprep_to_capability(&caprep, &cap_raw);
71    if (cap_raw.type != ObjType_Frame) {
72        err = MON_ERR_WRONG_CAP_TYPE;
73        goto error;
74    }
75
76    struct capref frame;
77    err = slot_alloc(&frame);
78    if (err_is_fail(err)) {
79        goto error;
80    }
81
82    err = monitor_cap_create(frame, &cap_raw, core_id);
83    if (err_is_fail(err)) {
84        goto error;
85    }
86
87    /* Setup the connection */
88    void *buf;
89    err = vspace_map_one_frame(&buf, MON_URPC_SIZE, frame, NULL, NULL);
90    if (err_is_fail(err)) {
91        err = err_push(err, LIB_ERR_VSPACE_MAP);
92        goto error;
93    }
94
95    // setup our side of the binding
96    struct intermon_ump_binding *umpb;
97    umpb = malloc(sizeof(struct intermon_ump_binding));
98    assert(umpb != NULL);
99
100    err = intermon_ump_init(umpb, get_default_waitset(),
101                            (char *)buf + MON_URPC_CHANNEL_LEN,
102                            MON_URPC_CHANNEL_LEN,
103                            buf, MON_URPC_CHANNEL_LEN);
104    assert(err_is_ok(err));
105
106    // Identify UMP frame for tracing
107    umpb->ump_state.chan.sendid = (uintptr_t)cap_raw.u.frame.base;
108    umpb->ump_state.chan.recvid =
109        (uintptr_t)(cap_raw.u.frame.base + MON_URPC_CHANNEL_LEN);
110
111    // connect it to our request handlers
112    err = intermon_init(&umpb->b, core_id);
113    assert(err_is_ok(err));
114
115    /* Send reply */
116reply:
117    send_bind_monitor_reply(b, err);
118    return;
119
120error:
121    // FIXME: cleanup!
122    goto reply;
123}
124/**
125 * \brief The monitor that proxied the request for one monitor to
126 * setup a connection with another monitor gets the reply
127 */
128static void bind_monitor_reply(struct intermon_binding *closure,
129                               errval_t err)
130{
131    if (err_is_fail(err)) {
132        DEBUG_ERR(err, "Got error in bind monitor reply");
133    }
134    seen_connections++;
135}
136/**
137 * \brief A monitor asks this monitor to proxy
138 * its request to bind to another monitor
139 */
140
141/* ---------------------- BIND_MONITOR_PROXY CODE START --------------------- */
142struct bind_monitor_proxy_state {
143    struct intermon_msg_queue_elem elem;
144    struct intermon_binding *orig_binding;
145    coreid_t dst_core_id;
146    intermon_caprep_t caprep;
147};
148
149static void bind_monitor_proxy(struct intermon_binding *b,
150                               coreid_t dst_core_id,
151                               intermon_caprep_t caprep);
152
153static void bind_monitor_proxy_cont(struct intermon_binding *b,
154                                    struct intermon_msg_queue_elem *e)
155{
156    struct bind_monitor_proxy_state *st = (struct bind_monitor_proxy_state*)e;
157    bind_monitor_proxy(st->orig_binding, st->dst_core_id, st->caprep);
158    free(st);
159}
160/**
161 * \brief A monitor asks this monitor to proxy
162 * its request to bind to another monitor
163 */
164static void bind_monitor_proxy(struct intermon_binding *b,
165                               coreid_t dst_core_id,
166                               intermon_caprep_t caprep)
167{
168    errval_t err;
169
170    /* Get source monitor's core id */
171    coreid_t src_core_id = ((struct intermon_state *)b->st)->core_id;
172
173    /* Get destination monitor */
174    struct intermon_binding *dst_binding = NULL;
175    err = intermon_binding_get(dst_core_id, &dst_binding);
176    if (err_is_fail(err)) {
177        DEBUG_ERR(err, "intermon_binding_get failed");
178    }
179
180    // Proxy the request
181    err = dst_binding->tx_vtbl.
182        bind_monitor_request(dst_binding, NOP_CONT, src_core_id,
183                             caprep);
184    if (err_is_fail(err)) {
185        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
186            struct intermon_state *is = dst_binding->st;
187            struct bind_monitor_proxy_state *st =
188                malloc(sizeof(struct bind_monitor_proxy_state));
189            assert(st);
190
191            st->orig_binding = b;
192            st->elem.cont = bind_monitor_proxy_cont;
193            st->dst_core_id = dst_core_id;
194            st->caprep = caprep;
195
196            err = intermon_enqueue_send(dst_binding, &is->queue,
197                                        get_default_waitset(), &st->elem.queue);
198            assert(err_is_ok(err));
199
200        } else {
201            DEBUG_ERR(err, "forwarding bind request failed");
202        }
203    }
204}
205
206/* ---------------------- BIND_MONITOR_PROXY CODE END ----------------------- */
207
208/**
209 * \brief Notification of a newly booted monitor.
210 *  Setup our connection and request the sender to proxy
211 *  the bind request to the monitor
212 */
213static void new_monitor_notify(struct intermon_binding *st,
214                               coreid_t core_id)
215{
216    errval_t err;
217
218    /* Setup the connection */
219    struct capref frame;
220    err = frame_alloc(&frame, MON_URPC_SIZE, NULL);
221    if (err_is_fail(err)) {
222        DEBUG_ERR(err, "frame_alloc failed");
223        return; // FIXME: cleanup
224    }
225
226    void *buf;
227    err = vspace_map_one_frame(&buf, MON_URPC_SIZE, frame, NULL, NULL);
228    if (err_is_fail(err)) {
229        DEBUG_ERR(err, "vspace_map_one_frame failed");
230        assert(buf); // XXX
231    }
232
233    // init our end of the binding and channel
234    struct intermon_ump_binding *ump_binding = malloc(sizeof(struct intermon_ump_binding));
235    assert(ump_binding != NULL);
236    err = intermon_ump_init(ump_binding, get_default_waitset(),
237                            buf, MON_URPC_CHANNEL_LEN,
238                            (char *)buf + MON_URPC_CHANNEL_LEN,
239                            MON_URPC_CHANNEL_LEN);
240    assert(err_is_ok(err));
241    /* if (err_is_fail(err)) { */
242    /*     cap_destroy(frame); */
243    /*     return err_push(err, LIB_ERR_UMP_CHAN_BIND); */
244    /* } */
245
246    // Identify UMP frame for tracing
247    struct frame_identity umpid = { .base = 0, .bytes = 0 };
248    err = invoke_frame_identify(frame, &umpid);
249    assert(err_is_ok(err));
250    ump_binding->ump_state.chan.recvid = (uintptr_t)umpid.base;
251    ump_binding->ump_state.chan.sendid =
252        (uintptr_t)(umpid.base + MON_URPC_CHANNEL_LEN);
253
254    err = intermon_init(&ump_binding->b, core_id);
255    assert(err_is_ok(err));
256
257    /* Identify the frame cap */
258    struct capability frame_cap;
259    err = monitor_cap_identify(frame, &frame_cap);
260    if (err_is_fail(err)) {
261        DEBUG_ERR(err, "monitor_cap_identify failed");
262        return; // FIXME: cleanup
263    }
264
265    intermon_caprep_t caprep;
266    capability_to_caprep(&frame_cap, &caprep);
267
268    /* reply to the sending monitor to proxy request */
269    err = st->tx_vtbl.bind_monitor_proxy(st, NOP_CONT, core_id, caprep);
270    if (err_is_fail(err)) {
271        DEBUG_ERR(err, "bind proxy request failed");
272    }
273}
274errval_t arch_intermon_init(struct intermon_binding *b)
275{
276    b->rx_vtbl.bind_monitor_request = bind_monitor_request;
277    b->rx_vtbl.bind_monitor_reply = bind_monitor_reply;
278    b->rx_vtbl.bind_monitor_proxy = bind_monitor_proxy;
279    b->rx_vtbl.new_monitor_notify = new_monitor_notify;
280
281    return SYS_ERR_OK;
282}
283