Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 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" |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 35 | #include "hw/acpi/acpi_aml_interface.h" |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 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 "qemu/log.h" |
| 46 | #include "qemu/main-loop.h" |
| 47 | #include "qemu/module.h" |
| 48 | #include "trace.h" |
| 49 | #include "qom/object.h" |
| 50 | #include "fdc-internal.h" |
| 51 | |
| 52 | OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC) |
| 53 | |
| 54 | struct FDCtrlISABus { |
| 55 | /*< private >*/ |
| 56 | ISADevice parent_obj; |
| 57 | /*< public >*/ |
| 58 | |
| 59 | uint32_t iobase; |
| 60 | uint32_t irq; |
| 61 | uint32_t dma; |
| 62 | struct FDCtrl state; |
| 63 | int32_t bootindexA; |
| 64 | int32_t bootindexB; |
| 65 | }; |
| 66 | |
| 67 | static void fdctrl_external_reset_isa(DeviceState *d) |
| 68 | { |
| 69 | FDCtrlISABus *isa = ISA_FDC(d); |
| 70 | FDCtrl *s = &isa->state; |
| 71 | |
| 72 | fdctrl_reset(s, 0); |
| 73 | } |
| 74 | |
| 75 | void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds) |
| 76 | { |
| 77 | fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds); |
| 78 | } |
| 79 | |
| 80 | static const MemoryRegionPortio fdc_portio_list[] = { |
| 81 | { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write }, |
| 82 | { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write }, |
| 83 | PORTIO_END_OF_LIST(), |
| 84 | }; |
| 85 | |
| 86 | static void isabus_fdc_realize(DeviceState *dev, Error **errp) |
| 87 | { |
| 88 | ISADevice *isadev = ISA_DEVICE(dev); |
| 89 | FDCtrlISABus *isa = ISA_FDC(dev); |
| 90 | FDCtrl *fdctrl = &isa->state; |
| 91 | Error *err = NULL; |
| 92 | |
| 93 | isa_register_portio_list(isadev, &fdctrl->portio_list, |
| 94 | isa->iobase, fdc_portio_list, fdctrl, |
| 95 | "fdc"); |
| 96 | |
Bernhard Beschow | 215caca | 2022-03-01 23:00:37 +0100 | [diff] [blame] | 97 | fdctrl->irq = isa_get_irq(isadev, isa->irq); |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 98 | fdctrl->dma_chann = isa->dma; |
| 99 | if (fdctrl->dma_chann != -1) { |
| 100 | IsaDmaClass *k; |
| 101 | fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); |
| 102 | if (!fdctrl->dma) { |
| 103 | error_setg(errp, "ISA controller does not support DMA"); |
| 104 | return; |
| 105 | } |
| 106 | k = ISADMA_GET_CLASS(fdctrl->dma); |
| 107 | k->register_channel(fdctrl->dma, fdctrl->dma_chann, |
| 108 | &fdctrl_transfer_handler, fdctrl); |
| 109 | } |
| 110 | |
| 111 | qdev_set_legacy_instance_id(dev, isa->iobase, 2); |
| 112 | |
| 113 | fdctrl_realize_common(dev, fdctrl, &err); |
| 114 | if (err != NULL) { |
| 115 | error_propagate(errp, err); |
| 116 | return; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) |
| 121 | { |
| 122 | FDCtrlISABus *isa = ISA_FDC(fdc); |
| 123 | |
| 124 | return isa->state.drives[i].drive; |
| 125 | } |
| 126 | |
| 127 | static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc, |
| 128 | uint8_t *maxh, uint8_t *maxs) |
| 129 | { |
| 130 | const FDFormat *fdf; |
| 131 | |
| 132 | *maxc = *maxh = *maxs = 0; |
| 133 | for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) { |
| 134 | if (fdf->drive != type) { |
| 135 | continue; |
| 136 | } |
| 137 | if (*maxc < fdf->max_track) { |
| 138 | *maxc = fdf->max_track; |
| 139 | } |
| 140 | if (*maxh < fdf->max_head) { |
| 141 | *maxh = fdf->max_head; |
| 142 | } |
| 143 | if (*maxs < fdf->last_sect) { |
| 144 | *maxs = fdf->last_sect; |
| 145 | } |
| 146 | } |
| 147 | (*maxc)--; |
| 148 | } |
| 149 | |
| 150 | static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) |
| 151 | { |
| 152 | Aml *dev, *fdi; |
| 153 | uint8_t maxc, maxh, maxs; |
| 154 | |
| 155 | isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs); |
| 156 | |
| 157 | dev = aml_device("FLP%c", 'A' + idx); |
| 158 | |
| 159 | aml_append(dev, aml_name_decl("_ADR", aml_int(idx))); |
| 160 | |
| 161 | fdi = aml_package(16); |
| 162 | aml_append(fdi, aml_int(idx)); /* Drive Number */ |
| 163 | aml_append(fdi, |
| 164 | aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ |
| 165 | /* |
| 166 | * the values below are the limits of the drive, and are thus independent |
| 167 | * of the inserted media |
| 168 | */ |
| 169 | aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ |
| 170 | aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ |
| 171 | aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ |
| 172 | /* |
| 173 | * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of |
| 174 | * the drive type, so shall we |
| 175 | */ |
| 176 | aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ |
| 177 | aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ |
| 178 | aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ |
| 179 | aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ |
| 180 | aml_append(fdi, aml_int(0x12)); /* disk_eot */ |
| 181 | aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ |
| 182 | aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ |
| 183 | aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ |
| 184 | aml_append(fdi, aml_int(0xF6)); /* disk_fill */ |
| 185 | aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ |
| 186 | aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ |
| 187 | |
| 188 | aml_append(dev, aml_name_decl("_FDI", fdi)); |
| 189 | return dev; |
| 190 | } |
| 191 | |
| 192 | int cmos_get_fd_drive_type(FloppyDriveType fd0) |
| 193 | { |
| 194 | int val; |
| 195 | |
| 196 | switch (fd0) { |
| 197 | case FLOPPY_DRIVE_TYPE_144: |
| 198 | /* 1.44 Mb 3"5 drive */ |
| 199 | val = 4; |
| 200 | break; |
| 201 | case FLOPPY_DRIVE_TYPE_288: |
| 202 | /* 2.88 Mb 3"5 drive */ |
| 203 | val = 5; |
| 204 | break; |
| 205 | case FLOPPY_DRIVE_TYPE_120: |
| 206 | /* 1.2 Mb 5"5 drive */ |
| 207 | val = 2; |
| 208 | break; |
| 209 | case FLOPPY_DRIVE_TYPE_NONE: |
| 210 | default: |
| 211 | val = 0; |
| 212 | break; |
| 213 | } |
| 214 | return val; |
| 215 | } |
| 216 | |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 217 | static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope) |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 218 | { |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 219 | FDCtrlISABus *isa = ISA_FDC(adev); |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 220 | Aml *dev; |
| 221 | Aml *crs; |
| 222 | int i; |
| 223 | |
| 224 | #define ACPI_FDE_MAX_FD 4 |
| 225 | uint32_t fde_buf[5] = { |
| 226 | 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */ |
| 227 | cpu_to_le32(2) /* tape presence (2 == never present) */ |
| 228 | }; |
| 229 | |
| 230 | crs = aml_resource_template(); |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 231 | aml_append(crs, |
Bernhard Beschow | fdb8541 | 2022-02-09 20:15:58 +0100 | [diff] [blame] | 232 | aml_io(AML_DECODE16, isa->iobase + 2, isa->iobase + 2, 0x00, 0x04)); |
| 233 | aml_append(crs, |
| 234 | aml_io(AML_DECODE16, isa->iobase + 7, isa->iobase + 7, 0x00, 0x01)); |
| 235 | aml_append(crs, aml_irq_no_flags(isa->irq)); |
| 236 | aml_append(crs, |
| 237 | aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, isa->dma)); |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 238 | |
| 239 | dev = aml_device("FDC0"); |
| 240 | aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); |
| 241 | aml_append(dev, aml_name_decl("_CRS", crs)); |
| 242 | |
| 243 | for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 244 | FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i); |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 245 | |
| 246 | if (type < FLOPPY_DRIVE_TYPE_NONE) { |
| 247 | fde_buf[i] = cpu_to_le32(1); /* drive present */ |
| 248 | aml_append(dev, build_fdinfo_aml(i, type)); |
| 249 | } |
| 250 | } |
| 251 | aml_append(dev, aml_name_decl("_FDE", |
| 252 | aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf))); |
| 253 | |
| 254 | aml_append(scope, dev); |
| 255 | } |
| 256 | |
| 257 | static const VMStateDescription vmstate_isa_fdc = { |
| 258 | .name = "fdc", |
| 259 | .version_id = 2, |
| 260 | .minimum_version_id = 2, |
| 261 | .fields = (VMStateField[]) { |
| 262 | VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), |
| 263 | VMSTATE_END_OF_LIST() |
| 264 | } |
| 265 | }; |
| 266 | |
| 267 | static Property isa_fdc_properties[] = { |
| 268 | DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), |
| 269 | DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), |
| 270 | DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), |
| 271 | DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, |
| 272 | FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, |
| 273 | FloppyDriveType), |
| 274 | DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, |
| 275 | FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, |
| 276 | FloppyDriveType), |
| 277 | DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, |
| 278 | FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, |
| 279 | FloppyDriveType), |
| 280 | DEFINE_PROP_END_OF_LIST(), |
| 281 | }; |
| 282 | |
| 283 | static void isabus_fdc_class_init(ObjectClass *klass, void *data) |
| 284 | { |
| 285 | DeviceClass *dc = DEVICE_CLASS(klass); |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 286 | AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 287 | |
Philippe Mathieu-Daudé | 9362984 | 2021-06-14 21:32:20 +0200 | [diff] [blame] | 288 | dc->desc = "virtual floppy controller"; |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 289 | dc->realize = isabus_fdc_realize; |
| 290 | dc->fw_name = "fdc"; |
| 291 | dc->reset = fdctrl_external_reset_isa; |
| 292 | dc->vmsd = &vmstate_isa_fdc; |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 293 | adevc->build_dev_aml = build_fdc_aml; |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 294 | device_class_set_props(dc, isa_fdc_properties); |
| 295 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
| 296 | } |
| 297 | |
| 298 | static void isabus_fdc_instance_init(Object *obj) |
| 299 | { |
| 300 | FDCtrlISABus *isa = ISA_FDC(obj); |
| 301 | |
| 302 | device_add_bootindex_property(obj, &isa->bootindexA, |
| 303 | "bootindexA", "/floppy@0", |
| 304 | DEVICE(obj)); |
| 305 | device_add_bootindex_property(obj, &isa->bootindexB, |
| 306 | "bootindexB", "/floppy@1", |
| 307 | DEVICE(obj)); |
| 308 | } |
| 309 | |
| 310 | static const TypeInfo isa_fdc_info = { |
| 311 | .name = TYPE_ISA_FDC, |
| 312 | .parent = TYPE_ISA_DEVICE, |
| 313 | .instance_size = sizeof(FDCtrlISABus), |
| 314 | .class_init = isabus_fdc_class_init, |
| 315 | .instance_init = isabus_fdc_instance_init, |
Igor Mammedov | e7c72a6 | 2022-06-08 09:53:08 -0400 | [diff] [blame] | 316 | .interfaces = (InterfaceInfo[]) { |
| 317 | { TYPE_ACPI_DEV_AML_IF }, |
| 318 | { }, |
| 319 | }, |
Philippe Mathieu-Daudé | 72ea60e | 2021-06-14 21:32:18 +0200 | [diff] [blame] | 320 | }; |
| 321 | |
| 322 | static void isa_fdc_register_types(void) |
| 323 | { |
| 324 | type_register_static(&isa_fdc_info); |
| 325 | } |
| 326 | |
| 327 | type_init(isa_fdc_register_types) |