qemu/hw/block/swim.c
<<
>>
Prefs
   1/*
   2 * QEMU Macintosh floppy disk controller emulator (SWIM)
   3 *
   4 * Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2.  See
   7 * the COPYING file in the top-level directory.
   8 *
   9 * Only the basic support: it allows to switch from IWM (Integrated WOZ
  10 * Machine) mode to the SWIM mode and makes the linux driver happy.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "qemu/main-loop.h"
  15#include "qapi/error.h"
  16#include "sysemu/block-backend.h"
  17#include "hw/sysbus.h"
  18#include "migration/vmstate.h"
  19#include "hw/block/block.h"
  20#include "hw/block/swim.h"
  21#include "hw/qdev-properties.h"
  22
  23/* IWM registers */
  24
  25#define IWM_PH0L                0
  26#define IWM_PH0H                1
  27#define IWM_PH1L                2
  28#define IWM_PH1H                3
  29#define IWM_PH2L                4
  30#define IWM_PH2H                5
  31#define IWM_PH3L                6
  32#define IWM_PH3H                7
  33#define IWM_MTROFF              8
  34#define IWM_MTRON               9
  35#define IWM_INTDRIVE            10
  36#define IWM_EXTDRIVE            11
  37#define IWM_Q6L                 12
  38#define IWM_Q6H                 13
  39#define IWM_Q7L                 14
  40#define IWM_Q7H                 15
  41
  42/* SWIM registers */
  43
  44#define SWIM_WRITE_DATA         0
  45#define SWIM_WRITE_MARK         1
  46#define SWIM_WRITE_CRC          2
  47#define SWIM_WRITE_PARAMETER    3
  48#define SWIM_WRITE_PHASE        4
  49#define SWIM_WRITE_SETUP        5
  50#define SWIM_WRITE_MODE0        6
  51#define SWIM_WRITE_MODE1        7
  52
  53#define SWIM_READ_DATA          8
  54#define SWIM_READ_MARK          9
  55#define SWIM_READ_ERROR         10
  56#define SWIM_READ_PARAMETER     11
  57#define SWIM_READ_PHASE         12
  58#define SWIM_READ_SETUP         13
  59#define SWIM_READ_STATUS        14
  60#define SWIM_READ_HANDSHAKE     15
  61
  62#define REG_SHIFT               9
  63
  64#define SWIM_MODE_IWM  0
  65#define SWIM_MODE_SWIM 1
  66
  67/* bits in phase register */
  68
  69#define SWIM_SEEK_NEGATIVE   0x074
  70#define SWIM_STEP            0x071
  71#define SWIM_MOTOR_ON        0x072
  72#define SWIM_MOTOR_OFF       0x076
  73#define SWIM_INDEX           0x073
  74#define SWIM_EJECT           0x077
  75#define SWIM_SETMFM          0x171
  76#define SWIM_SETGCR          0x175
  77#define SWIM_RELAX           0x033
  78#define SWIM_LSTRB           0x008
  79#define SWIM_CA_MASK         0x077
  80
  81/* Select values for swim_select and swim_readbit */
  82
  83#define SWIM_READ_DATA_0     0x074
  84#define SWIM_TWOMEG_DRIVE    0x075
  85#define SWIM_SINGLE_SIDED    0x076
  86#define SWIM_DRIVE_PRESENT   0x077
  87#define SWIM_DISK_IN         0x170
  88#define SWIM_WRITE_PROT      0x171
  89#define SWIM_TRACK_ZERO      0x172
  90#define SWIM_TACHO           0x173
  91#define SWIM_READ_DATA_1     0x174
  92#define SWIM_MFM_MODE        0x175
  93#define SWIM_SEEK_COMPLETE   0x176
  94#define SWIM_ONEMEG_MEDIA    0x177
  95
  96/* Bits in handshake register */
  97
  98#define SWIM_MARK_BYTE       0x01
  99#define SWIM_CRC_ZERO        0x02
 100#define SWIM_RDDATA          0x04
 101#define SWIM_SENSE           0x08
 102#define SWIM_MOTEN           0x10
 103#define SWIM_ERROR           0x20
 104#define SWIM_DAT2BYTE        0x40
 105#define SWIM_DAT1BYTE        0x80
 106
 107/* bits in setup register */
 108
 109#define SWIM_S_INV_WDATA     0x01
 110#define SWIM_S_3_5_SELECT    0x02
 111#define SWIM_S_GCR           0x04
 112#define SWIM_S_FCLK_DIV2     0x08
 113#define SWIM_S_ERROR_CORR    0x10
 114#define SWIM_S_IBM_DRIVE     0x20
 115#define SWIM_S_GCR_WRITE     0x40
 116#define SWIM_S_TIMEOUT       0x80
 117
 118/* bits in mode register */
 119
 120#define SWIM_CLFIFO          0x01
 121#define SWIM_ENBL1           0x02
 122#define SWIM_ENBL2           0x04
 123#define SWIM_ACTION          0x08
 124#define SWIM_WRITE_MODE      0x10
 125#define SWIM_HEDSEL          0x20
 126#define SWIM_MOTON           0x80
 127
 128static void fd_recalibrate(FDrive *drive)
 129{
 130}
 131
 132static void swim_change_cb(void *opaque, bool load, Error **errp)
 133{
 134    FDrive *drive = opaque;
 135
 136    if (!load) {
 137        blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
 138    } else {
 139        if (!blkconf_apply_backend_options(drive->conf,
 140                                           blk_is_read_only(drive->blk), false,
 141                                           errp)) {
 142            return;
 143        }
 144    }
 145}
 146
 147static const BlockDevOps swim_block_ops = {
 148    .change_media_cb = swim_change_cb,
 149};
 150
 151static Property swim_drive_properties[] = {
 152    DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1),
 153    DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf),
 154    DEFINE_PROP_END_OF_LIST(),
 155};
 156
 157static void swim_drive_realize(DeviceState *qdev, Error **errp)
 158{
 159    SWIMDrive *dev = SWIM_DRIVE(qdev);
 160    SWIMBus *bus = SWIM_BUS(qdev->parent_bus);
 161    FDrive *drive;
 162    int ret;
 163
 164    if (dev->unit == -1) {
 165        for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) {
 166            drive = &bus->ctrl->drives[dev->unit];
 167            if (!drive->blk) {
 168                break;
 169            }
 170        }
 171    }
 172
 173    if (dev->unit >= SWIM_MAX_FD) {
 174        error_setg(errp, "Can't create floppy unit %d, bus supports "
 175                   "only %d units", dev->unit, SWIM_MAX_FD);
 176        return;
 177    }
 178
 179    drive = &bus->ctrl->drives[dev->unit];
 180    if (drive->blk) {
 181        error_setg(errp, "Floppy unit %d is in use", dev->unit);
 182        return;
 183    }
 184
 185    if (!dev->conf.blk) {
 186        /* Anonymous BlockBackend for an empty drive */
 187        dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
 188        ret = blk_attach_dev(dev->conf.blk, qdev);
 189        assert(ret == 0);
 190    }
 191
 192    if (!blkconf_blocksizes(&dev->conf, errp)) {
 193        return;
 194    }
 195
 196    if (dev->conf.logical_block_size != 512 ||
 197        dev->conf.physical_block_size != 512)
 198    {
 199        error_setg(errp, "Physical and logical block size must "
 200                   "be 512 for floppy");
 201        return;
 202    }
 203
 204    /*
 205     * rerror/werror aren't supported by fdc and therefore not even registered
 206     * with qdev. So set the defaults manually before they are used in
 207     * blkconf_apply_backend_options().
 208     */
 209    dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
 210    dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
 211
 212    if (!blkconf_apply_backend_options(&dev->conf,
 213                                       blk_is_read_only(dev->conf.blk),
 214                                       false, errp)) {
 215        return;
 216    }
 217
 218    /*
 219     * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
 220     * for empty drives.
 221     */
 222    if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
 223        blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
 224        error_setg(errp, "fdc doesn't support drive option werror");
 225        return;
 226    }
 227    if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
 228        error_setg(errp, "fdc doesn't support drive option rerror");
 229        return;
 230    }
 231
 232    drive->conf = &dev->conf;
 233    drive->blk = dev->conf.blk;
 234    drive->swimctrl = bus->ctrl;
 235
 236    blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
 237}
 238
 239static void swim_drive_class_init(ObjectClass *klass, void *data)
 240{
 241    DeviceClass *k = DEVICE_CLASS(klass);
 242    k->realize = swim_drive_realize;
 243    set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
 244    k->bus_type = TYPE_SWIM_BUS;
 245    device_class_set_props(k, swim_drive_properties);
 246    k->desc = "virtual SWIM drive";
 247}
 248
 249static const TypeInfo swim_drive_info = {
 250    .name = TYPE_SWIM_DRIVE,
 251    .parent = TYPE_DEVICE,
 252    .instance_size = sizeof(SWIMDrive),
 253    .class_init = swim_drive_class_init,
 254};
 255
 256static const TypeInfo swim_bus_info = {
 257    .name = TYPE_SWIM_BUS,
 258    .parent = TYPE_BUS,
 259    .instance_size = sizeof(SWIMBus),
 260};
 261
 262static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value,
 263                          unsigned size)
 264{
 265    SWIMCtrl *swimctrl = opaque;
 266
 267    reg >>= REG_SHIFT;
 268
 269    swimctrl->regs[reg >> 1] = reg & 1;
 270
 271    if (swimctrl->regs[IWM_Q6] &&
 272        swimctrl->regs[IWM_Q7]) {
 273        if (swimctrl->regs[IWM_MTR]) {
 274            /* data register */
 275            swimctrl->iwm_data = value;
 276        } else {
 277            /* mode register */
 278            swimctrl->iwm_mode = value;
 279            /* detect sequence to switch from IWM mode to SWIM mode */
 280            switch (swimctrl->iwm_switch) {
 281            case 0:
 282                if (value == 0x57) {
 283                    swimctrl->iwm_switch++;
 284                }
 285                break;
 286            case 1:
 287                if (value == 0x17) {
 288                    swimctrl->iwm_switch++;
 289                }
 290                break;
 291            case 2:
 292                if (value == 0x57) {
 293                    swimctrl->iwm_switch++;
 294                }
 295                break;
 296            case 3:
 297                if (value == 0x57) {
 298                    swimctrl->mode = SWIM_MODE_SWIM;
 299                    swimctrl->iwm_switch = 0;
 300                }
 301                break;
 302            }
 303        }
 304    }
 305}
 306
 307static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size)
 308{
 309    SWIMCtrl *swimctrl = opaque;
 310
 311    reg >>= REG_SHIFT;
 312
 313    swimctrl->regs[reg >> 1] = reg & 1;
 314
 315    return 0;
 316}
 317
 318static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value,
 319                           unsigned size)
 320{
 321    SWIMCtrl *swimctrl = opaque;
 322
 323    if (swimctrl->mode == SWIM_MODE_IWM) {
 324        iwmctrl_write(opaque, reg, value, size);
 325        return;
 326    }
 327
 328    reg >>= REG_SHIFT;
 329
 330    switch (reg) {
 331    case SWIM_WRITE_PHASE:
 332        swimctrl->swim_phase = value;
 333        break;
 334    case SWIM_WRITE_MODE0:
 335        swimctrl->swim_mode &= ~value;
 336        break;
 337    case SWIM_WRITE_MODE1:
 338        swimctrl->swim_mode |= value;
 339        break;
 340    case SWIM_WRITE_DATA:
 341    case SWIM_WRITE_MARK:
 342    case SWIM_WRITE_CRC:
 343    case SWIM_WRITE_PARAMETER:
 344    case SWIM_WRITE_SETUP:
 345        break;
 346    }
 347}
 348
 349static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size)
 350{
 351    SWIMCtrl *swimctrl = opaque;
 352    uint32_t value = 0;
 353
 354    if (swimctrl->mode == SWIM_MODE_IWM) {
 355        return iwmctrl_read(opaque, reg, size);
 356    }
 357
 358    reg >>= REG_SHIFT;
 359
 360    switch (reg) {
 361    case SWIM_READ_PHASE:
 362        value = swimctrl->swim_phase;
 363        break;
 364    case SWIM_READ_HANDSHAKE:
 365        if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
 366            /* always answer "no drive present" */
 367            value = SWIM_SENSE;
 368        }
 369        break;
 370    case SWIM_READ_DATA:
 371    case SWIM_READ_MARK:
 372    case SWIM_READ_ERROR:
 373    case SWIM_READ_PARAMETER:
 374    case SWIM_READ_SETUP:
 375    case SWIM_READ_STATUS:
 376        break;
 377    }
 378
 379    return value;
 380}
 381
 382static const MemoryRegionOps swimctrl_mem_ops = {
 383    .write = swimctrl_write,
 384    .read = swimctrl_read,
 385    .endianness = DEVICE_NATIVE_ENDIAN,
 386};
 387
 388static void sysbus_swim_reset(DeviceState *d)
 389{
 390    SWIM *sys = SWIM(d);
 391    SWIMCtrl *ctrl = &sys->ctrl;
 392    int i;
 393
 394    ctrl->mode = 0;
 395    ctrl->iwm_switch = 0;
 396    for (i = 0; i < 8; i++) {
 397        ctrl->regs[i] = 0;
 398    }
 399    ctrl->iwm_data = 0;
 400    ctrl->iwm_mode = 0;
 401    ctrl->swim_phase = 0;
 402    ctrl->swim_mode = 0;
 403    for (i = 0; i < SWIM_MAX_FD; i++) {
 404        fd_recalibrate(&ctrl->drives[i]);
 405    }
 406}
 407
 408static void sysbus_swim_init(Object *obj)
 409{
 410    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 411    SWIM *sbs = SWIM(obj);
 412    SWIMCtrl *swimctrl = &sbs->ctrl;
 413
 414    memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl,
 415                          "swim", 0x2000);
 416    sysbus_init_mmio(sbd, &swimctrl->iomem);
 417}
 418
 419static void sysbus_swim_realize(DeviceState *dev, Error **errp)
 420{
 421    SWIM *sys = SWIM(dev);
 422    SWIMCtrl *swimctrl = &sys->ctrl;
 423
 424    qbus_create_inplace(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev,
 425                        NULL);
 426    swimctrl->bus.ctrl = swimctrl;
 427}
 428
 429static const VMStateDescription vmstate_fdrive = {
 430    .name = "fdrive",
 431    .version_id = 1,
 432    .minimum_version_id = 1,
 433    .fields = (VMStateField[]) {
 434        VMSTATE_END_OF_LIST()
 435    },
 436};
 437
 438static const VMStateDescription vmstate_swim = {
 439    .name = "swim",
 440    .version_id = 1,
 441    .minimum_version_id = 1,
 442    .fields = (VMStateField[]) {
 443        VMSTATE_INT32(mode, SWIMCtrl),
 444        /* IWM mode */
 445        VMSTATE_INT32(iwm_switch, SWIMCtrl),
 446        VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8),
 447        VMSTATE_UINT8(iwm_data, SWIMCtrl),
 448        VMSTATE_UINT8(iwm_mode, SWIMCtrl),
 449        /* SWIM mode */
 450        VMSTATE_UINT8(swim_phase, SWIMCtrl),
 451        VMSTATE_UINT8(swim_mode, SWIMCtrl),
 452        /* Drives */
 453        VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
 454                             vmstate_fdrive, FDrive),
 455        VMSTATE_END_OF_LIST()
 456    },
 457};
 458
 459static const VMStateDescription vmstate_sysbus_swim = {
 460    .name = "SWIM",
 461    .version_id = 1,
 462    .fields = (VMStateField[]) {
 463        VMSTATE_STRUCT(ctrl, SWIM, 0, vmstate_swim, SWIMCtrl),
 464        VMSTATE_END_OF_LIST()
 465    }
 466};
 467
 468static void sysbus_swim_class_init(ObjectClass *oc, void *data)
 469{
 470    DeviceClass *dc = DEVICE_CLASS(oc);
 471
 472    dc->realize = sysbus_swim_realize;
 473    dc->reset = sysbus_swim_reset;
 474    dc->vmsd = &vmstate_sysbus_swim;
 475}
 476
 477static const TypeInfo sysbus_swim_info = {
 478    .name          = TYPE_SWIM,
 479    .parent        = TYPE_SYS_BUS_DEVICE,
 480    .instance_size = sizeof(SWIM),
 481    .instance_init = sysbus_swim_init,
 482    .class_init    = sysbus_swim_class_init,
 483};
 484
 485static void swim_register_types(void)
 486{
 487    type_register_static(&sysbus_swim_info);
 488    type_register_static(&swim_bus_info);
 489    type_register_static(&swim_drive_info);
 490}
 491
 492type_init(swim_register_types)
 493