On Thu, Jan 24, 2013 at 10:31 AM, Laszlo Ersek <ler...@redhat.com> wrote:
> From <http://mjg59.dreamwidth.org/3561.html>:
>
>   Traditional PCI config space access is achieved by writing a 32 bit
>   value to io port 0xcf8 to identify the bus, device, function and config
>   register. Port 0xcfc then contains the register in question. But if you
>   write the appropriate pair of magic values to 0xcf9, the machine will
>   reboot. Spectacular! And not standardised in any way (certainly not part
>   of the PCI spec), so different chipsets may have different requirements.
>   Booo.
>
> In the PIIX3 spec, IO port 0xcf9 is specified as the Reset Control
> Register. Bit 1 (System Reset, SRST) would normally differentiate between
> soft reset and hard reset, but we ignore the difference beyond allowing
> the guest to read it back.
>
> RHBZ reference: 890459
>
> This patch introduces the following overlap between the preexistent
> "pci-conf-idx" region and the "piix3-reset-control" region just being
> added. Partial output from "info mtree":
>
>   I/O
>   0000000000000000-000000000000ffff (prio 0, RW): io
>     0000000000000cf8-0000000000000cfb (prio 0, RW): pci-conf-idx
>     0000000000000cf9-0000000000000cf9 (prio 1, RW): piix3-reset-control
>
> I sanity-checked the patch by booting a RHEL-6.3 guest and found no
> problems. I summoned gdb and set a breakpoint on rcr_write() in order to
> gather a bit more confidence. Relevant frames of the stack:
>
>   kvm_handle_io (port=3321, data=0x7f3f5f3de000, direction=1, size=1,
>                  count=1)                                 [kvm-all.c:1422]
>     cpu_outb (addr=3321, val=6 '\006')                      [ioport.c:289]
>       ioport_write (index=0, address=3321, data=6)           [ioport.c:83]
>         ioport_writeb_thunk (opaque=0x7f3f622c4680, addr=3321, data=6)
>                                                             [ioport.c:212]
>           memory_region_iorange_write (iorange=0x7f3f622c4680, offset=0,
>                                        width=1, data=6)     [memory.c:439]
>             access_with_adjusted_size (addr=0, value=0x7f3f531fbac0,
>                                        size=1, access_size_min=1,
>                                        access_size_max=4,
>                                        access=0x7f3f5f6e0f90
>                                            <memory_region_write_accessor>,
>                                        opaque=0x7f3f6227b668)
>                                                             [memory.c:364]
>               memory_region_write_accessor (opaque=0x7f3f6227b668, addr=0,
>                                             value=0x7f3f531fbac0, size=1,
>                                             shift=0, mask=255)
>                                                             [memory.c:334]
>                 rcr_write (opaque=0x7f3f6227afb0, addr=0, val=6, len=1)
>                                                        [hw/piix_pci.c:498]
>
> The dispatch happens in ioport_write(); "index=0" means byte-wide access:
>
>     static void ioport_write(int index, uint32_t address, uint32_t data)
>     {
>         static IOPortWriteFunc * const default_func[3] = {
>             default_ioport_writeb,
>             default_ioport_writew,
>             default_ioport_writel
>         };
>         IOPortWriteFunc *func = ioport_write_table[index][address];
>         if (!func)
>             func = default_func[index];
>         func(ioport_opaque[address], address, data);
>     }
>
> The "ioport_write_table" and "ioport_opaque" arrays describe the flattened
> IO port space. The first array is less interesting (it selects a thunk
> function). The "ioport_opaque" array is interesting because it decides how
> writing to the port is implemented ultimately.
>
> 4-byte wide access to 0xcf8 (pci-conf-idx):
>
>   (gdb) print ioport_write_table[2][0xcf8]
>   $1 = (IOPortWriteFunc *) 0x7f3f5f6d99ba <ioport_writel_thunk>
>
>   (gdb) print \
>         ((struct MemoryRegionIORange*)ioport_opaque[0xcf8])->mr->ops.write
>   $2 = (void (*)(void *, hwaddr, uint64_t, unsigned int))
>        0x7f3f5f5575cb <pci_host_config_write>
>
> 1-byte wide access to 0xcf9 (piix3-reset-control):
>
>   (gdb) print ioport_write_table[0][0xcf9]
>   $3 = (IOPortWriteFunc *) 0x7f3f5f6d98d0 <ioport_writeb_thunk>
>
>   (gdb) print \
>         ((struct MemoryRegionIORange*)ioport_opaque[0xcf9])->mr->ops.write
>   $4 = (void (*)(void *, hwaddr, uint64_t, unsigned int))
>        0x7f3f5f6b42f1 <rcr_write>
>
> The higher priority of "piix3-reset-control" ensures that the 0xcf9
> entries in ioport_write_table / ioport_opaque will always belong to it,
> independently of its relative registration order versus "pci-conf-idx".
>
> Signed-off-by: Laszlo Ersek <ler...@redhat.com>
> ---
> v2->v3:
> - don't touch piix3_post_load(); take the RCR as it comes (Stefan).
>   Diff against v2:
>
>   diff --git a/hw/piix_pci.c b/hw/piix_pci.c
>   index 38a1027..4c97a84 100644
>   --- a/hw/piix_pci.c
>   +++ b/hw/piix_pci.c
>   @@ -462,7 +462,6 @@ static int piix3_post_load(void *opaque, int version_id)
>    {
>        PIIX3State *piix3 = opaque;
>        piix3_update_irq_levels(piix3);
>   -    piix3->rcr &= 2; /* keep System Reset type only */
>        return 0;
>    }
>
> v1->v2:
> - move the RCR into a subsection for migration (Paolo)
>
>  hw/piix_pci.c |   68 
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 68 insertions(+), 0 deletions(-)

Thanks Laszlo!

Reviewed-by: Stefan Hajnoczi <stefa...@gmail.com>

Reply via email to