diff options
Diffstat (limited to 'qemu-char.c')
-rw-r--r-- | qemu-char.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/qemu-char.c b/qemu-char.c index da1db1d104..ac5d62dd9a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -98,6 +98,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***********************************************************/ /* character device */ @@ -2643,6 +2644,199 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*********************************************************/ +/*CircularMemory chardev*/ + +typedef struct { + size_t size; + size_t prod; + size_t cons; + uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ + const CirMemCharDriver *d = chr->opaque; + + return d->cons == d->prod; +} + +static size_t qemu_chr_cirmem_count(const CharDriverState *chr) +{ + const CirMemCharDriver *d = chr->opaque; + + return (d->prod - d->cons); +} + +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + CirMemCharDriver *d = chr->opaque; + int i; + + if (!buf || (len < 0)) { + return -1; + } + + for (i = 0; i < len; i++ ) { + /* Avoid writing the IAC information to the queue. */ + if ((unsigned char)buf[i] == IAC) { + continue; + } + + d->cbuf[d->prod++ % d->size] = buf[i]; + if ((d->prod - d->cons) > d->size) { + d->cons = d->prod - d->size; + } + } + + return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ + CirMemCharDriver *d = chr->opaque; + int i; + + for (i = 0; i < len && !cirmem_chr_is_empty(chr); i++) { + buf[i] = d->cbuf[d->cons++ % d->size]; + } + + return i; +} + +static void cirmem_chr_close(struct CharDriverState *chr) +{ + CirMemCharDriver *d = chr->opaque; + + g_free(d->cbuf); + g_free(d); + chr->opaque = NULL; +} + +static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts) +{ + CharDriverState *chr; + CirMemCharDriver *d; + + chr = g_malloc0(sizeof(CharDriverState)); + d = g_malloc(sizeof(*d)); + + d->size = qemu_opt_get_number(opts, "maxcapacity", 0); + if (d->size == 0) { + d->size = CBUFF_SIZE; + } + + /* The size must be power of 2 */ + if (d->size & (d->size - 1)) { + fprintf(stderr, "chardev: size of memory device must be power of 2\n"); + goto fail; + } + + d->prod = 0; + d->cons = 0; + d->cbuf = g_malloc0(d->size); + + chr->opaque = d; + chr->chr_write = cirmem_chr_write; + chr->chr_close = cirmem_chr_close; + + return chr; + +fail: + g_free(d); + g_free(chr); + return NULL; +} + +static bool qemu_is_chr(const CharDriverState *chr, const char *filename) +{ + return strcmp(chr->filename, filename); +} + +void qmp_memchar_write(const char *device, int64_t size, + const char *data, bool has_format, + enum DataFormat format, + Error **errp) +{ + CharDriverState *chr; + guchar *write_data; + int ret; + gsize write_count; + + chr = qemu_chr_find(device); + if (!chr) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + if (qemu_is_chr(chr, "memory")) { + error_setg(errp,"%s is not memory char device", device); + return; + } + + write_count = (gsize)size; + + if (has_format && (format == DATA_FORMAT_BASE64)) { + write_data = g_base64_decode(data, &write_count); + } else { + write_data = (uint8_t *)data; + } + + ret = cirmem_chr_write(chr, write_data, write_count); + + if (ret < 0) { + error_setg(errp, "Failed to write to device %s", device); + return; + } +} + +MemCharRead *qmp_memchar_read(const char *device, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ + CharDriverState *chr; + guchar *read_data; + MemCharRead *meminfo; + size_t count; + + chr = qemu_chr_find(device); + if (!chr) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return NULL; + } + + if (qemu_is_chr(chr, "memory")) { + error_setg(errp,"%s is not memory char device", device); + return NULL; + } + + if (size <= 0) { + error_setg(errp, "size must be greater than zero"); + return NULL; + } + + meminfo = g_malloc0(sizeof(MemCharRead)); + + count = qemu_chr_cirmem_count(chr); + if (count == 0) { + meminfo->data = g_strdup(""); + return meminfo; + } + + size = size > count ? count : size; + read_data = g_malloc0(size + 1); + + meminfo->count = cirmem_chr_read(chr, read_data, size); + + if (has_format && (format == DATA_FORMAT_BASE64)) { + meminfo->data = g_base64_encode(read_data, (size_t)meminfo->count); + } else { + meminfo->data = (char *)read_data; + } + + return meminfo; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2697,6 +2891,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", filename); return opts; } + if (strstart(filename, "memory", &p)) { + qemu_opt_set(opts, "backend", "memory"); + qemu_opt_set(opts, "maxcapacity", p); + return opts; + } if (strstart(filename, "file:", &p)) { qemu_opt_set(opts, "backend", "file"); qemu_opt_set(opts, "path", p); @@ -2796,6 +2995,7 @@ static const struct { { .name = "udp", .open = qemu_chr_open_udp }, { .name = "msmouse", .open = qemu_chr_open_msmouse }, { .name = "vc", .open = text_console_init }, + { .name = "memory", .open = qemu_chr_open_cirmemchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, @@ -3055,6 +3255,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, + },{ + .name = "maxcapacity", + .type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, |