cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

lasips2.c (7640B)


      1/*
      2 * QEMU HP Lasi PS/2 interface emulation
      3 *
      4 * Copyright (c) 2019 Sven Schnelle
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a copy
      7 * of this software and associated documentation files (the "Software"), to deal
      8 * in the Software without restriction, including without limitation the rights
      9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 * copies of the Software, and to permit persons to whom the Software is
     11 * furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 * THE SOFTWARE.
     23 */
     24#include "qemu/osdep.h"
     25#include "qemu/log.h"
     26#include "hw/qdev-properties.h"
     27#include "hw/input/ps2.h"
     28#include "hw/input/lasips2.h"
     29#include "exec/hwaddr.h"
     30#include "trace.h"
     31#include "exec/address-spaces.h"
     32#include "migration/vmstate.h"
     33#include "hw/irq.h"
     34
     35
     36struct LASIPS2State;
     37typedef struct LASIPS2Port {
     38    struct LASIPS2State *parent;
     39    MemoryRegion reg;
     40    void *dev;
     41    uint8_t id;
     42    uint8_t control;
     43    uint8_t buf;
     44    bool loopback_rbne;
     45    bool irq;
     46} LASIPS2Port;
     47
     48typedef struct LASIPS2State {
     49    LASIPS2Port kbd;
     50    LASIPS2Port mouse;
     51    qemu_irq irq;
     52} LASIPS2State;
     53
     54static const VMStateDescription vmstate_lasips2 = {
     55    .name = "lasips2",
     56    .version_id = 0,
     57    .minimum_version_id = 0,
     58    .fields = (VMStateField[]) {
     59        VMSTATE_UINT8(kbd.control, LASIPS2State),
     60        VMSTATE_UINT8(kbd.id, LASIPS2State),
     61        VMSTATE_BOOL(kbd.irq, LASIPS2State),
     62        VMSTATE_UINT8(mouse.control, LASIPS2State),
     63        VMSTATE_UINT8(mouse.id, LASIPS2State),
     64        VMSTATE_BOOL(mouse.irq, LASIPS2State),
     65        VMSTATE_END_OF_LIST()
     66    }
     67};
     68
     69typedef enum {
     70    REG_PS2_ID = 0,
     71    REG_PS2_RCVDATA = 4,
     72    REG_PS2_CONTROL = 8,
     73    REG_PS2_STATUS = 12,
     74} lasips2_read_reg_t;
     75
     76typedef enum {
     77    REG_PS2_RESET = 0,
     78    REG_PS2_XMTDATA = 4,
     79} lasips2_write_reg_t;
     80
     81typedef enum {
     82    LASIPS2_CONTROL_ENABLE = 0x01,
     83    LASIPS2_CONTROL_LOOPBACK = 0x02,
     84    LASIPS2_CONTROL_DIAG = 0x20,
     85    LASIPS2_CONTROL_DATDIR = 0x40,
     86    LASIPS2_CONTROL_CLKDIR = 0x80,
     87} lasips2_control_reg_t;
     88
     89typedef enum {
     90    LASIPS2_STATUS_RBNE = 0x01,
     91    LASIPS2_STATUS_TBNE = 0x02,
     92    LASIPS2_STATUS_TERR = 0x04,
     93    LASIPS2_STATUS_PERR = 0x08,
     94    LASIPS2_STATUS_CMPINTR = 0x10,
     95    LASIPS2_STATUS_DATSHD = 0x40,
     96    LASIPS2_STATUS_CLKSHD = 0x80,
     97} lasips2_status_reg_t;
     98
     99static const char *artist_read_reg_name(uint64_t addr)
    100{
    101    switch (addr & 0xc) {
    102    case REG_PS2_ID:
    103        return " PS2_ID";
    104
    105    case REG_PS2_RCVDATA:
    106        return " PS2_RCVDATA";
    107
    108    case REG_PS2_CONTROL:
    109        return " PS2_CONTROL";
    110
    111    case REG_PS2_STATUS:
    112        return " PS2_STATUS";
    113
    114    default:
    115        return "";
    116    }
    117}
    118
    119static const char *artist_write_reg_name(uint64_t addr)
    120{
    121    switch (addr & 0x0c) {
    122    case REG_PS2_RESET:
    123        return " PS2_RESET";
    124
    125    case REG_PS2_XMTDATA:
    126        return " PS2_XMTDATA";
    127
    128    case REG_PS2_CONTROL:
    129        return " PS2_CONTROL";
    130
    131    default:
    132        return "";
    133    }
    134}
    135
    136static void lasips2_update_irq(LASIPS2State *s)
    137{
    138    trace_lasips2_intr(s->kbd.irq | s->mouse.irq);
    139    qemu_set_irq(s->irq, s->kbd.irq | s->mouse.irq);
    140}
    141
    142static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val,
    143                              unsigned size)
    144{
    145    LASIPS2Port *port = opaque;
    146
    147    trace_lasips2_reg_write(size, port->id, addr,
    148                            artist_write_reg_name(addr), val);
    149
    150    switch (addr & 0xc) {
    151    case REG_PS2_CONTROL:
    152        port->control = val;
    153        break;
    154
    155    case REG_PS2_XMTDATA:
    156        if (port->control & LASIPS2_CONTROL_LOOPBACK) {
    157            port->buf = val;
    158            port->irq = true;
    159            port->loopback_rbne = true;
    160            lasips2_update_irq(port->parent);
    161            break;
    162        }
    163
    164        if (port->id) {
    165            ps2_write_mouse(port->dev, val);
    166        } else {
    167            ps2_write_keyboard(port->dev, val);
    168        }
    169        break;
    170
    171    case REG_PS2_RESET:
    172        break;
    173
    174    default:
    175        qemu_log_mask(LOG_UNIMP, "%s: unknown register 0x%02" HWADDR_PRIx "\n",
    176                      __func__, addr);
    177        break;
    178    }
    179}
    180
    181static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size)
    182{
    183    LASIPS2Port *port = opaque;
    184    uint64_t ret = 0;
    185
    186    switch (addr & 0xc) {
    187    case REG_PS2_ID:
    188        ret = port->id;
    189        break;
    190
    191    case REG_PS2_RCVDATA:
    192        if (port->control & LASIPS2_CONTROL_LOOPBACK) {
    193            port->irq = false;
    194            port->loopback_rbne = false;
    195            lasips2_update_irq(port->parent);
    196            ret = port->buf;
    197            break;
    198        }
    199
    200        ret = ps2_read_data(port->dev);
    201        break;
    202
    203    case REG_PS2_CONTROL:
    204        ret = port->control;
    205        break;
    206
    207    case REG_PS2_STATUS:
    208
    209        ret = LASIPS2_STATUS_DATSHD | LASIPS2_STATUS_CLKSHD;
    210
    211        if (port->control & LASIPS2_CONTROL_DIAG) {
    212            if (!(port->control & LASIPS2_CONTROL_DATDIR)) {
    213                ret &= ~LASIPS2_STATUS_DATSHD;
    214            }
    215
    216            if (!(port->control & LASIPS2_CONTROL_CLKDIR)) {
    217                ret &= ~LASIPS2_STATUS_CLKSHD;
    218            }
    219        }
    220
    221        if (port->control & LASIPS2_CONTROL_LOOPBACK) {
    222            if (port->loopback_rbne) {
    223                ret |= LASIPS2_STATUS_RBNE;
    224            }
    225        } else {
    226            if (!ps2_queue_empty(port->dev)) {
    227                ret |= LASIPS2_STATUS_RBNE;
    228            }
    229        }
    230
    231        if (port->parent->kbd.irq || port->parent->mouse.irq) {
    232            ret |= LASIPS2_STATUS_CMPINTR;
    233        }
    234        break;
    235
    236    default:
    237        qemu_log_mask(LOG_UNIMP, "%s: unknown register 0x%02" HWADDR_PRIx "\n",
    238                      __func__, addr);
    239        break;
    240    }
    241    trace_lasips2_reg_read(size, port->id, addr,
    242                           artist_read_reg_name(addr), ret);
    243
    244    return ret;
    245}
    246
    247static const MemoryRegionOps lasips2_reg_ops = {
    248    .read = lasips2_reg_read,
    249    .write = lasips2_reg_write,
    250    .impl = {
    251        .min_access_size = 1,
    252        .max_access_size = 4,
    253    },
    254    .endianness = DEVICE_NATIVE_ENDIAN,
    255};
    256
    257static void ps2dev_update_irq(void *opaque, int level)
    258{
    259    LASIPS2Port *port = opaque;
    260    port->irq = level;
    261    lasips2_update_irq(port->parent);
    262}
    263
    264void lasips2_init(MemoryRegion *address_space,
    265                  hwaddr base, qemu_irq irq)
    266{
    267    LASIPS2State *s;
    268
    269    s = g_malloc0(sizeof(LASIPS2State));
    270
    271    s->irq = irq;
    272    s->mouse.id = 1;
    273    s->kbd.parent = s;
    274    s->mouse.parent = s;
    275
    276    vmstate_register(NULL, base, &vmstate_lasips2, s);
    277
    278    s->kbd.dev = ps2_kbd_init(ps2dev_update_irq, &s->kbd);
    279    s->mouse.dev = ps2_mouse_init(ps2dev_update_irq, &s->mouse);
    280
    281    memory_region_init_io(&s->kbd.reg, NULL, &lasips2_reg_ops, &s->kbd,
    282                          "lasips2-kbd", 0x100);
    283    memory_region_add_subregion(address_space, base, &s->kbd.reg);
    284
    285    memory_region_init_io(&s->mouse.reg, NULL, &lasips2_reg_ops, &s->mouse,
    286                          "lasips2-mouse", 0x100);
    287    memory_region_add_subregion(address_space, base + 0x100, &s->mouse.reg);
    288}