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

sh_serial.c (11647B)


      1/*
      2 * QEMU SCI/SCIF serial port emulation
      3 *
      4 * Copyright (c) 2007 Magnus Damm
      5 *
      6 * Based on serial.c - QEMU 16450 UART emulation
      7 * Copyright (c) 2003-2004 Fabrice Bellard
      8 *
      9 * Permission is hereby granted, free of charge, to any person obtaining a copy
     10 * of this software and associated documentation files (the "Software"), to deal
     11 * in the Software without restriction, including without limitation the rights
     12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     13 * copies of the Software, and to permit persons to whom the Software is
     14 * furnished to do so, subject to the following conditions:
     15 *
     16 * The above copyright notice and this permission notice shall be included in
     17 * all copies or substantial portions of the Software.
     18 *
     19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     25 * THE SOFTWARE.
     26 */
     27
     28#include "qemu/osdep.h"
     29#include "hw/irq.h"
     30#include "hw/sh4/sh.h"
     31#include "chardev/char-fe.h"
     32#include "qapi/error.h"
     33#include "qemu/timer.h"
     34
     35//#define DEBUG_SERIAL
     36
     37#define SH_SERIAL_FLAG_TEND (1 << 0)
     38#define SH_SERIAL_FLAG_TDE  (1 << 1)
     39#define SH_SERIAL_FLAG_RDF  (1 << 2)
     40#define SH_SERIAL_FLAG_BRK  (1 << 3)
     41#define SH_SERIAL_FLAG_DR   (1 << 4)
     42
     43#define SH_RX_FIFO_LENGTH (16)
     44
     45typedef struct {
     46    MemoryRegion iomem;
     47    MemoryRegion iomem_p4;
     48    MemoryRegion iomem_a7;
     49    uint8_t smr;
     50    uint8_t brr;
     51    uint8_t scr;
     52    uint8_t dr; /* ftdr / tdr */
     53    uint8_t sr; /* fsr / ssr */
     54    uint16_t fcr;
     55    uint8_t sptr;
     56
     57    uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
     58    uint8_t rx_cnt;
     59    uint8_t rx_tail;
     60    uint8_t rx_head;
     61
     62    int freq;
     63    int feat;
     64    int flags;
     65    int rtrg;
     66
     67    CharBackend chr;
     68    QEMUTimer *fifo_timeout_timer;
     69    uint64_t etu; /* Elementary Time Unit (ns) */
     70
     71    qemu_irq eri;
     72    qemu_irq rxi;
     73    qemu_irq txi;
     74    qemu_irq tei;
     75    qemu_irq bri;
     76} sh_serial_state;
     77
     78static void sh_serial_clear_fifo(sh_serial_state * s)
     79{
     80    memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
     81    s->rx_cnt = 0;
     82    s->rx_head = 0;
     83    s->rx_tail = 0;
     84}
     85
     86static void sh_serial_write(void *opaque, hwaddr offs,
     87                            uint64_t val, unsigned size)
     88{
     89    sh_serial_state *s = opaque;
     90    unsigned char ch;
     91
     92#ifdef DEBUG_SERIAL
     93    printf("sh_serial: write offs=0x%02x val=0x%02x\n",
     94           offs, val);
     95#endif
     96    switch(offs) {
     97    case 0x00: /* SMR */
     98        s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
     99        return;
    100    case 0x04: /* BRR */
    101        s->brr = val;
    102        return;
    103    case 0x08: /* SCR */
    104        /* TODO : For SH7751, SCIF mask should be 0xfb. */
    105        s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
    106        if (!(val & (1 << 5)))
    107            s->flags |= SH_SERIAL_FLAG_TEND;
    108        if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
    109            qemu_set_irq(s->txi, val & (1 << 7));
    110        }
    111        if (!(val & (1 << 6))) {
    112            qemu_set_irq(s->rxi, 0);
    113        }
    114        return;
    115    case 0x0c: /* FTDR / TDR */
    116        if (qemu_chr_fe_backend_connected(&s->chr)) {
    117            ch = val;
    118            /* XXX this blocks entire thread. Rewrite to use
    119             * qemu_chr_fe_write and background I/O callbacks */
    120            qemu_chr_fe_write_all(&s->chr, &ch, 1);
    121        }
    122        s->dr = val;
    123        s->flags &= ~SH_SERIAL_FLAG_TDE;
    124        return;
    125#if 0
    126    case 0x14: /* FRDR / RDR */
    127        ret = 0;
    128        break;
    129#endif
    130    }
    131    if (s->feat & SH_SERIAL_FEAT_SCIF) {
    132        switch(offs) {
    133        case 0x10: /* FSR */
    134            if (!(val & (1 << 6)))
    135                s->flags &= ~SH_SERIAL_FLAG_TEND;
    136            if (!(val & (1 << 5)))
    137                s->flags &= ~SH_SERIAL_FLAG_TDE;
    138            if (!(val & (1 << 4)))
    139                s->flags &= ~SH_SERIAL_FLAG_BRK;
    140            if (!(val & (1 << 1)))
    141                s->flags &= ~SH_SERIAL_FLAG_RDF;
    142            if (!(val & (1 << 0)))
    143                s->flags &= ~SH_SERIAL_FLAG_DR;
    144
    145            if (!(val & (1 << 1)) || !(val & (1 << 0))) {
    146                if (s->rxi) {
    147                    qemu_set_irq(s->rxi, 0);
    148                }
    149            }
    150            return;
    151        case 0x18: /* FCR */
    152            s->fcr = val;
    153            switch ((val >> 6) & 3) {
    154            case 0:
    155                s->rtrg = 1;
    156                break;
    157            case 1:
    158                s->rtrg = 4;
    159                break;
    160            case 2:
    161                s->rtrg = 8;
    162                break;
    163            case 3:
    164                s->rtrg = 14;
    165                break;
    166            }
    167            if (val & (1 << 1)) {
    168                sh_serial_clear_fifo(s);
    169                s->sr &= ~(1 << 1);
    170            }
    171
    172            return;
    173        case 0x20: /* SPTR */
    174            s->sptr = val & 0xf3;
    175            return;
    176        case 0x24: /* LSR */
    177            return;
    178        }
    179    }
    180    else {
    181        switch(offs) {
    182#if 0
    183        case 0x0c:
    184            ret = s->dr;
    185            break;
    186        case 0x10:
    187            ret = 0;
    188            break;
    189#endif
    190        case 0x1c:
    191            s->sptr = val & 0x8f;
    192            return;
    193        }
    194    }
    195
    196    fprintf(stderr, "sh_serial: unsupported write to 0x%02"
    197            HWADDR_PRIx "\n", offs);
    198    abort();
    199}
    200
    201static uint64_t sh_serial_read(void *opaque, hwaddr offs,
    202                               unsigned size)
    203{
    204    sh_serial_state *s = opaque;
    205    uint32_t ret = ~0;
    206
    207#if 0
    208    switch(offs) {
    209    case 0x00:
    210        ret = s->smr;
    211        break;
    212    case 0x04:
    213        ret = s->brr;
    214        break;
    215    case 0x08:
    216        ret = s->scr;
    217        break;
    218    case 0x14:
    219        ret = 0;
    220        break;
    221    }
    222#endif
    223    if (s->feat & SH_SERIAL_FEAT_SCIF) {
    224        switch(offs) {
    225        case 0x00: /* SMR */
    226            ret = s->smr;
    227            break;
    228        case 0x08: /* SCR */
    229            ret = s->scr;
    230            break;
    231        case 0x10: /* FSR */
    232            ret = 0;
    233            if (s->flags & SH_SERIAL_FLAG_TEND)
    234                ret |= (1 << 6);
    235            if (s->flags & SH_SERIAL_FLAG_TDE)
    236                ret |= (1 << 5);
    237            if (s->flags & SH_SERIAL_FLAG_BRK)
    238                ret |= (1 << 4);
    239            if (s->flags & SH_SERIAL_FLAG_RDF)
    240                ret |= (1 << 1);
    241            if (s->flags & SH_SERIAL_FLAG_DR)
    242                ret |= (1 << 0);
    243
    244            if (s->scr & (1 << 5))
    245                s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
    246
    247            break;
    248        case 0x14:
    249            if (s->rx_cnt > 0) {
    250                ret = s->rx_fifo[s->rx_tail++];
    251                s->rx_cnt--;
    252                if (s->rx_tail == SH_RX_FIFO_LENGTH)
    253                    s->rx_tail = 0;
    254                if (s->rx_cnt < s->rtrg)
    255                    s->flags &= ~SH_SERIAL_FLAG_RDF;
    256            }
    257            break;
    258        case 0x18:
    259            ret = s->fcr;
    260            break;
    261        case 0x1c:
    262            ret = s->rx_cnt;
    263            break;
    264        case 0x20:
    265            ret = s->sptr;
    266            break;
    267        case 0x24:
    268            ret = 0;
    269            break;
    270        }
    271    }
    272    else {
    273        switch(offs) {
    274#if 0
    275        case 0x0c:
    276            ret = s->dr;
    277            break;
    278        case 0x10:
    279            ret = 0;
    280            break;
    281        case 0x14:
    282            ret = s->rx_fifo[0];
    283            break;
    284#endif
    285        case 0x1c:
    286            ret = s->sptr;
    287            break;
    288        }
    289    }
    290#ifdef DEBUG_SERIAL
    291    printf("sh_serial: read offs=0x%02x val=0x%x\n",
    292           offs, ret);
    293#endif
    294
    295    if (ret & ~((1 << 16) - 1)) {
    296        fprintf(stderr, "sh_serial: unsupported read from 0x%02"
    297                HWADDR_PRIx "\n", offs);
    298        abort();
    299    }
    300
    301    return ret;
    302}
    303
    304static int sh_serial_can_receive(sh_serial_state *s)
    305{
    306    return s->scr & (1 << 4);
    307}
    308
    309static void sh_serial_receive_break(sh_serial_state *s)
    310{
    311    if (s->feat & SH_SERIAL_FEAT_SCIF)
    312        s->sr |= (1 << 4);
    313}
    314
    315static int sh_serial_can_receive1(void *opaque)
    316{
    317    sh_serial_state *s = opaque;
    318    return sh_serial_can_receive(s);
    319}
    320
    321static void sh_serial_timeout_int(void *opaque)
    322{
    323    sh_serial_state *s = opaque;
    324
    325    s->flags |= SH_SERIAL_FLAG_RDF;
    326    if (s->scr & (1 << 6) && s->rxi) {
    327        qemu_set_irq(s->rxi, 1);
    328    }
    329}
    330
    331static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
    332{
    333    sh_serial_state *s = opaque;
    334
    335    if (s->feat & SH_SERIAL_FEAT_SCIF) {
    336        int i;
    337        for (i = 0; i < size; i++) {
    338            if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
    339                s->rx_fifo[s->rx_head++] = buf[i];
    340                if (s->rx_head == SH_RX_FIFO_LENGTH) {
    341                    s->rx_head = 0;
    342                }
    343                s->rx_cnt++;
    344                if (s->rx_cnt >= s->rtrg) {
    345                    s->flags |= SH_SERIAL_FLAG_RDF;
    346                    if (s->scr & (1 << 6) && s->rxi) {
    347                        timer_del(s->fifo_timeout_timer);
    348                        qemu_set_irq(s->rxi, 1);
    349                    }
    350                } else {
    351                    timer_mod(s->fifo_timeout_timer,
    352                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
    353                }
    354            }
    355        }
    356    } else {
    357        s->rx_fifo[0] = buf[0];
    358    }
    359}
    360
    361static void sh_serial_event(void *opaque, QEMUChrEvent event)
    362{
    363    sh_serial_state *s = opaque;
    364    if (event == CHR_EVENT_BREAK)
    365        sh_serial_receive_break(s);
    366}
    367
    368static const MemoryRegionOps sh_serial_ops = {
    369    .read = sh_serial_read,
    370    .write = sh_serial_write,
    371    .endianness = DEVICE_NATIVE_ENDIAN,
    372};
    373
    374void sh_serial_init(MemoryRegion *sysmem,
    375                    hwaddr base, int feat,
    376                    uint32_t freq, Chardev *chr,
    377                    qemu_irq eri_source,
    378                    qemu_irq rxi_source,
    379                    qemu_irq txi_source,
    380                    qemu_irq tei_source,
    381                    qemu_irq bri_source)
    382{
    383    sh_serial_state *s;
    384
    385    s = g_malloc0(sizeof(sh_serial_state));
    386
    387    s->feat = feat;
    388    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
    389    s->rtrg = 1;
    390
    391    s->smr = 0;
    392    s->brr = 0xff;
    393    s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
    394    s->sptr = 0;
    395
    396    if (feat & SH_SERIAL_FEAT_SCIF) {
    397        s->fcr = 0;
    398    }
    399    else {
    400        s->dr = 0xff;
    401    }
    402
    403    sh_serial_clear_fifo(s);
    404
    405    memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s,
    406                          "serial", 0x100000000ULL);
    407
    408    memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem,
    409                             0, 0x28);
    410    memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
    411
    412    memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem,
    413                             0, 0x28);
    414    memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
    415
    416    if (chr) {
    417        qemu_chr_fe_init(&s->chr, chr, &error_abort);
    418        qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
    419                                 sh_serial_receive1,
    420                                 sh_serial_event, NULL, s, NULL, true);
    421    }
    422
    423    s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
    424                                         sh_serial_timeout_int, s);
    425    s->etu = NANOSECONDS_PER_SECOND / 9600;
    426    s->eri = eri_source;
    427    s->rxi = rxi_source;
    428    s->txi = txi_source;
    429    s->tei = tei_source;
    430    s->bri = bri_source;
    431}