2483 lines · 62.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Support routines for initializing a PCI subsystem * * Extruded from code written by *      Dave Rusling (david.rusling@reo.mts.dec.com) *      David Mosberger (davidm@cs.arizona.edu) *	David Miller (davem@redhat.com) * * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> *	     PCI-PCI bridges cleanup, sorted resource allocation. * Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru> *	     Converted to allocation in 3 passes, which gives *	     tighter packing. Prefetchable range support. */ #include <linux/align.h>#include <linux/bitops.h>#include <linux/bug.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/minmax.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/cache.h>#include <linux/limits.h>#include <linux/sizes.h>#include <linux/slab.h>#include <linux/acpi.h>#include "pci.h" #define PCI_RES_TYPE_MASK \	(IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\	 IORESOURCE_MEM_64) unsigned int pci_flags;EXPORT_SYMBOL_GPL(pci_flags); struct pci_dev_resource {	struct list_head list;	struct resource *res;	struct pci_dev *dev;	resource_size_t start;	resource_size_t end;	resource_size_t add_size;	resource_size_t min_align;	unsigned long flags;}; static void pci_dev_res_free_list(struct list_head *head){	struct pci_dev_resource *dev_res, *tmp; 	list_for_each_entry_safe(dev_res, tmp, head, list) {		list_del(&dev_res->list);		kfree(dev_res);	}} /** * pci_dev_res_add_to_list() - Add a new resource tracker to the list * @head:	Head of the list * @dev:	Device to which the resource belongs * @res:	Resource to be tracked * @add_size:	Additional size to be optionally added to the resource * @min_align:	Minimum memory window alignment */int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev,			    struct resource *res, resource_size_t add_size,			    resource_size_t min_align){	struct pci_dev_resource *tmp; 	tmp = kzalloc_obj(*tmp);	if (!tmp)		return -ENOMEM; 	tmp->res = res;	tmp->dev = dev;	tmp->start = res->start;	tmp->end = res->end;	tmp->flags = res->flags;	tmp->add_size = add_size;	tmp->min_align = min_align; 	list_add(&tmp->list, head); 	return 0;} static void pci_dev_res_remove_from_list(struct list_head *head,					 struct resource *res){	struct pci_dev_resource *dev_res, *tmp; 	list_for_each_entry_safe(dev_res, tmp, head, list) {		if (dev_res->res == res) {			list_del(&dev_res->list);			kfree(dev_res);			break;		}	}} static struct pci_dev_resource *res_to_dev_res(struct list_head *head,					       struct resource *res){	struct pci_dev_resource *dev_res; 	list_for_each_entry(dev_res, head, list) {		if (dev_res->res == res)			return dev_res;	} 	return NULL;} static resource_size_t get_res_add_size(struct list_head *head,					struct resource *res){	struct pci_dev_resource *dev_res; 	dev_res = res_to_dev_res(head, res);	return dev_res ? dev_res->add_size : 0;} static void pci_dev_res_restore(struct pci_dev_resource *dev_res){	struct resource *res = dev_res->res;	struct pci_dev *dev = dev_res->dev;	int idx = pci_resource_num(dev, res);	const char *res_name = pci_resource_name(dev, idx); 	if (WARN_ON_ONCE(resource_assigned(res)))		return; 	res->start = dev_res->start;	res->end = dev_res->end;	res->flags = dev_res->flags; 	pci_dbg(dev, "%s %pR: resource restored\n", res_name, res);} /* * Helper function for sizing routines.  Assigned resources have non-NULL * parent resource. * * Return first unassigned resource of the correct type.  If there is none, * return first assigned resource of the correct type.  If none of the * above, return NULL. * * Returning an assigned resource of the correct type allows the caller to * distinguish between already assigned and no resource of the correct type. */static struct resource *find_bus_resource_of_type(struct pci_bus *bus,						  unsigned long type_mask,						  unsigned long type){	struct resource *r, *r_assigned = NULL; 	pci_bus_for_each_resource(bus, r) {		if (!r || r == &ioport_resource || r == &iomem_resource)			continue; 		if ((r->flags & type_mask) != type)			continue; 		if (!resource_assigned(r))			return r;		if (!r_assigned)			r_assigned = r;	}	return r_assigned;} /** * pbus_select_window_for_type - Select bridge window for a resource type * @bus: PCI bus * @type: Resource type (resource flags can be passed as is) * * Select the bridge window based on a resource @type. * * For memory resources, the selection is done as follows: * * Any non-prefetchable resource is put into the non-prefetchable window. * * If there is no prefetchable MMIO window, put all memory resources into the * non-prefetchable window. * * If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable * resources into it and place 32-bit prefetchable memory into the * non-prefetchable window. * * Otherwise, put all prefetchable resources into the prefetchable window. * * Return: the bridge window resource or NULL if no bridge window is found. */static struct resource *pbus_select_window_for_type(struct pci_bus *bus,						    unsigned long type){	int iores_type = type & IORESOURCE_TYPE_BITS;	/* w/o 64bit & pref */	struct resource *mmio, *mmio_pref, *win; 	type &= PCI_RES_TYPE_MASK;			/* with 64bit & pref */ 	if ((iores_type != IORESOURCE_IO) && (iores_type != IORESOURCE_MEM))		return NULL; 	if (pci_is_root_bus(bus)) {		win = find_bus_resource_of_type(bus, type, type);		if (win)			return win; 		type &= ~IORESOURCE_MEM_64;		win = find_bus_resource_of_type(bus, type, type);		if (win)			return win; 		type &= ~IORESOURCE_PREFETCH;		return find_bus_resource_of_type(bus, type, type);	} 	switch (iores_type) {	case IORESOURCE_IO:		win = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_IO_WINDOW);		if (win && (win->flags & IORESOURCE_IO))			return win;		return NULL; 	case IORESOURCE_MEM:		mmio = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_MEM_WINDOW);		mmio_pref = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_PREF_MEM_WINDOW); 		if (mmio && !(mmio->flags & IORESOURCE_MEM))			mmio = NULL;		if (mmio_pref && !(mmio_pref->flags & IORESOURCE_MEM))			mmio_pref = NULL; 		if (!(type & IORESOURCE_PREFETCH) || !mmio_pref)			return mmio; 		if ((type & IORESOURCE_MEM_64) ||		    !(mmio_pref->flags & IORESOURCE_MEM_64))			return mmio_pref; 		return mmio;	default:		return NULL;	}} /** * pbus_select_window - Select bridge window for a resource * @bus: PCI bus * @res: Resource * * Select the bridge window for @res. If the resource is already assigned, * return the current bridge window. * * For memory resources, the selection is done as follows: * * Any non-prefetchable resource is put into the non-prefetchable window. * * If there is no prefetchable MMIO window, put all memory resources into the * non-prefetchable window. * * If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable * resources into it and place 32-bit prefetchable memory into the * non-prefetchable window. * * Otherwise, put all prefetchable resources into the prefetchable window. * * Return: the bridge window resource or NULL if no bridge window is found. */struct resource *pbus_select_window(struct pci_bus *bus,				    const struct resource *res){	if (resource_assigned(res))		return res->parent; 	return pbus_select_window_for_type(bus, res->flags);} static bool pdev_resources_assignable(struct pci_dev *dev){	u16 class = dev->class >> 8, command; 	/* Don't touch classless devices or host bridges or IOAPICs */	if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST)		return false; 	/* Don't touch IOAPIC devices already enabled by firmware */	if (class == PCI_CLASS_SYSTEM_PIC) {		pci_read_config_word(dev, PCI_COMMAND, &command);		if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))			return false;	} 	return true;} static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res){	int idx = pci_resource_num(dev, res); 	if (!res->flags)		return false; 	if (pci_resource_is_bridge_win(idx) && res->flags & IORESOURCE_DISABLED)		return false; 	return true;} static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res){	if (resource_assigned(res))		return false; 	if (res->flags & IORESOURCE_PCI_FIXED)		return false; 	return pdev_resource_assignable(dev, res);} /* Sort resources by alignment */static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head){	struct resource *r;	int i; 	if (!pdev_resources_assignable(dev))		return; 	pci_dev_for_each_resource(dev, r, i) {		const char *r_name = pci_resource_name(dev, i);		struct pci_dev_resource *dev_res, *tmp;		resource_size_t r_align;		struct list_head *n; 		if (!pdev_resource_should_fit(dev, r))			continue; 		r_align = pci_resource_alignment(dev, r);		if (!r_align) {			pci_warn(dev, "%s %pR: alignment must not be zero\n",				 r_name, r);			continue;		} 		tmp = kzalloc_obj(*tmp);		if (!tmp)			panic("%s: kzalloc() failed!\n", __func__);		tmp->res = r;		tmp->dev = dev;		tmp->start = r->start;		tmp->end = r->end;		tmp->flags = r->flags; 		/* Fallback is smallest one or list is empty */		n = head;		list_for_each_entry(dev_res, head, list) {			resource_size_t align; 			align = pci_resource_alignment(dev_res->dev,							 dev_res->res); 			if (r_align > align) {				n = &dev_res->list;				break;			}		}		/* Insert it just before n */		list_add_tail(&tmp->list, n);	}} bool pci_resource_is_optional(const struct pci_dev *dev, int resno){	const struct resource *res = pci_resource_n(dev, resno); 	if (pci_resource_is_iov(resno))		return true;	if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE))		return true;	if (pci_resource_is_bridge_win(resno) && !resource_size(res))		return true; 	return false;} static void reset_resource(struct pci_dev *dev, struct resource *res){	int idx = pci_resource_num(dev, res);	const char *res_name = pci_resource_name(dev, idx); 	if (pci_resource_is_bridge_win(idx)) {		res->flags |= IORESOURCE_UNSET;		return;	} 	pci_dbg(dev, "%s %pR: resetting resource\n", res_name, res); 	res->start = 0;	res->end = 0;	res->flags = 0;} /** * reassign_resources_sorted() - Satisfy any additional resource requests * * @realloc_head:	Head of the list tracking requests requiring *			additional resources * @head:		Head of the list tracking requests with allocated *			resources * * Walk through each element of the realloc_head and try to procure additional * resources for the element, provided the element is in the head list. */static void reassign_resources_sorted(struct list_head *realloc_head,				      struct list_head *head){	struct pci_dev_resource *add_res, *tmp;	struct pci_dev *dev;	struct resource *res;	const char *res_name;	resource_size_t add_size, align;	int idx; 	list_for_each_entry_safe(add_res, tmp, realloc_head, list) {		res = add_res->res;		dev = add_res->dev;		idx = pci_resource_num(dev, res); 		/* Skip this resource if not found in head list */		if (!res_to_dev_res(head, res))			continue; 		/*		 * Skip resource that failed the earlier assignment and is		 * not optional as it would just fail again.		 */		if (!resource_assigned(res) && resource_size(res) &&		    !pci_resource_is_optional(dev, idx))			goto out; 		res_name = pci_resource_name(dev, idx);		add_size = add_res->add_size;		align = add_res->min_align;		if (!resource_assigned(res)) {			resource_set_range(res, align,					   resource_size(res) + add_size);			if (pci_assign_resource(dev, idx)) {				pci_dbg(dev,					"%s %pR: ignoring failure in optional allocation\n",					res_name, res);			}		} else if (add_size > 0 || !IS_ALIGNED(res->start, align)) {			res->flags |= add_res->flags &				 (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);			if (pci_reassign_resource(dev, idx, add_size, align))				pci_info(dev, "%s %pR: failed to add optional %llx\n",					 res_name, res,					 (unsigned long long) add_size);		}out:		list_del(&add_res->list);		kfree(add_res);	}} /** * assign_requested_resources_sorted() - Satisfy resource requests * * @head:	Head of the list tracking requests for resources * @fail_head:	Head of the list tracking requests that could not be *		allocated * @optional:	Assign also optional resources * * Satisfy resource requests of each element in the list.  Add requests that * could not be satisfied to the failed_list. */static void assign_requested_resources_sorted(struct list_head *head,					      struct list_head *fail_head,					      bool optional){	struct pci_dev_resource *dev_res;	struct resource *res;	struct pci_dev *dev;	bool optional_res;	int idx; 	list_for_each_entry(dev_res, head, list) {		res = dev_res->res;		dev = dev_res->dev;		idx = pci_resource_num(dev, res);		optional_res = pci_resource_is_optional(dev, idx); 

Lines 1–500 of 2483.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t