175bcd456Sjg /*
275bcd456Sjg * CDDL HEADER START
375bcd456Sjg *
475bcd456Sjg * The contents of this file are subject to the terms of the
575bcd456Sjg * Common Development and Distribution License (the "License").
675bcd456Sjg * You may not use this file except in compliance with the License.
775bcd456Sjg *
875bcd456Sjg * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
975bcd456Sjg * or http://www.opensolaris.org/os/licensing.
1075bcd456Sjg * See the License for the specific language governing permissions
1175bcd456Sjg * and limitations under the License.
1275bcd456Sjg *
1375bcd456Sjg * When distributing Covered Code, include this CDDL HEADER in each
1475bcd456Sjg * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1575bcd456Sjg * If applicable, add the following below this CDDL HEADER, with the
1675bcd456Sjg * fields enclosed by brackets "[]" replaced with your own identifying
1775bcd456Sjg * information: Portions Copyright [yyyy] [name of copyright owner]
1875bcd456Sjg *
1975bcd456Sjg * CDDL HEADER END
2075bcd456Sjg */
2175bcd456Sjg /*
220db3240dSStephen Hanson * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23d851c238SAndy Fiddaman * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
24*cd0d4b40SRobert Mustacchi * Copyright 2022 Oxide Computer Company
2575bcd456Sjg */
2675bcd456Sjg
2775bcd456Sjg #include <sys/types.h>
2875bcd456Sjg #include <sys/stat.h>
2975bcd456Sjg #include <sys/sunndi.h>
3075bcd456Sjg #include <sys/pci.h>
3175bcd456Sjg #include <sys/pci_impl.h>
3275bcd456Sjg #include <sys/pci_cfgspace.h>
3375bcd456Sjg #include <sys/pci_cfgspace_impl.h>
3475bcd456Sjg #include <sys/memlist.h>
3575bcd456Sjg #include <sys/bootconf.h>
3675bcd456Sjg #include <sys/psw.h>
3775bcd456Sjg
3875bcd456Sjg /*
3975bcd456Sjg * pci irq routing information table
4075bcd456Sjg */
410db3240dSStephen Hanson int pci_irq_nroutes;
4275bcd456Sjg static pci_irq_route_t *pci_irq_routes;
4375bcd456Sjg
4475bcd456Sjg
4575bcd456Sjg static int pci_bios_get_irq_routing(pci_irq_route_t *, int, int *);
4675bcd456Sjg static void pci_get_irq_routing_table(void);
4775bcd456Sjg
4875bcd456Sjg
4975bcd456Sjg /*
5075bcd456Sjg * Retrieve information from the bios needed for system
5175bcd456Sjg * configuration early during startup.
5275bcd456Sjg */
5375bcd456Sjg void
startup_pci_bios(void)5475bcd456Sjg startup_pci_bios(void)
5575bcd456Sjg {
5675bcd456Sjg pci_get_irq_routing_table();
5775bcd456Sjg }
5875bcd456Sjg
5975bcd456Sjg
6075bcd456Sjg /*
6175bcd456Sjg * Issue the bios get irq routing information table interrupt
6275bcd456Sjg *
6375bcd456Sjg * Despite the name, the information in the table is only
6475bcd456Sjg * used to derive slot names for some named pci hot-plug slots.
6575bcd456Sjg *
6675bcd456Sjg * Returns the number of irq routing table entries returned
6775bcd456Sjg * by the bios, or 0 and optionally, the number of entries required.
6875bcd456Sjg */
6975bcd456Sjg static int
pci_bios_get_irq_routing(pci_irq_route_t * routes,int nroutes,int * nneededp)7075bcd456Sjg pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp)
7175bcd456Sjg {
7275bcd456Sjg struct bop_regs regs;
7375bcd456Sjg uchar_t *hdrp;
7475bcd456Sjg uchar_t *bufp;
75d851c238SAndy Fiddaman int i, n;
7675bcd456Sjg int rval = 0;
7775bcd456Sjg
7875bcd456Sjg if (nneededp)
7975bcd456Sjg *nneededp = 0;
8075bcd456Sjg
81a62d310aSToomas Soome /* in UEFI system, there is no BIOS data */
82a62d310aSToomas Soome if (BOP_GETPROPLEN(bootops, "efi-systab") > 0)
83a62d310aSToomas Soome return (0);
84a62d310aSToomas Soome
8575bcd456Sjg /*
8675bcd456Sjg * Set up irq routing header with the size and address
8775bcd456Sjg * of some useable low-memory data addresses. Initalize
8875bcd456Sjg * data area to zero, avoiding memcpy/bzero.
8975bcd456Sjg */
9075bcd456Sjg hdrp = (uchar_t *)BIOS_IRQ_ROUTING_HDR;
9175bcd456Sjg bufp = (uchar_t *)BIOS_IRQ_ROUTING_DATA;
9275bcd456Sjg
9375bcd456Sjg n = nroutes * sizeof (pci_irq_route_t);
9475bcd456Sjg for (i = 0; i < n; i++)
9575bcd456Sjg bufp[i] = 0;
9675bcd456Sjg ((pci_irq_route_hdr_t *)hdrp)->pir_size = n;
9775bcd456Sjg ((pci_irq_route_hdr_t *)hdrp)->pir_addr = (uint32_t)(uintptr_t)bufp;
9875bcd456Sjg
9975bcd456Sjg bzero(®s, sizeof (regs));
10075bcd456Sjg regs.eax.word.ax = (PCI_FUNCTION_ID << 8) | PCI_GET_IRQ_ROUTING;
10175bcd456Sjg
10275bcd456Sjg regs.ds = 0xf000;
10375bcd456Sjg regs.es = FP_SEG((uint_t)(uintptr_t)hdrp);
10475bcd456Sjg regs.edi.word.di = FP_OFF((uint_t)(uintptr_t)hdrp);
10575bcd456Sjg
10675bcd456Sjg BOP_DOINT(bootops, 0x1a, ®s);
10775bcd456Sjg
10875bcd456Sjg n = (int)(((pci_irq_route_hdr_t *)hdrp)->pir_size /
10975bcd456Sjg sizeof (pci_irq_route_t));
11075bcd456Sjg
11175bcd456Sjg if ((regs.eflags & PS_C) != 0) {
11275bcd456Sjg if (nneededp)
11375bcd456Sjg *nneededp = n;
11475bcd456Sjg } else {
11575bcd456Sjg /*
11675bcd456Sjg * Copy resulting irq routing data from low memory up to
11775bcd456Sjg * the kernel address space, avoiding memcpy as usual.
11875bcd456Sjg */
11975bcd456Sjg if (n <= nroutes) {
12075bcd456Sjg for (i = 0; i < n * sizeof (pci_irq_route_t); i++)
12175bcd456Sjg ((uchar_t *)routes)[i] = bufp[i];
12275bcd456Sjg rval = n;
12375bcd456Sjg }
12475bcd456Sjg }
12575bcd456Sjg return (rval);
12675bcd456Sjg }
12775bcd456Sjg
12875bcd456Sjg static void
pci_get_irq_routing_table(void)12975bcd456Sjg pci_get_irq_routing_table(void)
13075bcd456Sjg {
13175bcd456Sjg pci_irq_route_t *routes;
13275bcd456Sjg int n = N_PCI_IRQ_ROUTES;
13375bcd456Sjg int nneeded = 0;
13475bcd456Sjg int nroutes;
13575bcd456Sjg
13675bcd456Sjg /*
13775bcd456Sjg * Get irq routing table information.
13875bcd456Sjg * Allocate a buffer for an initial default number of entries.
13975bcd456Sjg * If the bios indicates it needs a larger buffer, try it again.
14075bcd456Sjg * Drive on if it still won't cooperate and play nice after that.
14175bcd456Sjg */
14275bcd456Sjg routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
14375bcd456Sjg nroutes = pci_bios_get_irq_routing(routes, n, &nneeded);
14475bcd456Sjg if (nroutes == 0 && nneeded > n) {
14575bcd456Sjg kmem_free(routes, n * sizeof (pci_irq_route_t));
14675bcd456Sjg if (nneeded > N_PCI_IRQ_ROUTES_MAX) {
14775bcd456Sjg cmn_err(CE_CONT,
14875bcd456Sjg "pci: unable to get IRQ routing information, "
14975bcd456Sjg "required buffer space of %d entries exceeds max\n",
15075bcd456Sjg nneeded);
15175bcd456Sjg return;
15275bcd456Sjg }
15375bcd456Sjg n = nneeded;
15475bcd456Sjg routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
15575bcd456Sjg nroutes = pci_bios_get_irq_routing(routes, n, NULL);
15675bcd456Sjg if (nroutes == 0) {
15775bcd456Sjg cmn_err(CE_CONT,
15875bcd456Sjg "pci: unable to get IRQ routing information, "
15975bcd456Sjg "required buffer space for %d entries\n", n);
16075bcd456Sjg }
16175bcd456Sjg }
16275bcd456Sjg
16375bcd456Sjg if (nroutes > 0) {
16475bcd456Sjg pci_irq_routes = routes;
16575bcd456Sjg pci_irq_nroutes = nroutes;
166d851c238SAndy Fiddaman } else {
167d851c238SAndy Fiddaman kmem_free(routes, n * sizeof (pci_irq_route_t));
16875bcd456Sjg }
16975bcd456Sjg }
17075bcd456Sjg
17175bcd456Sjg /*
17275bcd456Sjg * Use the results of the PCI BIOS call that returned the routing tables
17375bcd456Sjg * to build the 1275 slot-names property for the indicated bus.
17475bcd456Sjg * Results are returned in buf. Length is return value, -1 is returned on
17575bcd456Sjg * overflow and zero is returned if no data exists to build a property.
17675bcd456Sjg */
17775bcd456Sjg int
pci_slot_names_prop(int bus,char * buf,int len)17875bcd456Sjg pci_slot_names_prop(int bus, char *buf, int len)
17975bcd456Sjg {
18075bcd456Sjg uchar_t dev;
18175bcd456Sjg uchar_t slot[N_PCI_IRQ_ROUTES_MAX+1];
18275bcd456Sjg uint32_t mask;
18375bcd456Sjg int i, nnames, plen;
18475bcd456Sjg
18575bcd456Sjg ASSERT(pci_irq_nroutes <= N_PCI_IRQ_ROUTES_MAX);
18675bcd456Sjg
18775bcd456Sjg if (pci_irq_nroutes == 0)
18875bcd456Sjg return (0);
18975bcd456Sjg nnames = 0;
19075bcd456Sjg mask = 0;
19175bcd456Sjg for (i = 0; i < pci_irq_nroutes; i++)
19275bcd456Sjg slot[i] = 0xff;
19375bcd456Sjg for (i = 0; i < pci_irq_nroutes; i++) {
19475bcd456Sjg if (pci_irq_routes[i].pir_bus != bus)
19575bcd456Sjg continue;
19675bcd456Sjg if (pci_irq_routes[i].pir_slot != 0) {
19775bcd456Sjg dev = (pci_irq_routes[i].pir_dev & 0xf8) >> 3;
19875bcd456Sjg slot[dev] = pci_irq_routes[i].pir_slot;
19975bcd456Sjg mask |= (1 << dev);
20075bcd456Sjg nnames++;
20175bcd456Sjg }
20275bcd456Sjg }
20375bcd456Sjg
20475bcd456Sjg if (nnames == 0)
20575bcd456Sjg return (0);
20675bcd456Sjg
20775bcd456Sjg if (len < (4 + nnames * 8))
20875bcd456Sjg return (-1);
20975bcd456Sjg *(uint32_t *)buf = mask;
21075bcd456Sjg plen = 4;
21175bcd456Sjg for (i = 0; i < pci_irq_nroutes; i++) {
21275bcd456Sjg if (slot[i] == 0xff)
21375bcd456Sjg continue;
21475bcd456Sjg (void) sprintf(buf + plen, "Slot%d", slot[i]);
21575bcd456Sjg plen += strlen(buf+plen) + 1;
21675bcd456Sjg *(buf + plen) = 0;
21775bcd456Sjg }
21875bcd456Sjg for (; plen % 4; plen++)
21975bcd456Sjg *(buf + plen) = 0;
22075bcd456Sjg return (plen);
22175bcd456Sjg }
222*cd0d4b40SRobert Mustacchi
223*cd0d4b40SRobert Mustacchi /*
224*cd0d4b40SRobert Mustacchi * This is used to discover additional PCI buses that may exist in the system in
225*cd0d4b40SRobert Mustacchi * addition to the ACPI _BBN method. Historically these were discovered by
226*cd0d4b40SRobert Mustacchi * asking if there was a valid slot property, e.g. pci_slot_names_prop()
227*cd0d4b40SRobert Mustacchi * returned valid data. In this case we return any entry that has a bus number
228*cd0d4b40SRobert Mustacchi * and a non-zero slot value. We rely on the core PCI code to do dedup for us.
229*cd0d4b40SRobert Mustacchi */
230*cd0d4b40SRobert Mustacchi void
pci_bios_bus_iter(pci_prd_root_complex_f cbfunc,void * arg)231*cd0d4b40SRobert Mustacchi pci_bios_bus_iter(pci_prd_root_complex_f cbfunc, void *arg)
232*cd0d4b40SRobert Mustacchi {
233*cd0d4b40SRobert Mustacchi int i;
234*cd0d4b40SRobert Mustacchi for (i = 0; i < pci_irq_nroutes; i++) {
235*cd0d4b40SRobert Mustacchi if (pci_irq_routes[i].pir_slot != 0) {
236*cd0d4b40SRobert Mustacchi if (!cbfunc(pci_irq_routes[i].pir_bus, arg)) {
237*cd0d4b40SRobert Mustacchi return;
238*cd0d4b40SRobert Mustacchi }
239*cd0d4b40SRobert Mustacchi }
240*cd0d4b40SRobert Mustacchi }
241*cd0d4b40SRobert Mustacchi }
242