#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "minixend.h" #define NETWORK_SCRIPT "/etc/xen/scripts/network" #define VIFBRIDGE_SCRIPT "/etc/xen/scripts/vif-bridge" #define MINIXEND_PORT 5123 #define mb() asm volatile ("" ::: "memory") static void send_control_message(int type, int subtype, int id, int size, void *payload, struct domain *target); struct list_head head_domain = LIST_HEAD(&head_domain); static struct list_head head_connection = LIST_HEAD(&head_connection); struct list_head head_console = LIST_HEAD(&head_console); #define foreach_open_connection(d) \ foreach_item(d, &head_connection, struct open_connection, connection_list) /* Not modified after initial start up */ static struct domain *dom0; unsigned xc_handle; static int listen_fd; int evtchn_fd; static struct list_head head_event_receiver = LIST_HEAD(&head_event_receiver); struct event_receiver { struct list_head list; int id; pthread_cond_t cond; }; /* We're protected by the dom0 mutex in here */ static struct event_receiver * allocate_event_receiver(struct domain *d) { static int next_message_id; struct event_receiver *work; assert(d == dom0); work = xmalloc(sizeof(*work)); work->id = next_message_id++; pthread_cond_init(&work->cond, NULL); list_insert_after(&work->list, &head_event_receiver); return work; } static struct event_receiver * find_event_receiver(int id) { struct event_receiver *work; foreach_item(work, &head_event_receiver, struct event_receiver, list) if (work->id == id) return work; return NULL; } static void release_event_receiver(struct event_receiver *w) { list_remove(&w->list); pthread_cond_destroy(&w->cond); free(w); } /* Send a message to dom0, and then block awaiting a reply. */ /* Make sure we don't hold any domain mutexs */ static void send_dom0_message_block(control_msg_t *msg) { CONTROL_RING_IDX c; struct event_receiver *er; control_msg_t buf; PRINTF(0, "sending message to dom0 and blocking for reply.\n"); pthread_mutex_lock(&dom0->mux); PRINTF(0, "got dom0 lock.\n"); er = allocate_event_receiver(dom0); PRINTF(0, "allocated evetn receiver.\n"); msg->id = er->id; PRINTF(1, "sending message with id %d\n", msg->id); send_control_message(msg->type, msg->subtype, msg->id, msg->length, msg->msg, dom0); xc_evtchn_send(xc_handle, dom0->control_evtchn); PRINTF(0, "waiting for reply\n"); pthread_cond_wait(&er->cond, &dom0->mux); PRINTF(0, "got reply\n"); c = dom0->rx_resp_cons % CONTROL_RING_SIZE; memcpy(&buf, &dom0->ctrl_if->rx_ring[c], sizeof(buf)); assert(msg->id == buf.id); assert(msg->type == buf.type); assert(msg->subtype == buf.subtype); memcpy(msg, &buf, sizeof(*msg)); dom0->rx_resp_cons++; release_event_receiver(er); pthread_mutex_unlock(&dom0->mux); PRINTF(1, "got reply to message with id %d\n", msg->id); } /* Allocate an interdomain event channel. event_ports[0] is the local event port number, event_ports[1] the remote */ int allocate_event_channel(struct domain *d, int event_ports[2]) { return xc_evtchn_bind_interdomain(xc_handle, DOMID_SELF, d->domid, event_ports, event_ports+1); } static void accept_new_connection(void) { int fd; struct open_connection *oc; fd = accept(listen_fd, NULL, NULL); if (fd < 0) return; oc = xmalloc(sizeof(*oc)); oc->fd = fd; oc->state = OC_STATE_CONNECTED; oc->buf_used = 0; oc->buf_allocated = 16; oc->buf = xmalloc(oc->buf_allocated); list_insert_after(&oc->connection_list, &head_connection); } static void closedown_connection(struct open_connection *oc) { close(oc->fd); ass
module test(
    input             WR1_CLK,  WR2_CLK,
    input             WR1_WEN,  WR2_WEN,
    input      [7:0]  WR1_ADDR, WR2_ADDR,
    input      [7:0]  WR1_DATA, WR2_DATA,
    input             RD1_CLK,  RD2_CLK,
    input      [7:0]  RD1_ADDR, RD2_ADDR,
    output reg [7:0]  RD1_DATA, RD2_DATA
);

reg [7:0] memory [0:255];

always @(posedge WR1_CLK)
    if (WR1_WEN)
        memory[WR1_ADDR] <= WR1_DATA;

always @(posedge WR2_CLK)
    if (WR2_WEN)
        memory[WR2_ADDR] <= WR2_DATA;

always @(posedge RD1_CLK)
    RD1_DATA <= memory[RD1_ADDR];

always @(posedge RD2_CLK)
    RD2_DATA <= memory[RD2_ADDR];

endmodule
ld.\n", d->tx_req_cons, d->ctrl_if->tx_req_prod); } /* Take any replies off, and discard them. */ if (d->rx_resp_cons != d->ctrl_if->rx_resp_prod) PRINTF(1, "discard %ld events\n", d->ctrl_if->rx_resp_prod - d->rx_resp_cons); d->rx_resp_cons = d->ctrl_if->rx_resp_prod; } /* This is the main function for domain control threads */ void * domain_thread_func(void *D) { struct domain *d = D; int r; CONTROL_RING_IDX old_resp_prod, old_req_prod; pthread_mutex_lock(&d->mux); for (;;) { pthread_cond_wait(&d->cond, &d->mux); old_resp_prod = d->ctrl_if->tx_resp_prod; old_req_prod = d->ctrl_if->rx_req_prod; domain_did_control_event(d); if (d->cc && d->cc->in_buf_used != 0 && d->plugged == 0) { r = d->cc->in_buf_used; if (r > 60) r = 60; PRINTF(1, "Sending to domain: %.*s\n", r, d->cc->in_buf); send_control_message(CMSG_CONSOLE, CMSG_CONSOLE_DATA, 0, r, d->cc->in_buf, d); memmove(d->cc->in_buf, d->cc->in_buf + r, d->cc->in_buf_used - r); d->cc->in_buf_used -= r; } if (d->ctrl_if->tx_resp_prod != old_resp_prod || d->ctrl_if->rx_req_prod != old_req_prod) xc_evtchn_send(xc_handle, d->control_evtchn); } } /* This is the only thing you can do with a domain structure if you're not in the thread which controls that domain. Domain 0 is special. */ void signal_domain(struct domain *d) { CONTROL_RING_IDX c; int id; struct event_receiver *evt; pthread_mutex_lock(&d->mux); if (d == dom0) { /* Take events off of dom0's control ring, and send them to the event receivers. */ while (d->tx_req_cons != d->ctrl_if->tx_req_prod) { c = d->tx_req_cons % CONTROL_RING_SIZE; id = d->ctrl_if->tx_ring[c].id; evt = find_event_receiver(id); if (evt != NULL) { PRINTF(1, "delivering event id %d\n", evt->id); pthread_cond_broadcast(&evt->cond); pthread_mutex_unlock(&d->mux); sched_yield(); pthread_mutex_lock(&d->mux); } else { warnx("unexpected message id %d discarded", id); d->tx_req_cons++; } } while (d->rx_resp_cons != d->ctrl_if->rx_resp_prod) { c = d->rx_resp_cons % CONTROL_RING_SIZE; id = d->ctrl_if->rx_ring[c].id; evt = find_event_receiver(id); if (evt != NULL) { PRINTF(1, "delivering event rep id %d\n", evt->id); pthread_cond_broadcast(&evt->cond); pthread_mutex_unlock(&d->mux); sched_yield(); pthread_mutex_lock(&d->mux); } else { warnx("unexpected message reply id %d discarded", id); d->rx_resp_cons++; } } } else { if (d->plugged) { d->event_pending = 1; } else { pthread_cond_broadcast(&d->cond); } } pthread_mutex_unlock(&d->mux); } static void handle_evtchn_event(void) { short port; struct domain *d; read(evtchn_fd, &port, sizeof(short)); write(evtchn_fd, &port, sizeof(short)); foreach_domain (d) { if (d->control_evtchn == port) { signal_domain(d); return; } } warnx("got an event on an unknown port %d", port); } void * map_domain_mem(struct domain *d, unsigned long mfn) { return xc_map_foreign_range(xc_handle, d->domid, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); } static void handle_console_event(struct console_connection *cc) { int r; int fd; switch (cc->state) { case CC_STATE_ERROR: /* Errors shouldn't get here. */ abort(); case CC_STATE_PENDING: fd = accept(cc->fd, NULL, NULL); if (fd >= 0) { PRINTF(3, "Accepted console connection for domain %d", cc->dom->domid); close(cc->fd); cc->fd = fd; cc->state = CC_STATE_CONNECTED; while (cc->buf_used != 0) { r = write(cc->fd, cc->buf, cc->buf_used); if (r <= 0) { cc->state = CC_STATE_ERROR; break; } memmove(cc->buf, cc->buf + r, cc->buf_used - r); cc->buf_used -= r; } free(cc->buf); cc->buf = NULL; cc->buf_allocated = 0; } else { PRINTF(1, "error %s accepting console", strerror(errno)); } pthread_mutex_unlock(&cc->dom->mux); break; case CC_STATE_CONNECTED: if (cc->in_buf_allocated == 0) { assert(cc->in_buf_used == 0); cc->in_buf_allocated = 128; cc->in_buf = xmalloc(cc->in_buf_allocated); } if (cc->in_buf_used == cc->in_buf_allocated) { cc->in_buf_allocated *= 2; cc->in_buf = xrealloc(cc->in_buf, cc->in_buf_allocated); } r = read(cc->fd, cc->in_buf + cc->in_buf_used, cc->in_buf_allocated - cc->in_buf_used); if (r <= 0) { cc->state = CC_STATE_ERROR; } else { cc->in_buf_used += r; } pthread_mutex_unlock(&cc->dom->mux); signal_domain(cc->dom); break; } } static void handle_connection_event(struct open_connection *oc) { int r; /* We know that some amount of data is ready and waiting for us. Slurp it in. */ if (oc->buf_used == oc->buf_allocated) { oc->buf_allocated *= 2; oc->buf = xrealloc(oc->buf, oc->buf_allocated); } r = read(oc->fd, oc->buf + oc->buf_used, oc->buf_allocated - oc->buf_used); if (r < 0) { warn("reading command from remote"); oc->state = OC_STATE_ERROR; } else if (r == 0) { warnx("reading command from remote"); oc->state = OC_STATE_ERROR; } else { oc->buf_used += r; if (strchr(oc->buf, '\n')) oc->state = OC_STATE_COMMAND_PENDING; } } static void get_and_process_event(void) { fd_set read_fds, except_fds; struct open_connection *oc; struct console_connection *cc; int max_fd = listen_fd; int r; struct list_head *li, *temp_li; FD_ZERO(&read_fds); FD_ZERO(&except_fds); FD_SET(listen_fd, &read_fds); FD_SET(evtchn_fd, &read_fds); if (evtchn_fd > max_fd) max_fd = evtchn_fd; foreach_open_connection(oc) { FD_SET(oc->fd, &read_fds); FD_SET(oc->fd, &except_fds); if (oc->fd > max_fd) max_fd = oc->fd; } foreach_console_connection(cc) { FD_SET(cc->fd, &read_fds); FD_SET(cc->fd, &except_fds); if (cc->fd > max_fd) max_fd = cc->fd; } r = select(max_fd + 1, &read_fds, NULL, &except_fds, NULL); if (r < 0) err(1, "select"); if (FD_ISSET(listen_fd, &read_fds)) { accept_new_connection(); } else if (FD_ISSET(evtchn_fd, &read_fds)) handle_evtchn_event(); foreach_open_connection(oc) { if (FD_ISSET(oc->fd, &read_fds)) handle_connection_event(oc); if (FD_ISSET(oc->fd, &except_fds)) oc->state = OC_STATE_ERROR; } list_foreach_safe(&head_console, li, temp_li) { cc = list_item(li, struct console_connection, list); if (FD_ISSET(cc->fd, &read_fds)) handle_console_event(cc); if (FD_ISSET(cc->fd, &except_fds) || cc->state == CC_STATE_ERROR) { PRINTF(1, "Cleaning up console connection"); cc->dom->cc = NULL; list_remove(&cc->list); close(cc->fd); if (cc->buf_allocated != 0) free(cc->buf); if (cc->in_buf_allocated != 0) free(cc->in_buf); free(cc); } } /* Run pending stuff on the open connections. */ list_foreach_safe(&head_connection, li, temp_li) { oc = list_item(li, struct open_connection, connection_list); switch (oc->state) { case OC_STATE_ERROR: list_remove(&oc->connection_list); closedown_connection(oc); break; case OC_STATE_COMMAND_PENDING: process_command(oc); break; case OC_STATE_CONNECTED: /* Don't need to do anything */ break; } } } static int start_listening(void) { int sock; struct sockaddr_in inaddr; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) err(1, "creating socket"); memset(&inaddr, 0, sizeof(inaddr)); inaddr.sin_family = AF_INET; inaddr.sin_port = htons(MINIXEND_PORT); if (bind(sock, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0) err(1, "binding to port %d", MINIXEND_PORT); if (listen(sock, 5) < 0) err(1, "listening for connections"); return sock; } static struct domain * find_dom0(void) { int r; xc_dominfo_t info; struct domain *work; r = xc_domain_getinfo(xc_handle, 0, 1, &info); if (r < 0) err(1, "getting domain 0 information"); work = xmalloc(sizeof(*work)); work->control_evtchn = 2; if (ioctl(evtchn_fd, EVTCHN_BIND, 2) < 0) err(1, "binding to domain 0 control event channel"); work->domid = 0; work->name = strdup("dom0"); work->mem_kb = info.max_memkb; work->state = DOM_STATE_RUNNING; work->shared_info_mfn = info.shared_info_frame; work->shared_info = map_domain_mem(work, info.shared_info_frame); work->ctrl_if = (control_if_t *)((unsigned)work->shared_info + 2048); work->tx_req_cons = work->ctrl_if->tx_req_prod; work->rx_resp_cons = work->ctrl_if->rx_resp_prod; pthread_mutex_init(&work->mux, NULL); pthread_cond_init(&work->cond, NULL); list_insert_after(&work->domain_list, &head_domain); return work; } int main(int argc, char *argv[]) { int r; r = our_system(NETWORK_SCRIPT " start antispoof=no"); if (r < 0) err(1, "running " NETWORK_SCRIPT); if (!WIFEXITED(r)) { if (WIFSIGNALED(r)) { errx(1, NETWORK_SCRIPT " killed by signal %d", WTERMSIG(r)); } errx(1, NETWORK_SCRIPT " terminated abnormally"); } if (WEXITSTATUS(r) != 0) errx(1, NETWORK_SCRIPT " returned error status %d", WEXITSTATUS(r)); xc_handle = xc_interface_open(); listen_fd = start_listening(); evtchn_fd = open("/dev/xen/evtchn", O_RDWR); if (evtchn_fd < 0) err(1, "openning /dev/xen/evtchn"); dom0 = find_dom0(); while (1) { get_and_process_event(); PRINTF(5, "Dom0 ring state:\n"); PRINTF(5, "RX: req_prod %ld, resp_prod %ld, resp_cons %ld\n", dom0->ctrl_if->rx_req_prod, dom0->ctrl_if->rx_resp_prod, dom0->rx_resp_cons); PRINTF(5, "TX: req_prod %ld, resp_prod %ld, req_cons %ld\n", dom0->ctrl_if->tx_req_prod, dom0->ctrl_if->tx_resp_prod, dom0->tx_req_cons); } return 0; }