📄 xenfb.c
字号:
#include <stdarg.h>#include <stdlib.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>#include <xenctrl.h>#include <xen/io/xenbus.h>#include <xen/io/fbif.h>#include <xen/io/kbdif.h>#include <xen/io/protocols.h>#include <stdbool.h>#include <xen/event_channel.h>#include <sys/mman.h>#include <errno.h>#include <stdio.h>#include <string.h>#include <time.h>#include <xs.h>#include "xenfb.h"#ifndef BTN_LEFT#define BTN_LEFT 0x110 /* from <linux/input.h> */#endifstruct xenfb;struct xenfb_device { const char *devicetype; char nodename[64]; /* backend xenstore dir */ char otherend[64]; /* frontend xenstore dir */ int otherend_id; /* frontend domid */ enum xenbus_state state; /* backend state */ void *page; /* shared page */ evtchn_port_t port; struct xenfb *xenfb;};struct xenfb { DisplayState *ds; /* QEMU graphical console state */ int evt_xch; /* event channel driver handle */ int xc; /* hypervisor interface handle */ struct xs_handle *xsh; /* xs daemon handle */ struct xenfb_device fb, kbd; void *pixels; /* guest framebuffer data */ size_t fb_len; /* size of framebuffer */ int row_stride; /* width of one row in framebuffer */ int depth; /* colour depth of guest framebuffer */ int width; /* pixel width of guest framebuffer */ int height; /* pixel height of guest framebuffer */ int offset; /* offset of the framebuffer */ int abs_pointer_wanted; /* Whether guest supports absolute pointer */ int button_state; /* Last seen pointer button state */ int refresh_period; /* The refresh period we have advised */ char protocol[64]; /* frontend protocol */};/* Functions for frontend/backend state machine*/static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler);static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler);static void xenfb_backend_created_kbd(void *opaque);static void xenfb_backend_created_fb(void *opaque);static void xenfb_frontend_initialized_kbd(void *opaque);static void xenfb_frontend_initialized_fb(void *opaque);static void xenfb_frontend_connected_kbd(void *opaque);/* Helper functions for checking state of frontend/backend devices */static int xenfb_frontend_connected(struct xenfb_device *dev);static int xenfb_frontend_initialized(struct xenfb_device *dev);static int xenfb_backend_created(struct xenfb_device *dev);/* Functions which tie the PVFB into the QEMU device model */static void xenfb_key_event(void *opaque, int keycode);static void xenfb_mouse_event(void *opaque, int dx, int dy, int dz, int button_state);static void xenfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h);static void xenfb_update(void *opaque);static void xenfb_invalidate(void *opaque);static void xenfb_screen_dump(void *opaque, const char *name);static int xenfb_register_console(struct xenfb *xenfb);/* * Tables to map from scancode to Linux input layer keycode. * Scancodes are hardware-specific. These maps assumes a * standard AT or PS/2 keyboard which is what QEMU feeds us. */const unsigned char atkbd_set2_keycode[512] = { 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,};const unsigned char atkbd_unxlate_table[128] = { 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110};static unsigned char scancode2linux[512];static int xenfb_xs_scanf1(struct xs_handle *xsh, const char *dir, const char *node, const char *fmt, void *dest){ char buf[1024]; char *p; int ret; if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { errno = ENOENT; return -1; } p = xs_read(xsh, XBT_NULL, buf, NULL); if (!p) { errno = ENOENT; return -1; } ret = sscanf(p, fmt, dest); free(p); if (ret != 1) { errno = EDOM; return -1; } return ret;}static int xenfb_xs_printf(struct xs_handle *xsh, const char *dir, const char *node, char *fmt, ...){ va_list ap; char key[1024]; char val[1024]; int n; if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { errno = ENOENT; return -1; } va_start(ap, fmt); n = vsnprintf(val, sizeof(val), fmt, ap); va_end(ap); if (n >= sizeof(val)) { errno = ENOSPC; /* close enough */ return -1; } if (!xs_write(xsh, XBT_NULL, key, val, n)) return -1; return 0;}static void xenfb_device_init(struct xenfb_device *dev, const char *type, struct xenfb *xenfb){ dev->devicetype = type; dev->otherend_id = -1; dev->port = -1; dev->xenfb = xenfb;}static char *xenfb_path_in_dom(struct xs_handle *xsh, char *buf, size_t size, unsigned domid, const char *fmt, ...){ va_list ap; char *domp = xs_get_domain_path(xsh, domid); int n; if (domp == NULL) return NULL; n = snprintf(buf, size, "%s/", domp); free(domp); if (n >= size) return NULL; va_start(ap, fmt); n += vsnprintf(buf + n, size - n, fmt, ap); va_end(ap); if (n >= size) return NULL; return buf;}static int xenfb_device_set_domain(struct xenfb_device *dev, int domid){ dev->otherend_id = domid; if (!xenfb_path_in_dom(dev->xenfb->xsh, dev->otherend, sizeof(dev->otherend), domid, "device/%s/0", dev->devicetype)) { errno = ENOENT; return -1; } if (!xenfb_path_in_dom(dev->xenfb->xsh, dev->nodename, sizeof(dev->nodename), 0, "backend/%s/%d/0", dev->devicetype, domid)) { errno = ENOENT; return -1; } return 0;}struct xenfb *xenfb_new(int domid, DisplayState *ds){ struct xenfb *xenfb = qemu_malloc(sizeof(struct xenfb)); int serrno; int i; if (xenfb == NULL) return NULL; /* Prepare scancode mapping table */ for (i = 0; i < 128; i++) { scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; } memset(xenfb, 0, sizeof(*xenfb)); xenfb->evt_xch = xenfb->xc = -1; xenfb_device_init(&xenfb->fb, "vfb", xenfb); xenfb_device_init(&xenfb->kbd, "vkbd", xenfb); xenfb->evt_xch = xc_evtchn_open(); if (xenfb->evt_xch == -1) goto fail; xenfb->xc = xc_interface_open(); if (xenfb->xc == -1) goto fail; xenfb->xsh = xs_daemon_open(); if (!xenfb->xsh) goto fail; xenfb->ds = ds; xenfb_device_set_domain(&xenfb->fb, domid); xenfb_device_set_domain(&xenfb->kbd, domid); fprintf(stderr, "FB: Waiting for KBD backend creation\n"); xenfb_wait_for_backend(&xenfb->kbd, xenfb_backend_created_kbd); return xenfb; fail: serrno = errno; xenfb_shutdown(xenfb); errno = serrno; return NULL;}static enum xenbus_state xenfb_read_state(struct xs_handle *xsh, const char *dir){ int ret, state; ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state); if (ret < 0) return XenbusStateUnknown; if ((unsigned)state > XenbusStateClosed) state = XenbusStateUnknown; return state;}static int xenfb_switch_state(struct xenfb_device *dev, enum xenbus_state state){ struct xs_handle *xsh = dev->xenfb->xsh; if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0) return -1; dev->state = state; return 0;}static int xenfb_hotplug(struct xenfb_device *dev){ if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, "hotplug-status", "connected")) return -1; return 0;}static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src){ uint32_t *src32 = src; uint64_t *src64 = src; int i; for (i = 0; i < count; i++) dst[i] = (mode == 32) ? src32[i] : src64[i];}static int xenfb_map_fb(struct xenfb *xenfb, int domid){ struct xenfb_page *page = xenfb->fb.page; int n_fbmfns; int n_fbdirs; unsigned long *pgmfns = NULL; unsigned long *fbmfns = NULL; void *map, *pd; int mode, ret = -1; /* default to native */ pd = page->pd; mode = sizeof(unsigned long) * 8; if (0 == strlen(xenfb->protocol)) { /* * Undefined protocol, some guesswork needed. * * Old frontends which don't set the protocol use * one page directory only, thus pd[1] must be zero. * pd[1] of the 32bit struct layout and the lower * 32 bits of pd[0] of the 64bit struct layout have * the same location, so we can check that ... */ uint32_t *ptr32 = NULL; uint32_t *ptr64 = NULL;#if defined(__i386__) ptr32 = (void*)page->pd; ptr64 = ((void*)page->pd) + 4;#elif defined(__x86_64__) ptr32 = ((void*)page->pd) - 4; ptr64 = (void*)page->pd;#endif if (ptr32) { if (0 == ptr32[1]) { mode = 32; pd = ptr32; } else { mode = 64; pd = ptr64; } }#if defined(__x86_64__) } else if (0 == strcmp(xenfb->protocol, XEN_IO_PROTO_ABI_X86_32)) { /* 64bit dom0, 32bit domU */ mode = 32; pd = ((void*)page->pd) - 4;#elif defined(__i386__) } else if (0 == strcmp(xenfb->protocol, XEN_IO_PROTO_ABI_X86_64)) { /* 32bit dom0, 64bit domU */ mode = 64; pd = ((void*)page->pd) + 4;#endif } n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; n_fbdirs = n_fbmfns * mode / 8; n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; pgmfns = malloc(sizeof(unsigned long) * n_fbdirs); fbmfns = malloc(sizeof(unsigned long) * n_fbmfns); if (!pgmfns || !fbmfns) goto out; xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); map = xc_map_foreign_pages(xenfb->xc, domid, PROT_READ, pgmfns, n_fbdirs); if (map == NULL) goto out; xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map); munmap(map, n_fbdirs * XC_PAGE_SIZE); xenfb->pixels = xc_map_foreign_pages(xenfb->xc, domid, PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); if (xenfb->pixels == NULL) goto out; ret = 0; /* all is fine */ out: if (pgmfns) free(pgmfns); if (fbmfns) free(fbmfns); return ret;}static int xenfb_bind(struct xenfb_device *dev){ struct xenfb *xenfb = dev->xenfb; unsigned long mfn; evtchn_port_t evtchn; if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu", &mfn) < 0) return -1; if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u", &evtchn) < 0) return -1; dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch, dev->otherend_id, evtchn); if (dev->port == -1) return -1; dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id, XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); if (dev->page == NULL) return -1; return 0;}static void xenfb_unbind(struct xenfb_device *dev){ if (dev->page) { munmap(dev->page, XC_PAGE_SIZE); dev->page = NULL; } if (dev->port >= 0) { xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port); dev->port = -1; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -