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