/* Simple hacked up console userspace. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define XEN_CONSOLE_FLAG_OVERFLOW 0x1 #define XEN_CONSOLE_FLAG_EIO 0x2 #define EVENTCHN_BIND _IO('E', 2) #define EVENTCHN_UNBIND _IO('E', 3) struct ringbuf_head { u32 write; /* Next place to write to */ u32 read; /* Next place to read from */ u8 flags; char buf[0]; } __attribute__((packed)); /* Two circular buffers: small one for input, large one for output. */ struct xen_console_buffer { struct ringbuf_head inbuf; char inbuf_buf[128 - sizeof(struct ringbuf_head)]; struct ringbuf_head outbuf; char outbuf_buf[4096 - 128 - sizeof(struct ringbuf_head)]; } __attribute__((packed, aligned(4096))); static int xc_handle; struct xen_console { struct xen_console_buffer *buf; unsigned int evtchn; }; static inline int check_buffer(const struct ringbuf_head *h, u32 bufsize) { return (h->write < bufsize && h->read < bufsize); } /* We can't fill last byte: would look like empty buffer. */ static char *get_write_chunk(const struct ringbuf_head *h, char *buf, u32 bufsize, u32 *len) { u32 read_mark; if (h->read == 0) read_mark = bufsize - 1; else read_mark = h->read - 1; /* Here to the end of buffer, unless they haven't read some out. */ *len = bufsize - h->write; if (read_mark >= h->write) *len = read_mark - h->write; return buf + h->write; } static void update_write_chunk(struct ringbuf_head *h, u32 bufsize, u32 len) { h->write += len; if (h->write == bufsize) h->write = 0; } static const char *get_read_chunk(const struct ringbuf_head *h, const char *buf, u32 bufsize, u32 *len) { /* Here to the end of buffer, unless they haven't written some. */ *len = bufsize - h->read; if (h->write >= h->read) *len = h->write - h->read; return buf + h->read; } static void update_read_chunk(struct ringbuf_head *h, u32 bufsize, u32 len) { h->read += len; if (h->read == bufsize) h->read = 0; } static int write_buf(struct ringbuf_head *head, u32 bufsize, const char *src, u32 len) { int ret = 0; struct ringbuf_head h; /* Must read head once, and before anything else. */ h = *head; mb(); if (!check_buffer(&h, bufsize)) { errno = EIO; return -1; } while (len > 0) { u32 thislen; char *dst = get_write_chunk(&h, head->buf, bufsize, &thislen); if (thislen == 0) break; if (thislen > len) thislen = len; memcpy(dst, src, thislen); update_write_chunk(&h, bufsize, thislen); src += thislen; len -= thislen; ret += thislen; } /* Must have written data before updating head. */ mb(); *head = h; return ret; } static int read_buf(struct ringbuf_head *head, u32 bufsize, char *dst, u32 len) { int ret = 0; struct ringbuf_head h; /* Must read head once, and before anything else. */ h = *head; mb(); if (!check_buffer(&h, bufsize)) { errno = EIO; return -1; } while (len > 0) { u32 thislen; const char *src; src = get_read_chunk(&h, head->buf, bufsize, &thislen); if (thislen == 0) break; if (thislen > len) thislen = len; memcpy(dst, src, thislen); update_read_chunk(&h, bufsize, thislen); dst += thislen; len -= thislen; ret += thislen; } /* Must have read data before updating head. */ mb(); *head = h; return ret; } static int write_console(struct xen_console *con, const void *data, unsigned int len) { int ret; ret = write_buf(&con->buf->inbuf, sizeof(con->buf->inbuf_buf), data, len); if (ret >= 0) xc_evtchn_send(xc_handle, con->evtchn); return ret; } static int read_console(struct xen_console *con, void *data, unsigned int len) { int ret; if (con->buf->outbuf.flags) fprintf(stderr, "*** WARNING: console %s\n", con->buf->outbuf.flags & XEN_CONSOLE_FLAG_EIO ? "reports IO error" : con->buf->outbuf.flags & XEN_CONSOLE_FLAG_OVERFLOW ? "has overflowed: missed output" : "deeply confused"); ret = read_buf(&con->buf->outbuf, sizeof(con->buf->outbuf_buf), data, len); if (ret >= 0) xc_evtchn_send(xc_handle, con->evtchn); return ret; } static void report_buffer(const struct xen_console *con) { printf("input: write marker %i read marker %i\n", con->buf->inbuf.write, con->buf->inbuf.read); printf("output: write marker %i read marker %i\n", con->buf->outbuf.write, con->buf->outbuf.read); } int main(int argc, char **argv) { int port1 = 0, port2 = 0, eventchn_fd, rlen; long frame; unsigned int len; struct xs_handle *xs; char buffer[4096]; struct xen_console console; char *p; if (argc != 2) { printf("Usage: %s DOMID\n", argv[0]); return 1; } xc_handle = xc_interface_open(); if (xc_handle == -1) { printf("xc_interface_open() failed: %m"); return 1; } xs = xs_daemon_open(); if (xs == NULL) { printf("xs_daemon_open() failed: %m\n"); return 1; } sprintf(buffer, "/domain/%s/console/event-channel", argv[1]); p = xs_read(xs, buffer, &len); if (!p) { printf("reading %s failed: %m\n", buffer); return 1; } port2 = atoi(p); /* FIXME: Handle multi-page */ sprintf(buffer, "/domain/%s/console/frames", argv[1]); p = xs_read(xs, buffer, &len); if (!p) { printf("reading %s failed: %m\n", buffer); return 1; } frame = atol(p); if (xc_evtchn_bind_interdomain(xc_handle, DOMID_SELF, atoi(argv[1]), &port1, &port2) == -1) { printf("xc_evtchn_bind_interdomain() failed: %m\n"); return 1; } console.evtchn = port1; console.buf = xc_map_foreign_range(xc_handle, atoi(argv[1]), getpagesize(), PROT_READ|PROT_WRITE, frame); if (!console.buf) { printf("xc_map_foreign_range of %li failed: %m\n", frame); return 1; } eventchn_fd = open("/dev/xen/evtchn", O_RDWR); if (eventchn_fd < 0) { printf("opening %s: %m\n", "/dev/xen/evtchn"); return 1; } if (ioctl(eventchn_fd, EVENTCHN_BIND, console.evtchn) != 0) { printf("binding to %i: %m\n", console.evtchn); return 1; } printf("Got event channel %i, frame %li\n", port2, frame); /* Dump backlog. */ while ((rlen = read_console(&console, buffer, 4096))>0) write(STDOUT_FILENO, buffer, rlen); if (rlen < 0) { printf("Failed to read from console: %m\n"); exit(1); } /* FIXME: We assume we can write without blocking. */ for (;;) { fd_set inset; FD_ZERO(&inset); FD_SET(STDIN_FILENO, &inset); FD_SET(eventchn_fd, &inset); select(eventchn_fd+1, &inset, NULL, NULL, NULL); report_buffer(&console); if (FD_ISSET(eventchn_fd, &inset)) { u16 port; printf("Eventchn fd!\n"); if (read(eventchn_fd, &port, 2) != 2) { printf("Failed to read from event fd: %m\n"); exit(1); } while ((rlen = read_console(&console, buffer, 4096))>0) write(STDOUT_FILENO, buffer, rlen); if (rlen < 0) { printf("Failed to read from console: %m\n"); exit(1); } if (write(eventchn_fd, &port, 2) != 2) { printf("Failed to write to event fd: %m\n"); exit(1); } } if (FD_ISSET(STDIN_FILENO, &inset)) { printf("Stdin fd!\n"); rlen = read(STDIN_FILENO, buffer, 4096); if (rlen < 0) { printf("Failed to read from stdin: %m\n"); exit(1); } if (write_console(&console, buffer, rlen) != rlen) { printf("Failed to write to console: %m\n"); exit(1); } } report_buffer(&console); } }