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