📄 ibmphp_res.c
字号:
/* * IBM Hot Plug Controller Driver * * Written By: Irene Zubarev, IBM Corporation * * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2001,2002 IBM Corp. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Send feedback to <gregkh@us.ibm.com> * */#include <linux/module.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/list.h>#include <linux/init.h>#include "ibmphp.h"static int flags = 0; /* for testing */static void update_resources (struct bus_node *bus_cur, int type, int rangeno);static int once_over (void);static int remove_ranges (struct bus_node *, struct bus_node *);static int update_bridge_ranges (struct bus_node **);static int add_range (int type, struct range_node *, struct bus_node *);static void fix_resources (struct bus_node *);static struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);static LIST_HEAD(gbuses);static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag){ struct bus_node * newbus; if (!(curr) && !(flag)) { err ("NULL pointer passed\n"); return NULL; } newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); if (!newbus) { err ("out of system memory\n"); return NULL; } memset (newbus, 0, sizeof (struct bus_node)); if (flag) newbus->busno = busno; else newbus->busno = curr->bus_num; list_add_tail (&newbus->bus_list, &gbuses); return newbus;}static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr){ struct resource_node *rs; if (!curr) { err ("NULL passed to allocate\n"); return NULL; } rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL); if (!rs) { err ("out of system memory\n"); return NULL; } memset (rs, 0, sizeof (struct resource_node)); rs->busno = curr->bus_num; rs->devfunc = curr->dev_fun; rs->start = curr->start_addr; rs->end = curr->end_addr; rs->len = curr->end_addr - curr->start_addr + 1; return rs;}static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus){ struct bus_node * newbus; struct range_node *newrange; u8 num_ranges = 0; if (first_bus) { newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); if (!newbus) { err ("out of system memory.\n"); return -ENOMEM; } memset (newbus, 0, sizeof (struct bus_node)); newbus->busno = curr->bus_num; } else { newbus = *new_bus; switch (flag) { case MEM: num_ranges = newbus->noMemRanges; break; case PFMEM: num_ranges = newbus->noPFMemRanges; break; case IO: num_ranges = newbus->noIORanges; break; } } newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL); if (!newrange) { if (first_bus) kfree (newbus); err ("out of system memory\n"); return -ENOMEM; } memset (newrange, 0, sizeof (struct range_node)); newrange->start = curr->start_addr; newrange->end = curr->end_addr; if (first_bus || (!num_ranges)) newrange->rangeno = 1; else { /* need to insert our range */ add_range (flag, newrange, newbus); debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end); } switch (flag) { case MEM: newbus->rangeMem = newrange; if (first_bus) newbus->noMemRanges = 1; else { debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); ++newbus->noMemRanges; fix_resources (newbus); } break; case IO: newbus->rangeIO = newrange; if (first_bus) newbus->noIORanges = 1; else { debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); ++newbus->noIORanges; fix_resources (newbus); } break; case PFMEM: newbus->rangePFMem = newrange; if (first_bus) newbus->noPFMemRanges = 1; else { debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); ++newbus->noPFMemRanges; fix_resources (newbus); } break; } *new_bus = newbus; *new_range = newrange; return 0;}/* Notes: * 1. The ranges are ordered. The buses are not ordered. (First come) * * 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem * are not sorted. (no need since use mem node). To not change the entire code, we * also add mem node whenever this case happens so as not to change * ibmphp_check_mem_resource etc (and since it really is taking Mem resource) *//***************************************************************************** * This is the Resource Management initialization function. It will go through * the Resource list taken from EBDA and fill in this module's data structures * * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW * * Input: ptr to the head of the resource list from EBDA * Output: 0, -1 or error codes ***************************************************************************/int __init ibmphp_rsrc_init (void){ struct ebda_pci_rsrc *curr; struct range_node *newrange = NULL; struct bus_node *newbus = NULL; struct bus_node *bus_cur; struct bus_node *bus_prev; struct list_head *tmp; struct resource_node *new_io = NULL; struct resource_node *new_mem = NULL; struct resource_node *new_pfmem = NULL; int rc; struct list_head *tmp_ebda; list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) { curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list); if (!(curr->rsrc_type & PCIDEVMASK)) { /* EBDA still lists non PCI devices, so ignore... */ debug ("this is not a PCI DEVICE in rsrc_init, please take care\n"); // continue; } /* this is a primary bus resource */ if (curr->rsrc_type & PRIMARYBUSMASK) { /* memory */ if ((curr->rsrc_type & RESTYPE) == MMASK) { /* no bus structure exists in place yet */ if (list_empty (&gbuses)) { if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) return rc; list_add_tail (&newbus->bus_list, &gbuses); debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); } else { bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); /* found our bus */ if (bus_cur) { rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0); if (rc) return rc; } else { /* went through all the buses and didn't find ours, need to create a new bus node */ if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) return rc; list_add_tail (&newbus->bus_list, &gbuses); debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); } } } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { /* prefetchable memory */ if (list_empty (&gbuses)) { /* no bus structure exists in place yet */ if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) return rc; list_add_tail (&newbus->bus_list, &gbuses); debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); } else { bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); if (bus_cur) { /* found our bus */ rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0); if (rc) return rc; } else { /* went through all the buses and didn't find ours, need to create a new bus node */ if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) return rc; list_add_tail (&newbus->bus_list, &gbuses); debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); } } } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { /* IO */ if (list_empty (&gbuses)) { /* no bus structure exists in place yet */ if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) return rc; list_add_tail (&newbus->bus_list, &gbuses); debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); } else { bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); if (bus_cur) { rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0); if (rc) return rc; } else { /* went through all the buses and didn't find ours, need to create a new bus node */ if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) return rc; list_add_tail (&newbus->bus_list, &gbuses); debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); } } } else { ; /* type is reserved WHAT TO DO IN THIS CASE??? NOTHING TO DO??? */ } } else { /* regular pci device resource */ if ((curr->rsrc_type & RESTYPE) == MMASK) { /* Memory resource */ new_mem = alloc_resources (curr); if (!new_mem) return -ENOMEM; new_mem->type = MEM; /* * if it didn't find the bus, means PCI dev * came b4 the Primary Bus info, so need to * create a bus rangeno becomes a problem... * assign a -1 and then update once the range * actually appears... */ if (ibmphp_add_resource (new_mem) < 0) { newbus = alloc_error_bus (curr, 0, 0); if (!newbus) return -ENOMEM; newbus->firstMem = new_mem; ++newbus->needMemUpdate; new_mem->rangeno = -1; } debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end); } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { /* PFMemory resource */ new_pfmem = alloc_resources (curr); if (!new_pfmem) return -ENOMEM; new_pfmem->type = PFMEM; new_pfmem->fromMem = FALSE; if (ibmphp_add_resource (new_pfmem) < 0) { newbus = alloc_error_bus (curr, 0, 0); if (!newbus) return -ENOMEM; newbus->firstPFMem = new_pfmem; ++newbus->needPFMemUpdate; new_pfmem->rangeno = -1; } debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end); } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { /* IO resource */ new_io = alloc_resources (curr); if (!new_io) return -ENOMEM; new_io->type = IO; /* * if it didn't find the bus, means PCI dev * came b4 the Primary Bus info, so need to * create a bus rangeno becomes a problem... * Can assign a -1 and then update once the * range actually appears... */ if (ibmphp_add_resource (new_io) < 0) { newbus = alloc_error_bus (curr, 0, 0); if (!newbus) return -ENOMEM; newbus->firstIO = new_io; ++newbus->needIOUpdate; new_io->rangeno = -1; } debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end); } } } list_for_each (tmp, &gbuses) { bus_cur = list_entry (tmp, struct bus_node, bus_list); /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */ rc = update_bridge_ranges (&bus_cur); if (rc) return rc; } rc = once_over (); /* This is to align ranges (so no -1) */ if (rc) return rc; return 0;}/******************************************************************************** * This function adds a range into a sorted list of ranges per bus for a particular * range type, it then calls another routine to update the range numbers on the * pci devices' resources for the appropriate resource * * Input: type of the resource, range to add, current bus * Output: 0 or -1, bus and range ptrs ********************************************************************************/static int add_range (int type, struct range_node *range, struct bus_node *bus_cur){ struct range_node *range_cur = NULL; struct range_node *range_prev; int count = 0, i_init; int noRanges = 0; switch (type) { case MEM: range_cur = bus_cur->rangeMem; noRanges = bus_cur->noMemRanges; break; case PFMEM: range_cur = bus_cur->rangePFMem; noRanges = bus_cur->noPFMemRanges; break; case IO: range_cur = bus_cur->rangeIO; noRanges = bus_cur->noIORanges; break; } range_prev = NULL; while (range_cur) { if (range->start < range_cur->start) break; range_prev = range_cur; range_cur = range_cur->next; count = count + 1; } if (!count) { /* our range will go at the beginning of the list */ switch (type) { case MEM: bus_cur->rangeMem = range; break; case PFMEM: bus_cur->rangePFMem = range; break; case IO: bus_cur->rangeIO = range; break; } range->next = range_cur; range->rangeno = 1; i_init = 0; } else if (!range_cur) { /* our range will go at the end of the list */ range->next = NULL; range_prev->next = range; range->rangeno = range_prev->rangeno + 1; return 0; } else { /* the range is in the middle */ range_prev->next = range; range->next = range_cur; range->rangeno = range_cur->rangeno; i_init = range_prev->rangeno; } for (count = i_init; count < noRanges; ++count) { ++range_cur->rangeno; range_cur = range_cur->next; } update_resources (bus_cur, type, i_init + 1); return 0;}/******************************************************************************* * This routine goes through the list of resources of type 'type' and updates * the range numbers that they correspond to. It was called from add_range fnc * * Input: bus, type of the resource, the rangeno starting from which to update ******************************************************************************/static void update_resources (struct bus_node *bus_cur, int type, int rangeno){ struct resource_node *res = NULL; u8 eol = FALSE; /* end of list indicator */ switch (type) { case MEM: if (bus_cur->firstMem) res = bus_cur->firstMem; break; case PFMEM: if (bus_cur->firstPFMem) res = bus_cur->firstPFMem; break; case IO: if (bus_cur->firstIO) res = bus_cur->firstIO; break; } if (res) { while (res) { if (res->rangeno == rangeno) break; if (res->next) res = res->next; else if (res->nextRange) res = res->nextRange; else { eol = TRUE; break; } } if (!eol) { /* found the range */ while (res) { ++res->rangeno; res = res->next; } } }}static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range){ char * str = ""; switch (res->type) { case IO: str = "io"; break; case MEM: str = "mem"; break; case PFMEM: str = "pfmem"; break; } while (res) { if (res->rangeno == -1) { while (range) { if ((res->start >= range->start) && (res->end <= range->end)) { res->rangeno = range->rangeno; debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno); switch (res->type) { case IO: --bus_cur->needIOUpdate; break; case MEM: --bus_cur->needMemUpdate; break; case PFMEM: --bus_cur->needPFMemUpdate; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -