diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /backends | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/Makefile.objs | 11 | ||||
| -rw-r--r-- | backends/baum.c | 635 | ||||
| -rw-r--r-- | backends/hostmem-file.c | 134 | ||||
| -rw-r--r-- | backends/hostmem-ram.c | 53 | ||||
| -rw-r--r-- | backends/hostmem.c | 372 | ||||
| -rw-r--r-- | backends/msmouse.c | 85 | ||||
| -rw-r--r-- | backends/rng-egd.c | 233 | ||||
| -rw-r--r-- | backends/rng-random.c | 156 | ||||
| -rw-r--r-- | backends/rng.c | 109 | ||||
| -rw-r--r-- | backends/testdev.c | 131 | ||||
| -rw-r--r-- | backends/tpm.c | 196 | 
11 files changed, 2115 insertions, 0 deletions
diff --git a/backends/Makefile.objs b/backends/Makefile.objs new file mode 100644 index 00000000..31a3a894 --- /dev/null +++ b/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/backends/baum.c b/backends/baum.c new file mode 100644 index 00000000..a69aafff --- /dev/null +++ b/backends/baum.c @@ -0,0 +1,635 @@ +/* + * 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 %d 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); +} + +CharDriverState *chr_baum_init(void) +{ +    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) { +        brlapi_perror("baum_init: brlapi_openConnection"); +        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) { +        brlapi_perror("baum_init: brlapi_getDisplaySize"); +        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) { +        brlapi_perror("baum_init: brlapi_enterTtyMode"); +        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); +} + +type_init(register_types); diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c new file mode 100644 index 00000000..4b553610 --- /dev/null +++ b/backends/hostmem-file.c @@ -0,0 +1,134 @@ +/* + * 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; +    } +    if (fb->mem_path) { +        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/backends/hostmem-ram.c b/backends/hostmem-ram.c new file mode 100644 index 00000000..a67a1345 --- /dev/null +++ b/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/backends/hostmem.c b/backends/hostmem.c new file mode 100644 index 00000000..41ba2af5 --- /dev/null +++ b/backends/hostmem.c @@ -0,0 +1,372 @@ +/* + * 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)) { +            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/backends/msmouse.c b/backends/msmouse.c new file mode 100644 index 00000000..0119110a --- /dev/null +++ b/backends/msmouse.c @@ -0,0 +1,85 @@ +/* + * 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); +} + +CharDriverState *qemu_chr_open_msmouse(void) +{ +    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); +} + +type_init(register_types); diff --git a/backends/rng-egd.c b/backends/rng-egd.c new file mode 100644 index 00000000..6c134096 --- /dev/null +++ b/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/backends/rng-random.c b/backends/rng-random.c new file mode 100644 index 00000000..4e51f460 --- /dev/null +++ b/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/backends/rng.c b/backends/rng.c new file mode 100644 index 00000000..5065fdc1 --- /dev/null +++ b/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/backends/testdev.c b/backends/testdev.c new file mode 100644 index 00000000..eba396ae --- /dev/null +++ b/backends/testdev.c @@ -0,0 +1,131 @@ +/* + * 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); +} + +CharDriverState *chr_testdev_init(void) +{ +    TestdevCharState *testdev; +    CharDriverState *chr; + +    testdev = g_malloc0(sizeof(TestdevCharState)); +    testdev->chr = chr = g_malloc0(sizeof(CharDriverState)); + +    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); +} + +type_init(register_types); diff --git a/backends/tpm.c b/backends/tpm.c new file mode 100644 index 00000000..a512693b --- /dev/null +++ b/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);  | 
