diff options
Diffstat (limited to 'extras/mini-os/xenbus/xenbus_comms.c')
-rw-r--r-- | extras/mini-os/xenbus/xenbus_comms.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/extras/mini-os/xenbus/xenbus_comms.c b/extras/mini-os/xenbus/xenbus_comms.c new file mode 100644 index 0000000000..b299c05683 --- /dev/null +++ b/extras/mini-os/xenbus/xenbus_comms.c @@ -0,0 +1,231 @@ +/****************************************************************************** + * xenbus_comms.c + * + * Low level code to talks to Xen Store: ringbuffer and event channel. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <types.h> +#include <wait.h> +#include <mm.h> +#include <hypervisor.h> +#include <events.h> +#include <os.h> +#include <lib.h> + + +#ifdef XENBUS_COMMS_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=xenbus_comms.c, line=%d) " _f "\n", __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + + +#define RINGBUF_DATASIZE ((PAGE_SIZE / 2) - sizeof(struct ringbuf_head)) +struct ringbuf_head +{ + u32 write; /* Next place to write to */ + u32 read; /* Next place to read from */ + u8 flags; + char buf[0]; +} __attribute__((packed)); + +DECLARE_WAIT_QUEUE_HEAD(xb_waitq); + +static inline struct ringbuf_head *outbuf(void) +{ + return mfn_to_virt(start_info.store_mfn); +} + +static inline struct ringbuf_head *inbuf(void) +{ + return (struct ringbuf_head *)((char *)mfn_to_virt(start_info.store_mfn) + PAGE_SIZE/2); +} + +static void wake_waiting(int port, struct pt_regs *regs) +{ + wake_up(&xb_waitq); +} + +static int check_buffer(const struct ringbuf_head *h) +{ + return (h->write < RINGBUF_DATASIZE && h->read < RINGBUF_DATASIZE); +} + +/* We can't fill last byte: would look like empty buffer. */ +static void *get_output_chunk(const struct ringbuf_head *h, + void *buf, u32 *len) +{ + u32 read_mark; + + if (h->read == 0) + read_mark = RINGBUF_DATASIZE - 1; + else + read_mark = h->read - 1; + + /* Here to the end of buffer, unless they haven't read some out. */ + *len = RINGBUF_DATASIZE - h->write; + if (read_mark >= h->write) + *len = read_mark - h->write; + return (void *)((char *)buf + h->write); +} + +static const void *get_input_chunk(const struct ringbuf_head *h, + const void *buf, u32 *len) +{ + /* Here to the end of buffer, unless they haven't written some. */ + *len = RINGBUF_DATASIZE - h->read; + if (h->write >= h->read) + *len = h->write - h->read; + return (void *)((char *)buf + h->read); +} + +static void update_output_chunk(struct ringbuf_head *h, u32 len) +{ + h->write += len; + if (h->write == RINGBUF_DATASIZE) + h->write = 0; +} + +static void update_input_chunk(struct ringbuf_head *h, u32 len) +{ + h->read += len; + if (h->read == RINGBUF_DATASIZE) + h->read = 0; +} + +static int output_avail(struct ringbuf_head *out) +{ + unsigned int avail; + + get_output_chunk(out, out->buf, &avail); + return avail != 0; +} + +int xb_write(const void *data, unsigned len) +{ + struct ringbuf_head h; + struct ringbuf_head *out = outbuf(); + + do { + void *dst; + unsigned int avail; + + wait_event(xb_waitq, output_avail(out)); + + /* Read, then check: not that we don't trust store. + * Hell, some of my best friends are daemons. But, + * in this post-911 world... */ + h = *out; + mb(); + if (!check_buffer(&h)) { + return -1; /* ETERRORIST! */ + } + + dst = get_output_chunk(&h, out->buf, &avail); + if (avail > len) + avail = len; + memcpy(dst, data, avail); + data = (void *)((char *)data + avail); + len -= avail; + update_output_chunk(out, avail); + notify_via_evtchn(start_info.store_evtchn); + } while (len != 0); + + return 0; +} + +int xs_input_avail(void) +{ + unsigned int avail; + struct ringbuf_head *in = inbuf(); + + get_input_chunk(in, in->buf, &avail); + return avail != 0; +} + +int xb_read(void *data, unsigned len) +{ + struct ringbuf_head h; + struct ringbuf_head *in = inbuf(); + int was_full; + + while (len != 0) { + unsigned int avail; + const char *src; + + wait_event(xb_waitq, xs_input_avail()); + h = *in; + mb(); + if (!check_buffer(&h)) { + return -1; + } + + src = get_input_chunk(&h, in->buf, &avail); + if (avail > len) + avail = len; + was_full = !output_avail(&h); + + memcpy(data, src, avail); + data = (void *)((char *)data + avail); + len -= avail; + update_input_chunk(in, avail); + DEBUG("Finished read of %i bytes (%i to go)\n", avail, len); + /* If it was full, tell them we've taken some. */ + if (was_full) + notify_via_evtchn(start_info.store_evtchn); + } + + /* If we left something, wake watch thread to deal with it. */ + if (xs_input_avail()) + wake_up(&xb_waitq); + + return 0; +} + +/* Set up interrupt handler off store event channel. */ +int xb_init_comms(void) +{ + printk("Init xenbus comms, store event channel %d\n", start_info.store_evtchn); + if (!start_info.store_evtchn) + return 0; + printk("Binding virq\n"); + bind_evtchn(start_info.store_evtchn, &wake_waiting); + + /* FIXME zero out page -- domain builder should probably do this*/ + memset(mfn_to_virt(start_info.store_mfn), 0, PAGE_SIZE); + notify_via_evtchn(start_info.store_evtchn); + return 0; +} + +void xb_suspend_comms(void) +{ + + if (!start_info.store_evtchn) + return; + + // TODO + //unbind_evtchn_from_irqhandler(xen_start_info.store_evtchn, &xb_waitq); +} |