1/*
2 * Copyright (c) 2012 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <barrelfish/barrelfish.h>
11#include <barrelfish/caddr.h>
12#include <if/intermon_defs.h>
13#include <monitor.h>
14#include <monitor_invocations.h>
15#include <dom_invocations.h>
16#include "capops.h"
17#include "capsend.h"
18#include "caplock.h"
19#include "internal.h"
20
21/*
22 * Retype states
23 */
24
25struct retype_check_st {
26    enum objtype type;
27    size_t objsize;
28    size_t count;
29    size_t offset;
30    struct domcapref src;
31    struct result_closure cont;
32};
33
34struct retype_output_st {
35    struct domcapref destcn;
36    cslot_t start_slot;
37    struct result_closure cont;
38};
39
40struct requested_retype_st {
41    struct intermon_msg_queue_elem queue_elem;
42    struct retype_check_st check;
43    struct capref src;
44    coreid_t from;
45    errval_t status;
46    genvaddr_t request_st;
47};
48
49struct local_retype_st {
50    struct retype_check_st check;
51    struct retype_output_st output;
52};
53
54struct retype_request_st {
55    struct intermon_msg_queue_elem queue_elem;
56    intermon_caprep_t caprep;
57    struct retype_check_st check;
58    struct retype_output_st output;
59};
60
61static uint64_t retype_seqnum = 0;
62
63/*
64 * Prototypes for static functions so ordering does not matter
65 */
66
67static void check_retype__enq(struct retype_check_st *check_st);
68static void retype_check__rx(errval_t status, struct retype_check_st* check,
69                             struct retype_output_st *output, void *to_free);
70
71/**
72 * \brief Intermon is ready to send retype result
73 */
74static void
75retype_result__send(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
76{
77    TRACE(CAPOPS, RETYPE_RESULT_SEND, 0);
78    DEBUG_CAPOPS("%s\n", __FUNCTION__);
79    errval_t err;
80    struct requested_retype_st *req_st = (struct requested_retype_st*)e;
81    err = intermon_capops_retype_response__tx(b, NOP_CONT, req_st->status,
82                                              req_st->request_st);
83
84    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
85        DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
86        struct intermon_state *inter_st = (struct intermon_state *)b->st;
87        // requeue send request at front and return
88        err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
89                                             (struct msg_queue_elem *)e);
90        GOTO_IF_ERR(err, handle_err);
91        return;
92    }
93
94handle_err:
95    PANIC_IF_ERR(err, "sending retype result message");
96    free(req_st);
97}
98
99/**
100 * \brief Enqueue retype result
101 */
102static void
103retype_result__enq(struct requested_retype_st *req_st)
104{
105    TRACE(CAPOPS, RETYPE_RESULT_ENQ, 0);
106    DEBUG_CAPOPS("%s\n", __FUNCTION__);
107    req_st->queue_elem.cont = retype_result__send;
108    errval_t err = capsend_target(req_st->from, (struct msg_queue_elem*)req_st);
109    if (err_is_fail(err)) {
110        DEBUG_ERR(err, "failed to enqueue retype result");
111        free(req_st);
112    }
113}
114
115/**
116 * \brief Retype temporary cap has been deleted
117 */
118static void
119retype_tmpcap_delete__cont(errval_t status, void *st)
120{
121    TRACE(CAPOPS, RETYPE_DEL_TMPCAP_DONE, 0);
122    DEBUG_CAPOPS("%s\n", __FUNCTION__);
123    errval_t err;
124    struct requested_retype_st *req_st = (struct requested_retype_st*)st;
125
126    if (err_is_fail(status) && err_no(status) != SYS_ERR_CAP_NOT_FOUND) {
127        DEBUG_ERR(status, "deleting tmp retype cap, cap will leak");
128    }
129
130    err = slot_free(req_st->src);
131    DEBUG_IF_ERR(err, "freeing tmp retype slot, slot will leak");
132    req_st->src = NULL_CAP;
133    memset(&req_st->check.src, 0, sizeof(struct domcapref));
134
135    retype_result__enq(req_st);
136}
137
138/**
139 * \brief The check for a retype request has completed
140 */
141static void
142retype_request_check__rx(errval_t status, void *st)
143{
144    DEBUG_CAPOPS("%s\n", __FUNCTION__);
145    struct requested_retype_st *req_st = (struct requested_retype_st*)st;
146
147    if (err_is_ok(status)) {
148        status = monitor_remote_relations(req_st->src, RRELS_DESC_BIT,
149                                          RRELS_DESC_BIT, NULL);
150    }
151
152    req_st->status = status;
153
154    if (!capref_is_null(req_st->src)) {
155        DEBUG_CAPOPS("capops_retype: cleaning up our copy of src\n");
156        TRACE(CAPOPS, RETYPE_DEL_TMPCAP, 0);
157        capops_delete(req_st->check.src, retype_tmpcap_delete__cont, req_st);
158    }
159    else {
160        retype_result__enq(req_st);
161    }
162}
163
164void
165retype_request__rx(struct intermon_binding *b, intermon_caprep_t srcrep, uint64_t offset,
166                   uint32_t desttype, uint64_t destsize, uint64_t count, genvaddr_t st)
167{
168    TRACE(CAPOPS, RETYPE_REQUEST_RX, 0);
169    DEBUG_CAPOPS("%s\n", __FUNCTION__);
170    errval_t err;
171
172    // allocate and setup state
173    struct requested_retype_st *req_st;
174    err = calloce(1, sizeof(*req_st), &req_st);
175    PANIC_IF_ERR(err, "allocating retype request state");
176
177    req_st->queue_elem.cont = retype_result__send;
178    req_st->check.type = desttype;
179    req_st->check.objsize = destsize;
180    req_st->check.count = count;
181    req_st->check.offset = offset;
182    req_st->check.cont = MKRESCONT(retype_request_check__rx, req_st);
183    req_st->from = ((struct intermon_state*)b->st)->core_id;
184    req_st->request_st = st;
185
186    // get slot and cap
187    err = slot_alloc(&req_st->src);
188    GOTO_IF_ERR(err, cont_err);
189    req_st->check.src = get_cap_domref(req_st->src);
190
191    struct capability cap;
192    caprep_to_capability(&srcrep, &cap);
193    err = monitor_copy_if_exists(&cap, req_st->src);
194    GOTO_IF_ERR(err, cont_err);
195
196    // validate cap state
197    distcap_state_t state;
198    err = dom_cnode_get_state(req_st->check.src, &state);
199    GOTO_IF_ERR(err, cont_err);
200
201    if (distcap_state_is_foreign(state)) {
202        err = MON_ERR_CAP_FOREIGN;
203        goto cont_err;
204    }
205    if (distcap_state_is_busy(state)) {
206        err = MON_ERR_REMOTE_CAP_RETRY;
207        goto cont_err;
208    }
209
210    // check retypeability on self (owner)
211    err = monitor_is_retypeable(&cap, req_st->check.offset,
212                                req_st->check.objsize, req_st->check.count);
213    // If this returns an error, including SYS_ERR_REVOKE_FIRST, we do not
214    // have to check retypeability on the other cores.
215    if (err_is_fail(err)) {
216        goto cont_err;
217    }
218
219    // initiate check on other cores
220    check_retype__enq(&req_st->check);
221
222    return;
223
224cont_err:
225    retype_request_check__rx(err, req_st);
226}
227
228static void
229retype_result__rx(errval_t status, struct retype_request_st *req_st)
230{
231    TRACE(CAPOPS, RETYPE_RESULT_RX, 0);
232    DEBUG_CAPOPS("%s\n", __FUNCTION__);
233    retype_check__rx(status, &req_st->check, &req_st->output, req_st);
234}
235
236/**
237 * \brief Handle the response to a retype request
238 */
239void
240retype_response__rx(struct intermon_binding *b, errval_t status, genvaddr_t st)
241{
242    TRACE(CAPOPS, RETYPE_RESPONSE_RX, 0);
243    DEBUG_CAPOPS("%s\n", __FUNCTION__);
244    struct retype_request_st *req_st = (struct retype_request_st*)(lvaddr_t)st;
245    retype_result__rx(status, req_st);
246}
247
248/**
249 * \brief Intermon is ready to send request_retype
250 */
251static void
252retype_request__send(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
253{
254    TRACE(CAPOPS, RETYPE_REQUEST_SEND, 0);
255    DEBUG_CAPOPS("%s\n", __FUNCTION__);
256    struct retype_request_st *req_st = (struct retype_request_st*)e;
257    errval_t err;
258
259    err = intermon_capops_request_retype__tx(b, NOP_CONT, req_st->caprep,
260                                             req_st->check.offset,
261                                             req_st->check.type,
262                                             req_st->check.objsize,
263                                             req_st->check.count,
264                                             (lvaddr_t)req_st);
265
266
267    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
268        DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
269        struct intermon_state *inter_st = (struct intermon_state *)b->st;
270        // requeue send request at front and return
271        err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
272                                             (struct msg_queue_elem *)e);
273        GOTO_IF_ERR(err, handle_err);
274        return;
275    }
276
277handle_err:
278    if (err_is_fail(err)) {
279        retype_result__rx(err, req_st);
280    }
281}
282
283/**
284 * \brief Enqueue a retype request
285 */
286static void
287retype_request__enq(struct retype_request_st *req_st)
288{
289    TRACE(CAPOPS, RETYPE_REQUEST_ENQ, 0);
290    DEBUG_CAPOPS("%s\n", __FUNCTION__);
291    errval_t err;
292    struct capability cap;
293    err = monitor_domains_cap_identify(req_st->check.src.croot,
294                                       req_st->check.src.cptr,
295                                       req_st->check.src.level, &cap);
296    GOTO_IF_ERR(err, err_cont);
297
298    req_st->queue_elem.cont = retype_request__send;
299    capability_to_caprep(&cap, &req_st->caprep);
300
301    err = capsend_owner(req_st->check.src, (struct msg_queue_elem*)req_st);
302    GOTO_IF_ERR(err, err_cont);
303
304    return;
305
306err_cont:
307    retype_result__rx(err, req_st);
308}
309
310/**
311 * \brief The descendants search has completed
312 */
313static void
314check_retypeable__rx(errval_t status, void *st)
315{
316    TRACE(CAPOPS, RETYPE_CHECK_RETYPEABLE_RX, 0);
317    DEBUG_CAPOPS("%s: status=%s\n", __FUNCTION__, err_getcode(status));
318    struct retype_check_st *check_st = (struct retype_check_st*)st;
319
320    // need to translate error codes:
321    // - not found -> ok
322    // - otherwise -> unchanged
323    if (err_no(status) == SYS_ERR_CAP_NOT_FOUND) {
324        status = err_push(status, SYS_ERR_OK);
325    }
326
327    // unlock cap and procede with check result continuation
328    caplock_unlock(check_st->src);
329    CALLRESCONT(check_st->cont, status);
330}
331
332/**
333 * \brief Enqueue a retype check
334 */
335static void
336check_retype__enq(struct retype_check_st *check_st)
337{
338    TRACE(CAPOPS, RETYPE_CHECK_ENQ, 0);
339    DEBUG_CAPOPS("%s\n", __FUNCTION__);
340    errval_t err;
341
342    if (check_st->type == ObjType_EndPointLMP) {
343        DEBUG_CAPOPS("%s: type = EndPoint\n", __FUNCTION__);
344        // XXX: because of the current "multi-retype" hack for endpoints, a
345        // dispatcher->endpoint retype can happen irrespective of the existence
346        // of descendents on any core.
347        err = monitor_domcap_remote_relations(check_st->src.croot,
348                                              check_st->src.cptr,
349                                              check_st->src.level,
350                                              RRELS_DESC_BIT,
351                                              RRELS_DESC_BIT, NULL);
352        goto cont_err;
353    }
354
355    DEBUG_CAPOPS("%s: locking cap\n", __FUNCTION__);
356    err = monitor_lock_cap(check_st->src.croot, check_st->src.cptr,
357                           check_st->src.level);
358    GOTO_IF_ERR(err, cont_err);
359
360    DEBUG_CAPOPS("%s: checking retypeability of cap\n", __FUNCTION__);
361    err = capsend_check_retypeable(check_st->src, check_st->offset,
362                                   check_st->objsize, check_st->count,
363                                   check_retypeable__rx, check_st);
364    GOTO_IF_ERR(err, unlock_cap);
365
366    return;
367
368unlock_cap:
369    caplock_unlock(check_st->src);
370
371cont_err:
372    TRACE(CAPOPS, RETYPE_CALL_RESULT, retype_seqnum);
373    CALLRESCONT(check_st->cont, err);
374}
375
376/**
377 * \brief Handle a completed retype check
378 */
379static void
380retype_check__rx(errval_t status, struct retype_check_st* check,
381                 struct retype_output_st *output, void *to_free)
382{
383    TRACE(CAPOPS, RETYPE_CHECK_RX, retype_seqnum);
384    DEBUG_CAPOPS("%s\n", __FUNCTION__);
385    errval_t err;
386    struct domcapref *src = &check->src;
387    struct domcapref *destcn = &output->destcn;
388    if (err_is_ok(status)) {
389        // the retype may procede
390        TRACE(CAPOPS, MONITOR_RETYPE_CREATE_CAPS, retype_seqnum);
391        status = monitor_create_caps(src->croot, destcn->croot, check->type,
392                                  check->objsize, check->count, src->cptr,
393                                  src->level, check->offset, destcn->cptr,
394                                  destcn->level, output->start_slot);
395        TRACE(CAPOPS, MONITOR_RETYPE_CREATE_CAPS_DONE, retype_seqnum);
396    }
397    struct result_closure cont = output->cont;
398    assert(cont.handler);
399    free(to_free);
400    // Delete copies of domain's src/dest root cnodes
401    if (!capcmp(destcn->croot, src->croot)) {
402        err = cap_destroy(destcn->croot);
403        PANIC_IF_ERR(err, "deleting monitor's copy dest rootcn");
404    }
405    err = cap_destroy(src->croot);
406    PANIC_IF_ERR(err, "deleting monitor's copy of src rootcn");
407    TRACE(CAPOPS, RETYPE_CALL_RESULT, retype_seqnum);
408    CALLRESCONT(cont, status);
409}
410
411/**
412 * \brief Handle result of a owner-initiated retype check.
413 */
414static void
415local_retype_check__rx(errval_t status, void *st)
416{
417    TRACE(CAPOPS, RETYPE_LOCAL_CHECK_RX, 0);
418    DEBUG_CAPOPS("%s\n", __FUNCTION__);
419    struct local_retype_st *rtp_st = (struct local_retype_st*)st;
420    retype_check__rx(status, &rtp_st->check, &rtp_st->output, rtp_st);
421}
422
423/*
424 * Entry
425 */
426
427void
428capops_retype(enum objtype type, size_t objsize, size_t count, struct capref dest_root,
429              capaddr_t dest_cn, uint8_t dest_level, cslot_t dest_slot,
430              struct capref src_root, capaddr_t src, uint8_t src_level,
431              gensize_t offset, retype_result_handler_t result_handler, void *st)
432{
433    TRACE(CAPOPS, RETYPE_START, ++retype_seqnum);
434    DEBUG_CAPOPS("%s\n", __FUNCTION__);
435    errval_t err;
436    distcap_state_t src_state;
437    struct retype_request_st *rtp_req_st;
438    struct local_retype_st *rtp_loc_st;
439
440    err = invoke_cnode_get_state(src_root, src, src_level, &src_state);
441    GOTO_IF_ERR(err, err_cont);
442
443    if (distcap_state_is_busy(src_state)) {
444        err = MON_ERR_REMOTE_CAP_RETRY;
445        goto err_cont;
446    }
447
448    err = invoke_cnode_retype(cap_root, get_cap_addr(src_root), src,
449                              offset, type, objsize, count, get_cap_addr(dest_root),
450                              dest_cn, dest_level, dest_slot);
451    if (err_no(err) != SYS_ERR_RETRY_THROUGH_MONITOR) {
452        goto err_cont;
453    }
454
455    // if retype invocation failed with "retry through mon", we assume that
456    // distcap_needs_locality(cap) would return true.
457
458    if (distcap_state_is_foreign(src_state)) {
459        DEBUG_CAPOPS("source is foreign; forward request to owner\n");
460        // setup retype request
461        err = calloce(1, sizeof(*rtp_req_st), &rtp_req_st);
462        GOTO_IF_ERR(err, err_cont);
463
464        // fill in parameters
465        rtp_req_st->check.type = type;
466        rtp_req_st->check.objsize = objsize;
467        rtp_req_st->check.count = count;
468        rtp_req_st->check.offset = offset;
469        rtp_req_st->check.src = (struct domcapref){
470            .croot = src_root,
471            .cptr = src,
472            .level = src_level,
473        };
474        rtp_req_st->output.destcn = (struct domcapref){
475            .croot = dest_root,
476            .cptr = dest_cn,
477            .level = dest_level,
478        };
479        rtp_req_st->output.start_slot = dest_slot;
480        rtp_req_st->output.cont = MKRESCONT(result_handler, st);
481
482        // enqueue retype request
483        retype_request__enq(rtp_req_st);
484    }
485    else {
486        DEBUG_CAPOPS("source is local; run retype check\n");
487        // on owner, setup retype check
488        err = calloce(1, sizeof(*rtp_loc_st), &rtp_loc_st);
489        GOTO_IF_ERR(err, err_cont);
490
491        // fill in parameters
492        rtp_loc_st->check.type = type;
493        rtp_loc_st->check.objsize = objsize;
494        rtp_loc_st->check.count = count;
495        rtp_loc_st->check.offset = offset;
496        rtp_loc_st->check.src = (struct domcapref){
497            .croot = src_root,
498            .cptr = src,
499            .level = src_level,
500        };
501        rtp_loc_st->output.destcn = (struct domcapref){
502            .croot = dest_root,
503            .cptr = dest_cn,
504            .level = dest_level,
505        };
506        rtp_loc_st->output.start_slot = dest_slot;
507        rtp_loc_st->output.cont = MKRESCONT(result_handler, st);
508
509        // setup handler for retype check result
510        rtp_loc_st->check.cont = MKRESCONT(local_retype_check__rx, rtp_loc_st);
511
512        // Can skip testing retypeability on our core here, as that will be
513        // done by the final retype invocation anyway
514
515        // initiate check
516        check_retype__enq(&rtp_loc_st->check);
517    }
518
519    return;
520
521err_cont:
522    TRACE(CAPOPS, RETYPE_CALL_RESULT, retype_seqnum);
523    result_handler(err, st);
524}
525
526