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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/sunndi.h> 31 #include <sys/pci.h> 32 #include <sys/pci_impl.h> 33 #include <sys/pci_cfgspace.h> 34 #include <sys/pci_cfgspace_impl.h> 35 #include <sys/memlist.h> 36 #include <sys/bootconf.h> 37 #include <sys/psw.h> 38 39 /* 40 * pci irq routing information table 41 */ 42 static int pci_irq_nroutes; 43 static pci_irq_route_t *pci_irq_routes; 44 45 46 static int pci_bios_get_irq_routing(pci_irq_route_t *, int, int *); 47 static void pci_get_irq_routing_table(void); 48 49 50 /* 51 * Retrieve information from the bios needed for system 52 * configuration early during startup. 53 */ 54 void 55 startup_pci_bios(void) 56 { 57 pci_get_irq_routing_table(); 58 } 59 60 61 /* 62 * Issue the bios get irq routing information table interrupt 63 * 64 * Despite the name, the information in the table is only 65 * used to derive slot names for some named pci hot-plug slots. 66 * 67 * Returns the number of irq routing table entries returned 68 * by the bios, or 0 and optionally, the number of entries required. 69 */ 70 static int 71 pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp) 72 { 73 struct bop_regs regs; 74 uchar_t *hdrp; 75 uchar_t *bufp; 76 int i, n; 77 int rval = 0; 78 79 if (nneededp) 80 *nneededp = 0; 81 82 /* 83 * Set up irq routing header with the size and address 84 * of some useable low-memory data addresses. Initalize 85 * data area to zero, avoiding memcpy/bzero. 86 */ 87 hdrp = (uchar_t *)BIOS_IRQ_ROUTING_HDR; 88 bufp = (uchar_t *)BIOS_IRQ_ROUTING_DATA; 89 90 n = nroutes * sizeof (pci_irq_route_t); 91 for (i = 0; i < n; i++) 92 bufp[i] = 0; 93 ((pci_irq_route_hdr_t *)hdrp)->pir_size = n; 94 ((pci_irq_route_hdr_t *)hdrp)->pir_addr = (uint32_t)(uintptr_t)bufp; 95 96 bzero(®s, sizeof (regs)); 97 regs.eax.word.ax = (PCI_FUNCTION_ID << 8) | PCI_GET_IRQ_ROUTING; 98 99 regs.ds = 0xf000; 100 regs.es = FP_SEG((uint_t)(uintptr_t)hdrp); 101 regs.edi.word.di = FP_OFF((uint_t)(uintptr_t)hdrp); 102 103 BOP_DOINT(bootops, 0x1a, ®s); 104 105 n = (int)(((pci_irq_route_hdr_t *)hdrp)->pir_size / 106 sizeof (pci_irq_route_t)); 107 108 if ((regs.eflags & PS_C) != 0) { 109 if (nneededp) 110 *nneededp = n; 111 } else { 112 /* 113 * Copy resulting irq routing data from low memory up to 114 * the kernel address space, avoiding memcpy as usual. 115 */ 116 if (n <= nroutes) { 117 for (i = 0; i < n * sizeof (pci_irq_route_t); i++) 118 ((uchar_t *)routes)[i] = bufp[i]; 119 rval = n; 120 } 121 } 122 return (rval); 123 } 124 125 static void 126 pci_get_irq_routing_table(void) 127 { 128 pci_irq_route_t *routes; 129 int n = N_PCI_IRQ_ROUTES; 130 int nneeded = 0; 131 int nroutes; 132 133 /* 134 * Get irq routing table information. 135 * Allocate a buffer for an initial default number of entries. 136 * If the bios indicates it needs a larger buffer, try it again. 137 * Drive on if it still won't cooperate and play nice after that. 138 */ 139 routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP); 140 nroutes = pci_bios_get_irq_routing(routes, n, &nneeded); 141 if (nroutes == 0 && nneeded > n) { 142 kmem_free(routes, n * sizeof (pci_irq_route_t)); 143 if (nneeded > N_PCI_IRQ_ROUTES_MAX) { 144 cmn_err(CE_CONT, 145 "pci: unable to get IRQ routing information, " 146 "required buffer space of %d entries exceeds max\n", 147 nneeded); 148 return; 149 } 150 n = nneeded; 151 routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP); 152 nroutes = pci_bios_get_irq_routing(routes, n, NULL); 153 if (nroutes == 0) { 154 cmn_err(CE_CONT, 155 "pci: unable to get IRQ routing information, " 156 "required buffer space for %d entries\n", n); 157 kmem_free(routes, n * sizeof (pci_irq_route_t)); 158 } 159 } 160 161 if (nroutes > 0) { 162 pci_irq_routes = routes; 163 pci_irq_nroutes = nroutes; 164 } 165 } 166 167 /* 168 * Use the results of the PCI BIOS call that returned the routing tables 169 * to build the 1275 slot-names property for the indicated bus. 170 * Results are returned in buf. Length is return value, -1 is returned on 171 * overflow and zero is returned if no data exists to build a property. 172 */ 173 int 174 pci_slot_names_prop(int bus, char *buf, int len) 175 { 176 uchar_t dev; 177 uchar_t slot[N_PCI_IRQ_ROUTES_MAX+1]; 178 uint32_t mask; 179 int i, nnames, plen; 180 181 ASSERT(pci_irq_nroutes <= N_PCI_IRQ_ROUTES_MAX); 182 183 if (pci_irq_nroutes == 0) 184 return (0); 185 nnames = 0; 186 mask = 0; 187 for (i = 0; i < pci_irq_nroutes; i++) 188 slot[i] = 0xff; 189 for (i = 0; i < pci_irq_nroutes; i++) { 190 if (pci_irq_routes[i].pir_bus != bus) 191 continue; 192 if (pci_irq_routes[i].pir_slot != 0) { 193 dev = (pci_irq_routes[i].pir_dev & 0xf8) >> 3; 194 slot[dev] = pci_irq_routes[i].pir_slot; 195 mask |= (1 << dev); 196 nnames++; 197 } 198 } 199 200 if (nnames == 0) 201 return (0); 202 203 if (len < (4 + nnames * 8)) 204 return (-1); 205 *(uint32_t *)buf = mask; 206 plen = 4; 207 for (i = 0; i < pci_irq_nroutes; i++) { 208 if (slot[i] == 0xff) 209 continue; 210 (void) sprintf(buf + plen, "Slot%d", slot[i]); 211 plen += strlen(buf+plen) + 1; 212 *(buf + plen) = 0; 213 } 214 for (; plen % 4; plen++) 215 *(buf + plen) = 0; 216 return (plen); 217 } 218