hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 6f1b137..09635cd 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -334,6 +334,41 @@
BusState *idebus[2];
} pc_cmos_init_late_arg;
+typedef struct check_fdc_state {
+ ISADevice *floppy;
+ bool multiple;
+} CheckFdcState;
+
+static int check_fdc(Object *obj, void *opaque)
+{
+ CheckFdcState *state = opaque;
+ Object *fdc;
+ uint32_t iobase;
+ Error *local_err = NULL;
+
+ fdc = object_dynamic_cast(obj, TYPE_ISA_FDC);
+ if (!fdc) {
+ return 0;
+ }
+
+ iobase = object_property_get_int(obj, "iobase", &local_err);
+ if (local_err || iobase != 0x3f0) {
+ error_free(local_err);
+ return 0;
+ }
+
+ if (state->floppy) {
+ state->multiple = true;
+ } else {
+ state->floppy = ISA_DEVICE(obj);
+ }
+ return 0;
+}
+
+static const char * const fdc_container_path[] = {
+ "/unattached", "/peripheral", "/peripheral-anon"
+};
+
static void pc_cmos_init_late(void *opaque)
{
pc_cmos_init_late_arg *arg = opaque;
@@ -342,6 +377,8 @@
int8_t heads, sectors;
int val;
int i, trans;
+ Object *container;
+ CheckFdcState state = { 0 };
val = 0;
if (ide_get_geometry(arg->idebus[0], 0,
@@ -371,6 +408,23 @@
}
rtc_set_memory(s, 0x39, val);
+ /*
+ * Locate the FDC at IO address 0x3f0, and configure the CMOS registers
+ * accordingly.
+ */
+ for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) {
+ container = container_get(qdev_get_machine(), fdc_container_path[i]);
+ object_child_foreach(container, check_fdc, &state);
+ }
+
+ if (state.multiple) {
+ error_report("warning: multiple floppy disk controllers with "
+ "iobase=0x3f0 have been found;\n"
+ "the one being picked for CMOS setup might not reflect "
+ "your intent");
+ }
+ pc_cmos_init_floppy(s, state.floppy);
+
qemu_unregister_reset(pc_cmos_init_late, opaque);
}
@@ -440,9 +494,8 @@
val |= 0x02; /* FPU is there */
val |= 0x04; /* PS/2 mouse installed */
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
- pc_cmos_init_floppy(s, floppy);
- /* hard drives */
+ /* hard drives and FDC */
arg.rtc_state = s;
arg.idebus[0] = idebus0;
arg.idebus[1] = idebus1;