/* $NetBSD: ctrl_if.c,v 1.19 2008/10/21 15:46:32 cegger Exp $ */ /****************************************************************************** * ctrl_if.c * * Management functions for special interface to the domain controller. * * Copyright (c) 2004, K A Fraser */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: ctrl_if.c,v 1.19 2008/10/21 15:46:32 cegger Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/proc.h> #include <sys/kthread.h> #include <sys/malloc.h> #include <sys/simplelock.h> #include <xen/xen.h> #include <xen/hypervisor.h> #include <xen/ctrl_if.h> #include <xen/evtchn.h> #if 0 #define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \ __FILE__ , __LINE__ , ## _a ) #else #define DPRINTK(_f, _a...) ((void)0) #endif /* * Only used by initial domain which must create its own control-interface * event channel. This value is picked up by the user-space domain controller * via an ioctl. */ int initdom_ctrlif_domcontroller_port = -1; /* static */ int ctrl_if_evtchn = -1; static struct simplelock ctrl_if_lock; static CONTROL_RING_IDX ctrl_if_tx_resp_cons; static CONTROL_RING_IDX ctrl_if_rx_req_cons; /* Incoming message requests. */ /* Primary message type -> message handler. */ static ctrl_msg_handler_t ctrl_if_rxmsg_handler[256]; /* Primary message type -> callback in process context? */ static unsigned long ctrl_if_rxmsg_blocking_context[256/sizeof(unsigned long)]; /* Queue up messages to be handled in process context. */ static ctrl_msg_t ctrl_if_rxmsg_deferred[CONTROL_RING_SIZE]; static CONTROL_RING_IDX ctrl_if_rxmsg_deferred_prod; static CONTROL_RING_IDX ctrl_if_rxmsg_deferred_cons; /* Incoming message responses: message identifier -> message handler/id. */ static struct { ctrl_msg_handler_t fn; unsigned long id; } ctrl_if_txmsg_id_mapping[CONTROL_RING_SIZE]; /* For received messages that must be deferred to process context. */ static void __ctrl_if_rxmsg_deferred(void *unused); static int ctrl_if_tx_wait; static void __ctrl_if_tx_tasklet(unsigned long data); static void __ctrl_if_rx_tasklet(unsigned long data); #define get_ctrl_if() ((control_if_t *)((uintptr_t)HYPERVISOR_shared_info + 2048)) #define TX_FULL(_c) \ (((_c)->tx_req_prod - ctrl_if_tx_resp_cons) == CONTROL_RING_SIZE) static void ctrl_if_notify_controller(void) { hypervisor_notify_via_evtchn(ctrl_if_evtchn); } static void ctrl_if_rxmsg_default_handler(ctrl_msg_t *msg, unsigned long id) { msg->length = 0; ctrl_if_send_response(msg); } static void __ctrl_if_tx_tasklet(unsigned long data) { control_if_t *ctrl_if = get_ctrl_if(); ctrl_msg_t *msg; int was_full = TX_FULL(ctrl_if); CONTROL_RING_IDX rp; rp = ctrl_if->tx_resp_prod; x86_lfence(); /* Ensure we see all requests up to 'rp'. */ while ( ctrl_if_tx_resp_cons != rp ) { msg = &ctrl_if->tx_ring[MASK_CONTROL_IDX(ctrl_if_tx_resp_cons)]; DPRINTK("Rx-Rsp %u/%u :: %d/%d\n", ctrl_if_tx_resp_cons, ctrl_if->tx_resp_prod, msg->type, msg->subtype); /* Execute the callback handler, if one was specified. */ if ( msg->id != 0xFF ) { (*ctrl_if_txmsg_id_mapping[msg->id].fn)( msg, ctrl_if_txmsg_id_mapping[msg->id].id); __insn_barrier(); /* Execute, /then/ free. */ ctrl_if_txmsg_id_mapping[msg->id].fn = NULL; } /* * Step over the message in the ring /after/ finishing reading it. As * soon as the index is updated then the message may get blown away. */ __insn_barrier(); ctrl_if_tx_resp_cons++; } if ( was_full && !TX_FULL(ctrl_if) ) { wakeup(&ctrl_if_tx_wait); } } static void __ctrl_if_rxmsg_deferred(void *unused) { ctrl_msg_t *msg; CONTROL_RING_IDX dp; int s; while (1) { s = splsoftnet(); dp = ctrl_if_rxmsg_deferred_prod; x86_lfence(); /* Ensure we see all requests up to 'dp'. */ if (ctrl_if_rxmsg_deferred_cons == dp) { tsleep(&ctrl_if_rxmsg_deferred_cons, PRIBIO, "rxdef", 0); splx(s); continue; } splx(s); while ( ctrl_if_rxmsg_deferred_cons != dp ) { msg = &ctrl_if_rxmsg_deferred[ MASK_CONTROL_IDX(ctrl_if_rxmsg_deferred_cons)]; (*ctrl_if_rxmsg_handler[msg->type])(msg, 0); ctrl_if_rxmsg_deferred_cons++; } } } static void __ctrl_if_rx_tasklet(unsigned long data) { control_if_t *ctrl_if = get_ctrl_if(); ctrl_msg_t msg, *pmsg; CONTROL_RING_IDX rp, dp; dp = ctrl_if_rxmsg_deferred_prod; rp = ctrl_if->rx_req_prod; x86_lfence(); /* Ensure we see all requests up to 'rp'. */ while ( ctrl_if_rx_req_cons != rp ) { pmsg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(ctrl_if_rx_req_cons)]; memcpy(&msg, pmsg, offsetof(ctrl_msg_t, msg)); DPRINTK("Rx-Req %u/%u :: %d/%d\n", ctrl_if_rx_req_cons-1, ctrl_if->rx_req_prod, msg.type, msg.subtype); if ( msg.length != 0 ) memcpy(msg.msg, pmsg->msg, msg.length); /* * increase ctrl_if_rx_req_cons now, * as the handler may end up calling __ctrl_if_rx_tasklet() again * though the console polling code. */ ctrl_if_rx_req_cons++; if ( xen_atomic_test_bit( (unsigned long *)&ctrl_if_rxmsg_blocking_context, msg.type) ) memcpy(&ctrl_if_rxmsg_deferred[MASK_CONTROL_IDX(dp++)], &msg, offsetof(ctrl_msg_t, msg) + msg.length); else (*ctrl_if_rxmsg_handler[msg.type])(&msg, 0); /* update rp, in case the console polling code was used */ rp = ctrl_if->rx_req_prod; x86_lfence(); /* Ensure we see all requests up to 'rp'. */ } if ( dp != ctrl_if_rxmsg_deferred_prod ) { __insn_barrier(); ctrl_if_rxmsg_deferred_prod = dp; wakeup(&ctrl_if_rxmsg_deferred_cons); } } static int ctrl_if_interrupt(void *arg) { control_if_t *ctrl_if = get_ctrl_if(); int ret = 0; if ( ctrl_if_tx_resp_cons != ctrl_if->tx_resp_prod ) { ret = 1; __ctrl_if_tx_tasklet(0); } if ( ctrl_if_rx_req_cons != ctrl_if->rx_req_prod ) { ret = 1; __ctrl_if_rx_tasklet(0); } return ret; } int ctrl_if_send_message_noblock( ctrl_msg_t *msg, ctrl_msg_handler_t hnd, unsigned long id) { control_if_t *ctrl_if = get_ctrl_if(); unsigned long flags; int i; int s; save_and_cli(flags); simple_lock(&ctrl_if_lock); while ( TX_FULL(ctrl_if) ) { simple_unlock(&ctrl_if_lock); restore_flags(flags); s = splhigh(); if ( ctrl_if_tx_resp_cons != ctrl_if->tx_resp_prod ) { __ctrl_if_tx_tasklet(0); splx(s); save_and_cli(flags); simple_lock(&ctrl_if_lock); } else { splx(s); return EAGAIN; } } msg->id = 0xFF; if ( hnd != NULL ) { for ( i = 0; ctrl_if_txmsg_id_mapping[i].fn != NULL; i++ ) continue; ctrl_if_txmsg_id_mapping[i].fn = hnd; ctrl_if_txmsg_id_mapping[i].id = id; msg->id = i; } DPRINTK("Tx-Req %u/%u :: %d/%d\n", ctrl_if->tx_req_prod, ctrl_if_tx_resp_cons, msg->type, msg->subtype); memcpy(&ctrl_if->tx_ring[MASK_CONTROL_IDX(ctrl_if->tx_req_prod)], msg, sizeof(*msg)); x86_lfence(); /* Write the message before letting the controller peek at it. */ ctrl_if->tx_req_prod++; simple_unlock(&ctrl_if_lock); restore_flags(flags); ctrl_if_notify_controller(); return 0; } int ctrl_if_send_message_block( ctrl_msg_t *msg, ctrl_msg_handler_t hnd, unsigned long id, long wait_state) { int rc; while ((rc = ctrl_if_send_message_noblock(msg, hnd, id)) == EAGAIN) { /* XXXcl possible race -> add a lock and ltsleep */ #if 1 HYPERVISOR_yield(); #else rc = tsleep((void *) &ctrl_if_tx_wait, PUSER | PCATCH, "ctrl_if", 0); if (rc) break; #endif } return rc; } /* Allow a reponse-callback handler to find context of a blocked requester. */ struct rsp_wait { ctrl_msg_t *msg; /* Buffer for the response message. */ struct task_struct *task; /* The task that is blocked on the response. */ int done; /* Indicate to 'task' that response is rcv'ed. */ }; static void __ctrl_if_get_response(ctrl_msg_t *msg, unsigned long id) { struct rsp_wait *wait = (struct rsp_wait *)id; memcpy(wait->msg, msg, sizeof(*msg)); x86_lfence(); wait->done = 1; wakeup(wait); } int ctrl_if_send_message_and_get_response( ctrl_msg_t *msg, ctrl_msg_t *rmsg, long wait_state) { struct rsp_wait wait; int rc; wait.msg = rmsg; wait.done = 0; if ( (rc = ctrl_if_send_message_block(msg, __ctrl_if_get_response, (unsigned long)&wait, wait_state)) != 0 ) return rc; for ( ; ; ) { if ( wait.done ) break; tsleep((void *)&wait, PUSER | PCATCH, "ctrl_if", 0); } return 0; } #ifdef notyet int ctrl_if_enqueue_space_callback( struct tq_struct *task) { control_if_t *ctrl_if = get_ctrl_if(); /* Fast path. */ if ( !TX_FULL(ctrl_if) ) return 0; (void)queue_task(task, &ctrl_if_tx_tq); /* * We may race execution of the task queue, so return re-checked status. If * the task is not executed despite the ring being non-full then we will * certainly return 'not full'. */ x86_lfence(); return TX_FULL(ctrl_if); } #endif void ctrl_if_send_response( ctrl_msg_t *msg) { control_if_t *ctrl_if = get_ctrl_if(); unsigned long flags; ctrl_msg_t *dmsg; /* * NB. The response may the original request message, modified in-place. * In this situation we may have src==dst, so no copying is required. */ save_and_cli(flags); simple_lock(&ctrl_if_lock); DPRINTK("Tx-Rsp %u :: %d/%d\n", ctrl_if->rx_resp_prod, msg->type, msg->subtype); dmsg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(ctrl_if->rx_resp_prod)]; if ( dmsg != msg ) memcpy(dmsg, msg, sizeof(*msg)); x86_lfence(); /* Write the message before letting the controller peek at it. */ ctrl_if->rx_resp_prod++; simple_unlock(&ctrl_if_lock); restore_flags(flags); ctrl_if_notify_controller(); } int ctrl_if_register_receiver( uint8_t type, ctrl_msg_handler_t hnd, unsigned int flags) { unsigned long _flags; int inuse; save_and_cli(_flags); simple_lock(&ctrl_if_lock); inuse = (ctrl_if_rxmsg_handler[type] != ctrl_if_rxmsg_default_handler); if ( inuse ) { printf("Receiver %p already established for control " "messages of type %d.\n", ctrl_if_rxmsg_handler[type], type); } else { ctrl_if_rxmsg_handler[type] = hnd; xen_atomic_clear_bit((unsigned long *)&ctrl_if_rxmsg_blocking_context, type); if ( flags == CALLBACK_IN_BLOCKING_CONTEXT ) { xen_atomic_set_bit((unsigned long *)&ctrl_if_rxmsg_blocking_context, type); #if 0 if ( !safe_to_schedule_task ) BUG(); #endif } } simple_unlock(&ctrl_if_lock); restore_flags(_flags); return !inuse; } void ctrl_if_unregister_receiver( uint8_t type, ctrl_msg_handler_t hnd) { unsigned long flags; save_and_cli(flags); simple_lock(&ctrl_if_lock); if ( ctrl_if_rxmsg_handler[type] != hnd ) printf("Receiver %p is not registered for control " "messages of type %d.\n", hnd, type); else ctrl_if_rxmsg_handler[type] = ctrl_if_rxmsg_default_handler; simple_unlock(&ctrl_if_lock); restore_flags(flags); /* Ensure that @hnd will not be executed after this function returns. */ wakeup(&ctrl_if_rxmsg_deferred_cons); } #ifdef notyet void ctrl_if_suspend(void) { free_irq(ctrl_if_irq, NULL); unbind_evtchn_from_irq(ctrl_if_evtchn); } #endif void ctrl_if_resume(void) { control_if_t *ctrl_if = get_ctrl_if(); if (xendomain_is_dom0()) { /* * The initial domain must create its own domain-controller link. * The controller is probably not running at this point, but will * pick up its end of the event channel from */ evtchn_op_t op; op.cmd = EVTCHNOP_bind_interdomain; op.u.bind_interdomain.dom1 = DOMID_SELF; op.u.bind_interdomain.dom2 = DOMID_SELF; op.u.bind_interdomain.port1 = 0; op.u.bind_interdomain.port2 = 0; if ( HYPERVISOR_event_channel_op(&op) != 0 ) panic("EVTCHNOP_bind_interdomain"); xen_start_info.domain_controller_evtchn = op.u.bind_interdomain.port1; initdom_ctrlif_domcontroller_port = op.u.bind_interdomain.port2; } /* Sync up with shared indexes. */ ctrl_if_tx_resp_cons = ctrl_if->tx_resp_prod; ctrl_if_rx_req_cons = ctrl_if->rx_resp_prod; ctrl_if_evtchn = xen_start_info.domain_controller_evtchn; aprint_verbose("Domain controller: using event channel %d\n", ctrl_if_evtchn); event_set_handler(ctrl_if_evtchn, &ctrl_if_interrupt, NULL, IPL_SCHED, "ctrlev"); hypervisor_enable_event(ctrl_if_evtchn); } void ctrl_if_early_init(void) { simple_lock_init(&ctrl_if_lock); ctrl_if_evtchn = xen_start_info.domain_controller_evtchn; } void ctrl_if_init(void) { int i, error; for ( i = 0; i < 256; i++ ) ctrl_if_rxmsg_handler[i] = ctrl_if_rxmsg_default_handler; if (ctrl_if_evtchn == -1) ctrl_if_early_init(); error = kthread_create(PRI_NONE, 0, NULL, __ctrl_if_rxmsg_deferred, NULL, NULL, "ctrlif"); if (error != 0) { aprint_error("ctrlif: unable to create kernel thread: " "error %d\n", error); } ctrl_if_resume(); } /* * !! The following are DANGEROUS FUNCTIONS !! * Use with care [for example, see xencons_force_flush()]. */ int ctrl_if_transmitter_empty(void) { return (get_ctrl_if()->tx_req_prod == ctrl_if_tx_resp_cons); } void ctrl_if_discard_responses(void) { ctrl_if_tx_resp_cons = get_ctrl_if()->tx_resp_prod; } /* * polling functions, for xencons's benefit * We need to enable events before calling HYPERVISOR_block(). * Do this at a high enouth IPL so that no other interrupt will interfere * with our polling. */ void ctrl_if_console_poll(void) { int s = splhigh(); /* * Unmask the ctrl event, and block waiting for an interrupt. * Note that the event we get may not be for the console. * In this case, the console code will call us again. */ while (ctrl_if_interrupt(NULL) == 0) { /* enable interrupts. */ hypervisor_clear_event(ctrl_if_evtchn); hypervisor_unmask_event(ctrl_if_evtchn); HYPERVISOR_block(); } splx(s); }