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 }