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

pl050.c (5276B)


      1/*
      2 * Arm PrimeCell PL050 Keyboard / Mouse Interface
      3 *
      4 * Copyright (c) 2006-2007 CodeSourcery.
      5 * Written by Paul Brook
      6 *
      7 * This code is licensed under the GPL.
      8 */
      9
     10#include "qemu/osdep.h"
     11#include "hw/sysbus.h"
     12#include "migration/vmstate.h"
     13#include "hw/input/ps2.h"
     14#include "hw/irq.h"
     15#include "qemu/log.h"
     16#include "qemu/module.h"
     17#include "qom/object.h"
     18
     19#define TYPE_PL050 "pl050"
     20OBJECT_DECLARE_SIMPLE_TYPE(PL050State, PL050)
     21
     22struct PL050State {
     23    SysBusDevice parent_obj;
     24
     25    MemoryRegion iomem;
     26    void *dev;
     27    uint32_t cr;
     28    uint32_t clk;
     29    uint32_t last;
     30    int pending;
     31    qemu_irq irq;
     32    bool is_mouse;
     33};
     34
     35static const VMStateDescription vmstate_pl050 = {
     36    .name = "pl050",
     37    .version_id = 2,
     38    .minimum_version_id = 2,
     39    .fields = (VMStateField[]) {
     40        VMSTATE_UINT32(cr, PL050State),
     41        VMSTATE_UINT32(clk, PL050State),
     42        VMSTATE_UINT32(last, PL050State),
     43        VMSTATE_INT32(pending, PL050State),
     44        VMSTATE_END_OF_LIST()
     45    }
     46};
     47
     48#define PL050_TXEMPTY         (1 << 6)
     49#define PL050_TXBUSY          (1 << 5)
     50#define PL050_RXFULL          (1 << 4)
     51#define PL050_RXBUSY          (1 << 3)
     52#define PL050_RXPARITY        (1 << 2)
     53#define PL050_KMIC            (1 << 1)
     54#define PL050_KMID            (1 << 0)
     55
     56static const unsigned char pl050_id[] =
     57{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
     58
     59static void pl050_update(void *opaque, int level)
     60{
     61    PL050State *s = (PL050State *)opaque;
     62    int raise;
     63
     64    s->pending = level;
     65    raise = (s->pending && (s->cr & 0x10) != 0)
     66            || (s->cr & 0x08) != 0;
     67    qemu_set_irq(s->irq, raise);
     68}
     69
     70static uint64_t pl050_read(void *opaque, hwaddr offset,
     71                           unsigned size)
     72{
     73    PL050State *s = (PL050State *)opaque;
     74    if (offset >= 0xfe0 && offset < 0x1000)
     75        return pl050_id[(offset - 0xfe0) >> 2];
     76
     77    switch (offset >> 2) {
     78    case 0: /* KMICR */
     79        return s->cr;
     80    case 1: /* KMISTAT */
     81        {
     82            uint8_t val;
     83            uint32_t stat;
     84
     85            val = s->last;
     86            val = val ^ (val >> 4);
     87            val = val ^ (val >> 2);
     88            val = (val ^ (val >> 1)) & 1;
     89
     90            stat = PL050_TXEMPTY;
     91            if (val)
     92                stat |= PL050_RXPARITY;
     93            if (s->pending)
     94                stat |= PL050_RXFULL;
     95
     96            return stat;
     97        }
     98    case 2: /* KMIDATA */
     99        if (s->pending)
    100            s->last = ps2_read_data(s->dev);
    101        return s->last;
    102    case 3: /* KMICLKDIV */
    103        return s->clk;
    104    case 4: /* KMIIR */
    105        return s->pending | 2;
    106    default:
    107        qemu_log_mask(LOG_GUEST_ERROR,
    108                      "pl050_read: Bad offset %x\n", (int)offset);
    109        return 0;
    110    }
    111}
    112
    113static void pl050_write(void *opaque, hwaddr offset,
    114                        uint64_t value, unsigned size)
    115{
    116    PL050State *s = (PL050State *)opaque;
    117    switch (offset >> 2) {
    118    case 0: /* KMICR */
    119        s->cr = value;
    120        pl050_update(s, s->pending);
    121        /* ??? Need to implement the enable/disable bit.  */
    122        break;
    123    case 2: /* KMIDATA */
    124        /* ??? This should toggle the TX interrupt line.  */
    125        /* ??? This means kbd/mouse can block each other.  */
    126        if (s->is_mouse) {
    127            ps2_write_mouse(s->dev, value);
    128        } else {
    129            ps2_write_keyboard(s->dev, value);
    130        }
    131        break;
    132    case 3: /* KMICLKDIV */
    133        s->clk = value;
    134        return;
    135    default:
    136        qemu_log_mask(LOG_GUEST_ERROR,
    137                      "pl050_write: Bad offset %x\n", (int)offset);
    138    }
    139}
    140static const MemoryRegionOps pl050_ops = {
    141    .read = pl050_read,
    142    .write = pl050_write,
    143    .endianness = DEVICE_NATIVE_ENDIAN,
    144};
    145
    146static void pl050_realize(DeviceState *dev, Error **errp)
    147{
    148    PL050State *s = PL050(dev);
    149    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    150
    151    memory_region_init_io(&s->iomem, OBJECT(s), &pl050_ops, s, "pl050", 0x1000);
    152    sysbus_init_mmio(sbd, &s->iomem);
    153    sysbus_init_irq(sbd, &s->irq);
    154    if (s->is_mouse) {
    155        s->dev = ps2_mouse_init(pl050_update, s);
    156    } else {
    157        s->dev = ps2_kbd_init(pl050_update, s);
    158    }
    159}
    160
    161static void pl050_keyboard_init(Object *obj)
    162{
    163    PL050State *s = PL050(obj);
    164
    165    s->is_mouse = false;
    166}
    167
    168static void pl050_mouse_init(Object *obj)
    169{
    170    PL050State *s = PL050(obj);
    171
    172    s->is_mouse = true;
    173}
    174
    175static const TypeInfo pl050_kbd_info = {
    176    .name          = "pl050_keyboard",
    177    .parent        = TYPE_PL050,
    178    .instance_init = pl050_keyboard_init,
    179};
    180
    181static const TypeInfo pl050_mouse_info = {
    182    .name          = "pl050_mouse",
    183    .parent        = TYPE_PL050,
    184    .instance_init = pl050_mouse_init,
    185};
    186
    187static void pl050_class_init(ObjectClass *oc, void *data)
    188{
    189    DeviceClass *dc = DEVICE_CLASS(oc);
    190
    191    dc->realize = pl050_realize;
    192    dc->vmsd = &vmstate_pl050;
    193}
    194
    195static const TypeInfo pl050_type_info = {
    196    .name          = TYPE_PL050,
    197    .parent        = TYPE_SYS_BUS_DEVICE,
    198    .instance_size = sizeof(PL050State),
    199    .abstract      = true,
    200    .class_init    = pl050_class_init,
    201};
    202
    203static void pl050_register_types(void)
    204{
    205    type_register_static(&pl050_type_info);
    206    type_register_static(&pl050_kbd_info);
    207    type_register_static(&pl050_mouse_info);
    208}
    209
    210type_init(pl050_register_types)