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

pl041.c (17375B)


      1/*
      2 * Arm PrimeCell PL041 Advanced Audio Codec Interface
      3 *
      4 * Copyright (c) 2011
      5 * Written by Mathieu Sonet - www.elasticsheep.com
      6 *
      7 * This code is licensed under the GPL.
      8 *
      9 * *****************************************************************
     10 *
     11 * This driver emulates the ARM AACI interface
     12 * connected to a LM4549 codec.
     13 *
     14 * Limitations:
     15 * - Supports only a playback on one channel (Versatile/Vexpress)
     16 * - Supports only one TX FIFO in compact-mode or non-compact mode.
     17 * - Supports playback of 12, 16, 18 and 20 bits samples.
     18 * - Record is not supported.
     19 * - The PL041 is hardwired to a LM4549 codec.
     20 *
     21 */
     22
     23#include "qemu/osdep.h"
     24#include "hw/irq.h"
     25#include "hw/qdev-properties.h"
     26#include "hw/sysbus.h"
     27#include "qemu/log.h"
     28#include "qemu/module.h"
     29
     30#include "pl041.h"
     31#include "lm4549.h"
     32#include "migration/vmstate.h"
     33#include "qom/object.h"
     34
     35#if 0
     36#define PL041_DEBUG_LEVEL 1
     37#endif
     38
     39#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1)
     40#define DBG_L1(fmt, ...) \
     41do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
     42#else
     43#define DBG_L1(fmt, ...) \
     44do { } while (0)
     45#endif
     46
     47#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
     48#define DBG_L2(fmt, ...) \
     49do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
     50#else
     51#define DBG_L2(fmt, ...) \
     52do { } while (0)
     53#endif
     54
     55
     56#define MAX_FIFO_DEPTH      (1024)
     57#define DEFAULT_FIFO_DEPTH  (8)
     58
     59#define SLOT1_RW    (1 << 19)
     60
     61/* This FIFO only stores 20-bit samples on 32-bit words.
     62   So its level is independent of the selected mode */
     63typedef struct {
     64    uint32_t level;
     65    uint32_t data[MAX_FIFO_DEPTH];
     66} pl041_fifo;
     67
     68typedef struct {
     69    pl041_fifo tx_fifo;
     70    uint8_t tx_enabled;
     71    uint8_t tx_compact_mode;
     72    uint8_t tx_sample_size;
     73
     74    pl041_fifo rx_fifo;
     75    uint8_t rx_enabled;
     76    uint8_t rx_compact_mode;
     77    uint8_t rx_sample_size;
     78} pl041_channel;
     79
     80#define TYPE_PL041 "pl041"
     81OBJECT_DECLARE_SIMPLE_TYPE(PL041State, PL041)
     82
     83struct PL041State {
     84    SysBusDevice parent_obj;
     85
     86    MemoryRegion iomem;
     87    qemu_irq irq;
     88
     89    uint32_t fifo_depth; /* FIFO depth in non-compact mode */
     90
     91    pl041_regfile regs;
     92    pl041_channel fifo1;
     93    lm4549_state codec;
     94};
     95
     96
     97static const unsigned char pl041_default_id[8] = {
     98    0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
     99};
    100
    101#if defined(PL041_DEBUG_LEVEL)
    102#define REGISTER(name, offset) #name,
    103static const char *pl041_regs_name[] = {
    104    #include "pl041.hx"
    105};
    106#undef REGISTER
    107#endif
    108
    109
    110#if defined(PL041_DEBUG_LEVEL)
    111static const char *get_reg_name(hwaddr offset)
    112{
    113    if (offset <= PL041_dr1_7) {
    114        return pl041_regs_name[offset >> 2];
    115    }
    116
    117    return "unknown";
    118}
    119#endif
    120
    121static uint8_t pl041_compute_periphid3(PL041State *s)
    122{
    123    uint8_t id3 = 1; /* One channel */
    124
    125    /* Add the fifo depth information */
    126    switch (s->fifo_depth) {
    127    case 8:
    128        id3 |= 0 << 3;
    129        break;
    130    case 32:
    131        id3 |= 1 << 3;
    132        break;
    133    case 64:
    134        id3 |= 2 << 3;
    135        break;
    136    case 128:
    137        id3 |= 3 << 3;
    138        break;
    139    case 256:
    140        id3 |= 4 << 3;
    141        break;
    142    case 512:
    143        id3 |= 5 << 3;
    144        break;
    145    case 1024:
    146        id3 |= 6 << 3;
    147        break;
    148    case 2048:
    149        id3 |= 7 << 3;
    150        break;
    151    }
    152
    153    return id3;
    154}
    155
    156static void pl041_reset(PL041State *s)
    157{
    158    DBG_L1("pl041_reset\n");
    159
    160    memset(&s->regs, 0x00, sizeof(pl041_regfile));
    161
    162    s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY;
    163    s->regs.sr1 = TXFE | RXFE | TXHE;
    164    s->regs.isr1 = 0;
    165
    166    memset(&s->fifo1, 0x00, sizeof(s->fifo1));
    167}
    168
    169
    170static void pl041_fifo1_write(PL041State *s, uint32_t value)
    171{
    172    pl041_channel *channel = &s->fifo1;
    173    pl041_fifo *fifo = &s->fifo1.tx_fifo;
    174
    175    /* Push the value in the FIFO */
    176    if (channel->tx_compact_mode == 0) {
    177        /* Non-compact mode */
    178
    179        if (fifo->level < s->fifo_depth) {
    180            /* Pad the value with 0 to obtain a 20-bit sample */
    181            switch (channel->tx_sample_size) {
    182            case 12:
    183                value = (value << 8) & 0xFFFFF;
    184                break;
    185            case 16:
    186                value = (value << 4) & 0xFFFFF;
    187                break;
    188            case 18:
    189                value = (value << 2) & 0xFFFFF;
    190                break;
    191            case 20:
    192            default:
    193                break;
    194            }
    195
    196            /* Store the sample in the FIFO */
    197            fifo->data[fifo->level++] = value;
    198        }
    199#if defined(PL041_DEBUG_LEVEL)
    200        else {
    201            DBG_L1("fifo1 write: overrun\n");
    202        }
    203#endif
    204    } else {
    205        /* Compact mode */
    206
    207        if ((fifo->level + 2) < s->fifo_depth) {
    208            uint32_t i = 0;
    209            uint32_t sample = 0;
    210
    211            for (i = 0; i < 2; i++) {
    212                sample = value & 0xFFFF;
    213                value = value >> 16;
    214
    215                /* Pad each sample with 0 to obtain a 20-bit sample */
    216                switch (channel->tx_sample_size) {
    217                case 12:
    218                    sample = sample << 8;
    219                    break;
    220                case 16:
    221                default:
    222                    sample = sample << 4;
    223                    break;
    224                }
    225
    226                /* Store the sample in the FIFO */
    227                fifo->data[fifo->level++] = sample;
    228            }
    229        }
    230#if defined(PL041_DEBUG_LEVEL)
    231        else {
    232            DBG_L1("fifo1 write: overrun\n");
    233        }
    234#endif
    235    }
    236
    237    /* Update the status register */
    238    if (fifo->level > 0) {
    239        s->regs.sr1 &= ~(TXUNDERRUN | TXFE);
    240    }
    241
    242    if (fifo->level >= (s->fifo_depth / 2)) {
    243        s->regs.sr1 &= ~TXHE;
    244    }
    245
    246    if (fifo->level >= s->fifo_depth) {
    247        s->regs.sr1 |= TXFF;
    248    }
    249
    250    DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1);
    251}
    252
    253static void pl041_fifo1_transmit(PL041State *s)
    254{
    255    pl041_channel *channel = &s->fifo1;
    256    pl041_fifo *fifo = &s->fifo1.tx_fifo;
    257    uint32_t slots = s->regs.txcr1 & TXSLOT_MASK;
    258    uint32_t written_samples;
    259
    260    /* Check if FIFO1 transmit is enabled */
    261    if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) {
    262        if (fifo->level >= (s->fifo_depth / 2)) {
    263            int i;
    264
    265            DBG_L1("Transfer FIFO level = %i\n", fifo->level);
    266
    267            /* Try to transfer the whole FIFO */
    268            for (i = 0; i < (fifo->level / 2); i++) {
    269                uint32_t left = fifo->data[i * 2];
    270                uint32_t right = fifo->data[i * 2 + 1];
    271
    272                 /* Transmit two 20-bit samples to the codec */
    273                if (lm4549_write_samples(&s->codec, left, right) == 0) {
    274                    DBG_L1("Codec buffer full\n");
    275                    break;
    276                }
    277            }
    278
    279            written_samples = i * 2;
    280            if (written_samples > 0) {
    281                /* Update the FIFO level */
    282                fifo->level -= written_samples;
    283
    284                /* Move back the pending samples to the start of the FIFO */
    285                for (i = 0; i < fifo->level; i++) {
    286                    fifo->data[i] = fifo->data[written_samples + i];
    287                }
    288
    289                /* Update the status register */
    290                s->regs.sr1 &= ~TXFF;
    291
    292                if (fifo->level <= (s->fifo_depth / 2)) {
    293                    s->regs.sr1 |= TXHE;
    294                }
    295
    296                if (fifo->level == 0) {
    297                    s->regs.sr1 |= TXFE | TXUNDERRUN;
    298                    DBG_L1("Empty FIFO\n");
    299                }
    300            }
    301        }
    302    }
    303}
    304
    305static void pl041_isr1_update(PL041State *s)
    306{
    307    /* Update ISR1 */
    308    if (s->regs.sr1 & TXUNDERRUN) {
    309        s->regs.isr1 |= URINTR;
    310    } else {
    311        s->regs.isr1 &= ~URINTR;
    312    }
    313
    314    if (s->regs.sr1 & TXHE) {
    315        s->regs.isr1 |= TXINTR;
    316    } else {
    317        s->regs.isr1 &= ~TXINTR;
    318    }
    319
    320    if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) {
    321        s->regs.isr1 |= TXCINTR;
    322    } else {
    323        s->regs.isr1 &= ~TXCINTR;
    324    }
    325
    326    /* Update the irq state */
    327    qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0);
    328    DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n",
    329           s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1);
    330}
    331
    332static void pl041_request_data(void *opaque)
    333{
    334    PL041State *s = (PL041State *)opaque;
    335
    336    /* Trigger pending transfers */
    337    pl041_fifo1_transmit(s);
    338    pl041_isr1_update(s);
    339}
    340
    341static uint64_t pl041_read(void *opaque, hwaddr offset,
    342                                unsigned size)
    343{
    344    PL041State *s = (PL041State *)opaque;
    345    int value;
    346
    347    if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) {
    348        if (offset == PL041_periphid3) {
    349            value = pl041_compute_periphid3(s);
    350        } else {
    351            value = pl041_default_id[(offset - PL041_periphid0) >> 2];
    352        }
    353
    354        DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value);
    355        return value;
    356    } else if (offset <= PL041_dr4_7) {
    357        value = *((uint32_t *)&s->regs + (offset >> 2));
    358    } else {
    359        DBG_L1("pl041_read: Reserved offset %x\n", (int)offset);
    360        return 0;
    361    }
    362
    363    switch (offset) {
    364    case PL041_allints:
    365        value = s->regs.isr1 & 0x7F;
    366        break;
    367    }
    368
    369    DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset,
    370           get_reg_name(offset), value);
    371
    372    return value;
    373}
    374
    375static void pl041_write(void *opaque, hwaddr offset,
    376                             uint64_t value, unsigned size)
    377{
    378    PL041State *s = (PL041State *)opaque;
    379    uint16_t control, data;
    380    uint32_t result;
    381
    382    DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset,
    383           get_reg_name(offset), (unsigned int)value);
    384
    385    /* Write the register */
    386    if (offset <= PL041_dr4_7) {
    387        *((uint32_t *)&s->regs + (offset >> 2)) = value;
    388    } else {
    389        DBG_L1("pl041_write: Reserved offset %x\n", (int)offset);
    390        return;
    391    }
    392
    393    /* Execute the actions */
    394    switch (offset) {
    395    case PL041_txcr1:
    396    {
    397        pl041_channel *channel = &s->fifo1;
    398
    399        uint32_t txen = s->regs.txcr1 & TXEN;
    400        uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT;
    401        uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0;
    402#if defined(PL041_DEBUG_LEVEL)
    403        uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT;
    404        uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0;
    405#endif
    406
    407        DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i "
    408               "txfen = %i\n", txen, slots,  tsize, compact_mode, txfen);
    409
    410        channel->tx_enabled = txen;
    411        channel->tx_compact_mode = compact_mode;
    412
    413        switch (tsize) {
    414        case 0:
    415            channel->tx_sample_size = 16;
    416            break;
    417        case 1:
    418            channel->tx_sample_size = 18;
    419            break;
    420        case 2:
    421            channel->tx_sample_size = 20;
    422            break;
    423        case 3:
    424            channel->tx_sample_size = 12;
    425            break;
    426        }
    427
    428        DBG_L1("TX enabled = %i\n", channel->tx_enabled);
    429        DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode);
    430        DBG_L1("TX sample width = %i\n", channel->tx_sample_size);
    431
    432        /* Check if compact mode is allowed with selected tsize */
    433        if (channel->tx_compact_mode == 1) {
    434            if ((channel->tx_sample_size == 18) ||
    435                (channel->tx_sample_size == 20)) {
    436                channel->tx_compact_mode = 0;
    437                DBG_L1("Compact mode not allowed with 18/20-bit sample size\n");
    438            }
    439        }
    440
    441        break;
    442    }
    443    case PL041_sl1tx:
    444        s->regs.slfr &= ~SL1TXEMPTY;
    445
    446        control = (s->regs.sl1tx >> 12) & 0x7F;
    447        data = (s->regs.sl2tx >> 4) & 0xFFFF;
    448
    449        if ((s->regs.sl1tx & SLOT1_RW) == 0) {
    450            /* Write operation */
    451            lm4549_write(&s->codec, control, data);
    452        } else {
    453            /* Read operation */
    454            result = lm4549_read(&s->codec, control);
    455
    456            /* Store the returned value */
    457            s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW;
    458            s->regs.sl2rx = result << 4;
    459
    460            s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY);
    461            s->regs.slfr |= SL1RXVALID | SL2RXVALID;
    462        }
    463        break;
    464
    465    case PL041_sl2tx:
    466        s->regs.sl2tx = value;
    467        s->regs.slfr &= ~SL2TXEMPTY;
    468        break;
    469
    470    case PL041_intclr:
    471        DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n",
    472               s->regs.intclr, s->regs.isr1);
    473
    474        if (s->regs.intclr & TXUEC1) {
    475            s->regs.sr1 &= ~TXUNDERRUN;
    476        }
    477        break;
    478
    479    case PL041_maincr:
    480    {
    481#if defined(PL041_DEBUG_LEVEL)
    482        char debug[] = " AACIFE  SL1RXEN  SL1TXEN";
    483        if (!(value & AACIFE)) {
    484            debug[0] = '!';
    485        }
    486        if (!(value & SL1RXEN)) {
    487            debug[8] = '!';
    488        }
    489        if (!(value & SL1TXEN)) {
    490            debug[17] = '!';
    491        }
    492        DBG_L1("%s\n", debug);
    493#endif
    494
    495        if ((s->regs.maincr & AACIFE) == 0) {
    496            pl041_reset(s);
    497        }
    498        break;
    499    }
    500
    501    case PL041_dr1_0:
    502    case PL041_dr1_1:
    503    case PL041_dr1_2:
    504    case PL041_dr1_3:
    505        pl041_fifo1_write(s, value);
    506        break;
    507    }
    508
    509    /* Transmit the FIFO content */
    510    pl041_fifo1_transmit(s);
    511
    512    /* Update the ISR1 register */
    513    pl041_isr1_update(s);
    514}
    515
    516static void pl041_device_reset(DeviceState *d)
    517{
    518    PL041State *s = PL041(d);
    519
    520    pl041_reset(s);
    521}
    522
    523static const MemoryRegionOps pl041_ops = {
    524    .read = pl041_read,
    525    .write = pl041_write,
    526    .endianness = DEVICE_NATIVE_ENDIAN,
    527};
    528
    529static void pl041_init(Object *obj)
    530{
    531    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
    532    PL041State *s = PL041(dev);
    533
    534    DBG_L1("pl041_init 0x%08x\n", (uint32_t)s);
    535
    536    /* Connect the device to the sysbus */
    537    memory_region_init_io(&s->iomem, obj, &pl041_ops, s, "pl041", 0x1000);
    538    sysbus_init_mmio(dev, &s->iomem);
    539    sysbus_init_irq(dev, &s->irq);
    540}
    541
    542static void pl041_realize(DeviceState *dev, Error **errp)
    543{
    544    PL041State *s = PL041(dev);
    545
    546    /* Check the device properties */
    547    switch (s->fifo_depth) {
    548    case 8:
    549    case 32:
    550    case 64:
    551    case 128:
    552    case 256:
    553    case 512:
    554    case 1024:
    555    case 2048:
    556        break;
    557    case 16:
    558    default:
    559        /* NC FIFO depth of 16 is not allowed because its id bits in
    560           AACIPERIPHID3 overlap with the id for the default NC FIFO depth */
    561        qemu_log_mask(LOG_UNIMP,
    562                      "pl041: unsupported non-compact fifo depth [%i]\n",
    563                      s->fifo_depth);
    564    }
    565
    566    /* Init the codec */
    567    lm4549_init(&s->codec, &pl041_request_data, (void *)s);
    568}
    569
    570static const VMStateDescription vmstate_pl041_regfile = {
    571    .name = "pl041_regfile",
    572    .version_id = 1,
    573    .minimum_version_id = 1,
    574    .fields = (VMStateField[]) {
    575#define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile),
    576        #include "pl041.hx"
    577#undef REGISTER
    578        VMSTATE_END_OF_LIST()
    579    }
    580};
    581
    582static const VMStateDescription vmstate_pl041_fifo = {
    583    .name = "pl041_fifo",
    584    .version_id = 1,
    585    .minimum_version_id = 1,
    586    .fields = (VMStateField[]) {
    587        VMSTATE_UINT32(level, pl041_fifo),
    588        VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH),
    589        VMSTATE_END_OF_LIST()
    590    }
    591};
    592
    593static const VMStateDescription vmstate_pl041_channel = {
    594    .name = "pl041_channel",
    595    .version_id = 1,
    596    .minimum_version_id = 1,
    597    .fields = (VMStateField[]) {
    598        VMSTATE_STRUCT(tx_fifo, pl041_channel, 0,
    599                       vmstate_pl041_fifo, pl041_fifo),
    600        VMSTATE_UINT8(tx_enabled, pl041_channel),
    601        VMSTATE_UINT8(tx_compact_mode, pl041_channel),
    602        VMSTATE_UINT8(tx_sample_size, pl041_channel),
    603        VMSTATE_STRUCT(rx_fifo, pl041_channel, 0,
    604                       vmstate_pl041_fifo, pl041_fifo),
    605        VMSTATE_UINT8(rx_enabled, pl041_channel),
    606        VMSTATE_UINT8(rx_compact_mode, pl041_channel),
    607        VMSTATE_UINT8(rx_sample_size, pl041_channel),
    608        VMSTATE_END_OF_LIST()
    609    }
    610};
    611
    612static const VMStateDescription vmstate_pl041 = {
    613    .name = "pl041",
    614    .version_id = 1,
    615    .minimum_version_id = 1,
    616    .fields = (VMStateField[]) {
    617        VMSTATE_UINT32(fifo_depth, PL041State),
    618        VMSTATE_STRUCT(regs, PL041State, 0,
    619                       vmstate_pl041_regfile, pl041_regfile),
    620        VMSTATE_STRUCT(fifo1, PL041State, 0,
    621                       vmstate_pl041_channel, pl041_channel),
    622        VMSTATE_STRUCT(codec, PL041State, 0,
    623                       vmstate_lm4549_state, lm4549_state),
    624        VMSTATE_END_OF_LIST()
    625    }
    626};
    627
    628static Property pl041_device_properties[] = {
    629    DEFINE_AUDIO_PROPERTIES(PL041State, codec.card),
    630    /* Non-compact FIFO depth property */
    631    DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth,
    632                       DEFAULT_FIFO_DEPTH),
    633    DEFINE_PROP_END_OF_LIST(),
    634};
    635
    636static void pl041_device_class_init(ObjectClass *klass, void *data)
    637{
    638    DeviceClass *dc = DEVICE_CLASS(klass);
    639
    640    dc->realize = pl041_realize;
    641    set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
    642    dc->reset = pl041_device_reset;
    643    dc->vmsd = &vmstate_pl041;
    644    device_class_set_props(dc, pl041_device_properties);
    645}
    646
    647static const TypeInfo pl041_device_info = {
    648    .name          = TYPE_PL041,
    649    .parent        = TYPE_SYS_BUS_DEVICE,
    650    .instance_size = sizeof(PL041State),
    651    .instance_init = pl041_init,
    652    .class_init    = pl041_device_class_init,
    653};
    654
    655static void pl041_register_types(void)
    656{
    657    type_register_static(&pl041_device_info);
    658}
    659
    660type_init(pl041_register_types)