tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

take 2: MI overrides of bus_dma(9), bus_space(9), pci(9)



I would like for MI drivers to be able to override pci(9), bus_space(9),
and bus_dma(9) behavior for the purpose of handling exceptions, managing
bus resources, creating test harnesses, and counting events.

Matt Thomas nudged me in some private discussions to leave MD the
bus_space_tag_t and pci_chipset_tag_t, instead of embedding an MI tag,
and to let each architecture provide its own implementation of an
MI override functions for bus_space(9) and pci(9).  Matt powerfully
influenced the look of the override API.

I will describe the overrides API for pci(9), first:

/* This is part of the ABI.  Do not re-assign bits to names. */
enum pci_override_idx {
          PCI_OVERRIDE_CONF_READ                = __BIT(0)
        , PCI_OVERRIDE_CONF_WRITE               = __BIT(1)
        , PCI_OVERRIDE_INTR_MAP                 = __BIT(2)
        , PCI_OVERRIDE_INTR_STRING              = __BIT(3)
        , PCI_OVERRIDE_INTR_EVCNT               = __BIT(4)
        , PCI_OVERRIDE_INTR_ESTABLISH           = __BIT(5)
        , PCI_OVERRIDE_INTR_DISESTABLISH        = __BIT(6)
        , PCI_OVERRIDE_MAKE_TAG                 = __BIT(7)
        , PCI_OVERRIDE_DECOMPOSE_TAG            = __BIT(8)
};

/* This is part of the ABI.  Only add new fields to the end of this
 * structure!
 */
struct pci_overrides {
        pcireg_t (*ov_conf_read)(void *, pci_chipset_tag_t, pcitag_t, int);
        void (*ov_conf_write)(void *, pci_chipset_tag_t, pcitag_t, int,
            pcireg_t);
        int (*ov_intr_map)(void *, struct pci_attach_args *,
           pci_intr_handle_t *);
        const char *(*ov_intr_string)(void *, pci_chipset_tag_t,
            pci_intr_handle_t);
        const struct evcnt *(*ov_intr_evcnt)(void *, pci_chipset_tag_t,
            pci_intr_handle_t);
        void *(*ov_intr_establish)(void *, pci_chipset_tag_t, pci_intr_handle_t,
            int, int (*)(void *), void *);
        void (*ov_intr_disestablish)(void *, pci_chipset_tag_t, void *);
        pcitag_t (*ov_make_tag)(void *, pci_chipset_tag_t, int, int, int);
        void (*ov_decompose_tag)(void *, pci_chipset_tag_t, pcitag_t,
            int *, int *, int *);
};

int     pci_chipset_tag_create(pci_chipset_tag_t opc, uint64_t present,
                               const struct pci_overrides *ov,
                               void *ctx, pci_chipset_tag_t *pcp);

        Create a copy of the tag opc at *pcp.  Except for the
        behavior overridden by ov,  *pcp inherits opc's behavior
        under pci(9) calls.

        ov contains function pointers corresponding to pci(9)
        routines.  Each function pointer has a corresponding bit
        in `present', and if that bit is 1, the function pointer
        overrides the corresponding pci(9) call for the new tag.

        pci_chipset_tag_create(9) does not copy ov.  After a new
        tag is created by pci_chipset_tag_create(9), ov must not
        be destroyed until after the tag is destroyed by
        pci_chipset_tag_destroy(9).

        The first argument of every override-function is a void *,
        and ctx is passed in that argument.

        Return 0 if the call succeeds.  Return EOPNOTSUPP if the
        architecture does not support overrides.  Return EINVAL if
        `present' is 0, if `ov' is NULL, or if `present' indicates
        that an override is present, but the corresponding override
        in `ov' is NULL.

        If the call does not succeed, *pcp is undefined.

void    pci_chipset_tag_destroy(pci_chipset_tag_t pc);

        Destroy a tag created by a prior call to pci_chipset_tag_create(9).
        If a tag that was not created by pci_chipset_tag_create(9)
        is destroyed, results are undefined.  If a tag that was
        already destroyed is a destroyed a second time, results
        are undefined.

Both of those routines and the types are declared in sys/dev/pci/pcivar.h.
A weak alias for eopnotsupp, pci_chipset_tag_create, and a weak
alias for voidop, pci_chipset_tag_destroy, provide every architecture
with a default implementation.

I have implemented this API on i386 and used it to override
pci_intr_string(9), pci_intr_map(9), pci_conf_read(9),
pci_intr_establish(9), and pci_intr_disestablish(9) in cbb(4).  I was
subsequently able to reduce the differences between if_ath_pci.c and
if_ath_cardbus.c to these:

-       if (Cardbus_mapreg_map(ct, ATH_PCI_MMBA, mem_type, 0, &psc->sc_iot,
+       if (pci_mapreg_map(pa, ATH_PCI_MMBA, mem_type, 0, &psc->sc_iot,

 bad1:
-       Cardbus_mapreg_unmap(psc->sc_ct, ATH_PCI_MMBA, psc->sc_iot, psc->sc_ioh,
-           psc->sc_mapsz);
+       bus_space_unmap(psc->sc_iot, psc->sc_ioh, psc->sc_mapsz);

-       Cardbus_mapreg_unmap(psc->sc_ct, ATH_PCI_MMBA, psc->sc_iot, psc->sc_ioh,
-           psc->sc_mapsz);
+       bus_space_unmap(psc->sc_iot, psc->sc_ioh, psc->sc_mapsz);
        return 0;

To eliminate the remaining differences, I need either to override
pci_mapreg_map(9), or to revamp CardBus resource management, and I need
to override bus_space_unmap(9).  That brings me to the bus_space(9)
overrides:

enum bus_space_override_idx {
          BUS_SPACE_OVERRIDE_SPACE_MAP          = __BIT(0)
        , BUS_SPACE_OVERRIDE_SPACE_UNMAP        = __BIT(1)
        , BUS_SPACE_OVERRIDE_SPACE_ALLOC        = __BIT(2)
        , BUS_SPACE_OVERRIDE_SPACE_FREE         = __BIT(3)
        , BUS_SPACE_OVERRIDE_SPACE_EXTEND       = __BIT(4)
        , BUS_SPACE_OVERRIDE_SPACE_TRIM         = __BIT(5)
};

/* Only add new members at the end of this struct! */
struct bus_space_overrides {
        int (*bs_space_map)(void *, bus_space_tag_t, bus_addr_t, bus_size_t,
            int, bus_space_handle_t *);

        void (*bs_space_unmap)(void *, bus_space_tag_t, bus_space_handle_t,
            bus_size_t);

        int (*bs_space_alloc)(void *, bus_space_tag_t, bus_addr_t, bus_addr_t,
            bus_size_t, bus_size_t, bus_size_t, int, bus_addr_t *,
            bus_space_handle_t *);

        void (*bs_space_free)(void *, bus_space_tag_t, bus_space_handle_t,
            bus_size_t);
};

int     bus_space_tag_create(bus_space_tag_t, uint64_t,
                             const struct bus_space_overrides *, void *,
                             bus_space_tag_t *);
void    bus_space_tag_destroy(bus_space_tag_t);

Those overrides work by analogy to the pci(9) overrides.

Finally, here are extensions to bus_space(9) that remain a
work-in-progress:

/* Return true iff the two tags correspond to the same bus space.
 * The default implementation memcmp()'s the two tags.
 */
bool    bus_space_is_equal(bus_space_tag_t, bus_space_tag_t);

/*
 * Routines for reserving regions of bus space without mapping them,
 * and for extending/reducing existing reservations.
 */

/* Reserve a region of bus space.  Reserved bus space cannot be allocated
 * with bus_space_alloc().  Unlike bus_space_alloc()'d space, reserved
 * space is not bus_space_map()'d.
 */
int     bus_space_reserve(bus_space_tag_t, bus_addr_t, bus_size_t,
                          bus_space_reservation_t *);

/* Cancel a reservation. */
void    bus_space_release(bus_space_tag_t, bus_space_reservation_t);

/* Extend a reservation to the left and/or to the right.  The extension
 * will not be bus_space_map()'d.
 */
int     bus_space_extend(bus_space_tag_t, bus_space_reservation_t, bus_size_t,
                         bus_size_t);

/* Trim bus space from a reservation on the left and/or on the right.
 * The trimmed region must not be currently bus_space_map()'d.
 */
void    bus_space_trim(bus_space_tag_t, bus_space_reservation_t, bus_size_t,
                       bus_size_t);

Dave

-- 
David Young             OJC Technologies
dyoung%ojctech.com@localhost      Urbana, IL * (217) 278-3933


Home | Main Index | Thread Index | Old Index