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

bcm2835_aux.c (9063B)


      1/*
      2 * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
      3 * Copyright (c) 2015, Microsoft
      4 * Written by Andrew Baumann
      5 * Based on pl011.c, copyright terms below:
      6 *
      7 * Arm PrimeCell PL011 UART
      8 *
      9 * Copyright (c) 2006 CodeSourcery.
     10 * Written by Paul Brook
     11 *
     12 * This code is licensed under the GPL.
     13 *
     14 * At present only the core UART functions (data path for tx/rx) are
     15 * implemented. The following features/registers are unimplemented:
     16 *  - Line/modem control
     17 *  - Scratch register
     18 *  - Extra control
     19 *  - Baudrate
     20 *  - SPI interfaces
     21 */
     22
     23#include "qemu/osdep.h"
     24#include "hw/char/bcm2835_aux.h"
     25#include "hw/irq.h"
     26#include "hw/qdev-properties.h"
     27#include "hw/qdev-properties-system.h"
     28#include "migration/vmstate.h"
     29#include "qemu/log.h"
     30#include "qemu/module.h"
     31
     32#define AUX_IRQ         0x0
     33#define AUX_ENABLES     0x4
     34#define AUX_MU_IO_REG   0x40
     35#define AUX_MU_IER_REG  0x44
     36#define AUX_MU_IIR_REG  0x48
     37#define AUX_MU_LCR_REG  0x4c
     38#define AUX_MU_MCR_REG  0x50
     39#define AUX_MU_LSR_REG  0x54
     40#define AUX_MU_MSR_REG  0x58
     41#define AUX_MU_SCRATCH  0x5c
     42#define AUX_MU_CNTL_REG 0x60
     43#define AUX_MU_STAT_REG 0x64
     44#define AUX_MU_BAUD_REG 0x68
     45
     46/* bits in IER/IIR registers */
     47#define RX_INT  0x1
     48#define TX_INT  0x2
     49
     50static void bcm2835_aux_update(BCM2835AuxState *s)
     51{
     52    /* signal an interrupt if either:
     53     * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
     54     * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
     55     */
     56    s->iir = 0;
     57    if ((s->ier & RX_INT) && s->read_count != 0) {
     58        s->iir |= RX_INT;
     59    }
     60    if (s->ier & TX_INT) {
     61        s->iir |= TX_INT;
     62    }
     63    qemu_set_irq(s->irq, s->iir != 0);
     64}
     65
     66static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
     67{
     68    BCM2835AuxState *s = opaque;
     69    uint32_t c, res;
     70
     71    switch (offset) {
     72    case AUX_IRQ:
     73        return s->iir != 0;
     74
     75    case AUX_ENABLES:
     76        return 1; /* mini UART permanently enabled */
     77
     78    case AUX_MU_IO_REG:
     79        /* "DLAB bit set means access baudrate register" is NYI */
     80        c = s->read_fifo[s->read_pos];
     81        if (s->read_count > 0) {
     82            s->read_count--;
     83            if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
     84                s->read_pos = 0;
     85            }
     86        }
     87        qemu_chr_fe_accept_input(&s->chr);
     88        bcm2835_aux_update(s);
     89        return c;
     90
     91    case AUX_MU_IER_REG:
     92        /* "DLAB bit set means access baudrate register" is NYI */
     93        return 0xc0 | s->ier; /* FIFO enables always read 1 */
     94
     95    case AUX_MU_IIR_REG:
     96        res = 0xc0; /* FIFO enables */
     97        /* The spec is unclear on what happens when both tx and rx
     98         * interrupts are active, besides that this cannot occur. At
     99         * present, we choose to prioritise the rx interrupt, since
    100         * the tx fifo is always empty. */
    101        if (s->read_count != 0) {
    102            res |= 0x4;
    103        } else {
    104            res |= 0x2;
    105        }
    106        if (s->iir == 0) {
    107            res |= 0x1;
    108        }
    109        return res;
    110
    111    case AUX_MU_LCR_REG:
    112        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
    113        return 0;
    114
    115    case AUX_MU_MCR_REG:
    116        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
    117        return 0;
    118
    119    case AUX_MU_LSR_REG:
    120        res = 0x60; /* tx idle, empty */
    121        if (s->read_count != 0) {
    122            res |= 0x1;
    123        }
    124        return res;
    125
    126    case AUX_MU_MSR_REG:
    127        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
    128        return 0;
    129
    130    case AUX_MU_SCRATCH:
    131        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
    132        return 0;
    133
    134    case AUX_MU_CNTL_REG:
    135        return 0x3; /* tx, rx enabled */
    136
    137    case AUX_MU_STAT_REG:
    138        res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
    139        if (s->read_count > 0) {
    140            res |= 0x1; /* data in input buffer */
    141            assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
    142            res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
    143        }
    144        return res;
    145
    146    case AUX_MU_BAUD_REG:
    147        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
    148        return 0;
    149
    150    default:
    151        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    152                      __func__, offset);
    153        return 0;
    154    }
    155}
    156
    157static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
    158                              unsigned size)
    159{
    160    BCM2835AuxState *s = opaque;
    161    unsigned char ch;
    162
    163    switch (offset) {
    164    case AUX_ENABLES:
    165        if (value != 1) {
    166            qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI"
    167                                     " or disable UART: 0x%"PRIx64"\n",
    168                          __func__, value);
    169        }
    170        break;
    171
    172    case AUX_MU_IO_REG:
    173        /* "DLAB bit set means access baudrate register" is NYI */
    174        ch = value;
    175        /* XXX this blocks entire thread. Rewrite to use
    176         * qemu_chr_fe_write and background I/O callbacks */
    177        qemu_chr_fe_write_all(&s->chr, &ch, 1);
    178        break;
    179
    180    case AUX_MU_IER_REG:
    181        /* "DLAB bit set means access baudrate register" is NYI */
    182        s->ier = value & (TX_INT | RX_INT);
    183        bcm2835_aux_update(s);
    184        break;
    185
    186    case AUX_MU_IIR_REG:
    187        if (value & 0x2) {
    188            s->read_count = 0;
    189        }
    190        break;
    191
    192    case AUX_MU_LCR_REG:
    193        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
    194        break;
    195
    196    case AUX_MU_MCR_REG:
    197        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
    198        break;
    199
    200    case AUX_MU_SCRATCH:
    201        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
    202        break;
    203
    204    case AUX_MU_CNTL_REG:
    205        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
    206        break;
    207
    208    case AUX_MU_BAUD_REG:
    209        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
    210        break;
    211
    212    default:
    213        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    214                      __func__, offset);
    215    }
    216
    217    bcm2835_aux_update(s);
    218}
    219
    220static int bcm2835_aux_can_receive(void *opaque)
    221{
    222    BCM2835AuxState *s = opaque;
    223
    224    return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
    225}
    226
    227static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
    228{
    229    BCM2835AuxState *s = opaque;
    230    int slot;
    231
    232    slot = s->read_pos + s->read_count;
    233    if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
    234        slot -= BCM2835_AUX_RX_FIFO_LEN;
    235    }
    236    s->read_fifo[slot] = value;
    237    s->read_count++;
    238    if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
    239        /* buffer full */
    240    }
    241    bcm2835_aux_update(s);
    242}
    243
    244static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
    245{
    246    bcm2835_aux_put_fifo(opaque, *buf);
    247}
    248
    249static const MemoryRegionOps bcm2835_aux_ops = {
    250    .read = bcm2835_aux_read,
    251    .write = bcm2835_aux_write,
    252    .endianness = DEVICE_NATIVE_ENDIAN,
    253    .impl.min_access_size = 4,
    254    .impl.max_access_size = 4,
    255    .valid.min_access_size = 1,
    256    .valid.max_access_size = 4,
    257};
    258
    259static const VMStateDescription vmstate_bcm2835_aux = {
    260    .name = TYPE_BCM2835_AUX,
    261    .version_id = 1,
    262    .minimum_version_id = 1,
    263    .fields = (VMStateField[]) {
    264        VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
    265                            BCM2835_AUX_RX_FIFO_LEN),
    266        VMSTATE_UINT8(read_pos, BCM2835AuxState),
    267        VMSTATE_UINT8(read_count, BCM2835AuxState),
    268        VMSTATE_UINT8(ier, BCM2835AuxState),
    269        VMSTATE_UINT8(iir, BCM2835AuxState),
    270        VMSTATE_END_OF_LIST()
    271    }
    272};
    273
    274static void bcm2835_aux_init(Object *obj)
    275{
    276    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    277    BCM2835AuxState *s = BCM2835_AUX(obj);
    278
    279    memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
    280                          TYPE_BCM2835_AUX, 0x100);
    281    sysbus_init_mmio(sbd, &s->iomem);
    282    sysbus_init_irq(sbd, &s->irq);
    283}
    284
    285static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
    286{
    287    BCM2835AuxState *s = BCM2835_AUX(dev);
    288
    289    qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
    290                             bcm2835_aux_receive, NULL, NULL, s, NULL, true);
    291}
    292
    293static Property bcm2835_aux_props[] = {
    294    DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
    295    DEFINE_PROP_END_OF_LIST(),
    296};
    297
    298static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
    299{
    300    DeviceClass *dc = DEVICE_CLASS(oc);
    301
    302    dc->realize = bcm2835_aux_realize;
    303    dc->vmsd = &vmstate_bcm2835_aux;
    304    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
    305    device_class_set_props(dc, bcm2835_aux_props);
    306}
    307
    308static const TypeInfo bcm2835_aux_info = {
    309    .name          = TYPE_BCM2835_AUX,
    310    .parent        = TYPE_SYS_BUS_DEVICE,
    311    .instance_size = sizeof(BCM2835AuxState),
    312    .instance_init = bcm2835_aux_init,
    313    .class_init    = bcm2835_aux_class_init,
    314};
    315
    316static void bcm2835_aux_register_types(void)
    317{
    318    type_register_static(&bcm2835_aux_info);
    319}
    320
    321type_init(bcm2835_aux_register_types)