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