OpenGrok

Cross Reference: pci_bios.c
xref: /onnv/onnv-gate/usr/src/uts/i86pc/os/pci_bios.c
Home | History | Annotate | Line # | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
     23  */
     24 
     25 #include <sys/types.h>
     26 #include <sys/stat.h>
     27 #include <sys/sunndi.h>
     28 #include <sys/pci.h>
     29 #include <sys/pci_impl.h>
     30 #include <sys/pci_cfgspace.h>
     31 #include <sys/pci_cfgspace_impl.h>
     32 #include <sys/memlist.h>
     33 #include <sys/bootconf.h>
     34 #include <sys/psw.h>
     35 
     36 /*
     37  * pci irq routing information table
     38  */
     39 int				pci_irq_nroutes;
     40 static pci_irq_route_t		*pci_irq_routes;
     41 
     42 
     43 static int pci_bios_get_irq_routing(pci_irq_route_t *, int, int *);
     44 static void pci_get_irq_routing_table(void);
     45 
     46 
     47 /*
     48  * Retrieve information from the bios needed for system
     49  * configuration early during startup.
     50  */
     51 void
     52 startup_pci_bios(void)
     53 {
     54 	pci_get_irq_routing_table();
     55 }
     56 
     57 
     58 /*
     59  * Issue the bios get irq routing information table interrupt
     60  *
     61  * Despite the name, the information in the table is only
     62  * used to derive slot names for some named pci hot-plug slots.
     63  *
     64  * Returns the number of irq routing table entries returned
     65  * by the bios, or 0 and optionally, the number of entries required.
     66  */
     67 static int
     68 pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp)
     69 {
     70 	struct bop_regs regs;
     71 	uchar_t		*hdrp;
     72 	uchar_t		*bufp;
     73 	int 		i, n;
     74 	int		rval = 0;
     75 
     76 	if (nneededp)
     77 		*nneededp = 0;
     78 
     79 	/*
     80 	 * Set up irq routing header with the size and address
     81 	 * of some useable low-memory data addresses.  Initalize
     82 	 * data area to zero, avoiding memcpy/bzero.
     83 	 */
     84 	hdrp = (uchar_t *)BIOS_IRQ_ROUTING_HDR;
     85 	bufp = (uchar_t *)BIOS_IRQ_ROUTING_DATA;
     86 
     87 	n = nroutes * sizeof (pci_irq_route_t);
     88 	for (i = 0; i < n; i++)
     89 		bufp[i] = 0;
     90 	((pci_irq_route_hdr_t *)hdrp)->pir_size = n;
     91 	((pci_irq_route_hdr_t *)hdrp)->pir_addr = (uint32_t)(uintptr_t)bufp;
     92 
     93 	bzero(&regs, sizeof (regs));
     94 	regs.eax.word.ax = (PCI_FUNCTION_ID << 8) | PCI_GET_IRQ_ROUTING;
     95 
     96 	regs.ds = 0xf000;
     97 	regs.es = FP_SEG((uint_t)(uintptr_t)hdrp);
     98 	regs.edi.word.di = FP_OFF((uint_t)(uintptr_t)hdrp);
     99 
    100 	BOP_DOINT(bootops, 0x1a, &regs);
    101 
    102 	n = (int)(((pci_irq_route_hdr_t *)hdrp)->pir_size /
    103 	    sizeof (pci_irq_route_t));
    104 
    105 	if ((regs.eflags & PS_C) != 0) {
    106 		if (nneededp)
    107 			*nneededp = n;
    108 	} else {
    109 		/*
    110 		 * Copy resulting irq routing data from low memory up to
    111 		 * the kernel address space, avoiding memcpy as usual.
    112 		 */
    113 		if (n <= nroutes) {
    114 			for (i = 0; i < n * sizeof (pci_irq_route_t); i++)
    115 				((uchar_t *)routes)[i] = bufp[i];
    116 			rval = n;
    117 		}
    118 	}
    119 	return (rval);
    120 }
    121 
    122 static void
    123 pci_get_irq_routing_table(void)
    124 {
    125 	pci_irq_route_t	*routes;
    126 	int		n = N_PCI_IRQ_ROUTES;
    127 	int		nneeded = 0;
    128 	int		nroutes;
    129 
    130 	/*
    131 	 * Get irq routing table information.
    132 	 * Allocate a buffer for an initial default number of entries.
    133 	 * If the bios indicates it needs a larger buffer, try it again.
    134 	 * Drive on if it still won't cooperate and play nice after that.
    135 	 */
    136 	routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
    137 	nroutes = pci_bios_get_irq_routing(routes, n, &nneeded);
    138 	if (nroutes == 0 && nneeded > n) {
    139 		kmem_free(routes, n * sizeof (pci_irq_route_t));
    140 		if (nneeded > N_PCI_IRQ_ROUTES_MAX) {
    141 			cmn_err(CE_CONT,
    142 			    "pci: unable to get IRQ routing information, "
    143 			    "required buffer space of %d entries exceeds max\n",
    144 			    nneeded);
    145 			return;
    146 		}
    147 		n = nneeded;
    148 		routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
    149 		nroutes = pci_bios_get_irq_routing(routes, n, NULL);
    150 		if (nroutes == 0) {
    151 			cmn_err(CE_CONT,
    152 			    "pci: unable to get IRQ routing information, "
    153 			    "required buffer space for %d entries\n", n);
    154 			kmem_free(routes, n * sizeof (pci_irq_route_t));
    155 		}
    156 	}
    157 
    158 	if (nroutes > 0) {
    159 		pci_irq_routes = routes;
    160 		pci_irq_nroutes = nroutes;
    161 	}
    162 }
    163 
    164 /*
    165  * Use the results of the PCI BIOS call that returned the routing tables
    166  * to build the 1275 slot-names property for the indicated bus.
    167  * Results are returned in buf.  Length is return value, -1 is returned on
    168  * overflow and zero is returned if no data exists to build a property.
    169  */
    170 int
    171 pci_slot_names_prop(int bus, char *buf, int len)
    172 {
    173 	uchar_t		dev;
    174 	uchar_t		slot[N_PCI_IRQ_ROUTES_MAX+1];
    175 	uint32_t	 mask;
    176 	int		i, nnames, plen;
    177 
    178 	ASSERT(pci_irq_nroutes <= N_PCI_IRQ_ROUTES_MAX);
    179 
    180 	if (pci_irq_nroutes == 0)
    181 		return (0);
    182 	nnames = 0;
    183 	mask = 0;
    184 	for (i = 0; i < pci_irq_nroutes; i++)
    185 		slot[i] = 0xff;
    186 	for (i = 0; i < pci_irq_nroutes; i++) {
    187 		if (pci_irq_routes[i].pir_bus != bus)
    188 			continue;
    189 		if (pci_irq_routes[i].pir_slot != 0) {
    190 			dev = (pci_irq_routes[i].pir_dev & 0xf8) >> 3;
    191 			slot[dev] = pci_irq_routes[i].pir_slot;
    192 			mask |= (1 << dev);
    193 			nnames++;
    194 		}
    195 	}
    196 
    197 	if (nnames == 0)
    198 		return (0);
    199 
    200 	if (len < (4 + nnames * 8))
    201 		return (-1);
    202 	*(uint32_t *)buf = mask;
    203 	plen = 4;
    204 	for (i = 0; i < pci_irq_nroutes; i++) {
    205 		if (slot[i] == 0xff)
    206 			continue;
    207 		(void) sprintf(buf + plen, "Slot%d", slot[i]);
    208 		plen += strlen(buf+plen) + 1;
    209 		*(buf + plen) = 0;
    210 	}
    211 	for (; plen % 4; plen++)
    212 		*(buf + plen) = 0;
    213 	return (plen);
    214 }
    215