/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2012 Milan Jurik. All rights reserved.
*/
#if defined(DEBUG)
#define BUSRA_DEBUG
#endif
/*
* This module provides a set of resource management interfaces
* to manage bus resources globally in the system.
*
* The bus nexus drivers are typically responsible to setup resource
* maps for the bus resources available for a bus instance. However
* this module also provides resource setup functions for PCI bus
* (used by both SPARC and X86 platforms) and ISA bus instances (used
* only for X86 platforms).
*/
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/autoconf.h>
#if defined(BUSRA_DEBUG)
int busra_debug = 0;
#define DEBUGPRT \
if (busra_debug) cmn_err
#else
#define DEBUGPRT \
if (0) cmn_err
#endif
/*
* global mutex that protects the global list of resource maps.
*/
/*
* basic resource element
*/
struct ra_resource {
};
/*
* link list element for the list of dips (and their resource ranges)
* for a particular resource type.
* ra_rangeset points to the list of resources available
* for this type and this dip.
*/
struct ra_dip_type {
};
/*
* link list element for list of types resources. Each element
* has all resources for a particular type.
*/
struct ra_type_map {
char *type;
};
/*
* place holder to keep the head of the whole global list.
* the address of the first typemap would be stored in it.
*/
/*
* This is the loadable module wrapper.
* It is essentially boilerplate so isn't documented
*/
extern struct mod_ops mod_miscops;
#ifdef BUSRA_DEBUG
void ra_dump_all(char *, dev_info_t *);
#endif
/* internal function prototypes */
&mod_miscops, /* Type of module. This one is a module */
"Bus Resource Allocator (BUSRA)", /* Name of the module. */
};
};
int
_init()
{
int ret;
}
return (ret);
}
int
_fini()
{
int ret;
if (ra_map_list_head != NULL) {
return (EBUSY);
}
if (ret == 0)
return (ret);
}
int
{
}
/*
* set up an empty resource map for a given type and dip
*/
int
{
typemapp = (struct ra_type_map *)
KM_SLEEP);
} else {
}
/* allocate and insert in list of dips for this type */
dipmap = (struct ra_dip_type *)
}
}
return (NDI_SUCCESS);
}
/*
* destroys a resource map for a given dip and type
*/
int
{
return (NDI_FAILURE);
}
/*
* destroy all resources for this dip
* remove dip from type list
*/
}
/* remove from dip list */
/*
* This was the last dip with this resource type.
* Remove the type from the global list.
*/
}
return (NDI_SUCCESS);
}
static int
{
return (NDI_FAILURE);
}
return (NDI_SUCCESS);
}
/*
* Find a dip map for the specified type, if NDI_RA_PASS will go up on dev tree
* if found, backdip and backtype will be updated to point to the previous
* dip in the list and previous type for this dip in the list.
* If no such type at all in the resource list both backdip and backtype
* will be null. If the type found but no dip, back dip will be null.
*/
static struct ra_dip_type *
{
while (*prevmap) {
break;
}
if (*prevmap) {
while (dipmap) {
break;
}
/* found it */
break;
}
if (!(flag & NDI_RA_PASS)) {
break;
}
}
}
return (dipmap);
}
int
{
if (len == 0) {
return (NDI_SUCCESS);
}
return (NDI_FAILURE);
}
/* now find where range lies and fix things up */
/* check for overlap first */
/* overlap with mapp */
overlapmap = mapp;
goto overlap;
/* overlap with mapp->ra_next */
goto overlap;
}
/* simple - on front */
/*
* don't need to check if it merges with
* previous since that would match on on end
*/
break;
/* simple - on end */
/* merge with next node */
}
break;
/* somewhere in between so just an insert */
newmap = (struct ra_resource *)
break;
}
}
/* stick on end */
newmap = (struct ra_resource *)
}
/*
* Update dip's "available" property, adding this piece of
* resource to the pool.
*/
done:
return (NDI_SUCCESS);
/*
* Bad free may happen on some x86 platforms with BIOS exporting
* incorrect resource maps. The system is otherwise functioning
* normally. We send such messages to syslog only.
*/
overlapmap->ra_len);
return (NDI_FAILURE);
}
/* check to see if value is power of 2 or not. */
static int
{
/*
* ddi_ffs and ddi_fls gets long values, so in 32bit environment
* won't work correctly for 64bit values
*/
return (0);
return (1);
}
static void
{
/* in the middle or end */
/* on the end */
} else {
/* in the middle */
newmap = (struct ra_resource *)
}
} else {
/* at the beginning */
/* remove the whole node */
}
}
}
int
{
*retbasep = 0;
*retlenp = 0;
return (NDI_FAILURE);
}
}
return (NDI_FAILURE);
}
backlargestp = NULL;
largestbase = 0;
largestlen = 0;
lower = 0;
/* bounded so skip to first possible */
/*
* This elements end goes beyond max uint64_t.
* potential candidate, check end against lower
* would not be precise.
*/
break;
}
}
/* first fit - not user specified */
}
/*
* failed a critical constraint
* adjust and see if it still fits
*/
base);
/*
* Check to see if the new base is past
* the end of the resource.
*/
continue;
}
}
else
if ((backlargestp == NULL) ||
(largestlen < remlen)) {
largestbase = base;
largestlen = remlen;
}
}
/* a candidate -- apply constraints */
continue;
}
/* we have a fit */
rval = NDI_SUCCESS;
break;
}
}
} else {
/*
* This is the node with he requested base in
* its range
*/
/* length requirement not satisfied */
else
remlen =
(base -
}
largestbase = base;
largestlen = remlen;
base = 0;
} else {
/* We have a match */
rval = NDI_SUCCESS;
}
break;
}
}
}
if ((rval != NDI_SUCCESS) &&
(backlargestp != NULL)) {
base = largestbase;
len = largestlen;
}
if (rval == NDI_FAILURE) {
*retbasep = 0;
*retlenp = 0;
} else {
}
/*
* Update dip's "available" property, substract this piece of
* resource from the pool.
*/
return (rval);
}
/*
* isa_resource_setup
* check for /used-resources and initialize
* based on info there. If no /used-resources,
* fail.
*/
int
{
/*
* note that at this time bootconf creates 32 bit properties for
* io-space and device-memory
*/
struct iorange {
} *iorange;
struct memrange {
} *memrange;
int proplen;
int i, len;
int maxrange;
"isa_resource_setup: used-resources not found");
return (NDI_FAILURE);
}
/*
* initialize to all resources being present
* and then remove the ones in use.
*/
usedpdip = ddi_root_node();
return (NDI_FAILURE);
}
/* initialize io space, highest end base is 0xffff */
/* note that length is highest addr + 1 since starts from 0 */
/* remove the "used" I/O resources */
for (i = 0; i < maxrange; i++) {
NDI_RA_TYPE_IO, 0);
}
}
return (NDI_FAILURE);
}
/* initialize memory space where highest end base is 0xffffffff */
/* note that length is highest addr + 1 since starts from 0 */
NDI_RA_TYPE_MEM, 0);
/* remove the "used" memory resources */
for (i = 0; i < maxrange; i++) {
NDI_RA_TYPE_MEM, 0);
}
}
return (NDI_FAILURE);
}
/* initialize the interrupt space */
NDI_RA_TYPE_INTR, 0);
#endif
/* Initialize available interrupts by negating the used */
for (i = 0; i < len; i++) {
NDI_RA_TYPE_INTR, 0);
}
}
#ifdef BUSRA_DEBUG
if (busra_debug) {
}
#endif
return (NDI_SUCCESS);
}
#ifdef BUSRA_DEBUG
void
{
continue;
}
continue;
}
}
break;
}
break;
}
}
#endif
struct busnum_ctrl {
int rv;
};
/*
* Setup resource map for the pci bus node based on the "available"
* property and "bus-range" property.
*/
int
{
int len;
int circular_count;
/*
* If this is a pci bus node then look for "available" property
* to find the available resources on this bus.
*/
return (NDI_FAILURE);
return (NDI_FAILURE);
/*
* The pci-hotplug project addresses adding the call
* to pci_resource_setup from pci nexus driver.
* However that project would initially be only for x86,
* so for sparc pcmcia-pci support we still need to call
* pci_resource_setup in pcic driver. Once all pci nexus drivers
* are updated to call pci_resource_setup this portion of the
* code would really become an assert to make sure this
* function is not called for the same dip twice.
*/
/*
*
* For PCI/PCIE devices under a PCIE hierarchy, ndi_ra_alloc/free
* will update the devinfo node's "available" property, to reflect
* a devinfo node.
* During probe of a new PCI bridge in the hotplug case, PCI
* then calls ndi_ra_free() to use these resources to setup busra
* pool for the new bridge, as well as adding these resources to
* the "available" property of the new devinfo node. Then configu-
* rator will attach driver for the bridge before probing its
* children, and the bridge driver will then initialize its hotplug
* contollers (if it supports hotplug) and HPC driver will call
* this function to setup the busra pool, but the resource pool
* has already been setup at the first of pcicfg_probe_bridge(),
* thus we need the check below to return directly in this case.
* Otherwise the ndi_ra_free() below will see overlapping resources.
*/
{
return (NDI_FAILURE);
}
}
/*
* Create empty resource maps first.
*
* NOTE: If all the allocated resources are already assigned to
* device(s) in the hot plug slot then "available" property may not
* be present. But, subsequent hot plug operation may unconfigure
* the device in the slot and try to free up it's resources. So,
* at the minimum we should create empty maps here.
*/
return (NDI_FAILURE);
}
return (NDI_FAILURE);
}
return (NDI_FAILURE);
}
NDI_FAILURE) {
return (NDI_FAILURE);
}
/* read the "available" property if it is available */
/*
* Remove "available" property as the entries will be
* re-created in ndi_ra_free() below, note prom based
* property will not be removed. But in ndi_ra_free()
* we'll be creating non prom based property entries.
*/
/*
* create the available resource list for both memory and
* io space
*/
for (i = 0; i < rcount; i++) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
(void) ndi_ra_free(dip,
0);
break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
(void) ndi_ra_free(dip,
0);
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
(void) ndi_ra_free(dip,
0);
break;
case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
break;
default:
"pci_resource_setup: bad addr type: %x\n",
break;
}
}
}
/*
* update resource map for available bus numbers if the node
* has available-bus-range or bus-range property.
*/
DDI_SUCCESS) {
/*
* Add bus numbers in the range to the free list.
*/
1, NDI_RA_TYPE_PCI_BUSNUM, 0);
} else {
/*
* We don't have an available-bus-range property. If, instead,
* we have a bus-range property we add all the bus numbers
* in that range to the free list but we must then scan
* for pci-pci bridges on this bus to find out the if there
* are any of those bus numbers already in use. If so, we can
* reclaim them.
*/
&len) == DDI_SUCCESS) {
/*
* Add bus numbers other than the secondary
* bus number to the free list.
*/
(void) ndi_ra_free(dip,
/* scan for pci-pci bridges */
/* failed to create the map */
(void) ndi_ra_map_destroy(dip,
rval = NDI_FAILURE;
}
}
}
}
#ifdef BUSRA_DEBUG
if (busra_debug) {
}
#endif
return (rval);
}
/*
* If the device is a PCI bus device (i.e bus-range property exists) then
* claim the bus numbers used by the device from the specified bus
* resource map.
*/
static int
{
int len;
/* check if this is a PCI bus node */
return (DDI_WALK_PRUNECHILD);
return (DDI_WALK_PRUNECHILD);
/* look for the bus-range property */
/* claim the bus range from the bus resource map */
NDI_RA_TYPE_PCI_BUSNUM, 0) == NDI_SUCCESS)
return (DDI_WALK_PRUNECHILD);
}
}
/*
* Error return.
*/
return (DDI_WALK_TERMINATE);
}
void
{
}
int
{
int i;
return (NDI_FAILURE);
return (NDI_FAILURE);
return (NDI_FAILURE);
/* for each entry in the PCI "available" property */
goto err;
0);
}
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
break;
default:
goto err;
}
}
#ifdef BUSRA_DEBUG
if (busra_debug) {
}
#endif
return (NDI_SUCCESS);
err:
i, avail_p->pci_phys_hi);
return (NDI_FAILURE);
}
/*
* Return true if the devinfo node resides on PCI or PCI Express bus,
* sitting in a PCI Express hierarchy.
*/
static boolean_t
{
char *bus;
/*
*/
"\"device_type\" property for dip %p\n", (void *)dip);
return (B_FALSE);
}
/* pcie bus, done */
return (B_TRUE);
/*
* pci bus, fall through to check if it resides in
* a pcie hierarchy.
*/
} else {
/* other bus, return failure */
return (B_FALSE);
}
/*
* Does this device reside in a pcie fabric ?
*/
break;
}
return (found);
}
/*
*/
static int
char *busra_type)
{
int i, j, k;
return (DDI_SUCCESS);
if (!is_pcie_fabric(dip))
return (DDI_SUCCESS);
if (status != DDI_SUCCESS)
return (status);
/*
* The updated "available" property will at most have one more entry
* than existing one (when the requested range is in the middle of
* the matched property entry)
*/
for (i = 0, j = 0; i < rcount; i++) {
if ((base < range_base) ||
/*
* not a match, copy the entry
*/
goto copy_entry;
}
/*
* range_base base base+len range_base
* +range_len
* +------------+-----------+----------+
* | |///////////| |
* +------------+-----------+----------+
*/
/*
* Found a match, remove the range out of this entry.
*/
if (dlen != 0) {
newregs[j].pci_phys_mid =
newregs[j].pci_phys_low =
(uint32_t)(range_base);
j++;
}
if (dlen != 0) {
newregs[j].pci_phys_mid =
newregs[j].pci_phys_low =
j++;
}
/*
* We've allocated the resource from the matched
* entry, almost finished but still need to copy
* the rest entries from the original property
* array.
*/
for (k = i + 1; k < rcount; k++) {
j++;
}
goto done;
} else {
j++;
}
}
done:
/*
* This should not fail so assert it. For non-debug kernel we don't
* want to panic thus only logging a warning message.
*/
if (!found) {
return (DDI_FAILURE);
}
/*
* Found the resources from parent, update the "available"
* property.
*/
if (j == 0) {
/* all the resources are consumed, remove the property */
} else {
/*
* There are still resource available in the parent dip,
* update with the remaining resources.
*/
"available", (int *)newregs,
(j * sizeof (pci_regspec_t)) / sizeof (int));
}
return (DDI_SUCCESS);
}
/*
*/
static int
char *busra_type)
{
int i, j, k;
int matched = 0;
return (DDI_SUCCESS);
if (!is_pcie_fabric(dip))
return (DDI_SUCCESS);
switch (status) {
case DDI_PROP_NOT_FOUND:
goto not_found;
case DDI_PROP_SUCCESS:
break;
default:
return (status);
}
/*
* The "available" property exist on the node, try to put this
* resource back, merge if there are adjacent resources.
*
* The updated "available" property will at most have one more entry
* than existing one (when there is no adjacent entries thus the new
* resource is appended at the end)
*/
for (i = 0, j = 0; i < rcount; i++) {
/*
* Not adjacent, copy the entry and contiue
*/
goto copy_entry;
}
/*
* Adjacent or overlap?
*
* Should not have overlapping resources so assert it.
* For non-debug kernel we don't want to panic thus
* only logging a warning message.
*/
#if 0
#endif
"failed to add resource to dip %p : "
"overlaps with existing resource "
goto failure;
}
/*
* On the left:
*
* base range_base
* +-------------+-------------+
* |/////////////| |
* +-------------+-------------+
* len range_len
*
* On the right:
*
* range_base base
* +-------------+-------------+
* | |/////////////|
* +-------------+-------------+
* range_len len
*/
/*
* There are at most two piece of resources adjacent
* with this resource, assert it.
*/
if (!(matched < 2)) {
"failed to add resource to dip %p : "
"found overlaps in existing resources\n",
goto failure;
}
/* setup base & len to refer to the merged range */
base = range_base;
if (matched == 0) {
/*
* One adjacent entry, add this resource in
*/
newregs[j].pci_phys_mid =
matched = 1;
k = j;
j++;
} else { /* matched == 1 */
/*
* Two adjacent entries, merge them together
*/
newregs[k].pci_phys_mid =
matched = 2;
}
} else {
j++;
}
}
if (matched == 0) {
/* No adjacent entries, append at end */
/*
* According to page 15 of 1275 spec, bit "n" of "available"
* should be set to 1.
*/
j++;
}
"available", (int *)newregs,
(j * sizeof (pci_regspec_t)) / sizeof (int));
return (DDI_SUCCESS);
/*
* There is no "available" property on the parent node, create it.
*/
/*
* According to page 15 of 1275 spec, bit "n" of "available" should
* be set to 1.
*/
"available", (int *)newregs,
sizeof (pci_regspec_t) / sizeof (int));
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static uint32_t
{
/*
* No 64 bit mem support for now
*/
pci_type |= PCI_REG_PF_M;
}
return (pci_type);
}