diff mbox series

[v2,1/3] via-ide: Fix legacy mode emulation

Message ID f27e2af1a17e62ead8eda1e9e417f0f87f9c65f5.1696880742.git.balaton@eik.bme.hu
State New
Headers show
Series Add emulation of AmigaOne XE board | expand

Commit Message

BALATON Zoltan Oct. 9, 2023, 7:54 p.m. UTC
The initial value for BARs were set in reset method for emulating
legacy mode at start but this does not work because PCI code resets
BARs after calling device reset method. Remove this ineffective
default to avoid confusion.

Instead move setting the BARs to a callback on writing the PCI config
regsiter that sets legacy mode (which firmwares needing this mode seem
to do) and fix their values to program it to use legacy port numbers
in this case. This does not fully emulate what the data sheet says
(which is not very clear on this) but it implements enogh to allow
both modes as used by firmwares of machines we emulate.

Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
---
 hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 36 insertions(+), 5 deletions(-)

Comments

Mark Cave-Ayland Oct. 14, 2023, 12:34 p.m. UTC | #1
On 09/10/2023 20:54, BALATON Zoltan wrote:

> The initial value for BARs were set in reset method for emulating
> legacy mode at start but this does not work because PCI code resets
> BARs after calling device reset method. Remove this ineffective
> default to avoid confusion.
> 
> Instead move setting the BARs to a callback on writing the PCI config
> regsiter that sets legacy mode (which firmwares needing this mode seem
> to do) and fix their values to program it to use legacy port numbers
> in this case. This does not fully emulate what the data sheet says
> (which is not very clear on this) but it implements enogh to allow
> both modes as used by firmwares of machines we emulate.
> 
> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
> ---
>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 36 insertions(+), 5 deletions(-)
> 
> diff --git a/hw/ide/via.c b/hw/ide/via.c
> index fff23803a6..43e8af8d69 100644
> --- a/hw/ide/via.c
> +++ b/hw/ide/via.c
> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>                    PCI_STATUS_DEVSEL_MEDIUM);
>   
> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>   
>       /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>   }
>   
> +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
> +                              uint32_t val, int len)
> +{
> +    pci_default_write_config(pd, addr, val, len);
> +    /*
> +     * Bits 0 and 2 of the PCI programming interface register select between
> +     * legacy and native mode for the two IDE channels. We don't emulate this
> +     * because we cannot easily switch between ISA and PCI in QEMU so instead

As per my previous email, this statement is demonstrably false: this is now 
achievable using the portio_list*() APIs.

> +     * when guest selects legacy mode we set the PCI BARs to legacy ports which
> +     * works the same. We also don't care about setting each channel separately
> +     * as no guest is known to do or need that. We only do this when BARs are
> +     * unset when writing this register as logs from real hardware show that
> +     * setting legacy mode after BARs were set it will still use ports set by
> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware set
> +     * native mode and programmed BARs and calls it non-100% native mode).
> +     * But if 0x8a is written righr after reset without setting BARs then we
> +     * want legacy ports (this is done by the AmigaOne firmware).
> +     */
> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
> +        PCI_BASE_ADDRESS_SPACE_IO) {
> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
> +                     | PCI_BASE_ADDRESS_SPACE_IO);
> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
> +                     | PCI_BASE_ADDRESS_SPACE_IO);
> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
> +                     | PCI_BASE_ADDRESS_SPACE_IO);
> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
> +                     | PCI_BASE_ADDRESS_SPACE_IO);
> +        /* BMIBA: 20-23h */
> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
> +                     | PCI_BASE_ADDRESS_SPACE_IO);
> +    }
> +}

Another hint that this is not the right way to be doing this: the values you are 
placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 and bit 0 set 
to 1 which forces a minimum alignment of 4, so either the addresses 0x3f6/0x376 are 
being rounded internally to 0x3f4/0x374 and/or you're lucky that this just happens to 
work on QEMU.

Using the portio_list*() APIs really is the right way to implement this to avoid 
being affected by such issues.

>   static void via_ide_realize(PCIDevice *dev, Error **errp)
>   {
>       PCIIDEState *d = PCI_IDE(dev);
> @@ -221,6 +251,7 @@ static void via_ide_class_init(ObjectClass *klass, void *data)
>       /* Reason: only works as function of VIA southbridge */
>       dc->user_creatable = false;
>   
> +    k->config_write = via_ide_cfg_write;
>       k->realize = via_ide_realize;
>       k->exit = via_ide_exitfn;
>       k->vendor_id = PCI_VENDOR_ID_VIA;


ATB,

Mark.
BALATON Zoltan Oct. 14, 2023, 4:13 p.m. UTC | #2
On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
> On 09/10/2023 20:54, BALATON Zoltan wrote:
>
>> The initial value for BARs were set in reset method for emulating
>> legacy mode at start but this does not work because PCI code resets
>> BARs after calling device reset method. Remove this ineffective
>> default to avoid confusion.
>> 
>> Instead move setting the BARs to a callback on writing the PCI config
>> regsiter that sets legacy mode (which firmwares needing this mode seem
>> to do) and fix their values to program it to use legacy port numbers
>> in this case. This does not fully emulate what the data sheet says
>> (which is not very clear on this) but it implements enogh to allow
>> both modes as used by firmwares of machines we emulate.
>> 
>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>> ---
>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>   1 file changed, 36 insertions(+), 5 deletions(-)
>> 
>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>> index fff23803a6..43e8af8d69 100644
>> --- a/hw/ide/via.c
>> +++ b/hw/ide/via.c
>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
>> 20-23h */
>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>   }
>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>> +                              uint32_t val, int len)
>> +{
>> +    pci_default_write_config(pd, addr, val, len);
>> +    /*
>> +     * Bits 0 and 2 of the PCI programming interface register select 
>> between
>> +     * legacy and native mode for the two IDE channels. We don't emulate 
>> this
>> +     * because we cannot easily switch between ISA and PCI in QEMU so 
>> instead
>
> As per my previous email, this statement is demonstrably false: this is now 
> achievable using the portio_list*() APIs.
>
>> +     * when guest selects legacy mode we set the PCI BARs to legacy ports 
>> which
>> +     * works the same. We also don't care about setting each channel 
>> separately
>> +     * as no guest is known to do or need that. We only do this when BARs 
>> are
>> +     * unset when writing this register as logs from real hardware show 
>> that
>> +     * setting legacy mode after BARs were set it will still use ports set 
>> by
>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware 
>> set
>> +     * native mode and programmed BARs and calls it non-100% native mode).
>> +     * But if 0x8a is written righr after reset without setting BARs then 
>> we
>> +     * want legacy ports (this is done by the AmigaOne firmware).
>> +     */
>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        /* BMIBA: 20-23h */
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +    }
>> +}
>
> Another hint that this is not the right way to be doing this: the values you 
> are placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 
> and bit 0 set to 1 which forces a minimum alignment of 4, so either the 
> addresses 0x3f6/0x376 are being rounded internally to 0x3f4/0x374 and/or 
> you're lucky that this just happens to work on QEMU.

The data sheet lists these values for legacy mode bur it seems that bit 1 
is ignored for BAR here and it ends up set to 0x3x4 with the actual reg 
mapped to 0x3x7 for both values ending in 4 or 6 here and both works the 
same with AmigaOS even if I change the values here to 0x3[7f]4 so I can do 
that and that should then match the default values for these regs but not 
match the values listed for legacy mode so the data sheet is wrong either 
way. It still does not make sense to set these in reset method which will 
be overwritten so only works if I set them here.

> Using the portio_list*() APIs really is the right way to implement this to 
> avoid being affected by such issues.

Can you provide an alternative patch using portio_list? I don't know how 
to do that and have no example to follow either so it would be hard for me 
to figure out. Or give some pointers on how to do this if I missed 
something.

Regards,
BALATON Zoltan

>>   static void via_ide_realize(PCIDevice *dev, Error **errp)
>>   {
>>       PCIIDEState *d = PCI_IDE(dev);
>> @@ -221,6 +251,7 @@ static void via_ide_class_init(ObjectClass *klass, void 
>> *data)
>>       /* Reason: only works as function of VIA southbridge */
>>       dc->user_creatable = false;
>>   +    k->config_write = via_ide_cfg_write;
>>       k->realize = via_ide_realize;
>>       k->exit = via_ide_exitfn;
>>       k->vendor_id = PCI_VENDOR_ID_VIA;
>
>
> ATB,
>
> Mark.
>
>
>
BALATON Zoltan Oct. 14, 2023, 7:43 p.m. UTC | #3
On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
> On 09/10/2023 20:54, BALATON Zoltan wrote:
>> The initial value for BARs were set in reset method for emulating
>> legacy mode at start but this does not work because PCI code resets
>> BARs after calling device reset method. Remove this ineffective
>> default to avoid confusion.
>> 
>> Instead move setting the BARs to a callback on writing the PCI config
>> regsiter that sets legacy mode (which firmwares needing this mode seem
>> to do) and fix their values to program it to use legacy port numbers
>> in this case. This does not fully emulate what the data sheet says
>> (which is not very clear on this) but it implements enogh to allow
>> both modes as used by firmwares of machines we emulate.
>> 
>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>> ---
>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>   1 file changed, 36 insertions(+), 5 deletions(-)
>> 
>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>> index fff23803a6..43e8af8d69 100644
>> --- a/hw/ide/via.c
>> +++ b/hw/ide/via.c
>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
>> 20-23h */
>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>   }
>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>> +                              uint32_t val, int len)
>> +{
>> +    pci_default_write_config(pd, addr, val, len);
>> +    /*
>> +     * Bits 0 and 2 of the PCI programming interface register select 
>> between
>> +     * legacy and native mode for the two IDE channels. We don't emulate 
>> this
>> +     * because we cannot easily switch between ISA and PCI in QEMU so 
>> instead
>
> As per my previous email, this statement is demonstrably false: this is now 
> achievable using the portio_list*() APIs.
>
>> +     * when guest selects legacy mode we set the PCI BARs to legacy ports 
>> which
>> +     * works the same. We also don't care about setting each channel 
>> separately
>> +     * as no guest is known to do or need that. We only do this when BARs 
>> are
>> +     * unset when writing this register as logs from real hardware show 
>> that
>> +     * setting legacy mode after BARs were set it will still use ports set 
>> by
>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware 
>> set
>> +     * native mode and programmed BARs and calls it non-100% native mode).
>> +     * But if 0x8a is written righr after reset without setting BARs then 
>> we
>> +     * want legacy ports (this is done by the AmigaOne firmware).
>> +     */
>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +        /* BMIBA: 20-23h */
>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>> +    }
>> +}
>
> Another hint that this is not the right way to be doing this: the values you 
> are placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 
> and bit 0 set to 1 which forces a minimum alignment of 4, so either the 
> addresses 0x3f6/0x376 are being rounded internally to 0x3f4/0x374 and/or 
> you're lucky that this just happens to work on QEMU.
>
> Using the portio_list*() APIs really is the right way to implement this to 
> avoid being affected by such issues.

On second thought I don't think that would work as pegaos2 Linux writes 
0x8a to prog_if but then keeps using the ports as set by BARs so if you 
use ISA ports in this case this will break. I think that proves that real 
chip also uses BARs to emulate legacy mode similar to this approach so 
what we have in this patch is close enough and works well. Your proposed 
alternative is more complex, would not work any better and probably even 
does not work for all cases this works for so I think this is the better 
way now. I've sent a v3 with values changed to match BAR default values 
with 0x3x4 and updating comment and commit message.

Regards,
BALATON Zoltan

>>   static void via_ide_realize(PCIDevice *dev, Error **errp)
>>   {
>>       PCIIDEState *d = PCI_IDE(dev);
>> @@ -221,6 +251,7 @@ static void via_ide_class_init(ObjectClass *klass, void 
>> *data)
>>       /* Reason: only works as function of VIA southbridge */
>>       dc->user_creatable = false;
>>   +    k->config_write = via_ide_cfg_write;
>>       k->realize = via_ide_realize;
>>       k->exit = via_ide_exitfn;
>>       k->vendor_id = PCI_VENDOR_ID_VIA;
>
>
> ATB,
>
> Mark.
>
>
>
Bernhard Beschow Oct. 15, 2023, 10:23 a.m. UTC | #4
Am 14. Oktober 2023 16:13:19 UTC schrieb BALATON Zoltan <balaton@eik.bme.hu>:
>On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>> On 09/10/2023 20:54, BALATON Zoltan wrote:
>> 
>>> The initial value for BARs were set in reset method for emulating
>>> legacy mode at start but this does not work because PCI code resets
>>> BARs after calling device reset method. Remove this ineffective
>>> default to avoid confusion.
>>> 
>>> Instead move setting the BARs to a callback on writing the PCI config
>>> regsiter that sets legacy mode (which firmwares needing this mode seem
>>> to do) and fix their values to program it to use legacy port numbers
>>> in this case. This does not fully emulate what the data sheet says
>>> (which is not very clear on this) but it implements enogh to allow
>>> both modes as used by firmwares of machines we emulate.
>>> 
>>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>>> ---
>>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>> 
>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>> index fff23803a6..43e8af8d69 100644
>>> --- a/hw/ide/via.c
>>> +++ b/hw/ide/via.c
>>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
>>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>>   }
>>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>> +                              uint32_t val, int len)
>>> +{
>>> +    pci_default_write_config(pd, addr, val, len);
>>> +    /*
>>> +     * Bits 0 and 2 of the PCI programming interface register select between
>>> +     * legacy and native mode for the two IDE channels. We don't emulate this
>>> +     * because we cannot easily switch between ISA and PCI in QEMU so instead
>> 
>> As per my previous email, this statement is demonstrably false: this is now achievable using the portio_list*() APIs.
>> 
>>> +     * when guest selects legacy mode we set the PCI BARs to legacy ports which
>>> +     * works the same. We also don't care about setting each channel separately
>>> +     * as no guest is known to do or need that. We only do this when BARs are
>>> +     * unset when writing this register as logs from real hardware show that
>>> +     * setting legacy mode after BARs were set it will still use ports set by
>>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware set
>>> +     * native mode and programmed BARs and calls it non-100% native mode).
>>> +     * But if 0x8a is written righr after reset without setting BARs then we
>>> +     * want legacy ports (this is done by the AmigaOne firmware).
>>> +     */
>>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        /* BMIBA: 20-23h */
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +    }
>>> +}
>> 
>> Another hint that this is not the right way to be doing this: the values you are placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 and bit 0 set to 1 which forces a minimum alignment of 4, so either the addresses 0x3f6/0x376 are being rounded internally to 0x3f4/0x374 and/or you're lucky that this just happens to work on QEMU.
>
>The data sheet lists these values for legacy mode bur it seems that bit 1 is ignored for BAR here and it ends up set to 0x3x4 with the actual reg mapped to 0x3x7 for both values ending in 4 or 6 here and both works the same with AmigaOS even if I change the values here to 0x3[7f]4 so I can do that and that should then match the default values for these regs but not match the values listed for legacy mode so the data sheet is wrong either way.

The datasheet lists the command BARs to be mapped to 0x3x4. The command registers (legacy 0x3x6) are mapped at offset 2 in those BARs. So mapping the BARs to 0x3x4 would map the registers to their respective legacy addresses, no?

> It still does not make sense to set these in reset method which will be overwritten so only works if I set them here.
>
>> Using the portio_list*() APIs really is the right way to implement this to avoid being affected by such issues.
>
>Can you provide an alternative patch using portio_list? I don't know how to do that and have no example to follow either so it would be hard for me to figure out. Or give some pointers on how to do this if I missed something.
>
>Regards,
>BALATON Zoltan
>
>>>   static void via_ide_realize(PCIDevice *dev, Error **errp)
>>>   {
>>>       PCIIDEState *d = PCI_IDE(dev);
>>> @@ -221,6 +251,7 @@ static void via_ide_class_init(ObjectClass *klass, void *data)
>>>       /* Reason: only works as function of VIA southbridge */
>>>       dc->user_creatable = false;
>>>   +    k->config_write = via_ide_cfg_write;
>>>       k->realize = via_ide_realize;
>>>       k->exit = via_ide_exitfn;
>>>       k->vendor_id = PCI_VENDOR_ID_VIA;
>> 
>> 
>> ATB,
>> 
>> Mark.
>> 
>> 
>>
Mark Cave-Ayland Oct. 15, 2023, 1:36 p.m. UTC | #5
On 14/10/2023 17:13, BALATON Zoltan wrote:

> On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>> On 09/10/2023 20:54, BALATON Zoltan wrote:
>>
>>> The initial value for BARs were set in reset method for emulating
>>> legacy mode at start but this does not work because PCI code resets
>>> BARs after calling device reset method. Remove this ineffective
>>> default to avoid confusion.
>>>
>>> Instead move setting the BARs to a callback on writing the PCI config
>>> regsiter that sets legacy mode (which firmwares needing this mode seem
>>> to do) and fix their values to program it to use legacy port numbers
>>> in this case. This does not fully emulate what the data sheet says
>>> (which is not very clear on this) but it implements enogh to allow
>>> both modes as used by firmwares of machines we emulate.
>>>
>>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>>> ---
>>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>> index fff23803a6..43e8af8d69 100644
>>> --- a/hw/ide/via.c
>>> +++ b/hw/ide/via.c
>>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
>>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>>   }
>>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>> +                              uint32_t val, int len)
>>> +{
>>> +    pci_default_write_config(pd, addr, val, len);
>>> +    /*
>>> +     * Bits 0 and 2 of the PCI programming interface register select between
>>> +     * legacy and native mode for the two IDE channels. We don't emulate this
>>> +     * because we cannot easily switch between ISA and PCI in QEMU so instead
>>
>> As per my previous email, this statement is demonstrably false: this is now 
>> achievable using the portio_list*() APIs.
>>
>>> +     * when guest selects legacy mode we set the PCI BARs to legacy ports which
>>> +     * works the same. We also don't care about setting each channel separately
>>> +     * as no guest is known to do or need that. We only do this when BARs are
>>> +     * unset when writing this register as logs from real hardware show that
>>> +     * setting legacy mode after BARs were set it will still use ports set by
>>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware set
>>> +     * native mode and programmed BARs and calls it non-100% native mode).
>>> +     * But if 0x8a is written righr after reset without setting BARs then we
>>> +     * want legacy ports (this is done by the AmigaOne firmware).
>>> +     */
>>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        /* BMIBA: 20-23h */
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +    }
>>> +}
>>
>> Another hint that this is not the right way to be doing this: the values you are 
>> placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 and bit 0 
>> set to 1 which forces a minimum alignment of 4, so either the addresses 0x3f6/0x376 
>> are being rounded internally to 0x3f4/0x374 and/or you're lucky that this just 
>> happens to work on QEMU.
> 
> The data sheet lists these values for legacy mode bur it seems that bit 1 is ignored 
> for BAR here and it ends up set to 0x3x4 with the actual reg mapped to 0x3x7 for both 
> values ending in 4 or 6 here and both works the same with AmigaOS even if I change 
> the values here to 0x3[7f]4 so I can do that and that should then match the default 
> values for these regs but not match the values listed for legacy mode so the data 
> sheet is wrong either way. It still does not make sense to set these in reset method 
> which will be overwritten so only works if I set them here.
> 
>> Using the portio_list*() APIs really is the right way to implement this to avoid 
>> being affected by such issues.
> 
> Can you provide an alternative patch using portio_list? I don't know how to do that 
> and have no example to follow either so it would be hard for me to figure out. Or 
> give some pointers on how to do this if I missed something.

Here's a hacked up version based upon various bits and pieces from my WIP IDE mode 
switching branch. It's briefly compile tested, and checked with "info mtree" and a 
couple of test boots:


diff --git a/hw/ide/via.c b/hw/ide/via.c
index fff23803a6..82f2af1c78 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -28,12 +28,27 @@
  #include "hw/pci/pci.h"
  #include "migration/vmstate.h"
  #include "qemu/module.h"
+#include "qemu/range.h"
  #include "sysemu/dma.h"
  #include "hw/isa/vt82c686.h"
  #include "hw/ide/pci.h"
  #include "hw/irq.h"
  #include "trace.h"

+
+/* FIXME: export these from hw/ide/ioport.c */
+static const MemoryRegionPortio ide_portio_list[] = {
+    { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
+    { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew },
+    { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel },
+    PORTIO_END_OF_LIST(),
+};
+
+static const MemoryRegionPortio ide_portio2_list[] = {
+    { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write },
+    PORTIO_END_OF_LIST(),
+};
+
  static uint64_t bmdma_read(void *opaque, hwaddr addr,
                             unsigned size)
  {
@@ -137,7 +152,10 @@ static void via_ide_reset(DeviceState *dev)
      pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
      pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
      pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
-    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
+    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000000e);
+
+    /* Clear subsystem to match real hardware */
+    pci_set_long(pci_conf + 0x2c, 0x0);

      /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
      pci_set_long(pci_conf + 0x40, 0x0a090600);
@@ -159,6 +177,89 @@ static void via_ide_reset(DeviceState *dev)
      pci_set_long(pci_conf + 0xc0, 0x00020001);
  }

+static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
+                              uint32_t val, int len)
+{
+    uint8_t *pci_conf = pd->config;
+    PCIIDEState *d = PCI_IDE(pd);
+
+    pci_default_write_config(pd, addr, val, len);
+
+    if (range_covers_byte(addr, len, PCI_CLASS_PROG)) {
+        if (pci_conf[PCI_CLASS_PROG] == 0x8a) {
+            /* FIXME: don't disable BARs
+            pci_default_write_config(pd, PCI_BASE_ADDRESS_0, 0x1, 4);
+            pci_default_write_config(pd, PCI_BASE_ADDRESS_1, 0x1, 4);
+            pci_default_write_config(pd, PCI_BASE_ADDRESS_2, 0x1, 4);
+            pci_default_write_config(pd, PCI_BASE_ADDRESS_3, 0x1, 4);
+            */
+
+            pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x0);
+            pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x0);
+            pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x0);
+            pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x0);
+
+            /* Clear interrupt pin */
+            pci_config_set_interrupt_pin(pci_conf, 0);
+
+            /* Add legacy IDE ports */
+            if (!d->bus[0].portio_list.owner) {
+                portio_list_init(&d->bus[0].portio_list, OBJECT(pd),
+                                 ide_portio_list, &d->bus[0], "ide");
+                portio_list_add(&d->bus[0].portio_list,
+                                pci_address_space_io(pd), 0x1f0);
+            }
+
+            if (!d->bus[0].portio2_list.owner) {
+                portio_list_init(&d->bus[0].portio2_list, OBJECT(pd),
+                                 ide_portio2_list, &d->bus[0], "ide");
+                portio_list_add(&d->bus[0].portio2_list,
+                                pci_address_space_io(pd), 0x3f6);
+            }
+
+            if (!d->bus[1].portio_list.owner) {
+                portio_list_init(&d->bus[1].portio_list, OBJECT(pd),
+                                 ide_portio_list, &d->bus[1], "ide");
+                portio_list_add(&d->bus[1].portio_list,
+                                pci_address_space_io(pd), 0x170);
+            }
+
+            if (!d->bus[1].portio2_list.owner) {
+                portio_list_init(&d->bus[1].portio2_list, OBJECT(pd),
+                                 ide_portio2_list, &d->bus[1], "ide");
+                portio_list_add(&d->bus[1].portio2_list,
+                                pci_address_space_io(pd), 0x376);
+            }
+        }
+
+        if (pci_conf[PCI_CLASS_PROG] == 0x8f) {
+            /* Set interrupt pin */
+            pci_config_set_interrupt_pin(pci_conf, 1);
+
+            /* Remove legacy IDE ports */
+            if (d->bus[0].portio_list.owner) {
+                portio_list_del(&d->bus[0].portio_list);
+                portio_list_destroy(&d->bus[0].portio_list);
+            }
+
+            if (d->bus[0].portio2_list.owner) {
+                portio_list_del(&d->bus[0].portio2_list);
+                portio_list_destroy(&d->bus[0].portio2_list);
+            }
+
+            if (d->bus[1].portio_list.owner) {
+                portio_list_del(&d->bus[1].portio_list);
+                portio_list_destroy(&d->bus[1].portio_list);
+            }
+
+            if (d->bus[1].portio2_list.owner) {
+                portio_list_del(&d->bus[1].portio2_list);
+                portio_list_destroy(&d->bus[1].portio2_list);
+            }
+        }
+    }
+}
+
  static void via_ide_realize(PCIDevice *dev, Error **errp)
  {
      PCIIDEState *d = PCI_IDE(dev);
@@ -221,6 +322,7 @@ static void via_ide_class_init(ObjectClass *klass, void *data)
      /* Reason: only works as function of VIA southbridge */
      dc->user_creatable = false;

+    k->config_write = via_ide_cfg_write;
      k->realize = via_ide_realize;
      k->exit = via_ide_exitfn;
      k->vendor_id = PCI_VENDOR_ID_VIA;


Note that this also fixes the output of "lspci -vv" on Linux:

0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a [Master 
SecP PriP])
         Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
Stepping+ SERR- FastB2B- DisINTx-
         Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- 
<MAbort- >SERR- <PERR- INTx-
         Latency: 0
         Interrupt: pin ? routed to IRQ 14
         Region 0: [virtual] I/O ports at 1000 [size=8]
         Region 1: [virtual] I/O ports at 100c [size=4]
         Region 2: [virtual] I/O ports at 1010 [size=8]
         Region 3: [virtual] I/O ports at 101c [size=4]
         Region 4: I/O ports at 1020 [size=16]
         Kernel driver in use: pata_via


Currently the "[virtual]" prefix is missing in QEMU when compared with your lspci 
output from real hardware: this patch fixes it, because it allows the legacy IDE 
ioports to exist whilst having the BARs set to zero which isn't possible with your 
current patch.


ATB,

Mark.
Mark Cave-Ayland Oct. 15, 2023, 1:43 p.m. UTC | #6
On 14/10/2023 20:43, BALATON Zoltan wrote:

> On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>> On 09/10/2023 20:54, BALATON Zoltan wrote:
>>> The initial value for BARs were set in reset method for emulating
>>> legacy mode at start but this does not work because PCI code resets
>>> BARs after calling device reset method. Remove this ineffective
>>> default to avoid confusion.
>>>
>>> Instead move setting the BARs to a callback on writing the PCI config
>>> regsiter that sets legacy mode (which firmwares needing this mode seem
>>> to do) and fix their values to program it to use legacy port numbers
>>> in this case. This does not fully emulate what the data sheet says
>>> (which is not very clear on this) but it implements enogh to allow
>>> both modes as used by firmwares of machines we emulate.
>>>
>>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>>> ---
>>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>> index fff23803a6..43e8af8d69 100644
>>> --- a/hw/ide/via.c
>>> +++ b/hw/ide/via.c
>>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
>>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>>   }
>>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>> +                              uint32_t val, int len)
>>> +{
>>> +    pci_default_write_config(pd, addr, val, len);
>>> +    /*
>>> +     * Bits 0 and 2 of the PCI programming interface register select between
>>> +     * legacy and native mode for the two IDE channels. We don't emulate this
>>> +     * because we cannot easily switch between ISA and PCI in QEMU so instead
>>
>> As per my previous email, this statement is demonstrably false: this is now 
>> achievable using the portio_list*() APIs.
>>
>>> +     * when guest selects legacy mode we set the PCI BARs to legacy ports which
>>> +     * works the same. We also don't care about setting each channel separately
>>> +     * as no guest is known to do or need that. We only do this when BARs are
>>> +     * unset when writing this register as logs from real hardware show that
>>> +     * setting legacy mode after BARs were set it will still use ports set by
>>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware set
>>> +     * native mode and programmed BARs and calls it non-100% native mode).
>>> +     * But if 0x8a is written righr after reset without setting BARs then we
>>> +     * want legacy ports (this is done by the AmigaOne firmware).
>>> +     */
>>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +        /* BMIBA: 20-23h */
>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>> +    }
>>> +}
>>
>> Another hint that this is not the right way to be doing this: the values you are 
>> placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 and bit 0 
>> set to 1 which forces a minimum alignment of 4, so either the addresses 0x3f6/0x376 
>> are being rounded internally to 0x3f4/0x374 and/or you're lucky that this just 
>> happens to work on QEMU.
>>
>> Using the portio_list*() APIs really is the right way to implement this to avoid 
>> being affected by such issues.
> 
> On second thought I don't think that would work as pegaos2 Linux writes 0x8a to 
> prog_if but then keeps using the ports as set by BARs so if you use ISA ports in this 
> case this will break. I think that proves that real chip also uses BARs to emulate 
> legacy mode similar to this approach so what we have in this patch is close enough 
> and works well. Your proposed alternative is more complex, would not work any better 
> and probably even does not work for all cases this works for so I think this is the 
> better way now. I've sent a v3 with values changed to match BAR default values with 
> 0x3x4 and updating comment and commit message.

I've posted a hacked up (hopefully working) example from my IDE switching branch to 
demonstrate the concept, plus also confirms that BARs aren't being used on real 
hardware because they cannot be set to zero whilst legacy mode is enabled.

Another point to bear in mind is that with Bernhard's series and few extra patches 
from my series, using this approach it becomes possible to move all of this logic 
from the VIA device (and indeed all PCI IDE controllers, including the common BMDMA 
code) into PCIIDEState so the final result will end up much simpler. Obviously there 
is a need to allow behaviourable quirks for things such as VIA, but that can be 
solved easily enough with properties.


ATB,

Mark.
BALATON Zoltan Oct. 15, 2023, 2:23 p.m. UTC | #7
On Sun, 15 Oct 2023, Mark Cave-Ayland wrote:
> On 14/10/2023 17:13, BALATON Zoltan wrote:
>> On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>>> On 09/10/2023 20:54, BALATON Zoltan wrote:
>>> 
>>>> The initial value for BARs were set in reset method for emulating
>>>> legacy mode at start but this does not work because PCI code resets
>>>> BARs after calling device reset method. Remove this ineffective
>>>> default to avoid confusion.
>>>> 
>>>> Instead move setting the BARs to a callback on writing the PCI config
>>>> regsiter that sets legacy mode (which firmwares needing this mode seem
>>>> to do) and fix their values to program it to use legacy port numbers
>>>> in this case. This does not fully emulate what the data sheet says
>>>> (which is not very clear on this) but it implements enogh to allow
>>>> both modes as used by firmwares of machines we emulate.
>>>> 
>>>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>>>> ---
>>>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>>> 
>>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>>> index fff23803a6..43e8af8d69 100644
>>>> --- a/hw/ide/via.c
>>>> +++ b/hw/ide/via.c
>>>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
>>>> 20-23h */
>>>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO 
>>>> Configuration*/
>>>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>>>   }
>>>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>>> +                              uint32_t val, int len)
>>>> +{
>>>> +    pci_default_write_config(pd, addr, val, len);
>>>> +    /*
>>>> +     * Bits 0 and 2 of the PCI programming interface register select 
>>>> between
>>>> +     * legacy and native mode for the two IDE channels. We don't emulate 
>>>> this
>>>> +     * because we cannot easily switch between ISA and PCI in QEMU so 
>>>> instead
>>> 
>>> As per my previous email, this statement is demonstrably false: this is 
>>> now achievable using the portio_list*() APIs.
>>> 
>>>> +     * when guest selects legacy mode we set the PCI BARs to legacy 
>>>> ports which
>>>> +     * works the same. We also don't care about setting each channel 
>>>> separately
>>>> +     * as no guest is known to do or need that. We only do this when 
>>>> BARs are
>>>> +     * unset when writing this register as logs from real hardware show 
>>>> that
>>>> +     * setting legacy mode after BARs were set it will still use ports 
>>>> set by
>>>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware 
>>>> set
>>>> +     * native mode and programmed BARs and calls it non-100% native 
>>>> mode).
>>>> +     * But if 0x8a is written righr after reset without setting BARs 
>>>> then we
>>>> +     * want legacy ports (this is done by the AmigaOne firmware).
>>>> +     */
>>>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>>>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>>>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        /* BMIBA: 20-23h */
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +    }
>>>> +}
>>> 
>>> Another hint that this is not the right way to be doing this: the values 
>>> you are placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced 
>>> to 0 and bit 0 set to 1 which forces a minimum alignment of 4, so either 
>>> the addresses 0x3f6/0x376 are being rounded internally to 0x3f4/0x374 
>>> and/or you're lucky that this just happens to work on QEMU.
>> 
>> The data sheet lists these values for legacy mode bur it seems that bit 1 
>> is ignored for BAR here and it ends up set to 0x3x4 with the actual reg 
>> mapped to 0x3x7 for both values ending in 4 or 6 here and both works the 
>> same with AmigaOS even if I change the values here to 0x3[7f]4 so I can do 
>> that and that should then match the default values for these regs but not 
>> match the values listed for legacy mode so the data sheet is wrong either 
>> way. It still does not make sense to set these in reset method which will 
>> be overwritten so only works if I set them here.
>> 
>>> Using the portio_list*() APIs really is the right way to implement this to 
>>> avoid being affected by such issues.
>> 
>> Can you provide an alternative patch using portio_list? I don't know how to 
>> do that and have no example to follow either so it would be hard for me to 
>> figure out. Or give some pointers on how to do this if I missed something.
>
> Here's a hacked up version based upon various bits and pieces from my WIP IDE 
> mode switching branch. It's briefly compile tested, and checked with "info 
> mtree" and a couple of test boots:

Thanks. Could you please make it a proper patch so we can test this and if 
works with all guests could use as a replacement for patch 1 in the 
series? It probably just needs a commit message and your S-o-b without 
which I can't incorporate it in the series.

Regards,
BALATON Zoltan

> diff --git a/hw/ide/via.c b/hw/ide/via.c
> index fff23803a6..82f2af1c78 100644
> --- a/hw/ide/via.c
> +++ b/hw/ide/via.c
> @@ -28,12 +28,27 @@
> #include "hw/pci/pci.h"
> #include "migration/vmstate.h"
> #include "qemu/module.h"
> +#include "qemu/range.h"
> #include "sysemu/dma.h"
> #include "hw/isa/vt82c686.h"
> #include "hw/ide/pci.h"
> #include "hw/irq.h"
> #include "trace.h"
>
> +
> +/* FIXME: export these from hw/ide/ioport.c */
> +static const MemoryRegionPortio ide_portio_list[] = {
> +    { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
> +    { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew },
> +    { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel },
> +    PORTIO_END_OF_LIST(),
> +};
> +
> +static const MemoryRegionPortio ide_portio2_list[] = {
> +    { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write },
> +    PORTIO_END_OF_LIST(),
> +};
> +
> static uint64_t bmdma_read(void *opaque, hwaddr addr,
>                            unsigned size)
> {
> @@ -137,7 +152,10 @@ static void via_ide_reset(DeviceState *dev)
>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
> 20-23h */
> -    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
> +    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000000e);
> +
> +    /* Clear subsystem to match real hardware */
> +    pci_set_long(pci_conf + 0x2c, 0x0);
>
>     /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>     pci_set_long(pci_conf + 0x40, 0x0a090600);
> @@ -159,6 +177,89 @@ static void via_ide_reset(DeviceState *dev)
>     pci_set_long(pci_conf + 0xc0, 0x00020001);
> }
>
> +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
> +                              uint32_t val, int len)
> +{
> +    uint8_t *pci_conf = pd->config;
> +    PCIIDEState *d = PCI_IDE(pd);
> +
> +    pci_default_write_config(pd, addr, val, len);
> +
> +    if (range_covers_byte(addr, len, PCI_CLASS_PROG)) {
> +        if (pci_conf[PCI_CLASS_PROG] == 0x8a) {
> +            /* FIXME: don't disable BARs
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_0, 0x1, 4);
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_1, 0x1, 4);
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_2, 0x1, 4);
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_3, 0x1, 4);
> +            */
> +
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x0);
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x0);
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x0);
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x0);
> +
> +            /* Clear interrupt pin */
> +            pci_config_set_interrupt_pin(pci_conf, 0);
> +
> +            /* Add legacy IDE ports */
> +            if (!d->bus[0].portio_list.owner) {
> +                portio_list_init(&d->bus[0].portio_list, OBJECT(pd),
> +                                 ide_portio_list, &d->bus[0], "ide");
> +                portio_list_add(&d->bus[0].portio_list,
> +                                pci_address_space_io(pd), 0x1f0);
> +            }
> +
> +            if (!d->bus[0].portio2_list.owner) {
> +                portio_list_init(&d->bus[0].portio2_list, OBJECT(pd),
> +                                 ide_portio2_list, &d->bus[0], "ide");
> +                portio_list_add(&d->bus[0].portio2_list,
> +                                pci_address_space_io(pd), 0x3f6);
> +            }
> +
> +            if (!d->bus[1].portio_list.owner) {
> +                portio_list_init(&d->bus[1].portio_list, OBJECT(pd),
> +                                 ide_portio_list, &d->bus[1], "ide");
> +                portio_list_add(&d->bus[1].portio_list,
> +                                pci_address_space_io(pd), 0x170);
> +            }
> +
> +            if (!d->bus[1].portio2_list.owner) {
> +                portio_list_init(&d->bus[1].portio2_list, OBJECT(pd),
> +                                 ide_portio2_list, &d->bus[1], "ide");
> +                portio_list_add(&d->bus[1].portio2_list,
> +                                pci_address_space_io(pd), 0x376);
> +            }
> +        }
> +
> +        if (pci_conf[PCI_CLASS_PROG] == 0x8f) {
> +            /* Set interrupt pin */
> +            pci_config_set_interrupt_pin(pci_conf, 1);
> +
> +            /* Remove legacy IDE ports */
> +            if (d->bus[0].portio_list.owner) {
> +                portio_list_del(&d->bus[0].portio_list);
> +                portio_list_destroy(&d->bus[0].portio_list);
> +            }
> +
> +            if (d->bus[0].portio2_list.owner) {
> +                portio_list_del(&d->bus[0].portio2_list);
> +                portio_list_destroy(&d->bus[0].portio2_list);
> +            }
> +
> +            if (d->bus[1].portio_list.owner) {
> +                portio_list_del(&d->bus[1].portio_list);
> +                portio_list_destroy(&d->bus[1].portio_list);
> +            }
> +
> +            if (d->bus[1].portio2_list.owner) {
> +                portio_list_del(&d->bus[1].portio2_list);
> +                portio_list_destroy(&d->bus[1].portio2_list);
> +            }
> +        }
> +    }
> +}
> +
> static void via_ide_realize(PCIDevice *dev, Error **errp)
> {
>     PCIIDEState *d = PCI_IDE(dev);
> @@ -221,6 +322,7 @@ static void via_ide_class_init(ObjectClass *klass, void 
> *data)
>     /* Reason: only works as function of VIA southbridge */
>     dc->user_creatable = false;
>
> +    k->config_write = via_ide_cfg_write;
>     k->realize = via_ide_realize;
>     k->exit = via_ide_exitfn;
>     k->vendor_id = PCI_VENDOR_ID_VIA;
>
>
> Note that this also fixes the output of "lspci -vv" on Linux:
>
> 0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
> VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a 
> [Master SecP PriP])
>        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
> Stepping+ SERR- FastB2B- DisINTx-
>        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- 
> <TAbort- <MAbort- >SERR- <PERR- INTx-
>        Latency: 0
>        Interrupt: pin ? routed to IRQ 14
>        Region 0: [virtual] I/O ports at 1000 [size=8]
>        Region 1: [virtual] I/O ports at 100c [size=4]
>        Region 2: [virtual] I/O ports at 1010 [size=8]
>        Region 3: [virtual] I/O ports at 101c [size=4]
>        Region 4: I/O ports at 1020 [size=16]
>        Kernel driver in use: pata_via
>
>
> Currently the "[virtual]" prefix is missing in QEMU when compared with your 
> lspci output from real hardware: this patch fixes it, because it allows the 
> legacy IDE ioports to exist whilst having the BARs set to zero which isn't 
> possible with your current patch.
>
>
> ATB,
>
> Mark.
>
>
>
BALATON Zoltan Oct. 16, 2023, 10:16 p.m. UTC | #8
On Sun, 15 Oct 2023, Mark Cave-Ayland wrote:
> On 14/10/2023 17:13, BALATON Zoltan wrote:
>> On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>>> On 09/10/2023 20:54, BALATON Zoltan wrote:
>>>> The initial value for BARs were set in reset method for emulating
>>>> legacy mode at start but this does not work because PCI code resets
>>>> BARs after calling device reset method. Remove this ineffective
>>>> default to avoid confusion.
>>>> 
>>>> Instead move setting the BARs to a callback on writing the PCI config
>>>> regsiter that sets legacy mode (which firmwares needing this mode seem
>>>> to do) and fix their values to program it to use legacy port numbers
>>>> in this case. This does not fully emulate what the data sheet says
>>>> (which is not very clear on this) but it implements enogh to allow
>>>> both modes as used by firmwares of machines we emulate.
>>>> 
>>>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>>>> ---
>>>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>>> 
>>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>>> index fff23803a6..43e8af8d69 100644
>>>> --- a/hw/ide/via.c
>>>> +++ b/hw/ide/via.c
>>>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
>>>> 20-23h */
>>>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO 
>>>> Configuration*/
>>>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>>>   }
>>>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>>> +                              uint32_t val, int len)
>>>> +{
>>>> +    pci_default_write_config(pd, addr, val, len);
>>>> +    /*
>>>> +     * Bits 0 and 2 of the PCI programming interface register select 
>>>> between
>>>> +     * legacy and native mode for the two IDE channels. We don't emulate 
>>>> this
>>>> +     * because we cannot easily switch between ISA and PCI in QEMU so 
>>>> instead
>>> 
>>> As per my previous email, this statement is demonstrably false: this is 
>>> now achievable using the portio_list*() APIs.
>>> 
>>>> +     * when guest selects legacy mode we set the PCI BARs to legacy 
>>>> ports which
>>>> +     * works the same. We also don't care about setting each channel 
>>>> separately
>>>> +     * as no guest is known to do or need that. We only do this when 
>>>> BARs are
>>>> +     * unset when writing this register as logs from real hardware show 
>>>> that
>>>> +     * setting legacy mode after BARs were set it will still use ports 
>>>> set by
>>>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware 
>>>> set
>>>> +     * native mode and programmed BARs and calls it non-100% native 
>>>> mode).
>>>> +     * But if 0x8a is written righr after reset without setting BARs 
>>>> then we
>>>> +     * want legacy ports (this is done by the AmigaOne firmware).
>>>> +     */
>>>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>>>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>>>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +        /* BMIBA: 20-23h */
>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>> +    }
>>>> +}
>>> 
>>> Another hint that this is not the right way to be doing this: the values 
>>> you are placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced 
>>> to 0 and bit 0 set to 1 which forces a minimum alignment of 4, so either 
>>> the addresses 0x3f6/0x376 are being rounded internally to 0x3f4/0x374 
>>> and/or you're lucky that this just happens to work on QEMU.
>> 
>> The data sheet lists these values for legacy mode bur it seems that bit 1 
>> is ignored for BAR here and it ends up set to 0x3x4 with the actual reg 
>> mapped to 0x3x7 for both values ending in 4 or 6 here and both works the 
>> same with AmigaOS even if I change the values here to 0x3[7f]4 so I can do 
>> that and that should then match the default values for these regs but not 
>> match the values listed for legacy mode so the data sheet is wrong either 
>> way. It still does not make sense to set these in reset method which will 
>> be overwritten so only works if I set them here.
>> 
>>> Using the portio_list*() APIs really is the right way to implement this to 
>>> avoid being affected by such issues.
>> 
>> Can you provide an alternative patch using portio_list? I don't know how to 
>> do that and have no example to follow either so it would be hard for me to 
>> figure out. Or give some pointers on how to do this if I missed something.
>
> Here's a hacked up version based upon various bits and pieces from my WIP IDE 
> mode switching branch. It's briefly compile tested, and checked with "info 
> mtree" and a couple of test boots:


I gave this a try and while it works with the guests I've tried I'm still 
not convinced. See comments below.


> diff --git a/hw/ide/via.c b/hw/ide/via.c
> index fff23803a6..82f2af1c78 100644
> --- a/hw/ide/via.c
> +++ b/hw/ide/via.c
> @@ -28,12 +28,27 @@
> #include "hw/pci/pci.h"
> #include "migration/vmstate.h"
> #include "qemu/module.h"
> +#include "qemu/range.h"
> #include "sysemu/dma.h"
> #include "hw/isa/vt82c686.h"
> #include "hw/ide/pci.h"
> #include "hw/irq.h"
> #include "trace.h"
>
> +
> +/* FIXME: export these from hw/ide/ioport.c */
> +static const MemoryRegionPortio ide_portio_list[] = {
> +    { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
> +    { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew },
> +    { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel },
> +    PORTIO_END_OF_LIST(),
> +};
> +
> +static const MemoryRegionPortio ide_portio2_list[] = {
> +    { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write },
> +    PORTIO_END_OF_LIST(),
> +};
> +
> static uint64_t bmdma_read(void *opaque, hwaddr addr,
>                            unsigned size)
> {
> @@ -137,7 +152,10 @@ static void via_ide_reset(DeviceState *dev)
>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
> 20-23h */
> -    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
> +    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000000e);

The vt8231 data sheet says the interrupt pin should always be 1 while 
according to the vt82c686b data sheet this means legacy or native 
interrupt routing and defaults to 0. We probably don't do anything with it 
so no matter what we have here and we can change it to 0 but maybe there's 
someting off here in any case.

> +
> +    /* Clear subsystem to match real hardware */
> +    pci_set_long(pci_conf + 0x2c, 0x0);
>
>     /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>     pci_set_long(pci_conf + 0x40, 0x0a090600);
> @@ -159,6 +177,89 @@ static void via_ide_reset(DeviceState *dev)
>     pci_set_long(pci_conf + 0xc0, 0x00020001);
> }
>
> +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
> +                              uint32_t val, int len)
> +{
> +    uint8_t *pci_conf = pd->config;
> +    PCIIDEState *d = PCI_IDE(pd);
> +
> +    pci_default_write_config(pd, addr, val, len);
> +
> +    if (range_covers_byte(addr, len, PCI_CLASS_PROG)) {
> +        if (pci_conf[PCI_CLASS_PROG] == 0x8a) {
> +            /* FIXME: don't disable BARs
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_0, 0x1, 4);
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_1, 0x1, 4);
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_2, 0x1, 4);
> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_3, 0x1, 4);
> +            */
> +
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x0);
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x0);
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x0);
> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x0);

This is the part why I think this is not ready for merge yet. It seems to 
leave BARs enabled but 0 their regs so now we have ide ports *both* at the 
previous BAR values and at legacy ports while BAR values are 0. This does 
not look right and seems much more hacky than my patch that changes BARs 
to legacy ports to emulate legacy mode. You probably need this becuase of 
what Linux does on pegasos2 which I think is proving real chip is also 
using BARs as my patch.

Therefore I think we should go with my patch for now to not hold up adding 
amigaone machine and allow progress with it and then when you have more 
time to elaborate this idea of using portio_list to remove the FIXME 
comments from this PoC patch we can revisit this any time later. There's 
no reason to hold other's work back to get this sorted out now and solving 
a problem with my patch now does not mean we cannot improve this device 
model further in the future. So if you can't make this a clean patch now 
that can replace my patch please let my patch accepted until you can make 
this a viable alternative. For now this just seems like an idea that needs 
more work but not ready yet.

Regards,
BALATON Zoltan

> +
> +            /* Clear interrupt pin */
> +            pci_config_set_interrupt_pin(pci_conf, 0);
> +
> +            /* Add legacy IDE ports */
> +            if (!d->bus[0].portio_list.owner) {
> +                portio_list_init(&d->bus[0].portio_list, OBJECT(pd),
> +                                 ide_portio_list, &d->bus[0], "ide");
> +                portio_list_add(&d->bus[0].portio_list,
> +                                pci_address_space_io(pd), 0x1f0);
> +            }
> +
> +            if (!d->bus[0].portio2_list.owner) {
> +                portio_list_init(&d->bus[0].portio2_list, OBJECT(pd),
> +                                 ide_portio2_list, &d->bus[0], "ide");
> +                portio_list_add(&d->bus[0].portio2_list,
> +                                pci_address_space_io(pd), 0x3f6);
> +            }
> +
> +            if (!d->bus[1].portio_list.owner) {
> +                portio_list_init(&d->bus[1].portio_list, OBJECT(pd),
> +                                 ide_portio_list, &d->bus[1], "ide");
> +                portio_list_add(&d->bus[1].portio_list,
> +                                pci_address_space_io(pd), 0x170);
> +            }
> +
> +            if (!d->bus[1].portio2_list.owner) {
> +                portio_list_init(&d->bus[1].portio2_list, OBJECT(pd),
> +                                 ide_portio2_list, &d->bus[1], "ide");
> +                portio_list_add(&d->bus[1].portio2_list,
> +                                pci_address_space_io(pd), 0x376);
> +            }
> +        }
> +
> +        if (pci_conf[PCI_CLASS_PROG] == 0x8f) {
> +            /* Set interrupt pin */
> +            pci_config_set_interrupt_pin(pci_conf, 1);
> +
> +            /* Remove legacy IDE ports */
> +            if (d->bus[0].portio_list.owner) {
> +                portio_list_del(&d->bus[0].portio_list);
> +                portio_list_destroy(&d->bus[0].portio_list);
> +            }
> +
> +            if (d->bus[0].portio2_list.owner) {
> +                portio_list_del(&d->bus[0].portio2_list);
> +                portio_list_destroy(&d->bus[0].portio2_list);
> +            }
> +
> +            if (d->bus[1].portio_list.owner) {
> +                portio_list_del(&d->bus[1].portio_list);
> +                portio_list_destroy(&d->bus[1].portio_list);
> +            }
> +
> +            if (d->bus[1].portio2_list.owner) {
> +                portio_list_del(&d->bus[1].portio2_list);
> +                portio_list_destroy(&d->bus[1].portio2_list);
> +            }
> +        }
> +    }
> +}
> +
> static void via_ide_realize(PCIDevice *dev, Error **errp)
> {
>     PCIIDEState *d = PCI_IDE(dev);
> @@ -221,6 +322,7 @@ static void via_ide_class_init(ObjectClass *klass, void 
> *data)
>     /* Reason: only works as function of VIA southbridge */
>     dc->user_creatable = false;
>
> +    k->config_write = via_ide_cfg_write;
>     k->realize = via_ide_realize;
>     k->exit = via_ide_exitfn;
>     k->vendor_id = PCI_VENDOR_ID_VIA;
>
>
> Note that this also fixes the output of "lspci -vv" on Linux:
>
> 0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
> VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a 
> [Master SecP PriP])
>        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
> Stepping+ SERR- FastB2B- DisINTx-
>        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- 
> <TAbort- <MAbort- >SERR- <PERR- INTx-
>        Latency: 0
>        Interrupt: pin ? routed to IRQ 14
>        Region 0: [virtual] I/O ports at 1000 [size=8]
>        Region 1: [virtual] I/O ports at 100c [size=4]
>        Region 2: [virtual] I/O ports at 1010 [size=8]
>        Region 3: [virtual] I/O ports at 101c [size=4]
>        Region 4: I/O ports at 1020 [size=16]
>        Kernel driver in use: pata_via
>
>
> Currently the "[virtual]" prefix is missing in QEMU when compared with your 
> lspci output from real hardware: this patch fixes it, because it allows the 
> legacy IDE ioports to exist whilst having the BARs set to zero which isn't 
> possible with your current patch.
>
>
> ATB,
>
> Mark.
>
>
>
Mark Cave-Ayland Oct. 17, 2023, 7:56 p.m. UTC | #9
On 16/10/2023 23:16, BALATON Zoltan wrote:

> On Sun, 15 Oct 2023, Mark Cave-Ayland wrote:
>> On 14/10/2023 17:13, BALATON Zoltan wrote:
>>> On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>>>> On 09/10/2023 20:54, BALATON Zoltan wrote:
>>>>> The initial value for BARs were set in reset method for emulating
>>>>> legacy mode at start but this does not work because PCI code resets
>>>>> BARs after calling device reset method. Remove this ineffective
>>>>> default to avoid confusion.
>>>>>
>>>>> Instead move setting the BARs to a callback on writing the PCI config
>>>>> regsiter that sets legacy mode (which firmwares needing this mode seem
>>>>> to do) and fix their values to program it to use legacy port numbers
>>>>> in this case. This does not fully emulate what the data sheet says
>>>>> (which is not very clear on this) but it implements enogh to allow
>>>>> both modes as used by firmwares of machines we emulate.
>>>>>
>>>>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>>>>> ---
>>>>>   hw/ide/via.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>>>>
>>>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>>>> index fff23803a6..43e8af8d69 100644
>>>>> --- a/hw/ide/via.c
>>>>> +++ b/hw/ide/via.c
>>>>> @@ -132,11 +132,6 @@ static void via_ide_reset(DeviceState *dev)
>>>>>       pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
>>>>>                    PCI_STATUS_DEVSEL_MEDIUM);
>>>>>   -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
>>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
>>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>>>> -    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
>>>>>       pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>>>>         /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>>>>> @@ -159,6 +154,41 @@ static void via_ide_reset(DeviceState *dev)
>>>>>       pci_set_long(pci_conf + 0xc0, 0x00020001);
>>>>>   }
>>>>>   +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>>>> +                              uint32_t val, int len)
>>>>> +{
>>>>> +    pci_default_write_config(pd, addr, val, len);
>>>>> +    /*
>>>>> +     * Bits 0 and 2 of the PCI programming interface register select between
>>>>> +     * legacy and native mode for the two IDE channels. We don't emulate this
>>>>> +     * because we cannot easily switch between ISA and PCI in QEMU so instead
>>>>
>>>> As per my previous email, this statement is demonstrably false: this is now 
>>>> achievable using the portio_list*() APIs.
>>>>
>>>>> +     * when guest selects legacy mode we set the PCI BARs to legacy ports which
>>>>> +     * works the same. We also don't care about setting each channel separately
>>>>> +     * as no guest is known to do or need that. We only do this when BARs are
>>>>> +     * unset when writing this register as logs from real hardware show that
>>>>> +     * setting legacy mode after BARs were set it will still use ports set by
>>>>> +     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware set
>>>>> +     * native mode and programmed BARs and calls it non-100% native mode).
>>>>> +     * But if 0x8a is written righr after reset without setting BARs then we
>>>>> +     * want legacy ports (this is done by the AmigaOne firmware).
>>>>> +     */
>>>>> +    if (addr == PCI_CLASS_PROG && val == 0x8a &&
>>>>> +        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
>>>>> +        PCI_BASE_ADDRESS_SPACE_IO) {
>>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
>>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
>>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
>>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
>>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>>> +        /* BMIBA: 20-23h */
>>>>> +        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
>>>>> +                     | PCI_BASE_ADDRESS_SPACE_IO);
>>>>> +    }
>>>>> +}
>>>>
>>>> Another hint that this is not the right way to be doing this: the values you are 
>>>> placing in BARS 1 and 3 are illegal. PCI IO BARs have bit 1 forced to 0 and bit 0 
>>>> set to 1 which forces a minimum alignment of 4, so either the addresses 
>>>> 0x3f6/0x376 are being rounded internally to 0x3f4/0x374 and/or you're lucky that 
>>>> this just happens to work on QEMU.
>>>
>>> The data sheet lists these values for legacy mode bur it seems that bit 1 is 
>>> ignored for BAR here and it ends up set to 0x3x4 with the actual reg mapped to 
>>> 0x3x7 for both values ending in 4 or 6 here and both works the same with AmigaOS 
>>> even if I change the values here to 0x3[7f]4 so I can do that and that should then 
>>> match the default values for these regs but not match the values listed for legacy 
>>> mode so the data sheet is wrong either way. It still does not make sense to set 
>>> these in reset method which will be overwritten so only works if I set them here.
>>>
>>>> Using the portio_list*() APIs really is the right way to implement this to avoid 
>>>> being affected by such issues.
>>>
>>> Can you provide an alternative patch using portio_list? I don't know how to do 
>>> that and have no example to follow either so it would be hard for me to figure 
>>> out. Or give some pointers on how to do this if I missed something.
>>
>> Here's a hacked up version based upon various bits and pieces from my WIP IDE mode 
>> switching branch. It's briefly compile tested, and checked with "info mtree" and a 
>> couple of test boots:
> 
> 
> I gave this a try and while it works with the guests I've tried I'm still not 
> convinced. See comments below.

Okay, that's good news and proves the basic concept works as expected.

>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>> index fff23803a6..82f2af1c78 100644
>> --- a/hw/ide/via.c
>> +++ b/hw/ide/via.c
>> @@ -28,12 +28,27 @@
>> #include "hw/pci/pci.h"
>> #include "migration/vmstate.h"
>> #include "qemu/module.h"
>> +#include "qemu/range.h"
>> #include "sysemu/dma.h"
>> #include "hw/isa/vt82c686.h"
>> #include "hw/ide/pci.h"
>> #include "hw/irq.h"
>> #include "trace.h"
>>
>> +
>> +/* FIXME: export these from hw/ide/ioport.c */
>> +static const MemoryRegionPortio ide_portio_list[] = {
>> +    { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
>> +    { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew },
>> +    { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel },
>> +    PORTIO_END_OF_LIST(),
>> +};
>> +
>> +static const MemoryRegionPortio ide_portio2_list[] = {
>> +    { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write },
>> +    PORTIO_END_OF_LIST(),
>> +};
>> +
>> static uint64_t bmdma_read(void *opaque, hwaddr addr,
>>                            unsigned size)
>> {
>> @@ -137,7 +152,10 @@ static void via_ide_reset(DeviceState *dev)
>>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
>> -    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>> +    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000000e);
> 
> The vt8231 data sheet says the interrupt pin should always be 1 while according to 
> the vt82c686b data sheet this means legacy or native interrupt routing and defaults 
> to 0. We probably don't do anything with it so no matter what we have here and we can 
> change it to 0 but maybe there's someting off here in any case.

According to the VT8231 datasheet I have here, the interrupt pin register is 
read-only but defaults to zero. But then that's fine because that is the expected 
value in legacy mode which is what you would expect with a default PCI_CLASS_PROG set 
to 0x8a.

>> +
>> +    /* Clear subsystem to match real hardware */
>> +    pci_set_long(pci_conf + 0x2c, 0x0);
>>
>>     /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>>     pci_set_long(pci_conf + 0x40, 0x0a090600);
>> @@ -159,6 +177,89 @@ static void via_ide_reset(DeviceState *dev)
>>     pci_set_long(pci_conf + 0xc0, 0x00020001);
>> }
>>
>> +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>> +                              uint32_t val, int len)
>> +{
>> +    uint8_t *pci_conf = pd->config;
>> +    PCIIDEState *d = PCI_IDE(pd);
>> +
>> +    pci_default_write_config(pd, addr, val, len);
>> +
>> +    if (range_covers_byte(addr, len, PCI_CLASS_PROG)) {
>> +        if (pci_conf[PCI_CLASS_PROG] == 0x8a) {
>> +            /* FIXME: don't disable BARs
>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_0, 0x1, 4);
>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_1, 0x1, 4);
>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_2, 0x1, 4);
>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_3, 0x1, 4);
>> +            */
>> +
>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x0);
>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x0);
>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x0);
>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x0);
> 
> This is the part why I think this is not ready for merge yet. It seems to leave BARs 
> enabled but 0 their regs so now we have ide ports *both* at the previous BAR values 
> and at legacy ports while BAR values are 0. This does not look right and seems much 
> more hacky than my patch that changes BARs to legacy ports to emulate legacy mode. 
> You probably need this becuase of what Linux does on pegasos2 which I think is 
> proving real chip is also using BARs as my patch.

Well we both agree there are some quirks with this chip, but the reason for pursuing 
this approach is for 2 reasons: i) it matches the dumps you provided from real 
hardware (unlike your existing patch) and ii) it proves the basic concept and allows 
us to move forward with the consolidation work done by myself, Phil and Bernhard. 
Here's what I have in my tests here:


lspci output from real hardware (provided by you)
=================================================

0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a [Master 
SecP PriP])
          Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
Stepping- SERR- FastB2B- DisINTx-
          Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- 
<MAbort- >SERR- <PERR- INTx-
          Latency: 0
          Interrupt: pin ? routed to IRQ 14
          Region 0: [virtual] I/O ports at 1000 [size=8]
          Region 1: [virtual] I/O ports at 100c [size=4]
          Region 2: [virtual] I/O ports at 1010 [size=8]
          Region 3: [virtual] I/O ports at 101c [size=4]
          Region 4: I/O ports at 1020 [size=16]
          Capabilities: [c0] Power Management version 2
                  Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA 
PME(D0-,D1-,D2-,D3hot-,D3cold-)
                  Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
          Kernel driver in use: pata_via

0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06)
00: 06 11 71 05 07 00 90 02 06 8a 01 01 00 00 00 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: 61 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30: 00 00 00 00 c0 00 00 00 00 00 00 00 0e 00 00 00
40: 0b f2 09 35 18 1c c0 00 99 20 20 20 02 00 20 20
50: 17 f4 f0 f0 14 00 00 00 a8 a8 a8 a8 00 00 00 00
60: 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00
70: 02 01 00 00 00 00 00 00 02 01 00 00 00 00 00 00
80: 00 e0 b0 2f 00 00 00 00 00 f0 b0 2f 00 00 00 00
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0: 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 06 00 71 05 00 00 00 00 00 00 00 00 00 00 00 00
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Note that BARS 0-3 are all zeroed, the IDE controller reports legacy mode (0x8a) and 
the interrupt pin is set to 0x0.


lsipci output from your proposed patch
======================================

0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a [Master 
SecP PriP])
         Subsystem: Red Hat, Inc Device 1100
         Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
Stepping+ SERR- FastB2B- DisINTx-
         Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- 
<MAbort- >SERR- <PERR- INTx-
         Latency: 0
         Interrupt: pin A routed to IRQ 14
         Region 0: I/O ports at 1000 [size=8]
         Region 1: I/O ports at 100c [size=4]
         Region 2: I/O ports at 1010 [size=8]
         Region 3: I/O ports at 101c [size=4]
         Region 4: I/O ports at 1020 [size=16]
         Kernel driver in use: pata_via
00: 06 11 71 05 87 00 80 02 06 8a 01 01 00 00 00 00
10: 01 10 00 00 0d 10 00 00 11 10 00 00 1d 10 00 00
20: 21 10 00 00 00 00 00 00 00 00 00 00 f4 1a 00 11
30: 00 00 00 00 c0 00 00 00 00 00 00 00 0e 01 00 00
40: 0b f2 09 35 18 1c c0 00 99 31 99 99 a2 00 31 a8
50: 17 f0 17 17 14 00 00 00 00 00 00 00 00 00 00 00
60: 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0: 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Note that BARS 0-3 have values (incorrect), the IDE controller reports legacy mode 
(0x8a) and the interrupt pin is set to 0x1 (incorrect).


lsipci output from my proposed patch
====================================

0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a [Master 
SecP PriP])
         Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
Stepping+ SERR- FastB2B- DisINTx-
         Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- 
<MAbort- >SERR- <PERR- INTx-
         Latency: 0
         Interrupt: pin ? routed to IRQ 14
         Region 0: [virtual] I/O ports at 1000 [size=8]
         Region 1: [virtual] I/O ports at 100c [size=4]
         Region 2: [virtual] I/O ports at 1010 [size=8]
         Region 3: [virtual] I/O ports at 101c [size=4]
         Region 4: I/O ports at 1020 [size=16]
         Kernel driver in use: pata_via
00: 06 11 71 05 87 00 80 02 06 8a 01 01 00 00 00 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: 21 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30: 00 00 00 00 c0 00 00 00 00 00 00 00 0e 00 00 00
40: 0b f2 09 35 18 1c c0 00 99 31 99 99 a2 00 31 a8
50: 17 f0 17 17 14 00 00 00 00 00 00 00 00 00 00 00
60: 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0: 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Note that BARS 0-3 are all zeroed (correct), the IDE controller reports legacy mode 
(0x8a) and the interrupt pin is set to 0x0 (correct).


On the legacy ports being present on Linux, that is to be expected when setting the 
hardware to legacy mode which Linux does by writing 0x8a to PCI_CLASS_PROG. Why the 
BARs are still active in legacy mode is the better question to ask, but we won't know 
for sure unless someone can give me access to real hardware. The fact the legacy 
ports are there isn't an issue even if BARs are active as firmware avoids assigning 
PCI IO memory below 0x400 to avoid clashes with legacy and/or buggy hardware.

> Therefore I think we should go with my patch for now to not hold up adding amigaone 
> machine and allow progress with it and then when you have more time to elaborate this 
> idea of using portio_list to remove the FIXME comments from this PoC patch we can 
> revisit this any time later. There's no reason to hold other's work back to get this 
> sorted out now and solving a problem with my patch now does not mean we cannot 
> improve this device model further in the future. So if you can't make this a clean 
> patch now that can replace my patch please let my patch accepted until you can make 
> this a viable alternative. For now this just seems like an idea that needs more work 
> but not ready yet.

How is this holding your work back? I've pointed out the issues with your patch and 
provided you a near complete solution that i) you have confirmed working with your 
guests, ii) allows further work from myself and others in this area and iii) matches 
the information in the hardware dumps you provided from real hardware. I think it's 
fair to say that both ii) and iii) are notable improvements on your existing patch.

The FIXMEs are there because this patch comes from a branch with further work in this 
area: the only thing that remains are to split out the ide_portio_list[] and 
ide_portio2_list[] arrays so they can be reused from hw/ide/ioport.c and delete the 
second FIXME comment.

I'm sure you're more than capable of making these changes, and I'd appreciate that 
given my current time constraints. If not, then I can do this but it won't be for a 
few days - fortunately freeze is still a few weeks away, so there is no need for 
immediate urgency.


ATB,

Mark.
BALATON Zoltan Oct. 17, 2023, 9:39 p.m. UTC | #10
On Tue, 17 Oct 2023, Mark Cave-Ayland wrote:
> On 16/10/2023 23:16, BALATON Zoltan wrote:
>> On Sun, 15 Oct 2023, Mark Cave-Ayland wrote:
>>> On 14/10/2023 17:13, BALATON Zoltan wrote:
>>>> On Sat, 14 Oct 2023, Mark Cave-Ayland wrote:
>>>>> Using the portio_list*() APIs really is the right way to implement this 
>>>>> to avoid being affected by such issues.
>>>> 
>>>> Can you provide an alternative patch using portio_list? I don't know how 
>>>> to do that and have no example to follow either so it would be hard for 
>>>> me to figure out. Or give some pointers on how to do this if I missed 
>>>> something.
>>> 
>>> Here's a hacked up version based upon various bits and pieces from my WIP 
>>> IDE mode switching branch. It's briefly compile tested, and checked with 
>>> "info mtree" and a couple of test boots:
>> 
>> 
>> I gave this a try and while it works with the guests I've tried I'm still 
>> not convinced. See comments below.
>
> Okay, that's good news and proves the basic concept works as expected.

Well it works but seems much more hacky and complex at the moment than my 
patch which also not perfect but works as well and solves the problem with 
much less change so I still don't like this patch of yours too much. But 
if you can clean it in time before the freeze so this won't cause my other 
patches depending on this missing the release I'm OK with this alternative 
approach even if I think it's not necessary and could be done any time 
later. My patch does not prevent you to take this futther and do this 
later, but you not allowing my patch and then not providing alternative in 
time for the series to be merged would make me not able to progress with 
this machine model and prevent users start using it.

>>> diff --git a/hw/ide/via.c b/hw/ide/via.c
>>> index fff23803a6..82f2af1c78 100644
>>> --- a/hw/ide/via.c
>>> +++ b/hw/ide/via.c
>>> @@ -28,12 +28,27 @@
>>> #include "hw/pci/pci.h"
>>> #include "migration/vmstate.h"
>>> #include "qemu/module.h"
>>> +#include "qemu/range.h"
>>> #include "sysemu/dma.h"
>>> #include "hw/isa/vt82c686.h"
>>> #include "hw/ide/pci.h"
>>> #include "hw/irq.h"
>>> #include "trace.h"
>>> 
>>> +
>>> +/* FIXME: export these from hw/ide/ioport.c */
>>> +static const MemoryRegionPortio ide_portio_list[] = {
>>> +    { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
>>> +    { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew },
>>> +    { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel },
>>> +    PORTIO_END_OF_LIST(),
>>> +};
>>> +
>>> +static const MemoryRegionPortio ide_portio2_list[] = {
>>> +    { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write },
>>> +    PORTIO_END_OF_LIST(),
>>> +};
>>> +
>>> static uint64_t bmdma_read(void *opaque, hwaddr addr,
>>>                            unsigned size)
>>> {
>>> @@ -137,7 +152,10 @@ static void via_ide_reset(DeviceState *dev)
>>>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
>>>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
>>>     pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 
>>> 20-23h */
>>> -    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
>>> +    pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000000e);
>> 
>> The vt8231 data sheet says the interrupt pin should always be 1 while 
>> according to the vt82c686b data sheet this means legacy or native interrupt 
>> routing and defaults to 0. We probably don't do anything with it so no 
>> matter what we have here and we can change it to 0 but maybe there's 
>> someting off here in any case.
>
> According to the VT8231 datasheet I have here, the interrupt pin register is 
> read-only but defaults to zero. But then that's fine because that is the 
> expected value in legacy mode which is what you would expect with a default 
> PCI_CLASS_PROG set to 0x8a.

According to the data sheet I have (VT8231 South Bridge Revision 2.32, 
September 1, 2004) IDE func config reg 0x3d Interrupt Pin defaults to 01. 
Maybe you're looking at an even more buggy preliminary data sheet that I 
think Bernhard had before too. But it probably does not matter what's in 
this reg anyway so this is a small detail.

>>> +
>>> +    /* Clear subsystem to match real hardware */
>>> +    pci_set_long(pci_conf + 0x2c, 0x0);
>>> 
>>>     /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
>>>     pci_set_long(pci_conf + 0x40, 0x0a090600);
>>> @@ -159,6 +177,89 @@ static void via_ide_reset(DeviceState *dev)
>>>     pci_set_long(pci_conf + 0xc0, 0x00020001);
>>> }
>>> 
>>> +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
>>> +                              uint32_t val, int len)
>>> +{
>>> +    uint8_t *pci_conf = pd->config;
>>> +    PCIIDEState *d = PCI_IDE(pd);
>>> +
>>> +    pci_default_write_config(pd, addr, val, len);
>>> +
>>> +    if (range_covers_byte(addr, len, PCI_CLASS_PROG)) {
>>> +        if (pci_conf[PCI_CLASS_PROG] == 0x8a) {
>>> +            /* FIXME: don't disable BARs
>>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_0, 0x1, 4);
>>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_1, 0x1, 4);
>>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_2, 0x1, 4);
>>> +            pci_default_write_config(pd, PCI_BASE_ADDRESS_3, 0x1, 4);
>>> +            */
>>> +
>>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x0);
>>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x0);
>>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x0);
>>> +            pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x0);
>> 
>> This is the part why I think this is not ready for merge yet. It seems to 
>> leave BARs enabled but 0 their regs so now we have ide ports *both* at the 
>> previous BAR values and at legacy ports while BAR values are 0. This does 
>> not look right and seems much more hacky than my patch that changes BARs to 
>> legacy ports to emulate legacy mode. You probably need this becuase of what 
>> Linux does on pegasos2 which I think is proving real chip is also using 
>> BARs as my patch.
>
> Well we both agree there are some quirks with this chip, but the reason for 
> pursuing this approach is for 2 reasons: i) it matches the dumps you provided 
> from real hardware (unlike your existing patch) and ii) it proves the basic

Matching the dumps does not matter as long as guests work. All of QEMU is 
aiming to model devices enough to allow guests to run but does not care to 
faithfully emulate every detail unless that's needed for guest. It seems 
guests don't care about this so not absolutely needed to be modeled. It 
may be nice to do but should not be a requirement.

> concept and allows us to move forward with the consolidation work done by 
> myself, Phil and Bernhard. Here's what I have in my tests here:

As I said above taking my patch now does not prevent that and then you're 
free to submit these consolidation patches any time later when they are 
ready. There's not urgent need to do them now and hold back this very 
simple series by that and threaten with missing the release because of 
this.

> lspci output from real hardware (provided by you)
> =================================================
>
> 0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
> VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a 
> [Master SecP PriP])
>         Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
> Stepping- SERR- FastB2B- DisINTx-
>         Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- 
> <TAbort- <MAbort- >SERR- <PERR- INTx-
>         Latency: 0
>         Interrupt: pin ? routed to IRQ 14
>         Region 0: [virtual] I/O ports at 1000 [size=8]
>         Region 1: [virtual] I/O ports at 100c [size=4]
>         Region 2: [virtual] I/O ports at 1010 [size=8]
>         Region 3: [virtual] I/O ports at 101c [size=4]
>         Region 4: I/O ports at 1020 [size=16]
>         Capabilities: [c0] Power Management version 2
>                 Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA 
> PME(D0-,D1-,D2-,D3hot-,D3cold-)
>                 Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
>         Kernel driver in use: pata_via
>
> 0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
> VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06)
> 00: 06 11 71 05 07 00 90 02 06 8a 01 01 00 00 00 00
> 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 20: 61 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 30: 00 00 00 00 c0 00 00 00 00 00 00 00 0e 00 00 00
> 40: 0b f2 09 35 18 1c c0 00 99 20 20 20 02 00 20 20
> 50: 17 f4 f0 f0 14 00 00 00 a8 a8 a8 a8 00 00 00 00
> 60: 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00
> 70: 02 01 00 00 00 00 00 00 02 01 00 00 00 00 00 00
> 80: 00 e0 b0 2f 00 00 00 00 00 f0 b0 2f 00 00 00 00
> 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> c0: 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
> d0: 06 00 71 05 00 00 00 00 00 00 00 00 00 00 00 00
> e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>
> Note that BARS 0-3 are all zeroed, the IDE controller reports legacy mode 
> (0x8a) and the interrupt pin is set to 0x0.
>
>
> lsipci output from your proposed patch
> ======================================
>
> 0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
> VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a 
> [Master SecP PriP])
>        Subsystem: Red Hat, Inc Device 1100
>        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
> Stepping+ SERR- FastB2B- DisINTx-
>        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- 
> <TAbort- <MAbort- >SERR- <PERR- INTx-
>        Latency: 0
>        Interrupt: pin A routed to IRQ 14
>        Region 0: I/O ports at 1000 [size=8]
>        Region 1: I/O ports at 100c [size=4]
>        Region 2: I/O ports at 1010 [size=8]
>        Region 3: I/O ports at 101c [size=4]
>        Region 4: I/O ports at 1020 [size=16]
>        Kernel driver in use: pata_via
> 00: 06 11 71 05 87 00 80 02 06 8a 01 01 00 00 00 00
> 10: 01 10 00 00 0d 10 00 00 11 10 00 00 1d 10 00 00
> 20: 21 10 00 00 00 00 00 00 00 00 00 00 f4 1a 00 11
> 30: 00 00 00 00 c0 00 00 00 00 00 00 00 0e 01 00 00
> 40: 0b f2 09 35 18 1c c0 00 99 31 99 99 a2 00 31 a8
> 50: 17 f0 17 17 14 00 00 00 00 00 00 00 00 00 00 00
> 60: 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00
> 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> c0: 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
> d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>
> Note that BARS 0-3 have values (incorrect), the IDE controller reports legacy 
> mode (0x8a) and the interrupt pin is set to 0x1 (incorrect).
>
>
> lsipci output from my proposed patch
> ====================================
>
> 0000:00:0c.1 IDE interface: VIA Technologies, Inc. 
> VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a 
> [Master SecP PriP])
>        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- 
> Stepping+ SERR- FastB2B- DisINTx-
>        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- 
> <TAbort- <MAbort- >SERR- <PERR- INTx-
>        Latency: 0
>        Interrupt: pin ? routed to IRQ 14
>        Region 0: [virtual] I/O ports at 1000 [size=8]
>        Region 1: [virtual] I/O ports at 100c [size=4]
>        Region 2: [virtual] I/O ports at 1010 [size=8]
>        Region 3: [virtual] I/O ports at 101c [size=4]
>        Region 4: I/O ports at 1020 [size=16]
>        Kernel driver in use: pata_via
> 00: 06 11 71 05 87 00 80 02 06 8a 01 01 00 00 00 00
> 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 20: 21 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 30: 00 00 00 00 c0 00 00 00 00 00 00 00 0e 00 00 00
> 40: 0b f2 09 35 18 1c c0 00 99 31 99 99 a2 00 31 a8
> 50: 17 f0 17 17 14 00 00 00 00 00 00 00 00 00 00 00
> 60: 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00
> 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> c0: 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
> d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>
> Note that BARS 0-3 are all zeroed (correct), the IDE controller reports 
> legacy mode (0x8a) and the interrupt pin is set to 0x0 (correct).

But these differences aren't solving any issues with guests that I know of 
just makes the patch more complex for no good reason. So I don't see why 
these should matter now or be modelled unless we know this make a 
difference. We have other differences in other regs too that we don't care 
about. Why these should be important to match?

> On the legacy ports being present on Linux, that is to be expected when 
> setting the hardware to legacy mode which Linux does by writing 0x8a to 
> PCI_CLASS_PROG. Why the BARs are still active in legacy mode is the better 
> question to ask, but we won't know for sure unless someone can give me access 
> to real hardware. The fact the legacy ports are there isn't an issue even if 
> BARs are active as firmware avoids assigning PCI IO memory below 0x400 to 
> avoid clashes with legacy and/or buggy hardware.

Exactly because we can't answer these questions is why I think we should 
postpone this debate to when we have more information and answers as the 
change I proposed does not prevent this to be done later. Unless we find 
issue with my patch that cause a problem in a guest I don't see a need to 
make it more complex now. Your patch adds dependeny on portio that's not 
yet cleanly separated from ISA, dependency on ISA_IDE not marked in 
Kconfig (although likely satisfied by VT82C686) and causes two mappings of 
IDE ports in legacy mode which we are not sure matches real hardware so 
I'd argue my patch is a smaller change that's good enough to make some 
progress now and all these can be sorted out any time later without doing 
it all now.

>> Therefore I think we should go with my patch for now to not hold up adding 
>> amigaone machine and allow progress with it and then when you have more 
>> time to elaborate this idea of using portio_list to remove the FIXME 
>> comments from this PoC patch we can revisit this any time later. There's no 
>> reason to hold other's work back to get this sorted out now and solving a 
>> problem with my patch now does not mean we cannot improve this device model 
>> further in the future. So if you can't make this a clean patch now that can 
>> replace my patch please let my patch accepted until you can make this a 
>> viable alternative. For now this just seems like an idea that needs more 
>> work but not ready yet.
>
> How is this holding your work back? I've pointed out the issues with your

I think I answered that above but the point is that my goal is to add 
amigaone machine and this is a prerequisite for that. The patch I proposed 
is a simple fix allowing that while yours is more complex and not yet 
completely elaborated so I don't want this to hold back progress with 
amigaone, especially because my patch would not hold back your 
consolidation of this any time later.

> patch and provided you a near complete solution that i) you have confirmed 
> working with your guests, ii) allows further work from myself and others in 
> this area and iii) matches the information in the hardware dumps you provided 
> from real hardware. I think it's fair to say that both ii) and iii) are 
> notable improvements on your existing patch.

I don't say my patch is perfect and cannot be improved. What I say is that 
my patch solves the problem for now, does not make the model worse than it 
is and does not prevent your work in this area as it's easy to later 
replace that with your way when the still open questions about that are 
resolved. You blocking my patch to do all that now howaver would block my 
work on amigaone machine therefore I'd like to get a solution to this 
that's acceptable as soon as possible.

> The FIXMEs are there because this patch comes from a branch with further work 
> in this area: the only thing that remains are to split out the 
> ide_portio_list[] and ide_portio2_list[] arrays so they can be reused from 
> hw/ide/ioport.c and delete the second FIXME comment.
>
> I'm sure you're more than capable of making these changes, and I'd appreciate 
> that given my current time constraints. If not, then I can do this but it 
> won't be for a few days - fortunately freeze is still a few weeks away, so 
> there is no need for immediate urgency.

I also have time constraints and plan to furhtet improve amigaone machine 
rather than spending more time with this vie-ide model now that my simple 
patch already makes it suitable for amigaone without breaking what already 
works. Also I don't see your future plans with these and don't see where 
these are going so to me this looks just unnecessary compication that I'm 
not keen on working when I don't see why this would be necessary. So I 
think it would be best if we could agree on some solution that's 
acceptable for now and I don't mind getting back to it later and further 
improve it when it's found necessary or you finish these patches but I 
don't see this as something that needs to be done now or blocking the 
series. You were just reminded about it by this change and want to solve 
it now in a much more elaborate way than probably necessary.

Because of that if you can finish it at least 2 weeks before the freeze 
(that means practically this week or by next Monday latest) to make sure 
they don't miss the next release I'm OK with your proposed patch too but I 
don't have time and interest to realise your plans that you don't have 
time now (but that's also not needed as if you can't do it now I already 
have a working patch and that does not mean it can't be done later). I 
hope we can agree on that and you understand my point of view as well.

Regards,
BALATON Zoltan
diff mbox series

Patch

diff --git a/hw/ide/via.c b/hw/ide/via.c
index fff23803a6..43e8af8d69 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -132,11 +132,6 @@  static void via_ide_reset(DeviceState *dev)
     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
                  PCI_STATUS_DEVSEL_MEDIUM);
 
-    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
-    pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
-    pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
-    pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
-    pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
     pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
 
     /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
@@ -159,6 +154,41 @@  static void via_ide_reset(DeviceState *dev)
     pci_set_long(pci_conf + 0xc0, 0x00020001);
 }
 
+static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr,
+                              uint32_t val, int len)
+{
+    pci_default_write_config(pd, addr, val, len);
+    /*
+     * Bits 0 and 2 of the PCI programming interface register select between
+     * legacy and native mode for the two IDE channels. We don't emulate this
+     * because we cannot easily switch between ISA and PCI in QEMU so instead
+     * when guest selects legacy mode we set the PCI BARs to legacy ports which
+     * works the same. We also don't care about setting each channel separately
+     * as no guest is known to do or need that. We only do this when BARs are
+     * unset when writing this register as logs from real hardware show that
+     * setting legacy mode after BARs were set it will still use ports set by
+     * BARs not ISA ports (e.g. pegasos2 Linux does this after firmware set
+     * native mode and programmed BARs and calls it non-100% native mode).
+     * But if 0x8a is written righr after reset without setting BARs then we
+     * want legacy ports (this is done by the AmigaOne firmware).
+     */
+    if (addr == PCI_CLASS_PROG && val == 0x8a &&
+        pci_get_long(pd->config + PCI_BASE_ADDRESS_0) ==
+        PCI_BASE_ADDRESS_SPACE_IO) {
+        pci_set_long(pd->config + PCI_BASE_ADDRESS_0, 0x1f0
+                     | PCI_BASE_ADDRESS_SPACE_IO);
+        pci_set_long(pd->config + PCI_BASE_ADDRESS_1, 0x3f6
+                     | PCI_BASE_ADDRESS_SPACE_IO);
+        pci_set_long(pd->config + PCI_BASE_ADDRESS_2, 0x170
+                     | PCI_BASE_ADDRESS_SPACE_IO);
+        pci_set_long(pd->config + PCI_BASE_ADDRESS_3, 0x376
+                     | PCI_BASE_ADDRESS_SPACE_IO);
+        /* BMIBA: 20-23h */
+        pci_set_long(pd->config + PCI_BASE_ADDRESS_4, 0xcc00
+                     | PCI_BASE_ADDRESS_SPACE_IO);
+    }
+}
+
 static void via_ide_realize(PCIDevice *dev, Error **errp)
 {
     PCIIDEState *d = PCI_IDE(dev);
@@ -221,6 +251,7 @@  static void via_ide_class_init(ObjectClass *klass, void *data)
     /* Reason: only works as function of VIA southbridge */
     dc->user_creatable = false;
 
+    k->config_write = via_ide_cfg_write;
     k->realize = via_ide_realize;
     k->exit = via_ide_exitfn;
     k->vendor_id = PCI_VENDOR_ID_VIA;