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

pl110.c (15986B)


      1/*
      2 * Arm PrimeCell PL110 Color LCD Controller
      3 *
      4 * Copyright (c) 2005-2009 CodeSourcery.
      5 * Written by Paul Brook
      6 *
      7 * This code is licensed under the GNU LGPL
      8 */
      9
     10#include "qemu/osdep.h"
     11#include "hw/irq.h"
     12#include "hw/sysbus.h"
     13#include "migration/vmstate.h"
     14#include "ui/console.h"
     15#include "framebuffer.h"
     16#include "ui/pixel_ops.h"
     17#include "qemu/timer.h"
     18#include "qemu/log.h"
     19#include "qemu/module.h"
     20#include "qom/object.h"
     21
     22#define PL110_CR_EN   0x001
     23#define PL110_CR_BGR  0x100
     24#define PL110_CR_BEBO 0x200
     25#define PL110_CR_BEPO 0x400
     26#define PL110_CR_PWR  0x800
     27#define PL110_IE_NB   0x004
     28#define PL110_IE_VC   0x008
     29
     30enum pl110_bppmode
     31{
     32    BPP_1,
     33    BPP_2,
     34    BPP_4,
     35    BPP_8,
     36    BPP_16,
     37    BPP_32,
     38    BPP_16_565, /* PL111 only */
     39    BPP_12      /* PL111 only */
     40};
     41
     42
     43/* The Versatile/PB uses a slightly modified PL110 controller.  */
     44enum pl110_version
     45{
     46    VERSION_PL110,
     47    VERSION_PL110_VERSATILE,
     48    VERSION_PL111
     49};
     50
     51#define TYPE_PL110 "pl110"
     52OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110)
     53
     54struct PL110State {
     55    SysBusDevice parent_obj;
     56
     57    MemoryRegion iomem;
     58    MemoryRegionSection fbsection;
     59    QemuConsole *con;
     60    QEMUTimer *vblank_timer;
     61
     62    int version;
     63    uint32_t timing[4];
     64    uint32_t cr;
     65    uint32_t upbase;
     66    uint32_t lpbase;
     67    uint32_t int_status;
     68    uint32_t int_mask;
     69    int cols;
     70    int rows;
     71    enum pl110_bppmode bpp;
     72    int invalidate;
     73    uint32_t mux_ctrl;
     74    uint32_t palette[256];
     75    uint32_t raw_palette[128];
     76    qemu_irq irq;
     77};
     78
     79static int vmstate_pl110_post_load(void *opaque, int version_id);
     80
     81static const VMStateDescription vmstate_pl110 = {
     82    .name = "pl110",
     83    .version_id = 2,
     84    .minimum_version_id = 1,
     85    .post_load = vmstate_pl110_post_load,
     86    .fields = (VMStateField[]) {
     87        VMSTATE_INT32(version, PL110State),
     88        VMSTATE_UINT32_ARRAY(timing, PL110State, 4),
     89        VMSTATE_UINT32(cr, PL110State),
     90        VMSTATE_UINT32(upbase, PL110State),
     91        VMSTATE_UINT32(lpbase, PL110State),
     92        VMSTATE_UINT32(int_status, PL110State),
     93        VMSTATE_UINT32(int_mask, PL110State),
     94        VMSTATE_INT32(cols, PL110State),
     95        VMSTATE_INT32(rows, PL110State),
     96        VMSTATE_UINT32(bpp, PL110State),
     97        VMSTATE_INT32(invalidate, PL110State),
     98        VMSTATE_UINT32_ARRAY(palette, PL110State, 256),
     99        VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128),
    100        VMSTATE_UINT32_V(mux_ctrl, PL110State, 2),
    101        VMSTATE_END_OF_LIST()
    102    }
    103};
    104
    105static const unsigned char pl110_id[] =
    106{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
    107
    108static const unsigned char pl111_id[] = {
    109    0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
    110};
    111
    112
    113/* Indexed by pl110_version */
    114static const unsigned char *idregs[] = {
    115    pl110_id,
    116    /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board
    117     * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware
    118     * itself has the same ID values as a stock PL110, and guests (in
    119     * particular Linux) rely on this. We emulate what the hardware does,
    120     * rather than what the docs claim it ought to do.
    121     */
    122    pl110_id,
    123    pl111_id
    124};
    125
    126#define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
    127
    128#undef RGB
    129#define BORDER bgr
    130#define ORDER 0
    131#include "pl110_template.h"
    132#define ORDER 1
    133#include "pl110_template.h"
    134#define ORDER 2
    135#include "pl110_template.h"
    136#undef BORDER
    137#define RGB
    138#define BORDER rgb
    139#define ORDER 0
    140#include "pl110_template.h"
    141#define ORDER 1
    142#include "pl110_template.h"
    143#define ORDER 2
    144#include "pl110_template.h"
    145#undef BORDER
    146
    147#undef COPY_PIXEL
    148
    149static drawfn pl110_draw_fn_32[48] = {
    150    pl110_draw_line1_lblp_bgr,
    151    pl110_draw_line2_lblp_bgr,
    152    pl110_draw_line4_lblp_bgr,
    153    pl110_draw_line8_lblp_bgr,
    154    pl110_draw_line16_555_lblp_bgr,
    155    pl110_draw_line32_lblp_bgr,
    156    pl110_draw_line16_lblp_bgr,
    157    pl110_draw_line12_lblp_bgr,
    158
    159    pl110_draw_line1_bbbp_bgr,
    160    pl110_draw_line2_bbbp_bgr,
    161    pl110_draw_line4_bbbp_bgr,
    162    pl110_draw_line8_bbbp_bgr,
    163    pl110_draw_line16_555_bbbp_bgr,
    164    pl110_draw_line32_bbbp_bgr,
    165    pl110_draw_line16_bbbp_bgr,
    166    pl110_draw_line12_bbbp_bgr,
    167
    168    pl110_draw_line1_lbbp_bgr,
    169    pl110_draw_line2_lbbp_bgr,
    170    pl110_draw_line4_lbbp_bgr,
    171    pl110_draw_line8_lbbp_bgr,
    172    pl110_draw_line16_555_lbbp_bgr,
    173    pl110_draw_line32_lbbp_bgr,
    174    pl110_draw_line16_lbbp_bgr,
    175    pl110_draw_line12_lbbp_bgr,
    176
    177    pl110_draw_line1_lblp_rgb,
    178    pl110_draw_line2_lblp_rgb,
    179    pl110_draw_line4_lblp_rgb,
    180    pl110_draw_line8_lblp_rgb,
    181    pl110_draw_line16_555_lblp_rgb,
    182    pl110_draw_line32_lblp_rgb,
    183    pl110_draw_line16_lblp_rgb,
    184    pl110_draw_line12_lblp_rgb,
    185
    186    pl110_draw_line1_bbbp_rgb,
    187    pl110_draw_line2_bbbp_rgb,
    188    pl110_draw_line4_bbbp_rgb,
    189    pl110_draw_line8_bbbp_rgb,
    190    pl110_draw_line16_555_bbbp_rgb,
    191    pl110_draw_line32_bbbp_rgb,
    192    pl110_draw_line16_bbbp_rgb,
    193    pl110_draw_line12_bbbp_rgb,
    194
    195    pl110_draw_line1_lbbp_rgb,
    196    pl110_draw_line2_lbbp_rgb,
    197    pl110_draw_line4_lbbp_rgb,
    198    pl110_draw_line8_lbbp_rgb,
    199    pl110_draw_line16_555_lbbp_rgb,
    200    pl110_draw_line32_lbbp_rgb,
    201    pl110_draw_line16_lbbp_rgb,
    202    pl110_draw_line12_lbbp_rgb,
    203};
    204
    205static int pl110_enabled(PL110State *s)
    206{
    207  return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
    208}
    209
    210static void pl110_update_display(void *opaque)
    211{
    212    PL110State *s = (PL110State *)opaque;
    213    SysBusDevice *sbd;
    214    DisplaySurface *surface = qemu_console_surface(s->con);
    215    drawfn fn;
    216    int src_width;
    217    int bpp_offset;
    218    int first;
    219    int last;
    220
    221    if (!pl110_enabled(s)) {
    222        return;
    223    }
    224
    225    sbd = SYS_BUS_DEVICE(s);
    226
    227    if (s->cr & PL110_CR_BGR)
    228        bpp_offset = 0;
    229    else
    230        bpp_offset = 24;
    231
    232    if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) {
    233        /* The PL110's native 16 bit mode is 5551; however
    234         * most boards with a PL110 implement an external
    235         * mux which allows bits to be reshuffled to give
    236         * 565 format. The mux is typically controlled by
    237         * an external system register.
    238         * This is controlled by a GPIO input pin
    239         * so boards can wire it up to their register.
    240         *
    241         * The PL111 straightforwardly implements both
    242         * 5551 and 565 under control of the bpp field
    243         * in the LCDControl register.
    244         */
    245        switch (s->mux_ctrl) {
    246        case 3: /* 565 BGR */
    247            bpp_offset = (BPP_16_565 - BPP_16);
    248            break;
    249        case 1: /* 5551 */
    250            break;
    251        case 0: /* 888; also if we have loaded vmstate from an old version */
    252        case 2: /* 565 RGB */
    253        default:
    254            /* treat as 565 but honour BGR bit */
    255            bpp_offset += (BPP_16_565 - BPP_16);
    256            break;
    257        }
    258    }
    259
    260    if (s->cr & PL110_CR_BEBO) {
    261        fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset];
    262    } else if (s->cr & PL110_CR_BEPO) {
    263        fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset];
    264    } else {
    265        fn = pl110_draw_fn_32[s->bpp + bpp_offset];
    266    }
    267
    268    src_width = s->cols;
    269    switch (s->bpp) {
    270    case BPP_1:
    271        src_width >>= 3;
    272        break;
    273    case BPP_2:
    274        src_width >>= 2;
    275        break;
    276    case BPP_4:
    277        src_width >>= 1;
    278        break;
    279    case BPP_8:
    280        break;
    281    case BPP_16:
    282    case BPP_16_565:
    283    case BPP_12:
    284        src_width <<= 1;
    285        break;
    286    case BPP_32:
    287        src_width <<= 2;
    288        break;
    289    }
    290    first = 0;
    291    if (s->invalidate) {
    292        framebuffer_update_memory_section(&s->fbsection,
    293                                          sysbus_address_space(sbd),
    294                                          s->upbase,
    295                                          s->rows, src_width);
    296    }
    297
    298    framebuffer_update_display(surface, &s->fbsection,
    299                               s->cols, s->rows,
    300                               src_width, s->cols * 4, 0,
    301                               s->invalidate,
    302                               fn, s->palette,
    303                               &first, &last);
    304
    305    if (first >= 0) {
    306        dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
    307    }
    308    s->invalidate = 0;
    309}
    310
    311static void pl110_invalidate_display(void * opaque)
    312{
    313    PL110State *s = (PL110State *)opaque;
    314    s->invalidate = 1;
    315    if (pl110_enabled(s)) {
    316        qemu_console_resize(s->con, s->cols, s->rows);
    317    }
    318}
    319
    320static void pl110_update_palette(PL110State *s, int n)
    321{
    322    DisplaySurface *surface = qemu_console_surface(s->con);
    323    int i;
    324    uint32_t raw;
    325    unsigned int r, g, b;
    326
    327    raw = s->raw_palette[n];
    328    n <<= 1;
    329    for (i = 0; i < 2; i++) {
    330        r = (raw & 0x1f) << 3;
    331        raw >>= 5;
    332        g = (raw & 0x1f) << 3;
    333        raw >>= 5;
    334        b = (raw & 0x1f) << 3;
    335        /* The I bit is ignored.  */
    336        raw >>= 6;
    337        switch (surface_bits_per_pixel(surface)) {
    338        case 8:
    339            s->palette[n] = rgb_to_pixel8(r, g, b);
    340            break;
    341        case 15:
    342            s->palette[n] = rgb_to_pixel15(r, g, b);
    343            break;
    344        case 16:
    345            s->palette[n] = rgb_to_pixel16(r, g, b);
    346            break;
    347        case 24:
    348        case 32:
    349            s->palette[n] = rgb_to_pixel32(r, g, b);
    350            break;
    351        }
    352        n++;
    353    }
    354}
    355
    356static void pl110_resize(PL110State *s, int width, int height)
    357{
    358    if (width != s->cols || height != s->rows) {
    359        if (pl110_enabled(s)) {
    360            qemu_console_resize(s->con, width, height);
    361        }
    362    }
    363    s->cols = width;
    364    s->rows = height;
    365}
    366
    367/* Update interrupts.  */
    368static void pl110_update(PL110State *s)
    369{
    370    /* Raise IRQ if enabled and any status bit is 1 */
    371    if (s->int_status & s->int_mask) {
    372        qemu_irq_raise(s->irq);
    373    } else {
    374        qemu_irq_lower(s->irq);
    375    }
    376}
    377
    378static void pl110_vblank_interrupt(void *opaque)
    379{
    380    PL110State *s = opaque;
    381
    382    /* Fire the vertical compare and next base IRQs and re-arm */
    383    s->int_status |= (PL110_IE_NB | PL110_IE_VC);
    384    timer_mod(s->vblank_timer,
    385              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
    386                                NANOSECONDS_PER_SECOND / 60);
    387    pl110_update(s);
    388}
    389
    390static uint64_t pl110_read(void *opaque, hwaddr offset,
    391                           unsigned size)
    392{
    393    PL110State *s = (PL110State *)opaque;
    394
    395    if (offset >= 0xfe0 && offset < 0x1000) {
    396        return idregs[s->version][(offset - 0xfe0) >> 2];
    397    }
    398    if (offset >= 0x200 && offset < 0x400) {
    399        return s->raw_palette[(offset - 0x200) >> 2];
    400    }
    401    switch (offset >> 2) {
    402    case 0: /* LCDTiming0 */
    403        return s->timing[0];
    404    case 1: /* LCDTiming1 */
    405        return s->timing[1];
    406    case 2: /* LCDTiming2 */
    407        return s->timing[2];
    408    case 3: /* LCDTiming3 */
    409        return s->timing[3];
    410    case 4: /* LCDUPBASE */
    411        return s->upbase;
    412    case 5: /* LCDLPBASE */
    413        return s->lpbase;
    414    case 6: /* LCDIMSC */
    415        if (s->version != VERSION_PL110) {
    416            return s->cr;
    417        }
    418        return s->int_mask;
    419    case 7: /* LCDControl */
    420        if (s->version != VERSION_PL110) {
    421            return s->int_mask;
    422        }
    423        return s->cr;
    424    case 8: /* LCDRIS */
    425        return s->int_status;
    426    case 9: /* LCDMIS */
    427        return s->int_status & s->int_mask;
    428    case 11: /* LCDUPCURR */
    429        /* TODO: Implement vertical refresh.  */
    430        return s->upbase;
    431    case 12: /* LCDLPCURR */
    432        return s->lpbase;
    433    default:
    434        qemu_log_mask(LOG_GUEST_ERROR,
    435                      "pl110_read: Bad offset %x\n", (int)offset);
    436        return 0;
    437    }
    438}
    439
    440static void pl110_write(void *opaque, hwaddr offset,
    441                        uint64_t val, unsigned size)
    442{
    443    PL110State *s = (PL110State *)opaque;
    444    int n;
    445
    446    /* For simplicity invalidate the display whenever a control register
    447       is written to.  */
    448    s->invalidate = 1;
    449    if (offset >= 0x200 && offset < 0x400) {
    450        /* Palette.  */
    451        n = (offset - 0x200) >> 2;
    452        s->raw_palette[(offset - 0x200) >> 2] = val;
    453        pl110_update_palette(s, n);
    454        return;
    455    }
    456    switch (offset >> 2) {
    457    case 0: /* LCDTiming0 */
    458        s->timing[0] = val;
    459        n = ((val & 0xfc) + 4) * 4;
    460        pl110_resize(s, n, s->rows);
    461        break;
    462    case 1: /* LCDTiming1 */
    463        s->timing[1] = val;
    464        n = (val & 0x3ff) + 1;
    465        pl110_resize(s, s->cols, n);
    466        break;
    467    case 2: /* LCDTiming2 */
    468        s->timing[2] = val;
    469        break;
    470    case 3: /* LCDTiming3 */
    471        s->timing[3] = val;
    472        break;
    473    case 4: /* LCDUPBASE */
    474        s->upbase = val;
    475        break;
    476    case 5: /* LCDLPBASE */
    477        s->lpbase = val;
    478        break;
    479    case 6: /* LCDIMSC */
    480        if (s->version != VERSION_PL110) {
    481            goto control;
    482        }
    483    imsc:
    484        s->int_mask = val;
    485        pl110_update(s);
    486        break;
    487    case 7: /* LCDControl */
    488        if (s->version != VERSION_PL110) {
    489            goto imsc;
    490        }
    491    control:
    492        s->cr = val;
    493        s->bpp = (val >> 1) & 7;
    494        if (pl110_enabled(s)) {
    495            qemu_console_resize(s->con, s->cols, s->rows);
    496            timer_mod(s->vblank_timer,
    497                      qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
    498                                        NANOSECONDS_PER_SECOND / 60);
    499        } else {
    500            timer_del(s->vblank_timer);
    501        }
    502        break;
    503    case 10: /* LCDICR */
    504        s->int_status &= ~val;
    505        pl110_update(s);
    506        break;
    507    default:
    508        qemu_log_mask(LOG_GUEST_ERROR,
    509                      "pl110_write: Bad offset %x\n", (int)offset);
    510    }
    511}
    512
    513static const MemoryRegionOps pl110_ops = {
    514    .read = pl110_read,
    515    .write = pl110_write,
    516    .endianness = DEVICE_NATIVE_ENDIAN,
    517};
    518
    519static void pl110_mux_ctrl_set(void *opaque, int line, int level)
    520{
    521    PL110State *s = (PL110State *)opaque;
    522    s->mux_ctrl = level;
    523}
    524
    525static int vmstate_pl110_post_load(void *opaque, int version_id)
    526{
    527    PL110State *s = opaque;
    528    /* Make sure we redraw, and at the right size */
    529    pl110_invalidate_display(s);
    530    return 0;
    531}
    532
    533static const GraphicHwOps pl110_gfx_ops = {
    534    .invalidate  = pl110_invalidate_display,
    535    .gfx_update  = pl110_update_display,
    536};
    537
    538static void pl110_realize(DeviceState *dev, Error **errp)
    539{
    540    PL110State *s = PL110(dev);
    541    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    542
    543    memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
    544    sysbus_init_mmio(sbd, &s->iomem);
    545    sysbus_init_irq(sbd, &s->irq);
    546    s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
    547                                   pl110_vblank_interrupt, s);
    548    qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
    549    s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
    550}
    551
    552static void pl110_init(Object *obj)
    553{
    554    PL110State *s = PL110(obj);
    555
    556    s->version = VERSION_PL110;
    557}
    558
    559static void pl110_versatile_init(Object *obj)
    560{
    561    PL110State *s = PL110(obj);
    562
    563    s->version = VERSION_PL110_VERSATILE;
    564}
    565
    566static void pl111_init(Object *obj)
    567{
    568    PL110State *s = PL110(obj);
    569
    570    s->version = VERSION_PL111;
    571}
    572
    573static void pl110_class_init(ObjectClass *klass, void *data)
    574{
    575    DeviceClass *dc = DEVICE_CLASS(klass);
    576
    577    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
    578    dc->vmsd = &vmstate_pl110;
    579    dc->realize = pl110_realize;
    580}
    581
    582static const TypeInfo pl110_info = {
    583    .name          = TYPE_PL110,
    584    .parent        = TYPE_SYS_BUS_DEVICE,
    585    .instance_size = sizeof(PL110State),
    586    .instance_init = pl110_init,
    587    .class_init    = pl110_class_init,
    588};
    589
    590static const TypeInfo pl110_versatile_info = {
    591    .name          = "pl110_versatile",
    592    .parent        = TYPE_PL110,
    593    .instance_init = pl110_versatile_init,
    594};
    595
    596static const TypeInfo pl111_info = {
    597    .name          = "pl111",
    598    .parent        = TYPE_PL110,
    599    .instance_init = pl111_init,
    600};
    601
    602static void pl110_register_types(void)
    603{
    604    type_register_static(&pl110_info);
    605    type_register_static(&pl110_versatile_info);
    606    type_register_static(&pl111_info);
    607}
    608
    609type_init(pl110_register_types)