xref: /illumos-gate/usr/src/uts/i86pc/os/pci_bios.c (revision cd0d4b40)
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(&regs, 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, &regs);
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