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

allwinner-h3-dramc.c (11855B)


      1/*
      2 * Allwinner H3 SDRAM Controller emulation
      3 *
      4 * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
      5 *
      6 * This program is free software: you can redistribute it and/or modify
      7 * it under the terms of the GNU General Public License as published by
      8 * the Free Software Foundation, either version 2 of the License, or
      9 * (at your option) any later version.
     10 *
     11 * This program is distributed in the hope that it will be useful,
     12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 * GNU General Public License for more details.
     15 *
     16 * You should have received a copy of the GNU General Public License
     17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18 */
     19
     20#include "qemu/osdep.h"
     21#include "qemu/units.h"
     22#include "qemu/error-report.h"
     23#include "hw/sysbus.h"
     24#include "migration/vmstate.h"
     25#include "qemu/log.h"
     26#include "qemu/module.h"
     27#include "exec/address-spaces.h"
     28#include "hw/qdev-properties.h"
     29#include "qapi/error.h"
     30#include "hw/misc/allwinner-h3-dramc.h"
     31#include "trace.h"
     32
     33#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
     34
     35/* DRAMCOM register offsets */
     36enum {
     37    REG_DRAMCOM_CR    = 0x0000, /* Control Register */
     38};
     39
     40/* DRAMCTL register offsets */
     41enum {
     42    REG_DRAMCTL_PIR   = 0x0000, /* PHY Initialization Register */
     43    REG_DRAMCTL_PGSR  = 0x0010, /* PHY General Status Register */
     44    REG_DRAMCTL_STATR = 0x0018, /* Status Register */
     45};
     46
     47/* DRAMCTL register flags */
     48enum {
     49    REG_DRAMCTL_PGSR_INITDONE = (1 << 0),
     50};
     51
     52enum {
     53    REG_DRAMCTL_STATR_ACTIVE  = (1 << 0),
     54};
     55
     56static void allwinner_h3_dramc_map_rows(AwH3DramCtlState *s, uint8_t row_bits,
     57                                        uint8_t bank_bits, uint16_t page_size)
     58{
     59    /*
     60     * This function simulates row addressing behavior when bootloader
     61     * software attempts to detect the amount of available SDRAM. In U-Boot
     62     * the controller is configured with the widest row addressing available.
     63     * Then a pattern is written to RAM at an offset on the row boundary size.
     64     * If the value read back equals the value read back from the
     65     * start of RAM, the bootloader knows the amount of row bits.
     66     *
     67     * This function inserts a mirrored memory region when the configured row
     68     * bits are not matching the actual emulated memory, to simulate the
     69     * same behavior on hardware as expected by the bootloader.
     70     */
     71    uint8_t row_bits_actual = 0;
     72
     73    /* Calculate the actual row bits using the ram_size property */
     74    for (uint8_t i = 8; i < 12; i++) {
     75        if (1 << i == s->ram_size) {
     76            row_bits_actual = i + 3;
     77            break;
     78        }
     79    }
     80
     81    if (s->ram_size == (1 << (row_bits - 3))) {
     82        /* When row bits is the expected value, remove the mirror */
     83        memory_region_set_enabled(&s->row_mirror_alias, false);
     84        trace_allwinner_h3_dramc_rowmirror_disable();
     85
     86    } else if (row_bits_actual) {
     87        /* Row bits not matching ram_size, install the rows mirror */
     88        hwaddr row_mirror = s->ram_addr + ((1ULL << (row_bits_actual +
     89                                                     bank_bits)) * page_size);
     90
     91        memory_region_set_enabled(&s->row_mirror_alias, true);
     92        memory_region_set_address(&s->row_mirror_alias, row_mirror);
     93
     94        trace_allwinner_h3_dramc_rowmirror_enable(row_mirror);
     95    }
     96}
     97
     98static uint64_t allwinner_h3_dramcom_read(void *opaque, hwaddr offset,
     99                                          unsigned size)
    100{
    101    const AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
    102    const uint32_t idx = REG_INDEX(offset);
    103
    104    if (idx >= AW_H3_DRAMCOM_REGS_NUM) {
    105        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
    106                      __func__, (uint32_t)offset);
    107        return 0;
    108    }
    109
    110    trace_allwinner_h3_dramcom_read(offset, s->dramcom[idx], size);
    111
    112    return s->dramcom[idx];
    113}
    114
    115static void allwinner_h3_dramcom_write(void *opaque, hwaddr offset,
    116                                       uint64_t val, unsigned size)
    117{
    118    AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
    119    const uint32_t idx = REG_INDEX(offset);
    120
    121    trace_allwinner_h3_dramcom_write(offset, val, size);
    122
    123    if (idx >= AW_H3_DRAMCOM_REGS_NUM) {
    124        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
    125                      __func__, (uint32_t)offset);
    126        return;
    127    }
    128
    129    switch (offset) {
    130    case REG_DRAMCOM_CR:   /* Control Register */
    131        allwinner_h3_dramc_map_rows(s, ((val >> 4) & 0xf) + 1,
    132                                       ((val >> 2) & 0x1) + 2,
    133                                       1 << (((val >> 8) & 0xf) + 3));
    134        break;
    135    default:
    136        break;
    137    };
    138
    139    s->dramcom[idx] = (uint32_t) val;
    140}
    141
    142static uint64_t allwinner_h3_dramctl_read(void *opaque, hwaddr offset,
    143                                          unsigned size)
    144{
    145    const AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
    146    const uint32_t idx = REG_INDEX(offset);
    147
    148    if (idx >= AW_H3_DRAMCTL_REGS_NUM) {
    149        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
    150                      __func__, (uint32_t)offset);
    151        return 0;
    152    }
    153
    154    trace_allwinner_h3_dramctl_read(offset, s->dramctl[idx], size);
    155
    156    return s->dramctl[idx];
    157}
    158
    159static void allwinner_h3_dramctl_write(void *opaque, hwaddr offset,
    160                                       uint64_t val, unsigned size)
    161{
    162    AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
    163    const uint32_t idx = REG_INDEX(offset);
    164
    165    trace_allwinner_h3_dramctl_write(offset, val, size);
    166
    167    if (idx >= AW_H3_DRAMCTL_REGS_NUM) {
    168        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
    169                      __func__, (uint32_t)offset);
    170        return;
    171    }
    172
    173    switch (offset) {
    174    case REG_DRAMCTL_PIR:    /* PHY Initialization Register */
    175        s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_INITDONE;
    176        s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |= REG_DRAMCTL_STATR_ACTIVE;
    177        break;
    178    default:
    179        break;
    180    }
    181
    182    s->dramctl[idx] = (uint32_t) val;
    183}
    184
    185static uint64_t allwinner_h3_dramphy_read(void *opaque, hwaddr offset,
    186                                          unsigned size)
    187{
    188    const AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
    189    const uint32_t idx = REG_INDEX(offset);
    190
    191    if (idx >= AW_H3_DRAMPHY_REGS_NUM) {
    192        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
    193                      __func__, (uint32_t)offset);
    194        return 0;
    195    }
    196
    197    trace_allwinner_h3_dramphy_read(offset, s->dramphy[idx], size);
    198
    199    return s->dramphy[idx];
    200}
    201
    202static void allwinner_h3_dramphy_write(void *opaque, hwaddr offset,
    203                                       uint64_t val, unsigned size)
    204{
    205    AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
    206    const uint32_t idx = REG_INDEX(offset);
    207
    208    trace_allwinner_h3_dramphy_write(offset, val, size);
    209
    210    if (idx >= AW_H3_DRAMPHY_REGS_NUM) {
    211        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
    212                      __func__, (uint32_t)offset);
    213        return;
    214    }
    215
    216    s->dramphy[idx] = (uint32_t) val;
    217}
    218
    219static const MemoryRegionOps allwinner_h3_dramcom_ops = {
    220    .read = allwinner_h3_dramcom_read,
    221    .write = allwinner_h3_dramcom_write,
    222    .endianness = DEVICE_NATIVE_ENDIAN,
    223    .valid = {
    224        .min_access_size = 4,
    225        .max_access_size = 4,
    226    },
    227    .impl.min_access_size = 4,
    228};
    229
    230static const MemoryRegionOps allwinner_h3_dramctl_ops = {
    231    .read = allwinner_h3_dramctl_read,
    232    .write = allwinner_h3_dramctl_write,
    233    .endianness = DEVICE_NATIVE_ENDIAN,
    234    .valid = {
    235        .min_access_size = 4,
    236        .max_access_size = 4,
    237    },
    238    .impl.min_access_size = 4,
    239};
    240
    241static const MemoryRegionOps allwinner_h3_dramphy_ops = {
    242    .read = allwinner_h3_dramphy_read,
    243    .write = allwinner_h3_dramphy_write,
    244    .endianness = DEVICE_NATIVE_ENDIAN,
    245    .valid = {
    246        .min_access_size = 4,
    247        .max_access_size = 4,
    248    },
    249    .impl.min_access_size = 4,
    250};
    251
    252static void allwinner_h3_dramc_reset(DeviceState *dev)
    253{
    254    AwH3DramCtlState *s = AW_H3_DRAMC(dev);
    255
    256    /* Set default values for registers */
    257    memset(&s->dramcom, 0, sizeof(s->dramcom));
    258    memset(&s->dramctl, 0, sizeof(s->dramctl));
    259    memset(&s->dramphy, 0, sizeof(s->dramphy));
    260}
    261
    262static void allwinner_h3_dramc_realize(DeviceState *dev, Error **errp)
    263{
    264    AwH3DramCtlState *s = AW_H3_DRAMC(dev);
    265
    266    /* Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported */
    267    for (uint8_t i = 8; i < 13; i++) {
    268        if (1 << i == s->ram_size) {
    269            break;
    270        } else if (i == 12) {
    271            error_report("%s: ram-size %u MiB is not supported",
    272                          __func__, s->ram_size);
    273            exit(1);
    274        }
    275    }
    276
    277    /* Setup row mirror mappings */
    278    memory_region_init_ram(&s->row_mirror, OBJECT(s),
    279                           "allwinner-h3-dramc.row-mirror",
    280                            4 * KiB, &error_abort);
    281    memory_region_add_subregion_overlap(get_system_memory(), s->ram_addr,
    282                                       &s->row_mirror, 10);
    283
    284    memory_region_init_alias(&s->row_mirror_alias, OBJECT(s),
    285                            "allwinner-h3-dramc.row-mirror-alias",
    286                            &s->row_mirror, 0, 4 * KiB);
    287    memory_region_add_subregion_overlap(get_system_memory(),
    288                                        s->ram_addr + 1 * MiB,
    289                                       &s->row_mirror_alias, 10);
    290    memory_region_set_enabled(&s->row_mirror_alias, false);
    291}
    292
    293static void allwinner_h3_dramc_init(Object *obj)
    294{
    295    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    296    AwH3DramCtlState *s = AW_H3_DRAMC(obj);
    297
    298    /* DRAMCOM registers */
    299    memory_region_init_io(&s->dramcom_iomem, OBJECT(s),
    300                          &allwinner_h3_dramcom_ops, s,
    301                           TYPE_AW_H3_DRAMC, 4 * KiB);
    302    sysbus_init_mmio(sbd, &s->dramcom_iomem);
    303
    304    /* DRAMCTL registers */
    305    memory_region_init_io(&s->dramctl_iomem, OBJECT(s),
    306                          &allwinner_h3_dramctl_ops, s,
    307                           TYPE_AW_H3_DRAMC, 4 * KiB);
    308    sysbus_init_mmio(sbd, &s->dramctl_iomem);
    309
    310    /* DRAMPHY registers */
    311    memory_region_init_io(&s->dramphy_iomem, OBJECT(s),
    312                          &allwinner_h3_dramphy_ops, s,
    313                          TYPE_AW_H3_DRAMC, 4 * KiB);
    314    sysbus_init_mmio(sbd, &s->dramphy_iomem);
    315}
    316
    317static Property allwinner_h3_dramc_properties[] = {
    318    DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0),
    319    DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB),
    320    DEFINE_PROP_END_OF_LIST()
    321};
    322
    323static const VMStateDescription allwinner_h3_dramc_vmstate = {
    324    .name = "allwinner-h3-dramc",
    325    .version_id = 1,
    326    .minimum_version_id = 1,
    327    .fields = (VMStateField[]) {
    328        VMSTATE_UINT32_ARRAY(dramcom, AwH3DramCtlState, AW_H3_DRAMCOM_REGS_NUM),
    329        VMSTATE_UINT32_ARRAY(dramctl, AwH3DramCtlState, AW_H3_DRAMCTL_REGS_NUM),
    330        VMSTATE_UINT32_ARRAY(dramphy, AwH3DramCtlState, AW_H3_DRAMPHY_REGS_NUM),
    331        VMSTATE_END_OF_LIST()
    332    }
    333};
    334
    335static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data)
    336{
    337    DeviceClass *dc = DEVICE_CLASS(klass);
    338
    339    dc->reset = allwinner_h3_dramc_reset;
    340    dc->vmsd = &allwinner_h3_dramc_vmstate;
    341    dc->realize = allwinner_h3_dramc_realize;
    342    device_class_set_props(dc, allwinner_h3_dramc_properties);
    343}
    344
    345static const TypeInfo allwinner_h3_dramc_info = {
    346    .name          = TYPE_AW_H3_DRAMC,
    347    .parent        = TYPE_SYS_BUS_DEVICE,
    348    .instance_init = allwinner_h3_dramc_init,
    349    .instance_size = sizeof(AwH3DramCtlState),
    350    .class_init    = allwinner_h3_dramc_class_init,
    351};
    352
    353static void allwinner_h3_dramc_register(void)
    354{
    355    type_register_static(&allwinner_h3_dramc_info);
    356}
    357
    358type_init(allwinner_h3_dramc_register)