#include <asm/mmio.h>
+static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge,
+ paddr_t gpa)
+{
+ pci_sbdf_t sbdf;
+
+ if ( bridge )
+ {
+ sbdf.sbdf = VPCI_ECAM_BDF(gpa - bridge->cfg->phys_addr);
+ sbdf.seg = bridge->segment;
+ sbdf.bus += bridge->cfg->busn_start;
+ }
+ else
+ sbdf.sbdf = VPCI_ECAM_BDF(gpa - GUEST_VPCI_ECAM_BASE);
+
+ return sbdf;
+}
+
static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
register_t *r, void *p)
{
- pci_sbdf_t sbdf;
+ struct pci_host_bridge *bridge = p;
+ pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa);
/* data is needed to prevent a pointer cast on 32bit */
unsigned long data;
- /* We ignore segment part and always handle segment 0 */
- sbdf.sbdf = VPCI_ECAM_BDF(info->gpa - GUEST_VPCI_ECAM_BASE);
-
if ( vpci_ecam_read(sbdf, ECAM_REG_OFFSET(info->gpa),
1U << info->dabt.size, &data) )
{
static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info,
register_t r, void *p)
{
- pci_sbdf_t sbdf;
-
- /* We ignore segment part and always handle segment 0 */
- sbdf.sbdf = VPCI_ECAM_BDF(info->gpa - GUEST_VPCI_ECAM_BASE);
+ struct pci_host_bridge *bridge = p;
+ pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa);
return vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa),
1U << info->dabt.size, r);
.write = vpci_mmio_write,
};
+static int vpci_setup_mmio_handler_cb(struct domain *d,
+ struct pci_host_bridge *bridge)
+{
+ struct pci_config_window *cfg = bridge->cfg;
+
+ register_mmio_handler(d, &vpci_mmio_handler,
+ cfg->phys_addr, cfg->size, bridge);
+
+ /* We have registered a single MMIO handler. */
+ return 1;
+}
+
int domain_vpci_init(struct domain *d)
{
if ( !has_vpci(d) )
return 0;
- register_mmio_handler(d, &vpci_mmio_handler,
- GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, NULL);
+ /*
+ * The hardware domain gets as many MMIOs as required by the
+ * physical host bridge.
+ * Guests get the virtual platform layout: one virtual host bridge for now.
+ */
+ if ( is_hardware_domain(d) )
+ {
+ int ret;
+
+ ret = pci_host_iterate_bridges_and_count(d, vpci_setup_mmio_handler_cb);
+ if ( ret < 0 )
+ return ret;
+ }
+ else
+ register_mmio_handler(d, &vpci_mmio_handler,
+ GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, NULL);
+
+ return 0;
+}
+
+static int vpci_get_num_handlers_cb(struct domain *d,
+ struct pci_host_bridge *bridge)
+{
+ /* Each bridge has a single MMIO handler for the configuration space. */
+ return 1;
+}
+
+unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d)
+{
+ if ( !has_vpci(d) )
+ return 0;
+
+ if ( is_hardware_domain(d) )
+ {
+ int ret = pci_host_iterate_bridges_and_count(d, vpci_get_num_handlers_cb);
+
+ if ( ret < 0 )
+ {
+ ASSERT_UNREACHABLE();
+ return 0;
+ }
+
+ return ret;
+ }
return 0;
}