diff options
author | Timothy Pearson <tpearson@raptorengineering.com> | 2019-05-11 15:12:49 -0500 |
---|---|---|
committer | Timothy Pearson <tpearson@raptorengineering.com> | 2019-05-11 15:12:49 -0500 |
commit | 9e80202352dd49bdd9e67b8b906d86f058431505 (patch) | |
tree | 5673c17aad6e3833da8c4ff21b5a11f666ec9fbe /src/backends | |
download | hqemu-9e80202352dd49bdd9e67b8b906d86f058431505.zip hqemu-9e80202352dd49bdd9e67b8b906d86f058431505.tar.gz |
Diffstat (limited to 'src/backends')
-rw-r--r-- | src/backends/Makefile.objs | 11 | ||||
-rw-r--r-- | src/backends/baum.c | 642 | ||||
-rw-r--r-- | src/backends/hostmem-file.c | 132 | ||||
-rw-r--r-- | src/backends/hostmem-ram.c | 53 | ||||
-rw-r--r-- | src/backends/hostmem.c | 374 | ||||
-rw-r--r-- | src/backends/msmouse.c | 89 | ||||
-rw-r--r-- | src/backends/rng-egd.c | 233 | ||||
-rw-r--r-- | src/backends/rng-random.c | 156 | ||||
-rw-r--r-- | src/backends/rng.c | 109 | ||||
-rw-r--r-- | src/backends/testdev.c | 135 | ||||
-rw-r--r-- | src/backends/tpm.c | 196 |
11 files changed, 2130 insertions, 0 deletions
diff --git a/src/backends/Makefile.objs b/src/backends/Makefile.objs new file mode 100644 index 0000000..31a3a89 --- /dev/null +++ b/src/backends/Makefile.objs @@ -0,0 +1,11 @@ +common-obj-y += rng.o rng-egd.o +common-obj-$(CONFIG_POSIX) += rng-random.o + +common-obj-y += msmouse.o testdev.o +common-obj-$(CONFIG_BRLAPI) += baum.o +baum.o-cflags := $(SDL_CFLAGS) + +common-obj-$(CONFIG_TPM) += tpm.o + +common-obj-y += hostmem.o hostmem-ram.o +common-obj-$(CONFIG_LINUX) += hostmem-file.o diff --git a/src/backends/baum.c b/src/backends/baum.c new file mode 100644 index 0000000..723c658 --- /dev/null +++ b/src/backends/baum.c @@ -0,0 +1,642 @@ +/* + * QEMU Baum Braille Device + * + * Copyright (c) 2008 Samuel Thibault + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (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 "qemu-common.h" +#include "sysemu/char.h" +#include "qemu/timer.h" +#include "hw/usb.h" +#include <brlapi.h> +#include <brlapi_constants.h> +#include <brlapi_keycodes.h> +#ifdef CONFIG_SDL +#include <SDL_syswm.h> +#endif + +#if 0 +#define DPRINTF(fmt, ...) \ + printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +#define ESC 0x1B + +#define BAUM_REQ_DisplayData 0x01 +#define BAUM_REQ_GetVersionNumber 0x05 +#define BAUM_REQ_GetKeys 0x08 +#define BAUM_REQ_SetMode 0x12 +#define BAUM_REQ_SetProtocol 0x15 +#define BAUM_REQ_GetDeviceIdentity 0x84 +#define BAUM_REQ_GetSerialNumber 0x8A + +#define BAUM_RSP_CellCount 0x01 +#define BAUM_RSP_VersionNumber 0x05 +#define BAUM_RSP_ModeSetting 0x11 +#define BAUM_RSP_CommunicationChannel 0x16 +#define BAUM_RSP_PowerdownSignal 0x17 +#define BAUM_RSP_HorizontalSensors 0x20 +#define BAUM_RSP_VerticalSensors 0x21 +#define BAUM_RSP_RoutingKeys 0x22 +#define BAUM_RSP_Switches 0x23 +#define BAUM_RSP_TopKeys 0x24 +#define BAUM_RSP_HorizontalSensor 0x25 +#define BAUM_RSP_VerticalSensor 0x26 +#define BAUM_RSP_RoutingKey 0x27 +#define BAUM_RSP_FrontKeys6 0x28 +#define BAUM_RSP_BackKeys6 0x29 +#define BAUM_RSP_CommandKeys 0x2B +#define BAUM_RSP_FrontKeys10 0x2C +#define BAUM_RSP_BackKeys10 0x2D +#define BAUM_RSP_EntryKeys 0x33 +#define BAUM_RSP_JoyStick 0x34 +#define BAUM_RSP_ErrorCode 0x40 +#define BAUM_RSP_InfoBlock 0x42 +#define BAUM_RSP_DeviceIdentity 0x84 +#define BAUM_RSP_SerialNumber 0x8A +#define BAUM_RSP_BluetoothName 0x8C + +#define BAUM_TL1 0x01 +#define BAUM_TL2 0x02 +#define BAUM_TL3 0x04 +#define BAUM_TR1 0x08 +#define BAUM_TR2 0x10 +#define BAUM_TR3 0x20 + +#define BUF_SIZE 256 + +typedef struct { + CharDriverState *chr; + + brlapi_handle_t *brlapi; + int brlapi_fd; + unsigned int x, y; + + uint8_t in_buf[BUF_SIZE]; + uint8_t in_buf_used; + uint8_t out_buf[BUF_SIZE]; + uint8_t out_buf_used, out_buf_ptr; + + QEMUTimer *cellCount_timer; +} BaumDriverState; + +/* Let's assume NABCC by default */ +static const uint8_t nabcc_translation[256] = { + [0] = ' ', +#ifndef BRLAPI_DOTS +#define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \ + ((d1?BRLAPI_DOT1:0)|\ + (d2?BRLAPI_DOT2:0)|\ + (d3?BRLAPI_DOT3:0)|\ + (d4?BRLAPI_DOT4:0)|\ + (d5?BRLAPI_DOT5:0)|\ + (d6?BRLAPI_DOT6:0)|\ + (d7?BRLAPI_DOT7:0)|\ + (d8?BRLAPI_DOT8:0)) +#endif + [BRLAPI_DOTS(1,0,0,0,0,0,0,0)] = 'a', + [BRLAPI_DOTS(1,1,0,0,0,0,0,0)] = 'b', + [BRLAPI_DOTS(1,0,0,1,0,0,0,0)] = 'c', + [BRLAPI_DOTS(1,0,0,1,1,0,0,0)] = 'd', + [BRLAPI_DOTS(1,0,0,0,1,0,0,0)] = 'e', + [BRLAPI_DOTS(1,1,0,1,0,0,0,0)] = 'f', + [BRLAPI_DOTS(1,1,0,1,1,0,0,0)] = 'g', + [BRLAPI_DOTS(1,1,0,0,1,0,0,0)] = 'h', + [BRLAPI_DOTS(0,1,0,1,0,0,0,0)] = 'i', + [BRLAPI_DOTS(0,1,0,1,1,0,0,0)] = 'j', + [BRLAPI_DOTS(1,0,1,0,0,0,0,0)] = 'k', + [BRLAPI_DOTS(1,1,1,0,0,0,0,0)] = 'l', + [BRLAPI_DOTS(1,0,1,1,0,0,0,0)] = 'm', + [BRLAPI_DOTS(1,0,1,1,1,0,0,0)] = 'n', + [BRLAPI_DOTS(1,0,1,0,1,0,0,0)] = 'o', + [BRLAPI_DOTS(1,1,1,1,0,0,0,0)] = 'p', + [BRLAPI_DOTS(1,1,1,1,1,0,0,0)] = 'q', + [BRLAPI_DOTS(1,1,1,0,1,0,0,0)] = 'r', + [BRLAPI_DOTS(0,1,1,1,0,0,0,0)] = 's', + [BRLAPI_DOTS(0,1,1,1,1,0,0,0)] = 't', + [BRLAPI_DOTS(1,0,1,0,0,1,0,0)] = 'u', + [BRLAPI_DOTS(1,1,1,0,0,1,0,0)] = 'v', + [BRLAPI_DOTS(0,1,0,1,1,1,0,0)] = 'w', + [BRLAPI_DOTS(1,0,1,1,0,1,0,0)] = 'x', + [BRLAPI_DOTS(1,0,1,1,1,1,0,0)] = 'y', + [BRLAPI_DOTS(1,0,1,0,1,1,0,0)] = 'z', + + [BRLAPI_DOTS(1,0,0,0,0,0,1,0)] = 'A', + [BRLAPI_DOTS(1,1,0,0,0,0,1,0)] = 'B', + [BRLAPI_DOTS(1,0,0,1,0,0,1,0)] = 'C', + [BRLAPI_DOTS(1,0,0,1,1,0,1,0)] = 'D', + [BRLAPI_DOTS(1,0,0,0,1,0,1,0)] = 'E', + [BRLAPI_DOTS(1,1,0,1,0,0,1,0)] = 'F', + [BRLAPI_DOTS(1,1,0,1,1,0,1,0)] = 'G', + [BRLAPI_DOTS(1,1,0,0,1,0,1,0)] = 'H', + [BRLAPI_DOTS(0,1,0,1,0,0,1,0)] = 'I', + [BRLAPI_DOTS(0,1,0,1,1,0,1,0)] = 'J', + [BRLAPI_DOTS(1,0,1,0,0,0,1,0)] = 'K', + [BRLAPI_DOTS(1,1,1,0,0,0,1,0)] = 'L', + [BRLAPI_DOTS(1,0,1,1,0,0,1,0)] = 'M', + [BRLAPI_DOTS(1,0,1,1,1,0,1,0)] = 'N', + [BRLAPI_DOTS(1,0,1,0,1,0,1,0)] = 'O', + [BRLAPI_DOTS(1,1,1,1,0,0,1,0)] = 'P', + [BRLAPI_DOTS(1,1,1,1,1,0,1,0)] = 'Q', + [BRLAPI_DOTS(1,1,1,0,1,0,1,0)] = 'R', + [BRLAPI_DOTS(0,1,1,1,0,0,1,0)] = 'S', + [BRLAPI_DOTS(0,1,1,1,1,0,1,0)] = 'T', + [BRLAPI_DOTS(1,0,1,0,0,1,1,0)] = 'U', + [BRLAPI_DOTS(1,1,1,0,0,1,1,0)] = 'V', + [BRLAPI_DOTS(0,1,0,1,1,1,1,0)] = 'W', + [BRLAPI_DOTS(1,0,1,1,0,1,1,0)] = 'X', + [BRLAPI_DOTS(1,0,1,1,1,1,1,0)] = 'Y', + [BRLAPI_DOTS(1,0,1,0,1,1,1,0)] = 'Z', + + [BRLAPI_DOTS(0,0,1,0,1,1,0,0)] = '0', + [BRLAPI_DOTS(0,1,0,0,0,0,0,0)] = '1', + [BRLAPI_DOTS(0,1,1,0,0,0,0,0)] = '2', + [BRLAPI_DOTS(0,1,0,0,1,0,0,0)] = '3', + [BRLAPI_DOTS(0,1,0,0,1,1,0,0)] = '4', + [BRLAPI_DOTS(0,1,0,0,0,1,0,0)] = '5', + [BRLAPI_DOTS(0,1,1,0,1,0,0,0)] = '6', + [BRLAPI_DOTS(0,1,1,0,1,1,0,0)] = '7', + [BRLAPI_DOTS(0,1,1,0,0,1,0,0)] = '8', + [BRLAPI_DOTS(0,0,1,0,1,0,0,0)] = '9', + + [BRLAPI_DOTS(0,0,0,1,0,1,0,0)] = '.', + [BRLAPI_DOTS(0,0,1,1,0,1,0,0)] = '+', + [BRLAPI_DOTS(0,0,1,0,0,1,0,0)] = '-', + [BRLAPI_DOTS(1,0,0,0,0,1,0,0)] = '*', + [BRLAPI_DOTS(0,0,1,1,0,0,0,0)] = '/', + [BRLAPI_DOTS(1,1,1,0,1,1,0,0)] = '(', + [BRLAPI_DOTS(0,1,1,1,1,1,0,0)] = ')', + + [BRLAPI_DOTS(1,1,1,1,0,1,0,0)] = '&', + [BRLAPI_DOTS(0,0,1,1,1,1,0,0)] = '#', + + [BRLAPI_DOTS(0,0,0,0,0,1,0,0)] = ',', + [BRLAPI_DOTS(0,0,0,0,1,1,0,0)] = ';', + [BRLAPI_DOTS(1,0,0,0,1,1,0,0)] = ':', + [BRLAPI_DOTS(0,1,1,1,0,1,0,0)] = '!', + [BRLAPI_DOTS(1,0,0,1,1,1,0,0)] = '?', + [BRLAPI_DOTS(0,0,0,0,1,0,0,0)] = '"', + [BRLAPI_DOTS(0,0,1,0,0,0,0,0)] ='\'', + [BRLAPI_DOTS(0,0,0,1,0,0,0,0)] = '`', + [BRLAPI_DOTS(0,0,0,1,1,0,1,0)] = '^', + [BRLAPI_DOTS(0,0,0,1,1,0,0,0)] = '~', + [BRLAPI_DOTS(0,1,0,1,0,1,1,0)] = '[', + [BRLAPI_DOTS(1,1,0,1,1,1,1,0)] = ']', + [BRLAPI_DOTS(0,1,0,1,0,1,0,0)] = '{', + [BRLAPI_DOTS(1,1,0,1,1,1,0,0)] = '}', + [BRLAPI_DOTS(1,1,1,1,1,1,0,0)] = '=', + [BRLAPI_DOTS(1,1,0,0,0,1,0,0)] = '<', + [BRLAPI_DOTS(0,0,1,1,1,0,0,0)] = '>', + [BRLAPI_DOTS(1,1,0,1,0,1,0,0)] = '$', + [BRLAPI_DOTS(1,0,0,1,0,1,0,0)] = '%', + [BRLAPI_DOTS(0,0,0,1,0,0,1,0)] = '@', + [BRLAPI_DOTS(1,1,0,0,1,1,0,0)] = '|', + [BRLAPI_DOTS(1,1,0,0,1,1,1,0)] ='\\', + [BRLAPI_DOTS(0,0,0,1,1,1,0,0)] = '_', +}; + +/* The serial port can receive more of our data */ +static void baum_accept_input(struct CharDriverState *chr) +{ + BaumDriverState *baum = chr->opaque; + int room, first; + + if (!baum->out_buf_used) + return; + room = qemu_chr_be_can_write(chr); + if (!room) + return; + if (room > baum->out_buf_used) + room = baum->out_buf_used; + + first = BUF_SIZE - baum->out_buf_ptr; + if (room > first) { + qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, first); + baum->out_buf_ptr = 0; + baum->out_buf_used -= first; + room -= first; + } + qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, room); + baum->out_buf_ptr += room; + baum->out_buf_used -= room; +} + +/* We want to send a packet */ +static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len) +{ + uint8_t io_buf[1 + 2 * len], *cur = io_buf; + int room; + *cur++ = ESC; + while (len--) + if ((*cur++ = *buf++) == ESC) + *cur++ = ESC; + room = qemu_chr_be_can_write(baum->chr); + len = cur - io_buf; + if (len <= room) { + /* Fits */ + qemu_chr_be_write(baum->chr, io_buf, len); + } else { + int first; + uint8_t out; + /* Can't fit all, send what can be, and store the rest. */ + qemu_chr_be_write(baum->chr, io_buf, room); + len -= room; + cur = io_buf + room; + if (len > BUF_SIZE - baum->out_buf_used) { + /* Can't even store it, drop the previous data... */ + assert(len <= BUF_SIZE); + baum->out_buf_used = 0; + baum->out_buf_ptr = 0; + } + out = baum->out_buf_ptr; + baum->out_buf_used += len; + first = BUF_SIZE - baum->out_buf_ptr; + if (len > first) { + memcpy(baum->out_buf + out, cur, first); + out = 0; + len -= first; + cur += first; + } + memcpy(baum->out_buf + out, cur, len); + } +} + +/* Called when the other end seems to have a wrong idea of our display size */ +static void baum_cellCount_timer_cb(void *opaque) +{ + BaumDriverState *baum = opaque; + uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y }; + DPRINTF("Timeout waiting for DisplayData, sending cell count\n"); + baum_write_packet(baum, cell_count, sizeof(cell_count)); +} + +/* Try to interpret a whole incoming packet */ +static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) +{ + const uint8_t *cur = buf; + uint8_t req = 0; + + if (!len--) + return 0; + if (*cur++ != ESC) { + while (*cur != ESC) { + if (!len--) + return 0; + cur++; + } + DPRINTF("Dropped %td bytes!\n", cur - buf); + } + +#define EAT(c) do {\ + if (!len--) \ + return 0; \ + if ((c = *cur++) == ESC) { \ + if (!len--) \ + return 0; \ + if (*cur++ != ESC) { \ + DPRINTF("Broken packet %#2x, tossing\n", req); \ + if (timer_pending(baum->cellCount_timer)) { \ + timer_del(baum->cellCount_timer); \ + baum_cellCount_timer_cb(baum); \ + } \ + return (cur - 2 - buf); \ + } \ + } \ +} while (0) + + EAT(req); + switch (req) { + case BAUM_REQ_DisplayData: + { + uint8_t cells[baum->x * baum->y], c; + uint8_t text[baum->x * baum->y]; + uint8_t zero[baum->x * baum->y]; + int cursor = BRLAPI_CURSOR_OFF; + int i; + + /* Allow 100ms to complete the DisplayData packet */ + timer_mod(baum->cellCount_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + get_ticks_per_sec() / 10); + for (i = 0; i < baum->x * baum->y ; i++) { + EAT(c); + cells[i] = c; + if ((c & (BRLAPI_DOT7|BRLAPI_DOT8)) + == (BRLAPI_DOT7|BRLAPI_DOT8)) { + cursor = i + 1; + c &= ~(BRLAPI_DOT7|BRLAPI_DOT8); + } + if (!(c = nabcc_translation[c])) + c = '?'; + text[i] = c; + } + timer_del(baum->cellCount_timer); + + memset(zero, 0, sizeof(zero)); + + brlapi_writeArguments_t wa = { + .displayNumber = BRLAPI_DISPLAY_DEFAULT, + .regionBegin = 1, + .regionSize = baum->x * baum->y, + .text = (char *)text, + .textSize = baum->x * baum->y, + .andMask = zero, + .orMask = cells, + .cursor = cursor, + .charset = (char *)"ISO-8859-1", + }; + + if (brlapi__write(baum->brlapi, &wa) == -1) + brlapi_perror("baum brlapi_write"); + break; + } + case BAUM_REQ_SetMode: + { + uint8_t mode, setting; + DPRINTF("SetMode\n"); + EAT(mode); + EAT(setting); + /* ignore */ + break; + } + case BAUM_REQ_SetProtocol: + { + uint8_t protocol; + DPRINTF("SetProtocol\n"); + EAT(protocol); + /* ignore */ + break; + } + case BAUM_REQ_GetDeviceIdentity: + { + uint8_t identity[17] = { BAUM_RSP_DeviceIdentity, + 'B','a','u','m',' ','V','a','r','i','o' }; + DPRINTF("GetDeviceIdentity\n"); + identity[11] = '0' + baum->x / 10; + identity[12] = '0' + baum->x % 10; + baum_write_packet(baum, identity, sizeof(identity)); + break; + } + case BAUM_REQ_GetVersionNumber: + { + uint8_t version[] = { BAUM_RSP_VersionNumber, 1 }; /* ? */ + DPRINTF("GetVersionNumber\n"); + baum_write_packet(baum, version, sizeof(version)); + break; + } + case BAUM_REQ_GetSerialNumber: + { + uint8_t serial[] = { BAUM_RSP_SerialNumber, + '0','0','0','0','0','0','0','0' }; + DPRINTF("GetSerialNumber\n"); + baum_write_packet(baum, serial, sizeof(serial)); + break; + } + case BAUM_REQ_GetKeys: + { + DPRINTF("Get%0#2x\n", req); + /* ignore */ + break; + } + default: + DPRINTF("unrecognized request %0#2x\n", req); + do + if (!len--) + return 0; + while (*cur++ != ESC); + cur--; + break; + } + return cur - buf; +} + +/* The other end is writing some data. Store it and try to interpret */ +static int baum_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + BaumDriverState *baum = chr->opaque; + int tocopy, cur, eaten, orig_len = len; + + if (!len) + return 0; + if (!baum->brlapi) + return len; + + while (len) { + /* Complete our buffer as much as possible */ + tocopy = len; + if (tocopy > BUF_SIZE - baum->in_buf_used) + tocopy = BUF_SIZE - baum->in_buf_used; + + memcpy(baum->in_buf + baum->in_buf_used, buf, tocopy); + baum->in_buf_used += tocopy; + buf += tocopy; + len -= tocopy; + + /* Interpret it as much as possible */ + cur = 0; + while (cur < baum->in_buf_used && + (eaten = baum_eat_packet(baum, baum->in_buf + cur, baum->in_buf_used - cur))) + cur += eaten; + + /* Shift the remainder */ + if (cur) { + memmove(baum->in_buf, baum->in_buf + cur, baum->in_buf_used - cur); + baum->in_buf_used -= cur; + } + + /* And continue if any data left */ + } + return orig_len; +} + +/* Send the key code to the other end */ +static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) { + uint8_t packet[] = { type, value }; + DPRINTF("writing key %x %x\n", type, value); + baum_write_packet(baum, packet, sizeof(packet)); +} + +/* We got some data on the BrlAPI socket */ +static void baum_chr_read(void *opaque) +{ + BaumDriverState *baum = opaque; + brlapi_keyCode_t code; + int ret; + if (!baum->brlapi) + return; + while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) { + DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code); + /* Emulate */ + switch (code & BRLAPI_KEY_TYPE_MASK) { + case BRLAPI_KEY_TYPE_CMD: + switch (code & BRLAPI_KEY_CMD_BLK_MASK) { + case BRLAPI_KEY_CMD_ROUTE: + baum_send_key(baum, BAUM_RSP_RoutingKey, (code & BRLAPI_KEY_CMD_ARG_MASK)+1); + baum_send_key(baum, BAUM_RSP_RoutingKey, 0); + break; + case 0: + switch (code & BRLAPI_KEY_CMD_ARG_MASK) { + case BRLAPI_KEY_CMD_FWINLT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_FWINRT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR2); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_LNUP: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_LNDN: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_TOP: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_BOT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL3|BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_TOP_LEFT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_BOT_LEFT: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_HOME: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1|BAUM_TR3); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + case BRLAPI_KEY_CMD_PREFMENU: + baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TL3|BAUM_TR1); + baum_send_key(baum, BAUM_RSP_TopKeys, 0); + break; + } + } + break; + case BRLAPI_KEY_TYPE_SYM: + break; + } + } + if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) { + brlapi_perror("baum: brlapi_readKey"); + brlapi__closeConnection(baum->brlapi); + g_free(baum->brlapi); + baum->brlapi = NULL; + } +} + +static void baum_close(struct CharDriverState *chr) +{ + BaumDriverState *baum = chr->opaque; + + timer_free(baum->cellCount_timer); + if (baum->brlapi) { + brlapi__closeConnection(baum->brlapi); + g_free(baum->brlapi); + } + g_free(baum); +} + +static CharDriverState *chr_baum_init(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) +{ + BaumDriverState *baum; + CharDriverState *chr; + brlapi_handle_t *handle; +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + SDL_SysWMinfo info; +#endif +#endif + int tty; + + baum = g_malloc0(sizeof(BaumDriverState)); + baum->chr = chr = qemu_chr_alloc(); + + chr->opaque = baum; + chr->chr_write = baum_write; + chr->chr_accept_input = baum_accept_input; + chr->chr_close = baum_close; + + handle = g_malloc0(brlapi_getHandleSize()); + baum->brlapi = handle; + + baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); + if (baum->brlapi_fd == -1) { + error_setg(errp, "brlapi__openConnection: %s", + brlapi_strerror(brlapi_error_location())); + goto fail_handle; + } + + baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); + + if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) { + error_setg(errp, "brlapi__getDisplaySize: %s", + brlapi_strerror(brlapi_error_location())); + goto fail; + } + +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + memset(&info, 0, sizeof(info)); + SDL_VERSION(&info.version); + if (SDL_GetWMInfo(&info)) + tty = info.info.x11.wmwindow; + else +#endif +#endif + tty = BRLAPI_TTY_DEFAULT; + + if (brlapi__enterTtyMode(handle, tty, NULL) == -1) { + error_setg(errp, "brlapi__enterTtyMode: %s", + brlapi_strerror(brlapi_error_location())); + goto fail; + } + + qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); + + return chr; + +fail: + timer_free(baum->cellCount_timer); + brlapi__closeConnection(handle); +fail_handle: + g_free(handle); + g_free(chr); + g_free(baum); + return NULL; +} + +static void register_types(void) +{ + register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, + chr_baum_init); +} + +type_init(register_types); diff --git a/src/backends/hostmem-file.c b/src/backends/hostmem-file.c new file mode 100644 index 0000000..e9b6d21 --- /dev/null +++ b/src/backends/hostmem-file.c @@ -0,0 +1,132 @@ +/* + * QEMU Host Memory Backend for hugetlbfs + * + * Copyright (C) 2013-2014 Red Hat Inc + * + * Authors: + * Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu-common.h" +#include "sysemu/hostmem.h" +#include "sysemu/sysemu.h" +#include "qom/object_interfaces.h" + +/* hostmem-file.c */ +/** + * @TYPE_MEMORY_BACKEND_FILE: + * name of backend that uses mmap on a file descriptor + */ +#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file" + +#define MEMORY_BACKEND_FILE(obj) \ + OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE) + +typedef struct HostMemoryBackendFile HostMemoryBackendFile; + +struct HostMemoryBackendFile { + HostMemoryBackend parent_obj; + + bool share; + char *mem_path; +}; + +static void +file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + if (!fb->mem_path) { + error_setg(errp, "mem-path property not set"); + return; + } +#ifndef CONFIG_LINUX + error_setg(errp, "-mem-path not supported on this host"); +#else + if (!memory_region_size(&backend->mr)) { + backend->force_prealloc = mem_prealloc; + memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), + object_get_canonical_path(OBJECT(backend)), + backend->size, fb->share, + fb->mem_path, errp); + } +#endif +} + +static void +file_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = file_backend_memory_alloc; +} + +static char *get_mem_path(Object *o, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + return g_strdup(fb->mem_path); +} + +static void set_mem_path(Object *o, const char *str, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + if (memory_region_size(&backend->mr)) { + error_setg(errp, "cannot change property value"); + return; + } + g_free(fb->mem_path); + fb->mem_path = g_strdup(str); +} + +static bool file_memory_backend_get_share(Object *o, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + return fb->share; +} + +static void file_memory_backend_set_share(Object *o, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + if (memory_region_size(&backend->mr)) { + error_setg(errp, "cannot change property value"); + return; + } + fb->share = value; +} + +static void +file_backend_instance_init(Object *o) +{ + object_property_add_bool(o, "share", + file_memory_backend_get_share, + file_memory_backend_set_share, NULL); + object_property_add_str(o, "mem-path", get_mem_path, + set_mem_path, NULL); +} + +static const TypeInfo file_backend_info = { + .name = TYPE_MEMORY_BACKEND_FILE, + .parent = TYPE_MEMORY_BACKEND, + .class_init = file_backend_class_init, + .instance_init = file_backend_instance_init, + .instance_size = sizeof(HostMemoryBackendFile), +}; + +static void register_types(void) +{ + type_register_static(&file_backend_info); +} + +type_init(register_types); diff --git a/src/backends/hostmem-ram.c b/src/backends/hostmem-ram.c new file mode 100644 index 0000000..a67a134 --- /dev/null +++ b/src/backends/hostmem-ram.c @@ -0,0 +1,53 @@ +/* + * QEMU Host Memory Backend + * + * Copyright (C) 2013-2014 Red Hat Inc + * + * Authors: + * Igor Mammedov <imammedo@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "sysemu/hostmem.h" +#include "qom/object_interfaces.h" + +#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" + + +static void +ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + char *path; + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + + path = object_get_canonical_path_component(OBJECT(backend)); + memory_region_init_ram(&backend->mr, OBJECT(backend), path, + backend->size, errp); + g_free(path); +} + +static void +ram_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = ram_backend_memory_alloc; +} + +static const TypeInfo ram_backend_info = { + .name = TYPE_MEMORY_BACKEND_RAM, + .parent = TYPE_MEMORY_BACKEND, + .class_init = ram_backend_class_init, +}; + +static void register_types(void) +{ + type_register_static(&ram_backend_info); +} + +type_init(register_types); diff --git a/src/backends/hostmem.c b/src/backends/hostmem.c new file mode 100644 index 0000000..1b4eb45 --- /dev/null +++ b/src/backends/hostmem.c @@ -0,0 +1,374 @@ +/* + * QEMU Host Memory Backend + * + * Copyright (C) 2013-2014 Red Hat Inc + * + * Authors: + * Igor Mammedov <imammedo@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "sysemu/hostmem.h" +#include "hw/boards.h" +#include "qapi/visitor.h" +#include "qapi-types.h" +#include "qapi-visit.h" +#include "qemu/config-file.h" +#include "qom/object_interfaces.h" + +#ifdef CONFIG_NUMA +#include <numaif.h> +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_DEFAULT != MPOL_DEFAULT); +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_PREFERRED != MPOL_PREFERRED); +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); +QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); +#endif + +static void +host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + uint64_t value = backend->size; + + visit_type_size(v, &value, name, errp); +} + +static void +host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + Error *local_err = NULL; + uint64_t value; + + if (memory_region_size(&backend->mr)) { + error_setg(&local_err, "cannot change property value"); + goto out; + } + + visit_type_size(v, &value, name, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' doesn't take value '%" + PRIu64 "'", object_get_typename(obj), name, value); + goto out; + } + backend->size = value; +out: + error_propagate(errp, local_err); +} + +static void +host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + uint16List *host_nodes = NULL; + uint16List **node = &host_nodes; + unsigned long value; + + value = find_first_bit(backend->host_nodes, MAX_NODES); + if (value == MAX_NODES) { + return; + } + + *node = g_malloc0(sizeof(**node)); + (*node)->value = value; + node = &(*node)->next; + + do { + value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1); + if (value == MAX_NODES) { + break; + } + + *node = g_malloc0(sizeof(**node)); + (*node)->value = value; + node = &(*node)->next; + } while (true); + + visit_type_uint16List(v, &host_nodes, name, errp); +} + +static void +host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ +#ifdef CONFIG_NUMA + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + uint16List *l = NULL; + + visit_type_uint16List(v, &l, name, errp); + + while (l) { + bitmap_set(backend->host_nodes, l->value, 1); + l = l->next; + } +#else + error_setg(errp, "NUMA node binding are not supported by this QEMU"); +#endif +} + +static int +host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + return backend->policy; +} + +static void +host_memory_backend_set_policy(Object *obj, int policy, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + backend->policy = policy; + +#ifndef CONFIG_NUMA + if (policy != HOST_MEM_POLICY_DEFAULT) { + error_setg(errp, "NUMA policies are not supported by this QEMU"); + } +#endif +} + +static bool host_memory_backend_get_merge(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->merge; +} + +static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + if (!memory_region_size(&backend->mr)) { + backend->merge = value; + return; + } + + if (value != backend->merge) { + void *ptr = memory_region_get_ram_ptr(&backend->mr); + uint64_t sz = memory_region_size(&backend->mr); + + qemu_madvise(ptr, sz, + value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE); + backend->merge = value; + } +} + +static bool host_memory_backend_get_dump(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->dump; +} + +static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + if (!memory_region_size(&backend->mr)) { + backend->dump = value; + return; + } + + if (value != backend->dump) { + void *ptr = memory_region_get_ram_ptr(&backend->mr); + uint64_t sz = memory_region_size(&backend->mr); + + qemu_madvise(ptr, sz, + value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP); + backend->dump = value; + } +} + +static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->prealloc || backend->force_prealloc; +} + +static void host_memory_backend_set_prealloc(Object *obj, bool value, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + if (backend->force_prealloc) { + if (value) { + error_setg(errp, + "remove -mem-prealloc to use the prealloc property"); + return; + } + } + + if (!memory_region_size(&backend->mr)) { + backend->prealloc = value; + return; + } + + if (value && !backend->prealloc) { + int fd = memory_region_get_fd(&backend->mr); + void *ptr = memory_region_get_ram_ptr(&backend->mr); + uint64_t sz = memory_region_size(&backend->mr); + + os_mem_prealloc(fd, ptr, sz); + backend->prealloc = true; + } +} + +static void host_memory_backend_init(Object *obj) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + MachineState *machine = MACHINE(qdev_get_machine()); + + backend->merge = machine_mem_merge(machine); + backend->dump = machine_dump_guest_core(machine); + backend->prealloc = mem_prealloc; + + object_property_add_bool(obj, "merge", + host_memory_backend_get_merge, + host_memory_backend_set_merge, NULL); + object_property_add_bool(obj, "dump", + host_memory_backend_get_dump, + host_memory_backend_set_dump, NULL); + object_property_add_bool(obj, "prealloc", + host_memory_backend_get_prealloc, + host_memory_backend_set_prealloc, NULL); + object_property_add(obj, "size", "int", + host_memory_backend_get_size, + host_memory_backend_set_size, NULL, NULL, NULL); + object_property_add(obj, "host-nodes", "int", + host_memory_backend_get_host_nodes, + host_memory_backend_set_host_nodes, NULL, NULL, NULL); + object_property_add_enum(obj, "policy", "HostMemPolicy", + HostMemPolicy_lookup, + host_memory_backend_get_policy, + host_memory_backend_set_policy, NULL); +} + +MemoryRegion * +host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) +{ + return memory_region_size(&backend->mr) ? &backend->mr : NULL; +} + +static void +host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(uc); + HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); + Error *local_err = NULL; + void *ptr; + uint64_t sz; + + if (bc->alloc) { + bc->alloc(backend, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ptr = memory_region_get_ram_ptr(&backend->mr); + sz = memory_region_size(&backend->mr); + + if (backend->merge) { + qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); + } + if (!backend->dump) { + qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); + } +#ifdef CONFIG_NUMA + unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES); + /* lastbit == MAX_NODES means maxnode = 0 */ + unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1); + /* ensure policy won't be ignored in case memory is preallocated + * before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so + * this doesn't catch hugepage case. */ + unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE; + + /* check for invalid host-nodes and policies and give more verbose + * error messages than mbind(). */ + if (maxnode && backend->policy == MPOL_DEFAULT) { + error_setg(errp, "host-nodes must be empty for policy default," + " or you should explicitly specify a policy other" + " than default"); + return; + } else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) { + error_setg(errp, "host-nodes must be set for policy %s", + HostMemPolicy_lookup[backend->policy]); + return; + } + + /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1 + * as argument to mbind() due to an old Linux bug (feature?) which + * cuts off the last specified node. This means backend->host_nodes + * must have MAX_NODES+1 bits available. + */ + assert(sizeof(backend->host_nodes) >= + BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long)); + assert(maxnode <= MAX_NODES); + if (mbind(ptr, sz, backend->policy, + maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) { + if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) { + error_setg_errno(errp, errno, + "cannot bind memory to host NUMA nodes"); + return; + } + } +#endif + /* Preallocate memory after the NUMA policy has been instantiated. + * This is necessary to guarantee memory is allocated with + * specified NUMA policy in place. + */ + if (backend->prealloc) { + os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz); + } + } +} + +static bool +host_memory_backend_can_be_deleted(UserCreatable *uc, Error **errp) +{ + MemoryRegion *mr; + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(uc), errp); + if (memory_region_is_mapped(mr)) { + return false; + } else { + return true; + } +} + +static void +host_memory_backend_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = host_memory_backend_memory_complete; + ucc->can_be_deleted = host_memory_backend_can_be_deleted; +} + +static const TypeInfo host_memory_backend_info = { + .name = TYPE_MEMORY_BACKEND, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(HostMemoryBackendClass), + .class_init = host_memory_backend_class_init, + .instance_size = sizeof(HostMemoryBackend), + .instance_init = host_memory_backend_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&host_memory_backend_info); +} + +type_init(register_types); diff --git a/src/backends/msmouse.c b/src/backends/msmouse.c new file mode 100644 index 0000000..0126fa0 --- /dev/null +++ b/src/backends/msmouse.c @@ -0,0 +1,89 @@ +/* + * QEMU Microsoft serial mouse emulation + * + * Copyright (c) 2008 Lubomir Rintel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (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 <stdlib.h> +#include "qemu-common.h" +#include "sysemu/char.h" +#include "ui/console.h" + +#define MSMOUSE_LO6(n) ((n) & 0x3f) +#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) + +static void msmouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + CharDriverState *chr = (CharDriverState *)opaque; + + unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; + + /* Movement deltas */ + bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx); + bytes[1] |= MSMOUSE_LO6(dx); + bytes[2] |= MSMOUSE_LO6(dy); + + /* Buttons */ + bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00); + bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00); + bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00); + + /* We always send the packet of, so that we do not have to keep track + of previous state of the middle button. This can potentially confuse + some very old drivers for two button mice though. */ + qemu_chr_be_write(chr, bytes, 4); +} + +static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) +{ + /* Ignore writes to mouse port */ + return len; +} + +static void msmouse_chr_close (struct CharDriverState *chr) +{ + g_free (chr); +} + +static CharDriverState *qemu_chr_open_msmouse(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) +{ + CharDriverState *chr; + + chr = qemu_chr_alloc(); + chr->chr_write = msmouse_chr_write; + chr->chr_close = msmouse_chr_close; + chr->explicit_be_open = true; + + qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); + + return chr; +} + +static void register_types(void) +{ + register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, + qemu_chr_open_msmouse); +} + +type_init(register_types); diff --git a/src/backends/rng-egd.c b/src/backends/rng-egd.c new file mode 100644 index 0000000..6c13409 --- /dev/null +++ b/src/backends/rng-egd.c @@ -0,0 +1,233 @@ +/* + * QEMU Random Number Generator Backend + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "sysemu/rng.h" +#include "sysemu/char.h" +#include "qapi/qmp/qerror.h" +#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */ + +#define TYPE_RNG_EGD "rng-egd" +#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) + +typedef struct RngEgd +{ + RngBackend parent; + + CharDriverState *chr; + char *chr_name; + + GSList *requests; +} RngEgd; + +typedef struct RngRequest +{ + EntropyReceiveFunc *receive_entropy; + uint8_t *data; + void *opaque; + size_t offset; + size_t size; +} RngRequest; + +static void rng_egd_request_entropy(RngBackend *b, size_t size, + EntropyReceiveFunc *receive_entropy, + void *opaque) +{ + RngEgd *s = RNG_EGD(b); + RngRequest *req; + + req = g_malloc(sizeof(*req)); + + req->offset = 0; + req->size = size; + req->receive_entropy = receive_entropy; + req->opaque = opaque; + req->data = g_malloc(req->size); + + while (size > 0) { + uint8_t header[2]; + uint8_t len = MIN(size, 255); + + /* synchronous entropy request */ + header[0] = 0x02; + header[1] = len; + + qemu_chr_fe_write(s->chr, header, sizeof(header)); + + size -= len; + } + + s->requests = g_slist_append(s->requests, req); +} + +static void rng_egd_free_request(RngRequest *req) +{ + g_free(req->data); + g_free(req); +} + +static int rng_egd_chr_can_read(void *opaque) +{ + RngEgd *s = RNG_EGD(opaque); + GSList *i; + int size = 0; + + for (i = s->requests; i; i = i->next) { + RngRequest *req = i->data; + size += req->size - req->offset; + } + + return size; +} + +static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) +{ + RngEgd *s = RNG_EGD(opaque); + size_t buf_offset = 0; + + while (size > 0 && s->requests) { + RngRequest *req = s->requests->data; + int len = MIN(size, req->size - req->offset); + + memcpy(req->data + req->offset, buf + buf_offset, len); + buf_offset += len; + req->offset += len; + size -= len; + + if (req->offset == req->size) { + s->requests = g_slist_remove_link(s->requests, s->requests); + + req->receive_entropy(req->opaque, req->data, req->size); + + rng_egd_free_request(req); + } + } +} + +static void rng_egd_free_requests(RngEgd *s) +{ + GSList *i; + + for (i = s->requests; i; i = i->next) { + rng_egd_free_request(i->data); + } + + g_slist_free(s->requests); + s->requests = NULL; +} + +static void rng_egd_cancel_requests(RngBackend *b) +{ + RngEgd *s = RNG_EGD(b); + + /* We simply delete the list of pending requests. If there is data in the + * queue waiting to be read, this is okay, because there will always be + * more data than we requested originally + */ + rng_egd_free_requests(s); +} + +static void rng_egd_opened(RngBackend *b, Error **errp) +{ + RngEgd *s = RNG_EGD(b); + + if (s->chr_name == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "chardev", "a valid character device"); + return; + } + + s->chr = qemu_chr_find(s->chr_name); + if (s->chr == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", s->chr_name); + return; + } + + if (qemu_chr_fe_claim(s->chr) != 0) { + error_setg(errp, QERR_DEVICE_IN_USE, s->chr_name); + return; + } + + /* FIXME we should resubmit pending requests when the CDS reconnects. */ + qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read, + NULL, s); +} + +static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) +{ + RngBackend *b = RNG_BACKEND(obj); + RngEgd *s = RNG_EGD(b); + + if (b->opened) { + error_setg(errp, QERR_PERMISSION_DENIED); + } else { + g_free(s->chr_name); + s->chr_name = g_strdup(value); + } +} + +static char *rng_egd_get_chardev(Object *obj, Error **errp) +{ + RngEgd *s = RNG_EGD(obj); + + if (s->chr && s->chr->label) { + return g_strdup(s->chr->label); + } + + return NULL; +} + +static void rng_egd_init(Object *obj) +{ + object_property_add_str(obj, "chardev", + rng_egd_get_chardev, rng_egd_set_chardev, + NULL); +} + +static void rng_egd_finalize(Object *obj) +{ + RngEgd *s = RNG_EGD(obj); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL); + qemu_chr_fe_release(s->chr); + } + + g_free(s->chr_name); + + rng_egd_free_requests(s); +} + +static void rng_egd_class_init(ObjectClass *klass, void *data) +{ + RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); + + rbc->request_entropy = rng_egd_request_entropy; + rbc->cancel_requests = rng_egd_cancel_requests; + rbc->opened = rng_egd_opened; +} + +static const TypeInfo rng_egd_info = { + .name = TYPE_RNG_EGD, + .parent = TYPE_RNG_BACKEND, + .instance_size = sizeof(RngEgd), + .class_init = rng_egd_class_init, + .instance_init = rng_egd_init, + .instance_finalize = rng_egd_finalize, +}; + +static void register_types(void) +{ + type_register_static(&rng_egd_info); +} + +type_init(register_types); diff --git a/src/backends/rng-random.c b/src/backends/rng-random.c new file mode 100644 index 0000000..4e51f46 --- /dev/null +++ b/src/backends/rng-random.c @@ -0,0 +1,156 @@ +/* + * QEMU Random Number Generator Backend + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "sysemu/rng-random.h" +#include "sysemu/rng.h" +#include "qapi/qmp/qerror.h" +#include "qemu/main-loop.h" + +struct RndRandom +{ + RngBackend parent; + + int fd; + char *filename; + + EntropyReceiveFunc *receive_func; + void *opaque; + size_t size; +}; + +/** + * A simple and incomplete backend to request entropy from /dev/random. + * + * This backend exposes an additional "filename" property that can be used to + * set the filename to use to open the backend. + */ + +static void entropy_available(void *opaque) +{ + RndRandom *s = RNG_RANDOM(opaque); + uint8_t buffer[s->size]; + ssize_t len; + + len = read(s->fd, buffer, s->size); + if (len < 0 && errno == EAGAIN) { + return; + } + g_assert(len != -1); + + s->receive_func(s->opaque, buffer, len); + s->receive_func = NULL; + + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); +} + +static void rng_random_request_entropy(RngBackend *b, size_t size, + EntropyReceiveFunc *receive_entropy, + void *opaque) +{ + RndRandom *s = RNG_RANDOM(b); + + if (s->receive_func) { + s->receive_func(s->opaque, NULL, 0); + } + + s->receive_func = receive_entropy; + s->opaque = opaque; + s->size = size; + + qemu_set_fd_handler(s->fd, entropy_available, NULL, s); +} + +static void rng_random_opened(RngBackend *b, Error **errp) +{ + RndRandom *s = RNG_RANDOM(b); + + if (s->filename == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "filename", "a valid filename"); + } else { + s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); + if (s->fd == -1) { + error_setg_file_open(errp, errno, s->filename); + } + } +} + +static char *rng_random_get_filename(Object *obj, Error **errp) +{ + RndRandom *s = RNG_RANDOM(obj); + + return g_strdup(s->filename); +} + +static void rng_random_set_filename(Object *obj, const char *filename, + Error **errp) +{ + RngBackend *b = RNG_BACKEND(obj); + RndRandom *s = RNG_RANDOM(obj); + + if (b->opened) { + error_setg(errp, QERR_PERMISSION_DENIED); + return; + } + + g_free(s->filename); + s->filename = g_strdup(filename); +} + +static void rng_random_init(Object *obj) +{ + RndRandom *s = RNG_RANDOM(obj); + + object_property_add_str(obj, "filename", + rng_random_get_filename, + rng_random_set_filename, + NULL); + + s->filename = g_strdup("/dev/random"); + s->fd = -1; +} + +static void rng_random_finalize(Object *obj) +{ + RndRandom *s = RNG_RANDOM(obj); + + if (s->fd != -1) { + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + qemu_close(s->fd); + } + + g_free(s->filename); +} + +static void rng_random_class_init(ObjectClass *klass, void *data) +{ + RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); + + rbc->request_entropy = rng_random_request_entropy; + rbc->opened = rng_random_opened; +} + +static const TypeInfo rng_random_info = { + .name = TYPE_RNG_RANDOM, + .parent = TYPE_RNG_BACKEND, + .instance_size = sizeof(RndRandom), + .class_init = rng_random_class_init, + .instance_init = rng_random_init, + .instance_finalize = rng_random_finalize, +}; + +static void register_types(void) +{ + type_register_static(&rng_random_info); +} + +type_init(register_types); diff --git a/src/backends/rng.c b/src/backends/rng.c new file mode 100644 index 0000000..5065fdc --- /dev/null +++ b/src/backends/rng.c @@ -0,0 +1,109 @@ +/* + * QEMU Random Number Generator Backend + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "sysemu/rng.h" +#include "qapi/qmp/qerror.h" +#include "qom/object_interfaces.h" + +void rng_backend_request_entropy(RngBackend *s, size_t size, + EntropyReceiveFunc *receive_entropy, + void *opaque) +{ + RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + + if (k->request_entropy) { + k->request_entropy(s, size, receive_entropy, opaque); + } +} + +void rng_backend_cancel_requests(RngBackend *s) +{ + RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + + if (k->cancel_requests) { + k->cancel_requests(s); + } +} + +static bool rng_backend_prop_get_opened(Object *obj, Error **errp) +{ + RngBackend *s = RNG_BACKEND(obj); + + return s->opened; +} + +static void rng_backend_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "opened", errp); +} + +static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp) +{ + RngBackend *s = RNG_BACKEND(obj); + RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + Error *local_err = NULL; + + if (value == s->opened) { + return; + } + + if (!value && s->opened) { + error_setg(errp, QERR_PERMISSION_DENIED); + return; + } + + if (k->opened) { + k->opened(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + s->opened = true; +} + +static void rng_backend_init(Object *obj) +{ + object_property_add_bool(obj, "opened", + rng_backend_prop_get_opened, + rng_backend_prop_set_opened, + NULL); +} + +static void rng_backend_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = rng_backend_complete; +} + +static const TypeInfo rng_backend_info = { + .name = TYPE_RNG_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(RngBackend), + .instance_init = rng_backend_init, + .class_size = sizeof(RngBackendClass), + .class_init = rng_backend_class_init, + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&rng_backend_info); +} + +type_init(register_types); diff --git a/src/backends/testdev.c b/src/backends/testdev.c new file mode 100644 index 0000000..26d5c73 --- /dev/null +++ b/src/backends/testdev.c @@ -0,0 +1,135 @@ +/* + * QEMU Char Device for testsuite control + * + * Copyright (c) 2014 Red Hat, Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (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 "qemu-common.h" +#include "sysemu/char.h" + +#define BUF_SIZE 32 + +typedef struct { + CharDriverState *chr; + uint8_t in_buf[32]; + int in_buf_used; +} TestdevCharState; + +/* Try to interpret a whole incoming packet */ +static int testdev_eat_packet(TestdevCharState *testdev) +{ + const uint8_t *cur = testdev->in_buf; + int len = testdev->in_buf_used; + uint8_t c; + int arg; + +#define EAT(c) do { \ + if (!len--) { \ + return 0; \ + } \ + c = *cur++; \ +} while (0) + + EAT(c); + + while (isspace(c)) { + EAT(c); + } + + arg = 0; + while (isdigit(c)) { + arg = arg * 10 + c - '0'; + EAT(c); + } + + while (isspace(c)) { + EAT(c); + } + + switch (c) { + case 'q': + exit((arg << 1) | 1); + break; + default: + break; + } + return cur - testdev->in_buf; +} + +/* The other end is writing some data. Store it and try to interpret */ +static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + TestdevCharState *testdev = chr->opaque; + int tocopy, eaten, orig_len = len; + + while (len) { + /* Complete our buffer as much as possible */ + tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used); + + memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy); + testdev->in_buf_used += tocopy; + buf += tocopy; + len -= tocopy; + + /* Interpret it as much as possible */ + while (testdev->in_buf_used > 0 && + (eaten = testdev_eat_packet(testdev)) > 0) { + memmove(testdev->in_buf, testdev->in_buf + eaten, + testdev->in_buf_used - eaten); + testdev->in_buf_used -= eaten; + } + } + return orig_len; +} + +static void testdev_close(struct CharDriverState *chr) +{ + TestdevCharState *testdev = chr->opaque; + + g_free(testdev); +} + +static CharDriverState *chr_testdev_init(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) +{ + TestdevCharState *testdev; + CharDriverState *chr; + + testdev = g_new0(TestdevCharState, 1); + testdev->chr = chr = g_new0(CharDriverState, 1); + + chr->opaque = testdev; + chr->chr_write = testdev_write; + chr->chr_close = testdev_close; + + return chr; +} + +static void register_types(void) +{ + register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, + chr_testdev_init); +} + +type_init(register_types); diff --git a/src/backends/tpm.c b/src/backends/tpm.c new file mode 100644 index 0000000..a512693 --- /dev/null +++ b/src/backends/tpm.c @@ -0,0 +1,196 @@ +/* + * QEMU TPM Backend + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Stefan Berger <stefanb@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Based on backends/rng.c by Anthony Liguori + */ + +#include "sysemu/tpm_backend.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/tpm.h" +#include "qemu/thread.h" +#include "sysemu/tpm_backend_int.h" + +enum TpmType tpm_backend_get_type(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->type; +} + +const char *tpm_backend_get_desc(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->desc(); +} + +void tpm_backend_destroy(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->destroy(s); +} + +int tpm_backend_init(TPMBackend *s, TPMState *state, + TPMRecvDataCB *datacb) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->init(s, state, datacb); +} + +int tpm_backend_startup_tpm(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->startup_tpm(s); +} + +bool tpm_backend_had_startup_error(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->had_startup_error(s); +} + +size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->realloc_buffer(sb); +} + +void tpm_backend_deliver_request(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->deliver_request(s); +} + +void tpm_backend_reset(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->reset(s); +} + +void tpm_backend_cancel_cmd(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->ops->cancel_cmd(s); +} + +bool tpm_backend_get_tpm_established_flag(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->get_tpm_established_flag(s); +} + +int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->reset_tpm_established_flag(s, locty); +} + +TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->ops->get_tpm_version(s); +} + +static bool tpm_backend_prop_get_opened(Object *obj, Error **errp) +{ + TPMBackend *s = TPM_BACKEND(obj); + + return s->opened; +} + +void tpm_backend_open(TPMBackend *s, Error **errp) +{ + object_property_set_bool(OBJECT(s), true, "opened", errp); +} + +static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp) +{ + TPMBackend *s = TPM_BACKEND(obj); + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + Error *local_err = NULL; + + if (value == s->opened) { + return; + } + + if (!value && s->opened) { + error_setg(errp, QERR_PERMISSION_DENIED); + return; + } + + if (k->opened) { + k->opened(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + s->opened = true; +} + +static void tpm_backend_instance_init(Object *obj) +{ + object_property_add_bool(obj, "opened", + tpm_backend_prop_get_opened, + tpm_backend_prop_set_opened, + NULL); +} + +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt) +{ + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL); +} + +void tpm_backend_thread_create(TPMBackendThread *tbt, + GFunc func, gpointer user_data) +{ + if (!tbt->pool) { + tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL); + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL); + } +} + +void tpm_backend_thread_end(TPMBackendThread *tbt) +{ + if (tbt->pool) { + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL); + g_thread_pool_free(tbt->pool, FALSE, TRUE); + tbt->pool = NULL; + } +} + +static const TypeInfo tpm_backend_info = { + .name = TYPE_TPM_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(TPMBackend), + .instance_init = tpm_backend_instance_init, + .class_size = sizeof(TPMBackendClass), + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&tpm_backend_info); +} + +type_init(register_types); |