1 /* 2 * QEMU Floppy disk emulator (Intel 82078) 3 * 4 * Copyright (c) 2003, 2007 Jocelyn Mayer 5 * Copyright (c) 2008 Hervé Poussineau 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 * THE SOFTWARE. 24 */ 25 /* 26 * The controller is used in Sun4m systems in a slightly different 27 * way. There are changes in DOR register and DMA is not available. 28 */ 29 30 #include "qemu/osdep.h" 31 #include "hw/block/fdc.h" 32 #include "qapi/error.h" 33 #include "qemu/error-report.h" 34 #include "qemu/timer.h" 35 #include "hw/acpi/acpi_aml_interface.h" 36 #include "hw/irq.h" 37 #include "hw/isa/isa.h" 38 #include "hw/qdev-properties.h" 39 #include "hw/qdev-properties-system.h" 40 #include "migration/vmstate.h" 41 #include "hw/block/block.h" 42 #include "sysemu/block-backend.h" 43 #include "sysemu/blockdev.h" 44 #include "sysemu/sysemu.h" 45 #include "exec/ioport.h" 46 #include "qemu/log.h" 47 #include "qemu/main-loop.h" 48 #include "qemu/module.h" 49 #include "trace.h" 50 #include "qom/object.h" 51 #include "fdc-internal.h" 52 53 OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC) 54 55 struct FDCtrlISABus { 56 /*< private >*/ 57 ISADevice parent_obj; 58 /*< public >*/ 59 60 uint32_t iobase; 61 uint32_t irq; 62 uint32_t dma; 63 struct FDCtrl state; 64 PortioList portio_list; 65 int32_t bootindexA; 66 int32_t bootindexB; 67 }; 68 69 static void fdctrl_external_reset_isa(DeviceState *d) 70 { 71 FDCtrlISABus *isa = ISA_FDC(d); 72 FDCtrl *s = &isa->state; 73 74 fdctrl_reset(s, 0); 75 } 76 77 void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds) 78 { 79 fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds); 80 } 81 82 static const MemoryRegionPortio fdc_portio_list[] = { 83 { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write }, 84 { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write }, 85 PORTIO_END_OF_LIST(), 86 }; 87 88 static void isabus_fdc_realize(DeviceState *dev, Error **errp) 89 { 90 ISADevice *isadev = ISA_DEVICE(dev); 91 ISABus *bus = isa_bus_from_device(isadev); 92 FDCtrlISABus *isa = ISA_FDC(dev); 93 FDCtrl *fdctrl = &isa->state; 94 Error *err = NULL; 95 96 isa_register_portio_list(isadev, &isa->portio_list, 97 isa->iobase, fdc_portio_list, fdctrl, 98 "fdc"); 99 100 fdctrl->irq = isa_bus_get_irq(bus, isa->irq); 101 fdctrl->dma_chann = isa->dma; 102 if (fdctrl->dma_chann != -1) { 103 IsaDmaClass *k; 104 fdctrl->dma = isa_bus_get_dma(bus, isa->dma); 105 if (!fdctrl->dma) { 106 error_setg(errp, "ISA controller does not support DMA"); 107 return; 108 } 109 k = ISADMA_GET_CLASS(fdctrl->dma); 110 k->register_channel(fdctrl->dma, fdctrl->dma_chann, 111 &fdctrl_transfer_handler, fdctrl); 112 } 113 114 qdev_set_legacy_instance_id(dev, isa->iobase, 2); 115 116 fdctrl_realize_common(dev, fdctrl, &err); 117 if (err != NULL) { 118 error_propagate(errp, err); 119 return; 120 } 121 } 122 123 FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) 124 { 125 FDCtrlISABus *isa = ISA_FDC(fdc); 126 127 return isa->state.drives[i].drive; 128 } 129 130 static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc, 131 uint8_t *maxh, uint8_t *maxs) 132 { 133 const FDFormat *fdf; 134 135 *maxc = *maxh = *maxs = 0; 136 for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) { 137 if (fdf->drive != type) { 138 continue; 139 } 140 if (*maxc < fdf->max_track) { 141 *maxc = fdf->max_track; 142 } 143 if (*maxh < fdf->max_head) { 144 *maxh = fdf->max_head; 145 } 146 if (*maxs < fdf->last_sect) { 147 *maxs = fdf->last_sect; 148 } 149 } 150 /* fd_formats must contain at least one entry per FloppyDriveType */ 151 assert(*maxc); 152 (*maxc)--; 153 } 154 155 static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) 156 { 157 Aml *dev, *fdi; 158 uint8_t maxc, maxh, maxs; 159 160 isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs); 161 162 dev = aml_device("FLP%c", 'A' + idx); 163 164 aml_append(dev, aml_name_decl("_ADR", aml_int(idx))); 165 166 fdi = aml_package(16); 167 aml_append(fdi, aml_int(idx)); /* Drive Number */ 168 aml_append(fdi, 169 aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ 170 /* 171 * the values below are the limits of the drive, and are thus independent 172 * of the inserted media 173 */ 174 aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ 175 aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ 176 aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ 177 /* 178 * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of 179 * the drive type, so shall we 180 */ 181 aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ 182 aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ 183 aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ 184 aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ 185 aml_append(fdi, aml_int(0x12)); /* disk_eot */ 186 aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ 187 aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ 188 aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ 189 aml_append(fdi, aml_int(0xF6)); /* disk_fill */ 190 aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ 191 aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ 192 193 aml_append(dev, aml_name_decl("_FDI", fdi)); 194 return dev; 195 } 196 197 void isa_fdc_set_iobase(ISADevice *fdc, hwaddr iobase) 198 { 199 FDCtrlISABus *isa = ISA_FDC(fdc); 200 201 fdc->ioport_id = iobase; 202 isa->iobase = iobase; 203 portio_list_set_address(&isa->portio_list, isa->iobase); 204 } 205 206 void isa_fdc_set_enabled(ISADevice *fdc, bool enabled) 207 { 208 portio_list_set_enabled(&ISA_FDC(fdc)->portio_list, enabled); 209 } 210 211 int cmos_get_fd_drive_type(FloppyDriveType fd0) 212 { 213 int val; 214 215 switch (fd0) { 216 case FLOPPY_DRIVE_TYPE_144: 217 /* 1.44 Mb 3"5 drive */ 218 val = 4; 219 break; 220 case FLOPPY_DRIVE_TYPE_288: 221 /* 2.88 Mb 3"5 drive */ 222 val = 5; 223 break; 224 case FLOPPY_DRIVE_TYPE_120: 225 /* 1.2 Mb 5"5 drive */ 226 val = 2; 227 break; 228 case FLOPPY_DRIVE_TYPE_NONE: 229 default: 230 val = 0; 231 break; 232 } 233 return val; 234 } 235 236 static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope) 237 { 238 FDCtrlISABus *isa = ISA_FDC(adev); 239 Aml *dev; 240 Aml *crs; 241 int i; 242 243 #define ACPI_FDE_MAX_FD 4 244 uint32_t fde_buf[5] = { 245 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */ 246 cpu_to_le32(2) /* tape presence (2 == never present) */ 247 }; 248 249 crs = aml_resource_template(); 250 aml_append(crs, 251 aml_io(AML_DECODE16, isa->iobase + 2, isa->iobase + 2, 0x00, 0x04)); 252 aml_append(crs, 253 aml_io(AML_DECODE16, isa->iobase + 7, isa->iobase + 7, 0x00, 0x01)); 254 aml_append(crs, aml_irq_no_flags(isa->irq)); 255 aml_append(crs, 256 aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, isa->dma)); 257 258 dev = aml_device("FDC0"); 259 aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); 260 aml_append(dev, aml_name_decl("_CRS", crs)); 261 262 for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { 263 FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i); 264 265 if (type < FLOPPY_DRIVE_TYPE_NONE) { 266 fde_buf[i] = cpu_to_le32(1); /* drive present */ 267 aml_append(dev, build_fdinfo_aml(i, type)); 268 } 269 } 270 aml_append(dev, aml_name_decl("_FDE", 271 aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf))); 272 273 aml_append(scope, dev); 274 } 275 276 static const VMStateDescription vmstate_isa_fdc = { 277 .name = "fdc", 278 .version_id = 2, 279 .minimum_version_id = 2, 280 .fields = (const VMStateField[]) { 281 VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), 282 VMSTATE_END_OF_LIST() 283 } 284 }; 285 286 static Property isa_fdc_properties[] = { 287 DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), 288 DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), 289 DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), 290 DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, 291 FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, 292 FloppyDriveType), 293 DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, 294 FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, 295 FloppyDriveType), 296 DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, 297 FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, 298 FloppyDriveType), 299 DEFINE_PROP_END_OF_LIST(), 300 }; 301 302 static void isabus_fdc_class_init(ObjectClass *klass, void *data) 303 { 304 DeviceClass *dc = DEVICE_CLASS(klass); 305 AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); 306 307 dc->desc = "virtual floppy controller"; 308 dc->realize = isabus_fdc_realize; 309 dc->fw_name = "fdc"; 310 dc->reset = fdctrl_external_reset_isa; 311 dc->vmsd = &vmstate_isa_fdc; 312 adevc->build_dev_aml = build_fdc_aml; 313 device_class_set_props(dc, isa_fdc_properties); 314 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 315 } 316 317 static void isabus_fdc_instance_init(Object *obj) 318 { 319 FDCtrlISABus *isa = ISA_FDC(obj); 320 321 device_add_bootindex_property(obj, &isa->bootindexA, 322 "bootindexA", "/floppy@0", 323 DEVICE(obj)); 324 device_add_bootindex_property(obj, &isa->bootindexB, 325 "bootindexB", "/floppy@1", 326 DEVICE(obj)); 327 } 328 329 static const TypeInfo isa_fdc_info = { 330 .name = TYPE_ISA_FDC, 331 .parent = TYPE_ISA_DEVICE, 332 .instance_size = sizeof(FDCtrlISABus), 333 .class_init = isabus_fdc_class_init, 334 .instance_init = isabus_fdc_instance_init, 335 .interfaces = (InterfaceInfo[]) { 336 { TYPE_ACPI_DEV_AML_IF }, 337 { }, 338 }, 339 }; 340 341 static void isa_fdc_register_types(void) 342 { 343 type_register_static(&isa_fdc_info); 344 } 345 346 type_init(isa_fdc_register_types) 347