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

tcx.c (25655B)


      1/*
      2 * QEMU TCX Frame buffer
      3 *
      4 * Copyright (c) 2003-2005 Fabrice Bellard
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a copy
      7 * of this software and associated documentation files (the "Software"), to deal
      8 * in the Software without restriction, including without limitation the rights
      9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 * copies of the Software, and to permit persons to whom the Software is
     11 * furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 * THE SOFTWARE.
     23 */
     24
     25#include "qemu/osdep.h"
     26#include "qemu-common.h"
     27#include "qemu/datadir.h"
     28#include "qapi/error.h"
     29#include "ui/console.h"
     30#include "ui/pixel_ops.h"
     31#include "hw/loader.h"
     32#include "hw/qdev-properties.h"
     33#include "hw/sysbus.h"
     34#include "migration/vmstate.h"
     35#include "qemu/error-report.h"
     36#include "qemu/module.h"
     37#include "qom/object.h"
     38
     39#define TCX_ROM_FILE "QEMU,tcx.bin"
     40#define FCODE_MAX_ROM_SIZE 0x10000
     41
     42#define MAXX 1024
     43#define MAXY 768
     44#define TCX_DAC_NREGS    16
     45#define TCX_THC_NREGS    0x1000
     46#define TCX_DHC_NREGS    0x4000
     47#define TCX_TEC_NREGS    0x1000
     48#define TCX_ALT_NREGS    0x8000
     49#define TCX_STIP_NREGS   0x800000
     50#define TCX_BLIT_NREGS   0x800000
     51#define TCX_RSTIP_NREGS  0x800000
     52#define TCX_RBLIT_NREGS  0x800000
     53
     54#define TCX_THC_MISC     0x818
     55#define TCX_THC_CURSXY   0x8fc
     56#define TCX_THC_CURSMASK 0x900
     57#define TCX_THC_CURSBITS 0x980
     58
     59#define TYPE_TCX "sun-tcx"
     60OBJECT_DECLARE_SIMPLE_TYPE(TCXState, TCX)
     61
     62struct TCXState {
     63    SysBusDevice parent_obj;
     64
     65    QemuConsole *con;
     66    qemu_irq irq;
     67    uint8_t *vram;
     68    uint32_t *vram24, *cplane;
     69    hwaddr prom_addr;
     70    MemoryRegion rom;
     71    MemoryRegion vram_mem;
     72    MemoryRegion vram_8bit;
     73    MemoryRegion vram_24bit;
     74    MemoryRegion stip;
     75    MemoryRegion blit;
     76    MemoryRegion vram_cplane;
     77    MemoryRegion rstip;
     78    MemoryRegion rblit;
     79    MemoryRegion tec;
     80    MemoryRegion dac;
     81    MemoryRegion thc;
     82    MemoryRegion dhc;
     83    MemoryRegion alt;
     84    MemoryRegion thc24;
     85
     86    ram_addr_t vram24_offset, cplane_offset;
     87    uint32_t tmpblit;
     88    uint32_t vram_size;
     89    uint32_t palette[260];
     90    uint8_t r[260], g[260], b[260];
     91    uint16_t width, height, depth;
     92    uint8_t dac_index, dac_state;
     93    uint32_t thcmisc;
     94    uint32_t cursmask[32];
     95    uint32_t cursbits[32];
     96    uint16_t cursx;
     97    uint16_t cursy;
     98};
     99
    100static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
    101{
    102    memory_region_set_dirty(&s->vram_mem, addr, len);
    103
    104    if (s->depth == 24) {
    105        memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
    106                                len * 4);
    107        memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
    108                                len * 4);
    109    }
    110}
    111
    112static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
    113                           ram_addr_t addr, int len)
    114{
    115    int ret;
    116
    117    ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
    118
    119    if (s->depth == 24) {
    120        ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
    121                                       s->vram24_offset + addr * 4, len * 4);
    122        ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
    123                                       s->cplane_offset + addr * 4, len * 4);
    124    }
    125
    126    return ret;
    127}
    128
    129static void update_palette_entries(TCXState *s, int start, int end)
    130{
    131    int i;
    132
    133    for (i = start; i < end; i++) {
    134        s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
    135    }
    136    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    137}
    138
    139static void tcx_draw_line32(TCXState *s1, uint8_t *d,
    140                            const uint8_t *s, int width)
    141{
    142    int x;
    143    uint8_t val;
    144    uint32_t *p = (uint32_t *)d;
    145
    146    for (x = 0; x < width; x++) {
    147        val = *s++;
    148        *p++ = s1->palette[val];
    149    }
    150}
    151
    152static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
    153                              int y, int width)
    154{
    155    int x, len;
    156    uint32_t mask, bits;
    157    uint32_t *p = (uint32_t *)d;
    158
    159    y = y - s1->cursy;
    160    mask = s1->cursmask[y];
    161    bits = s1->cursbits[y];
    162    len = MIN(width - s1->cursx, 32);
    163    p = &p[s1->cursx];
    164    for (x = 0; x < len; x++) {
    165        if (mask & 0x80000000) {
    166            if (bits & 0x80000000) {
    167                *p = s1->palette[259];
    168            } else {
    169                *p = s1->palette[258];
    170            }
    171        }
    172        p++;
    173        mask <<= 1;
    174        bits <<= 1;
    175    }
    176}
    177
    178/*
    179 * XXX Could be much more optimal:
    180 * detect if line/page/whole screen is in 24 bit mode
    181 */
    182static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
    183                                     const uint8_t *s, int width,
    184                                     const uint32_t *cplane,
    185                                     const uint32_t *s24)
    186{
    187    int x, r, g, b;
    188    uint8_t val, *p8;
    189    uint32_t *p = (uint32_t *)d;
    190    uint32_t dval;
    191    for(x = 0; x < width; x++, s++, s24++) {
    192        if (be32_to_cpu(*cplane) & 0x03000000) {
    193            /* 24-bit direct, BGR order */
    194            p8 = (uint8_t *)s24;
    195            p8++;
    196            b = *p8++;
    197            g = *p8++;
    198            r = *p8;
    199            dval = rgb_to_pixel32(r, g, b);
    200        } else {
    201            /* 8-bit pseudocolor */
    202            val = *s;
    203            dval = s1->palette[val];
    204        }
    205        *p++ = dval;
    206        cplane++;
    207    }
    208}
    209
    210/* Fixed line length 1024 allows us to do nice tricks not possible on
    211   VGA... */
    212
    213static void tcx_update_display(void *opaque)
    214{
    215    TCXState *ts = opaque;
    216    DisplaySurface *surface = qemu_console_surface(ts->con);
    217    ram_addr_t page;
    218    DirtyBitmapSnapshot *snap = NULL;
    219    int y, y_start, dd, ds;
    220    uint8_t *d, *s;
    221
    222    assert(surface_bits_per_pixel(surface) == 32);
    223
    224    page = 0;
    225    y_start = -1;
    226    d = surface_data(surface);
    227    s = ts->vram;
    228    dd = surface_stride(surface);
    229    ds = 1024;
    230
    231    snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
    232                                             memory_region_size(&ts->vram_mem),
    233                                             DIRTY_MEMORY_VGA);
    234
    235    for (y = 0; y < ts->height; y++, page += ds) {
    236        if (tcx_check_dirty(ts, snap, page, ds)) {
    237            if (y_start < 0)
    238                y_start = y;
    239
    240            tcx_draw_line32(ts, d, s, ts->width);
    241            if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
    242                tcx_draw_cursor32(ts, d, y, ts->width);
    243            }
    244        } else {
    245            if (y_start >= 0) {
    246                /* flush to display */
    247                dpy_gfx_update(ts->con, 0, y_start,
    248                               ts->width, y - y_start);
    249                y_start = -1;
    250            }
    251        }
    252        s += ds;
    253        d += dd;
    254    }
    255    if (y_start >= 0) {
    256        /* flush to display */
    257        dpy_gfx_update(ts->con, 0, y_start,
    258                       ts->width, y - y_start);
    259    }
    260    g_free(snap);
    261}
    262
    263static void tcx24_update_display(void *opaque)
    264{
    265    TCXState *ts = opaque;
    266    DisplaySurface *surface = qemu_console_surface(ts->con);
    267    ram_addr_t page;
    268    DirtyBitmapSnapshot *snap = NULL;
    269    int y, y_start, dd, ds;
    270    uint8_t *d, *s;
    271    uint32_t *cptr, *s24;
    272
    273    assert(surface_bits_per_pixel(surface) == 32);
    274
    275    page = 0;
    276    y_start = -1;
    277    d = surface_data(surface);
    278    s = ts->vram;
    279    s24 = ts->vram24;
    280    cptr = ts->cplane;
    281    dd = surface_stride(surface);
    282    ds = 1024;
    283
    284    snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
    285                                             memory_region_size(&ts->vram_mem),
    286                                             DIRTY_MEMORY_VGA);
    287
    288    for (y = 0; y < ts->height; y++, page += ds) {
    289        if (tcx_check_dirty(ts, snap, page, ds)) {
    290            if (y_start < 0)
    291                y_start = y;
    292
    293            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
    294            if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
    295                tcx_draw_cursor32(ts, d, y, ts->width);
    296            }
    297        } else {
    298            if (y_start >= 0) {
    299                /* flush to display */
    300                dpy_gfx_update(ts->con, 0, y_start,
    301                               ts->width, y - y_start);
    302                y_start = -1;
    303            }
    304        }
    305        d += dd;
    306        s += ds;
    307        cptr += ds;
    308        s24 += ds;
    309    }
    310    if (y_start >= 0) {
    311        /* flush to display */
    312        dpy_gfx_update(ts->con, 0, y_start,
    313                       ts->width, y - y_start);
    314    }
    315    g_free(snap);
    316}
    317
    318static void tcx_invalidate_display(void *opaque)
    319{
    320    TCXState *s = opaque;
    321
    322    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    323    qemu_console_resize(s->con, s->width, s->height);
    324}
    325
    326static void tcx24_invalidate_display(void *opaque)
    327{
    328    TCXState *s = opaque;
    329
    330    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    331    qemu_console_resize(s->con, s->width, s->height);
    332}
    333
    334static int vmstate_tcx_post_load(void *opaque, int version_id)
    335{
    336    TCXState *s = opaque;
    337
    338    update_palette_entries(s, 0, 256);
    339    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    340    return 0;
    341}
    342
    343static const VMStateDescription vmstate_tcx = {
    344    .name ="tcx",
    345    .version_id = 4,
    346    .minimum_version_id = 4,
    347    .post_load = vmstate_tcx_post_load,
    348    .fields = (VMStateField[]) {
    349        VMSTATE_UINT16(height, TCXState),
    350        VMSTATE_UINT16(width, TCXState),
    351        VMSTATE_UINT16(depth, TCXState),
    352        VMSTATE_BUFFER(r, TCXState),
    353        VMSTATE_BUFFER(g, TCXState),
    354        VMSTATE_BUFFER(b, TCXState),
    355        VMSTATE_UINT8(dac_index, TCXState),
    356        VMSTATE_UINT8(dac_state, TCXState),
    357        VMSTATE_END_OF_LIST()
    358    }
    359};
    360
    361static void tcx_reset(DeviceState *d)
    362{
    363    TCXState *s = TCX(d);
    364
    365    /* Initialize palette */
    366    memset(s->r, 0, 260);
    367    memset(s->g, 0, 260);
    368    memset(s->b, 0, 260);
    369    s->r[255] = s->g[255] = s->b[255] = 255;
    370    s->r[256] = s->g[256] = s->b[256] = 255;
    371    s->r[258] = s->g[258] = s->b[258] = 255;
    372    update_palette_entries(s, 0, 260);
    373    memset(s->vram, 0, MAXX*MAXY);
    374    memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
    375                              DIRTY_MEMORY_VGA);
    376    s->dac_index = 0;
    377    s->dac_state = 0;
    378    s->cursx = 0xf000; /* Put cursor off screen */
    379    s->cursy = 0xf000;
    380}
    381
    382static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
    383                              unsigned size)
    384{
    385    TCXState *s = opaque;
    386    uint32_t val = 0;
    387
    388    switch (s->dac_state) {
    389    case 0:
    390        val = s->r[s->dac_index] << 24;
    391        s->dac_state++;
    392        break;
    393    case 1:
    394        val = s->g[s->dac_index] << 24;
    395        s->dac_state++;
    396        break;
    397    case 2:
    398        val = s->b[s->dac_index] << 24;
    399        s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
    400        /* fall through */
    401    default:
    402        s->dac_state = 0;
    403        break;
    404    }
    405
    406    return val;
    407}
    408
    409static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
    410                           unsigned size)
    411{
    412    TCXState *s = opaque;
    413    unsigned index;
    414
    415    switch (addr) {
    416    case 0: /* Address */
    417        s->dac_index = val >> 24;
    418        s->dac_state = 0;
    419        break;
    420    case 4:  /* Pixel colours */
    421    case 12: /* Overlay (cursor) colours */
    422        if (addr & 8) {
    423            index = (s->dac_index & 3) + 256;
    424        } else {
    425            index = s->dac_index;
    426        }
    427        switch (s->dac_state) {
    428        case 0:
    429            s->r[index] = val >> 24;
    430            update_palette_entries(s, index, index + 1);
    431            s->dac_state++;
    432            break;
    433        case 1:
    434            s->g[index] = val >> 24;
    435            update_palette_entries(s, index, index + 1);
    436            s->dac_state++;
    437            break;
    438        case 2:
    439            s->b[index] = val >> 24;
    440            update_palette_entries(s, index, index + 1);
    441            s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
    442            /* fall through */
    443        default:
    444            s->dac_state = 0;
    445            break;
    446        }
    447        break;
    448    default: /* Control registers */
    449        break;
    450    }
    451}
    452
    453static const MemoryRegionOps tcx_dac_ops = {
    454    .read = tcx_dac_readl,
    455    .write = tcx_dac_writel,
    456    .endianness = DEVICE_NATIVE_ENDIAN,
    457    .valid = {
    458        .min_access_size = 4,
    459        .max_access_size = 4,
    460    },
    461};
    462
    463static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
    464                               unsigned size)
    465{
    466    return 0;
    467}
    468
    469static void tcx_stip_writel(void *opaque, hwaddr addr,
    470                            uint64_t val, unsigned size)
    471{
    472    TCXState *s = opaque;
    473    int i;
    474    uint32_t col;
    475
    476    if (!(addr & 4)) {
    477        s->tmpblit = val;
    478    } else {
    479        addr = (addr >> 3) & 0xfffff;
    480        col = cpu_to_be32(s->tmpblit);
    481        if (s->depth == 24) {
    482            for (i = 0; i < 32; i++)  {
    483                if (val & 0x80000000) {
    484                    s->vram[addr + i] = s->tmpblit;
    485                    s->vram24[addr + i] = col;
    486                }
    487                val <<= 1;
    488            }
    489        } else {
    490            for (i = 0; i < 32; i++)  {
    491                if (val & 0x80000000) {
    492                    s->vram[addr + i] = s->tmpblit;
    493                }
    494                val <<= 1;
    495            }
    496        }
    497        tcx_set_dirty(s, addr, 32);
    498    }
    499}
    500
    501static void tcx_rstip_writel(void *opaque, hwaddr addr,
    502                             uint64_t val, unsigned size)
    503{
    504    TCXState *s = opaque;
    505    int i;
    506    uint32_t col;
    507
    508    if (!(addr & 4)) {
    509        s->tmpblit = val;
    510    } else {
    511        addr = (addr >> 3) & 0xfffff;
    512        col = cpu_to_be32(s->tmpblit);
    513        if (s->depth == 24) {
    514            for (i = 0; i < 32; i++) {
    515                if (val & 0x80000000) {
    516                    s->vram[addr + i] = s->tmpblit;
    517                    s->vram24[addr + i] = col;
    518                    s->cplane[addr + i] = col;
    519                }
    520                val <<= 1;
    521            }
    522        } else {
    523            for (i = 0; i < 32; i++)  {
    524                if (val & 0x80000000) {
    525                    s->vram[addr + i] = s->tmpblit;
    526                }
    527                val <<= 1;
    528            }
    529        }
    530        tcx_set_dirty(s, addr, 32);
    531    }
    532}
    533
    534static const MemoryRegionOps tcx_stip_ops = {
    535    .read = tcx_stip_readl,
    536    .write = tcx_stip_writel,
    537    .endianness = DEVICE_NATIVE_ENDIAN,
    538    .impl = {
    539        .min_access_size = 4,
    540        .max_access_size = 4,
    541    },
    542    .valid = {
    543        .min_access_size = 4,
    544        .max_access_size = 8,
    545    },
    546};
    547
    548static const MemoryRegionOps tcx_rstip_ops = {
    549    .read = tcx_stip_readl,
    550    .write = tcx_rstip_writel,
    551    .endianness = DEVICE_NATIVE_ENDIAN,
    552    .impl = {
    553        .min_access_size = 4,
    554        .max_access_size = 4,
    555    },
    556    .valid = {
    557        .min_access_size = 4,
    558        .max_access_size = 8,
    559    },
    560};
    561
    562static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
    563                               unsigned size)
    564{
    565    return 0;
    566}
    567
    568static void tcx_blit_writel(void *opaque, hwaddr addr,
    569                            uint64_t val, unsigned size)
    570{
    571    TCXState *s = opaque;
    572    uint32_t adsr, len;
    573    int i;
    574
    575    if (!(addr & 4)) {
    576        s->tmpblit = val;
    577    } else {
    578        addr = (addr >> 3) & 0xfffff;
    579        adsr = val & 0xffffff;
    580        len = ((val >> 24) & 0x1f) + 1;
    581        if (adsr == 0xffffff) {
    582            memset(&s->vram[addr], s->tmpblit, len);
    583            if (s->depth == 24) {
    584                val = s->tmpblit & 0xffffff;
    585                val = cpu_to_be32(val);
    586                for (i = 0; i < len; i++) {
    587                    s->vram24[addr + i] = val;
    588                }
    589            }
    590        } else {
    591            memcpy(&s->vram[addr], &s->vram[adsr], len);
    592            if (s->depth == 24) {
    593                memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
    594            }
    595        }
    596        tcx_set_dirty(s, addr, len);
    597    }
    598}
    599
    600static void tcx_rblit_writel(void *opaque, hwaddr addr,
    601                         uint64_t val, unsigned size)
    602{
    603    TCXState *s = opaque;
    604    uint32_t adsr, len;
    605    int i;
    606
    607    if (!(addr & 4)) {
    608        s->tmpblit = val;
    609    } else {
    610        addr = (addr >> 3) & 0xfffff;
    611        adsr = val & 0xffffff;
    612        len = ((val >> 24) & 0x1f) + 1;
    613        if (adsr == 0xffffff) {
    614            memset(&s->vram[addr], s->tmpblit, len);
    615            if (s->depth == 24) {
    616                val = s->tmpblit & 0xffffff;
    617                val = cpu_to_be32(val);
    618                for (i = 0; i < len; i++) {
    619                    s->vram24[addr + i] = val;
    620                    s->cplane[addr + i] = val;
    621                }
    622            }
    623        } else {
    624            memcpy(&s->vram[addr], &s->vram[adsr], len);
    625            if (s->depth == 24) {
    626                memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
    627                memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
    628            }
    629        }
    630        tcx_set_dirty(s, addr, len);
    631    }
    632}
    633
    634static const MemoryRegionOps tcx_blit_ops = {
    635    .read = tcx_blit_readl,
    636    .write = tcx_blit_writel,
    637    .endianness = DEVICE_NATIVE_ENDIAN,
    638    .impl = {
    639        .min_access_size = 4,
    640        .max_access_size = 4,
    641    },
    642    .valid = {
    643        .min_access_size = 4,
    644        .max_access_size = 8,
    645    },
    646};
    647
    648static const MemoryRegionOps tcx_rblit_ops = {
    649    .read = tcx_blit_readl,
    650    .write = tcx_rblit_writel,
    651    .endianness = DEVICE_NATIVE_ENDIAN,
    652    .impl = {
    653        .min_access_size = 4,
    654        .max_access_size = 4,
    655    },
    656    .valid = {
    657        .min_access_size = 4,
    658        .max_access_size = 8,
    659    },
    660};
    661
    662static void tcx_invalidate_cursor_position(TCXState *s)
    663{
    664    int ymin, ymax, start, end;
    665
    666    /* invalidate only near the cursor */
    667    ymin = s->cursy;
    668    if (ymin >= s->height) {
    669        return;
    670    }
    671    ymax = MIN(s->height, ymin + 32);
    672    start = ymin * 1024;
    673    end   = ymax * 1024;
    674
    675    tcx_set_dirty(s, start, end - start);
    676}
    677
    678static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
    679                            unsigned size)
    680{
    681    TCXState *s = opaque;
    682    uint64_t val;
    683
    684    if (addr == TCX_THC_MISC) {
    685        val = s->thcmisc | 0x02000000;
    686    } else {
    687        val = 0;
    688    }
    689    return val;
    690}
    691
    692static void tcx_thc_writel(void *opaque, hwaddr addr,
    693                         uint64_t val, unsigned size)
    694{
    695    TCXState *s = opaque;
    696
    697    if (addr == TCX_THC_CURSXY) {
    698        tcx_invalidate_cursor_position(s);
    699        s->cursx = val >> 16;
    700        s->cursy = val;
    701        tcx_invalidate_cursor_position(s);
    702    } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
    703        s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
    704        tcx_invalidate_cursor_position(s);
    705    } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
    706        s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
    707        tcx_invalidate_cursor_position(s);
    708    } else if (addr == TCX_THC_MISC) {
    709        s->thcmisc = val;
    710    }
    711
    712}
    713
    714static const MemoryRegionOps tcx_thc_ops = {
    715    .read = tcx_thc_readl,
    716    .write = tcx_thc_writel,
    717    .endianness = DEVICE_NATIVE_ENDIAN,
    718    .valid = {
    719        .min_access_size = 4,
    720        .max_access_size = 4,
    721    },
    722};
    723
    724static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
    725                            unsigned size)
    726{
    727    return 0;
    728}
    729
    730static void tcx_dummy_writel(void *opaque, hwaddr addr,
    731                         uint64_t val, unsigned size)
    732{
    733    return;
    734}
    735
    736static const MemoryRegionOps tcx_dummy_ops = {
    737    .read = tcx_dummy_readl,
    738    .write = tcx_dummy_writel,
    739    .endianness = DEVICE_NATIVE_ENDIAN,
    740    .valid = {
    741        .min_access_size = 4,
    742        .max_access_size = 4,
    743    },
    744};
    745
    746static const GraphicHwOps tcx_ops = {
    747    .invalidate = tcx_invalidate_display,
    748    .gfx_update = tcx_update_display,
    749};
    750
    751static const GraphicHwOps tcx24_ops = {
    752    .invalidate = tcx24_invalidate_display,
    753    .gfx_update = tcx24_update_display,
    754};
    755
    756static void tcx_initfn(Object *obj)
    757{
    758    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    759    TCXState *s = TCX(obj);
    760
    761    memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom",
    762                                     FCODE_MAX_ROM_SIZE, &error_fatal);
    763    sysbus_init_mmio(sbd, &s->rom);
    764
    765    /* 2/STIP : Stippler */
    766    memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
    767                          TCX_STIP_NREGS);
    768    sysbus_init_mmio(sbd, &s->stip);
    769
    770    /* 3/BLIT : Blitter */
    771    memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
    772                          TCX_BLIT_NREGS);
    773    sysbus_init_mmio(sbd, &s->blit);
    774
    775    /* 5/RSTIP : Raw Stippler */
    776    memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
    777                          TCX_RSTIP_NREGS);
    778    sysbus_init_mmio(sbd, &s->rstip);
    779
    780    /* 6/RBLIT : Raw Blitter */
    781    memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
    782                          TCX_RBLIT_NREGS);
    783    sysbus_init_mmio(sbd, &s->rblit);
    784
    785    /* 7/TEC : ??? */
    786    memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
    787                          TCX_TEC_NREGS);
    788    sysbus_init_mmio(sbd, &s->tec);
    789
    790    /* 8/CMAP : DAC */
    791    memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
    792                          TCX_DAC_NREGS);
    793    sysbus_init_mmio(sbd, &s->dac);
    794
    795    /* 9/THC : Cursor */
    796    memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
    797                          TCX_THC_NREGS);
    798    sysbus_init_mmio(sbd, &s->thc);
    799
    800    /* 11/DHC : ??? */
    801    memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
    802                          TCX_DHC_NREGS);
    803    sysbus_init_mmio(sbd, &s->dhc);
    804
    805    /* 12/ALT : ??? */
    806    memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
    807                          TCX_ALT_NREGS);
    808    sysbus_init_mmio(sbd, &s->alt);
    809}
    810
    811static void tcx_realizefn(DeviceState *dev, Error **errp)
    812{
    813    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    814    TCXState *s = TCX(dev);
    815    ram_addr_t vram_offset = 0;
    816    int size, ret;
    817    uint8_t *vram_base;
    818    char *fcode_filename;
    819
    820    memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram",
    821                           s->vram_size * (1 + 4 + 4), &error_fatal);
    822    vmstate_register_ram_global(&s->vram_mem);
    823    memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
    824    vram_base = memory_region_get_ram_ptr(&s->vram_mem);
    825
    826    /* 10/ROM : FCode ROM */
    827    vmstate_register_ram_global(&s->rom);
    828    fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
    829    if (fcode_filename) {
    830        ret = load_image_mr(fcode_filename, &s->rom);
    831        g_free(fcode_filename);
    832        if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
    833            warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
    834        }
    835    }
    836
    837    /* 0/DFB8 : 8-bit plane */
    838    s->vram = vram_base;
    839    size = s->vram_size;
    840    memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
    841                             &s->vram_mem, vram_offset, size);
    842    sysbus_init_mmio(sbd, &s->vram_8bit);
    843    vram_offset += size;
    844    vram_base += size;
    845
    846    /* 1/DFB24 : 24bit plane */
    847    size = s->vram_size * 4;
    848    s->vram24 = (uint32_t *)vram_base;
    849    s->vram24_offset = vram_offset;
    850    memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
    851                             &s->vram_mem, vram_offset, size);
    852    sysbus_init_mmio(sbd, &s->vram_24bit);
    853    vram_offset += size;
    854    vram_base += size;
    855
    856    /* 4/RDFB32 : Raw Framebuffer */
    857    size = s->vram_size * 4;
    858    s->cplane = (uint32_t *)vram_base;
    859    s->cplane_offset = vram_offset;
    860    memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
    861                             &s->vram_mem, vram_offset, size);
    862    sysbus_init_mmio(sbd, &s->vram_cplane);
    863
    864    /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
    865    if (s->depth == 8) {
    866        memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
    867                              "tcx.thc24", TCX_THC_NREGS);
    868        sysbus_init_mmio(sbd, &s->thc24);
    869    }
    870
    871    sysbus_init_irq(sbd, &s->irq);
    872
    873    if (s->depth == 8) {
    874        s->con = graphic_console_init(dev, 0, &tcx_ops, s);
    875    } else {
    876        s->con = graphic_console_init(dev, 0, &tcx24_ops, s);
    877    }
    878    s->thcmisc = 0;
    879
    880    qemu_console_resize(s->con, s->width, s->height);
    881}
    882
    883static Property tcx_properties[] = {
    884    DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
    885    DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
    886    DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
    887    DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
    888    DEFINE_PROP_END_OF_LIST(),
    889};
    890
    891static void tcx_class_init(ObjectClass *klass, void *data)
    892{
    893    DeviceClass *dc = DEVICE_CLASS(klass);
    894
    895    dc->realize = tcx_realizefn;
    896    dc->reset = tcx_reset;
    897    dc->vmsd = &vmstate_tcx;
    898    device_class_set_props(dc, tcx_properties);
    899}
    900
    901static const TypeInfo tcx_info = {
    902    .name          = TYPE_TCX,
    903    .parent        = TYPE_SYS_BUS_DEVICE,
    904    .instance_size = sizeof(TCXState),
    905    .instance_init = tcx_initfn,
    906    .class_init    = tcx_class_init,
    907};
    908
    909static void tcx_register_types(void)
    910{
    911    type_register_static(&tcx_info);
    912}
    913
    914type_init(tcx_register_types)