qemu/hw/misc/csu_stream_switch.c
<<
>>
Prefs
   1/*
   2 * QEMU model of ZynqMP CSU Secure Stream Switch (SSS)
   3 *
   4 * For the most part, a dummy device model. Consumes as much data off the stream
   5 * interface as you can throw at it and produces zeros as fast as the sink is
   6 * willing to accept them.
   7 *
   8 * Copyright (c) 2013 Peter Xilinx Inc
   9 * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
  10 *
  11 * Permission is hereby granted, free of charge, to any person obtaining a copy
  12 * of this software and associated documentation files (the "Software"), to deal
  13 * in the Software without restriction, including without limitation the rights
  14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15 * copies of the Software, and to permit persons to whom the Software is
  16 * furnished to do so, subject to the following conditions:
  17 *
  18 * The above copyright notice and this permission notice shall be included in
  19 * all copies or substantial portions of the Software.
  20 *
  21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27 * THE SOFTWARE.
  28 */
  29
  30#include "qemu/osdep.h"
  31#include "hw/sysbus.h"
  32#include "qapi/error.h"
  33#include "qemu/log.h"
  34
  35#include "hw/stream.h"
  36#include "qemu/bitops.h"
  37#include "qapi/qmp/qerror.h"
  38#include "hw/register.h"
  39
  40#ifndef ZYNQMP_CSU_SSS_ERR_DEBUG
  41#define ZYNQMP_CSU_SSS_ERR_DEBUG 0
  42#endif
  43
  44#define TYPE_ZYNQMP_CSU_SSS "zynqmp.csu-sss"
  45#define TYPE_ZYNQMP_CSU_SSS_STREAM "zynqmp.csu-sss-stream"
  46
  47#define ZYNQMP_CSU_SSS(obj) \
  48     OBJECT_CHECK(ZynqMPCSUSSS, (obj), TYPE_ZYNQMP_CSU_SSS)
  49
  50#define ZYNQMP_CSU_SSS_STREAM(obj) \
  51     OBJECT_CHECK(ZynqMPCSUSSSStream, (obj), TYPE_ZYNQMP_CSU_SSS_STREAM)
  52
  53
  54typedef enum {
  55    DMA,
  56    PCAP,
  57    AES,
  58    SHA,
  59    PSTP,
  60    ROM, /* FIXME: ROM, may have no software visibility - delete? */
  61    NUM_REMOTES
  62} ZynqMPCSUSSSRemote;
  63
  64#define NO_REMOTE NUM_REMOTES
  65
  66static const char *zynqmp_csu_sss_remote_names[] = {
  67    [PCAP] = "pcap",
  68    [DMA] = "dma",
  69    [AES] = "aes",
  70    [SHA] = "sha",
  71    [PSTP] = "pstp",
  72    [ROM] = "rom",
  73    /* FIXME: Add TMR */
  74};
  75
  76static const uint32_t zynqmp_csu_sss_population[] = {
  77    [PCAP] = (1 << DMA) | (1 << AES) | (1 << PSTP),
  78    [DMA] = (1 << DMA) | (1 << AES) | (1 << PCAP) | (1 << PSTP),
  79    [AES] = (1 << DMA),
  80    [SHA] = (1 << DMA) | (1 << ROM),
  81    [PSTP] = (1 << PCAP),
  82    [NO_REMOTE] = 0,
  83};
  84
  85#define R_CFG 0
  86
  87static const int r_cfg_sss_shifts[] = {
  88    [PCAP] = 0,
  89    [DMA] = 4,
  90    [AES] = 8,
  91    [SHA] = 12,
  92    [PSTP] = 16,
  93    [ROM] = -1,
  94};
  95
  96static const uint8_t r_cfg_sss_encodings[] = {
  97    [PCAP] = 0x3,
  98    [DMA] = 0x5,
  99    [AES] = 0xa,
 100    [SHA] = 0,
 101    [PSTP] = 0xc,
 102    [ROM] = 0x0,
 103};
 104
 105#define R_CFG_SSS_LENGTH 4
 106#define R_CFG_RSVD 0xFFF00000
 107
 108#define R_MAX (R_CFG + 1)
 109
 110typedef struct ZynqMPCSUSSS ZynqMPCSUSSS;
 111typedef struct ZynqMPCSUSSSStream ZynqMPCSUSSSStream;
 112
 113struct ZynqMPCSUSSSStream {
 114    DeviceState parent_obj;
 115
 116    ZynqMPCSUSSS *sss;
 117};
 118
 119struct ZynqMPCSUSSS {
 120    SysBusDevice busdev;
 121    MemoryRegion iomem;
 122    StreamSlave *tx_devs[NUM_REMOTES];
 123    ZynqMPCSUSSSStream rx_devs[NUM_REMOTES];
 124
 125    uint32_t regs[R_MAX];
 126    RegisterInfo regs_info[R_MAX];
 127
 128    StreamCanPushNotifyFn notifys[NUM_REMOTES];
 129    void *notify_opaques[NUM_REMOTES];
 130};
 131
 132static void zynqmp_csu_sss_notify_all(ZynqMPCSUSSS *s)
 133{
 134    ZynqMPCSUSSSRemote remote;
 135
 136    for (remote = 0; remote < NUM_REMOTES; ++remote) {
 137        if (s->notifys[remote]) {
 138            s->notifys[remote](s->notify_opaques[remote]);
 139            s->notifys[remote] = NULL;
 140        }
 141    }
 142}
 143
 144static void zynqmp_csu_sss_reset(DeviceState *dev)
 145{
 146    ZynqMPCSUSSS *s = ZYNQMP_CSU_SSS(dev);
 147    int i;
 148 
 149    for (i = 0; i < R_MAX; ++i) {
 150        register_reset(&s->regs_info[i]);
 151    }
 152    zynqmp_csu_sss_notify_all(s);
 153}
 154
 155static inline ZynqMPCSUSSSRemote
 156zynqmp_csu_sss_lookup_rx_remote(ZynqMPCSUSSS *s, ZynqMPCSUSSSStream *ss)
 157{
 158    ZynqMPCSUSSSRemote ret;
 159
 160    for (ret = 0; ret < NUM_REMOTES; ++ret) {
 161        if (ss == &s->rx_devs[ret]) {
 162            break;
 163        }
 164    }
 165    return ret;
 166}
 167
 168static inline ZynqMPCSUSSSRemote
 169zynqmp_csu_sss_lookup_tx_remote(ZynqMPCSUSSS *s,
 170                                 ZynqMPCSUSSSRemote rx_remote)
 171{
 172    uint32_t enc;
 173        if (rx_remote == NO_REMOTE) {
 174                return NO_REMOTE;
 175        }
 176
 177    ZynqMPCSUSSSRemote ret;
 178
 179    for (ret = 0; ret < NUM_REMOTES; ++ret) {
 180        if (r_cfg_sss_shifts[ret] == -1) {
 181            /* This unit has no input. Ignore it.  */
 182            continue;
 183        }
 184
 185        enc = extract32(s->regs[R_CFG], r_cfg_sss_shifts[ret],
 186                        R_CFG_SSS_LENGTH);
 187        if (r_cfg_sss_encodings[rx_remote] == enc) {
 188            break;
 189        }
 190    }
 191    return (zynqmp_csu_sss_population[ret] & (1 << rx_remote)) ?
 192                    ret : NO_REMOTE;
 193}
 194static bool
 195zynqmp_csu_sss_stream_can_push(StreamSlave *obj, StreamCanPushNotifyFn notify,
 196                                void *notify_opaque)
 197{
 198    ZynqMPCSUSSSStream *ss = ZYNQMP_CSU_SSS_STREAM(obj);
 199    ZynqMPCSUSSS *s = ZYNQMP_CSU_SSS(ss->sss);
 200    ZynqMPCSUSSSRemote rx = zynqmp_csu_sss_lookup_rx_remote(s, ss);
 201    ZynqMPCSUSSSRemote tx = zynqmp_csu_sss_lookup_tx_remote(s, rx);
 202
 203    if (tx != NO_REMOTE && s->tx_devs[tx] &&
 204            stream_can_push(s->tx_devs[tx], notify, notify_opaque)) {
 205        return true;
 206    }
 207
 208    s->notifys[rx] = notify;
 209    s->notify_opaques[rx] = notify_opaque;
 210    return false;
 211}
 212
 213
 214
 215static size_t zynqmp_csu_sss_stream_push(StreamSlave *obj, uint8_t *buf,
 216                                          size_t len, uint32_t attr)
 217{
 218    ZynqMPCSUSSSStream *ss = ZYNQMP_CSU_SSS_STREAM(obj);
 219    ZynqMPCSUSSS *s = ZYNQMP_CSU_SSS(ss->sss);
 220    ZynqMPCSUSSSRemote rx = zynqmp_csu_sss_lookup_rx_remote(s, ss);
 221    ZynqMPCSUSSSRemote tx = zynqmp_csu_sss_lookup_tx_remote(s, rx);
 222
 223    return (tx != NO_REMOTE) ? stream_push(s->tx_devs[tx], buf, len, attr) : 0;
 224}
 225
 226static void r_cfg_post_write(RegisterInfo *reg, uint64_t val) {
 227    ZynqMPCSUSSS *s = ZYNQMP_CSU_SSS(reg->opaque);
 228
 229    zynqmp_csu_sss_notify_all(s);
 230}
 231
 232static const RegisterAccessInfo zynqmp_csu_sss_regs_info[] = {
 233    [R_CFG] = { .name = "R_CFG", .ro = R_CFG_RSVD, .post_write = r_cfg_post_write },
 234};
 235
 236static const MemoryRegionOps zynqmp_csu_sss_ops = {
 237    .read = register_read_memory_le,
 238    .write = register_write_memory_le,
 239    .endianness = DEVICE_LITTLE_ENDIAN,
 240    .valid = {
 241        .min_access_size = 4,
 242        .max_access_size = 4,
 243    }
 244};
 245
 246static void zynqmp_csu_sss_realize(DeviceState *dev, Error **errp)
 247{
 248    ZynqMPCSUSSS *s = ZYNQMP_CSU_SSS(dev);
 249    Error *local_errp = NULL;
 250    ZynqMPCSUSSSRemote r;
 251    const char *prefix = object_get_canonical_path(OBJECT(dev));
 252    int i;
 253
 254    for (i = 0; i < R_MAX; ++i) {
 255        RegisterInfo *r = &s->regs_info[i];
 256
 257        *r = (RegisterInfo) {
 258            .data = (uint8_t *)&s->regs[i],
 259            .data_size = sizeof(uint32_t),
 260            .access = &zynqmp_csu_sss_regs_info[i],
 261            .debug = ZYNQMP_CSU_SSS_ERR_DEBUG,
 262            .prefix = prefix,
 263            .opaque = s,
 264        };
 265        memory_region_init_io(&r->mem, OBJECT(dev), &zynqmp_csu_sss_ops, r,
 266                              "sss-regs", 4);
 267        memory_region_add_subregion(&s->iomem, i * 4, &r->mem);
 268    }
 269
 270    for (r = 0; r < NUM_REMOTES; ++r) {
 271        ZynqMPCSUSSSStream *ss = ZYNQMP_CSU_SSS_STREAM(&s->rx_devs[r]);
 272
 273        object_property_add_link(OBJECT(ss), "sss", TYPE_ZYNQMP_CSU_SSS,
 274                                 (Object **)&ss->sss,
 275                                 qdev_prop_allow_set_link_before_realize,
 276                                 OBJ_PROP_LINK_UNREF_ON_RELEASE,
 277                                 &local_errp);
 278        if (local_errp) {
 279            goto zynqmp_csu_sss_realize_fail;
 280        }
 281        object_property_set_link(OBJECT(ss), OBJECT(s), "sss", &local_errp);
 282        if (local_errp) {
 283            goto zynqmp_csu_sss_realize_fail;
 284        }
 285    }
 286    return;
 287
 288zynqmp_csu_sss_realize_fail:
 289    if (!*errp) {
 290        *errp = local_errp;
 291    }
 292}
 293
 294static void zynqmp_csu_sss_init(Object *obj)
 295{
 296    ZynqMPCSUSSS *s = ZYNQMP_CSU_SSS(obj);
 297    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 298    ZynqMPCSUSSSRemote r;
 299
 300    for (r = 0; r < NUM_REMOTES; ++r) {
 301        char *name = g_strdup_printf("stream-connected-%s",
 302                                     zynqmp_csu_sss_remote_names[r]);
 303        object_property_add_link(obj, name, TYPE_STREAM_SLAVE,
 304                                 (Object **)&s->tx_devs[r],
 305                                 qdev_prop_allow_set_link_before_realize,
 306                                 OBJ_PROP_LINK_UNREF_ON_RELEASE,
 307                                 NULL);
 308        g_free(name);
 309        name = g_strdup_printf("stream-connected-%s-target",
 310                               zynqmp_csu_sss_remote_names[r]);
 311        object_initialize(&s->rx_devs[r], sizeof(s->rx_devs[r]),
 312                          TYPE_ZYNQMP_CSU_SSS_STREAM);
 313        object_property_add_child(OBJECT(s), name, (Object *)&s->rx_devs[r],
 314                                  &error_abort);
 315        g_free(name);
 316    }
 317
 318    memory_region_init_io(&s->iomem, obj, &zynqmp_csu_sss_ops, s,
 319                          "zynqmp.csu-stream-switch", R_MAX * 4);
 320    sysbus_init_mmio(sbd, &s->iomem);
 321}
 322
 323/* FIXME: With no regs we are actually stateless. Although post load we need
 324 * to call notify() to start up the fire-hose of zeros again.
 325 */
 326
 327static const VMStateDescription vmstate_zynqmp_csu_sss = {
 328    .name = "zynqmp_csu_sss",
 329    .version_id = 1,
 330    .minimum_version_id = 1,
 331    .minimum_version_id_old = 1,
 332    .fields = (VMStateField[]) {
 333        VMSTATE_UINT32_ARRAY(regs, ZynqMPCSUSSS, R_MAX),
 334        VMSTATE_END_OF_LIST(),
 335    }
 336};
 337
 338static void zynqmp_csu_sss_stream_class_init(ObjectClass *klass, void *data)
 339{
 340    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 341
 342    ssc->push = zynqmp_csu_sss_stream_push;
 343    ssc->can_push = zynqmp_csu_sss_stream_can_push;
 344}
 345
 346static void zynqmp_csu_sss_class_init(ObjectClass *klass, void *data)
 347{
 348    DeviceClass *dc = DEVICE_CLASS(klass);
 349
 350    dc->reset = zynqmp_csu_sss_reset;
 351    dc->realize = zynqmp_csu_sss_realize;
 352    dc->vmsd = &vmstate_zynqmp_csu_sss;
 353}
 354
 355static const TypeInfo zynqmp_csu_sss_info = {
 356    .name          = TYPE_ZYNQMP_CSU_SSS,
 357    .parent        = TYPE_SYS_BUS_DEVICE,
 358    .instance_size = sizeof(ZynqMPCSUSSS),
 359    .class_init    = zynqmp_csu_sss_class_init,
 360    .instance_init = zynqmp_csu_sss_init,
 361};
 362
 363static const TypeInfo zynqmp_csu_sss_stream_info = {
 364    .name          = TYPE_ZYNQMP_CSU_SSS_STREAM,
 365    .parent        = TYPE_DEVICE,
 366    .instance_size = sizeof(ZynqMPCSUSSSStream),
 367    .class_init    = zynqmp_csu_sss_stream_class_init,
 368    .interfaces = (InterfaceInfo[]) {
 369        { TYPE_STREAM_SLAVE },
 370        { }
 371    }
 372};
 373
 374static void zynqmp_csu_sss_register_types(void)
 375{
 376    type_register_static(&zynqmp_csu_sss_info);
 377    type_register_static(&zynqmp_csu_sss_stream_info);
 378}
 379
 380type_init(zynqmp_csu_sss_register_types)
 381