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

sifive_uart.c (7674B)


      1/*
      2 * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
      3 *
      4 * Copyright (c) 2016 Stefan O'Rear
      5 *
      6 * This program is free software; you can redistribute it and/or modify it
      7 * under the terms and conditions of the GNU General Public License,
      8 * version 2 or later, as published by the Free Software Foundation.
      9 *
     10 * This program is distributed in the hope it will be useful, but WITHOUT
     11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     13 * more details.
     14 *
     15 * You should have received a copy of the GNU General Public License along with
     16 * this program.  If not, see <http://www.gnu.org/licenses/>.
     17 */
     18
     19#include "qemu/osdep.h"
     20#include "qapi/error.h"
     21#include "qemu/log.h"
     22#include "migration/vmstate.h"
     23#include "chardev/char.h"
     24#include "chardev/char-fe.h"
     25#include "hw/irq.h"
     26#include "hw/char/sifive_uart.h"
     27#include "hw/qdev-properties-system.h"
     28
     29/*
     30 * Not yet implemented:
     31 *
     32 * Transmit FIFO using "qemu/fifo8.h"
     33 */
     34
     35/* Returns the state of the IP (interrupt pending) register */
     36static uint64_t sifive_uart_ip(SiFiveUARTState *s)
     37{
     38    uint64_t ret = 0;
     39
     40    uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl);
     41    uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl);
     42
     43    if (txcnt != 0) {
     44        ret |= SIFIVE_UART_IP_TXWM;
     45    }
     46    if (s->rx_fifo_len > rxcnt) {
     47        ret |= SIFIVE_UART_IP_RXWM;
     48    }
     49
     50    return ret;
     51}
     52
     53static void sifive_uart_update_irq(SiFiveUARTState *s)
     54{
     55    int cond = 0;
     56    if ((s->ie & SIFIVE_UART_IE_TXWM) ||
     57        ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) {
     58        cond = 1;
     59    }
     60    if (cond) {
     61        qemu_irq_raise(s->irq);
     62    } else {
     63        qemu_irq_lower(s->irq);
     64    }
     65}
     66
     67static uint64_t
     68sifive_uart_read(void *opaque, hwaddr addr, unsigned int size)
     69{
     70    SiFiveUARTState *s = opaque;
     71    unsigned char r;
     72    switch (addr) {
     73    case SIFIVE_UART_RXFIFO:
     74        if (s->rx_fifo_len) {
     75            r = s->rx_fifo[0];
     76            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
     77            s->rx_fifo_len--;
     78            qemu_chr_fe_accept_input(&s->chr);
     79            sifive_uart_update_irq(s);
     80            return r;
     81        }
     82        return 0x80000000;
     83
     84    case SIFIVE_UART_TXFIFO:
     85        return 0; /* Should check tx fifo */
     86    case SIFIVE_UART_IE:
     87        return s->ie;
     88    case SIFIVE_UART_IP:
     89        return sifive_uart_ip(s);
     90    case SIFIVE_UART_TXCTRL:
     91        return s->txctrl;
     92    case SIFIVE_UART_RXCTRL:
     93        return s->rxctrl;
     94    case SIFIVE_UART_DIV:
     95        return s->div;
     96    }
     97
     98    qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
     99                  __func__, (int)addr);
    100    return 0;
    101}
    102
    103static void
    104sifive_uart_write(void *opaque, hwaddr addr,
    105                  uint64_t val64, unsigned int size)
    106{
    107    SiFiveUARTState *s = opaque;
    108    uint32_t value = val64;
    109    unsigned char ch = value;
    110
    111    switch (addr) {
    112    case SIFIVE_UART_TXFIFO:
    113        qemu_chr_fe_write(&s->chr, &ch, 1);
    114        sifive_uart_update_irq(s);
    115        return;
    116    case SIFIVE_UART_IE:
    117        s->ie = val64;
    118        sifive_uart_update_irq(s);
    119        return;
    120    case SIFIVE_UART_TXCTRL:
    121        s->txctrl = val64;
    122        return;
    123    case SIFIVE_UART_RXCTRL:
    124        s->rxctrl = val64;
    125        return;
    126    case SIFIVE_UART_DIV:
    127        s->div = val64;
    128        return;
    129    }
    130    qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
    131                  __func__, (int)addr, (int)value);
    132}
    133
    134static const MemoryRegionOps sifive_uart_ops = {
    135    .read = sifive_uart_read,
    136    .write = sifive_uart_write,
    137    .endianness = DEVICE_NATIVE_ENDIAN,
    138    .valid = {
    139        .min_access_size = 4,
    140        .max_access_size = 4
    141    }
    142};
    143
    144static void sifive_uart_rx(void *opaque, const uint8_t *buf, int size)
    145{
    146    SiFiveUARTState *s = opaque;
    147
    148    /* Got a byte.  */
    149    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
    150        printf("WARNING: UART dropped char.\n");
    151        return;
    152    }
    153    s->rx_fifo[s->rx_fifo_len++] = *buf;
    154
    155    sifive_uart_update_irq(s);
    156}
    157
    158static int sifive_uart_can_rx(void *opaque)
    159{
    160    SiFiveUARTState *s = opaque;
    161
    162    return s->rx_fifo_len < sizeof(s->rx_fifo);
    163}
    164
    165static void sifive_uart_event(void *opaque, QEMUChrEvent event)
    166{
    167}
    168
    169static int sifive_uart_be_change(void *opaque)
    170{
    171    SiFiveUARTState *s = opaque;
    172
    173    qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx,
    174                             sifive_uart_event, sifive_uart_be_change, s,
    175                             NULL, true);
    176
    177    return 0;
    178}
    179
    180static Property sifive_uart_properties[] = {
    181    DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr),
    182    DEFINE_PROP_END_OF_LIST(),
    183};
    184
    185static void sifive_uart_init(Object *obj)
    186{
    187    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    188    SiFiveUARTState *s = SIFIVE_UART(obj);
    189
    190    memory_region_init_io(&s->mmio, OBJECT(s), &sifive_uart_ops, s,
    191                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
    192    sysbus_init_mmio(sbd, &s->mmio);
    193    sysbus_init_irq(sbd, &s->irq);
    194}
    195
    196static void sifive_uart_realize(DeviceState *dev, Error **errp)
    197{
    198    SiFiveUARTState *s = SIFIVE_UART(dev);
    199
    200    qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx,
    201                             sifive_uart_event, sifive_uart_be_change, s,
    202                             NULL, true);
    203
    204}
    205
    206static void sifive_uart_reset_enter(Object *obj, ResetType type)
    207{
    208    SiFiveUARTState *s = SIFIVE_UART(obj);
    209    s->ie = 0;
    210    s->ip = 0;
    211    s->txctrl = 0;
    212    s->rxctrl = 0;
    213    s->div = 0;
    214    s->rx_fifo_len = 0;
    215}
    216
    217static void sifive_uart_reset_hold(Object *obj)
    218{
    219    SiFiveUARTState *s = SIFIVE_UART(obj);
    220    qemu_irq_lower(s->irq);
    221}
    222
    223static const VMStateDescription vmstate_sifive_uart = {
    224    .name = TYPE_SIFIVE_UART,
    225    .version_id = 1,
    226    .minimum_version_id = 1,
    227    .fields = (VMStateField[]) {
    228        VMSTATE_UINT8_ARRAY(rx_fifo, SiFiveUARTState,
    229                            SIFIVE_UART_RX_FIFO_SIZE),
    230        VMSTATE_UINT8(rx_fifo_len, SiFiveUARTState),
    231        VMSTATE_UINT32(ie, SiFiveUARTState),
    232        VMSTATE_UINT32(ip, SiFiveUARTState),
    233        VMSTATE_UINT32(txctrl, SiFiveUARTState),
    234        VMSTATE_UINT32(rxctrl, SiFiveUARTState),
    235        VMSTATE_UINT32(div, SiFiveUARTState),
    236        VMSTATE_END_OF_LIST()
    237    },
    238};
    239
    240
    241static void sifive_uart_class_init(ObjectClass *oc, void *data)
    242{
    243    DeviceClass *dc = DEVICE_CLASS(oc);
    244    ResettableClass *rc = RESETTABLE_CLASS(oc);
    245
    246    dc->realize = sifive_uart_realize;
    247    dc->vmsd = &vmstate_sifive_uart;
    248    rc->phases.enter = sifive_uart_reset_enter;
    249    rc->phases.hold  = sifive_uart_reset_hold;
    250    device_class_set_props(dc, sifive_uart_properties);
    251    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
    252}
    253
    254static const TypeInfo sifive_uart_info = {
    255    .name          = TYPE_SIFIVE_UART,
    256    .parent        = TYPE_SYS_BUS_DEVICE,
    257    .instance_size = sizeof(SiFiveUARTState),
    258    .instance_init = sifive_uart_init,
    259    .class_init    = sifive_uart_class_init,
    260};
    261
    262static void sifive_uart_register_types(void)
    263{
    264    type_register_static(&sifive_uart_info);
    265}
    266
    267type_init(sifive_uart_register_types)
    268
    269/*
    270 * Create UART device.
    271 */
    272SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
    273    Chardev *chr, qemu_irq irq)
    274{
    275    DeviceState *dev;
    276    SysBusDevice *s;
    277    SiFiveUARTState *r;
    278
    279    dev = qdev_new("riscv.sifive.uart");
    280    s = SYS_BUS_DEVICE(dev);
    281    qdev_prop_set_chr(dev, "chardev", chr);
    282    sysbus_realize_and_unref(s, &error_fatal);
    283    memory_region_add_subregion(address_space, base,
    284                                sysbus_mmio_get_region(s, 0));
    285    sysbus_connect_irq(s, 0, irq);
    286
    287    r = SIFIVE_UART(dev);
    288    return r;
    289}