Commit b86f4613 authored by Laszlo Ersek's avatar Laszlo Ersek Committed by Michael S. Tsirkin
Browse files

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: default avatarJan Tomko <jtomko@redhat.com>
Suggested-by: default avatarMarkus Armbruster <armbru@redhat.com>
Signed-off-by: default avatarLaszlo Ersek <lersek@redhat.com>
Reviewed-by: default avatarJohn Snow <jsnow@redhat.com>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 7444ca4e
Loading
Loading
Loading
Loading
+55 −2
Original line number Diff line number Diff line
@@ -334,6 +334,41 @@ typedef struct pc_cmos_init_late_arg {
    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 @@ static void pc_cmos_init_late(void *opaque)
    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 @@ static void pc_cmos_init_late(void *opaque)
    }
    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 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
    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;