xref: /illumos-gate/usr/src/uts/sun4/io/px/px_tools.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/cpuvar.h>
29 #include <sys/kmem.h>
30 #include <sys/sunddi.h>
31 #include <sys/hotplug/pci/pcihp.h>
32 #include "px_obj.h"
33 #include <sys/pci_tools.h>
34 #include "px_tools_ext.h"
35 #include "px_tools_var.h"
36 
37 /*
38  * PCI Space definitions.
39  */
40 #define	PCI_CONFIG_SPACE	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
41 #define	PCI_IO_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_IO))
42 #define	PCI_MEM32_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
43 #define	PCI_MEM64_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
44 
45 /*
46  * Config space range for a device.  IEEE 1275 spec defines for PCI.
47  * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA
48  */
49 #define	DEV_CFG_SPACE_SIZE	\
50 	(1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA))
51 
52 /*
53  * Offsets of BARS in config space.  First entry of 0 means config space.
54  * Entries here correlate to pcitool_bars_t enumerated type.
55  */
56 uint8_t pci_bars[] = {
57 	0x0,
58 	PCI_CONF_BASE0,
59 	PCI_CONF_BASE1,
60 	PCI_CONF_BASE2,
61 	PCI_CONF_BASE3,
62 	PCI_CONF_BASE4,
63 	PCI_CONF_BASE5,
64 	PCI_CONF_ROM
65 };
66 
67 int	pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]);
68 
69 
70 /*ARGSUSED*/
71 static int
72 pxtool_intr_info(dev_info_t *dip, void *arg, int mode)
73 {
74 	px_t *px_p = DIP_TO_STATE(dip);
75 	px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
76 	pcitool_intr_info_t intr_info;
77 	int rval = SUCCESS;
78 
79 	/* If we need user_version, and to ret same user version as passed in */
80 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
81 	    DDI_SUCCESS) {
82 		return (EFAULT);
83 	}
84 
85 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
86 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
87 	if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
88 		intr_info.num_intr = msi_state_p->msi_cnt;
89 	else
90 		intr_info.num_intr = pxtool_num_inos;
91 
92 	intr_info.drvr_version = PCITOOL_VERSION;
93 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
94 	    DDI_SUCCESS) {
95 		rval = EFAULT;
96 	}
97 
98 	return (rval);
99 }
100 
101 
102 /*
103  * Get interrupt information for a given ino.
104  * Returns info only for inos mapped to devices.
105  *
106  * Returned info is valid only when iget.num_devs is returned > 0.
107  * If ino is not enabled or is not mapped to a device,
108  * iget.num_devs will be returned as = 0.
109  */
110 /*ARGSUSED*/
111 static int
112 pxtool_get_intr(dev_info_t *dip, void *arg, int mode)
113 {
114 	/* Array part isn't used here, but oh well... */
115 	pcitool_intr_get_t partial_iget;
116 	pcitool_intr_get_t *iget = &partial_iget;
117 	int copyout_rval;
118 	sysino_t sysino;
119 	intr_valid_state_t intr_valid_state;
120 	cpuid_t old_cpu_id;
121 	px_t *px_p = DIP_TO_STATE(dip);
122 	size_t	iget_kmem_alloc_size = 0;
123 	int rval = EIO;
124 
125 	/* Read in just the header part, no array section. */
126 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
127 	    DDI_SUCCESS)
128 		return (EFAULT);
129 
130 	iget->status = PCITOOL_IO_ERROR;
131 
132 	if (iget->flags & PCITOOL_INTR_FLAG_GET_MSI) {
133 		px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
134 		pci_msi_valid_state_t	msi_state;
135 		msiqid_t	msiq_id;
136 
137 		if ((iget->msi < msi_state_p->msi_1st_msinum) ||
138 		    (iget->msi >= (msi_state_p->msi_1st_msinum +
139 		    msi_state_p->msi_cnt))) {
140 			iget->status = PCITOOL_INVALID_MSI;
141 			rval = EINVAL;
142 			goto done_get_intr;
143 		}
144 
145 		if ((px_lib_msi_getvalid(dip, iget->msi,
146 		    &msi_state) != DDI_SUCCESS) ||
147 		    (msi_state != PCI_MSI_VALID))
148 			goto done_get_intr;
149 
150 		if (px_lib_msi_getmsiq(dip, iget->msi,
151 		    &msiq_id) != DDI_SUCCESS)
152 			goto done_get_intr;
153 
154 		iget->ino = px_msiqid_to_devino(px_p, msiq_id);
155 	} else {
156 		iget->msi = (uint32_t)-1;
157 	}
158 
159 	/* Validate argument. */
160 	if (iget->ino > pxtool_num_inos) {
161 		iget->status = PCITOOL_INVALID_INO;
162 		rval = EINVAL;
163 		goto done_get_intr;
164 	}
165 
166 	/* Caller wants device information returned. */
167 	if (iget->num_devs_ret > 0) {
168 		/*
169 		 * Allocate room.
170 		 * Note if num_devs == 0 iget remains pointing to
171 		 * partial_iget.
172 		 */
173 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget->num_devs_ret);
174 		iget = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP);
175 
176 		/* Read in whole structure to verify there's room. */
177 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
178 		    SUCCESS) {
179 
180 			/* Be consistent and just return EFAULT here. */
181 			kmem_free(iget, iget_kmem_alloc_size);
182 
183 			return (EFAULT);
184 		}
185 	}
186 
187 	/* Convert leaf-wide intr to system-wide intr */
188 	if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) !=
189 	    DDI_SUCCESS) {
190 		iget->status = PCITOOL_IO_ERROR;
191 		rval = EIO;
192 		goto done_get_intr;
193 	}
194 
195 	/* Operate only on inos which are already enabled. */
196 	if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
197 	    DDI_SUCCESS) {
198 		iget->status = PCITOOL_IO_ERROR;
199 		rval = EIO;
200 		goto done_get_intr;
201 	}
202 
203 	/*
204 	 * Consider all valid inos: those mapped to the root complex itself
205 	 * as well as those mapped to devices.
206 	 */
207 	if (intr_valid_state == INTR_VALID) {
208 		/*
209 		 * The following looks up the px_ino and returns
210 		 * info of devices mapped to this ino.
211 		 */
212 		iget->num_devs = pxtool_ib_get_ino_devs(px_p, iget->ino,
213 		    iget->msi, &iget->num_devs_ret, iget->dev);
214 
215 		if (px_ib_get_intr_target(px_p, iget->ino,
216 		    &old_cpu_id) != DDI_SUCCESS) {
217 			iget->status = PCITOOL_IO_ERROR;
218 			rval = EIO;
219 			goto done_get_intr;
220 		}
221 
222 		iget->cpu_id = old_cpu_id;
223 	}
224 
225 	iget->status = PCITOOL_SUCCESS;
226 	rval = SUCCESS;
227 
228 done_get_intr:
229 	iget->drvr_version = PCITOOL_VERSION;
230 	copyout_rval =
231 	    ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(iget->num_devs_ret), mode);
232 
233 	if (iget_kmem_alloc_size > 0)
234 		kmem_free(iget, iget_kmem_alloc_size);
235 
236 	if (copyout_rval != DDI_SUCCESS)
237 		rval = EFAULT;
238 
239 	return (rval);
240 }
241 
242 
243 /*
244  * Associate a new CPU with a given ino.
245  *
246  * Operate only on inos which are already mapped to devices.
247  */
248 static int
249 pxtool_set_intr(dev_info_t *dip, void *arg, int mode)
250 {
251 	pcitool_intr_set_t iset;
252 	cpuid_t old_cpu_id;
253 	sysino_t sysino;
254 	intr_valid_state_t intr_valid_state;
255 	px_t *px_p = DIP_TO_STATE(dip);
256 	msiqid_t msiq_id;
257 	int rval = EIO;
258 	int ret = DDI_SUCCESS;
259 	size_t copyinout_size;
260 
261 	bzero(&iset, sizeof (pcitool_intr_set_t));
262 
263 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
264 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
265 
266 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
267 		return (EFAULT);
268 
269 	switch (iset.user_version) {
270 	case PCITOOL_V1:
271 		break;
272 
273 	case PCITOOL_V2:
274 		copyinout_size = sizeof (pcitool_intr_set_t);
275 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
276 			return (EFAULT);
277 		break;
278 
279 	default:
280 		iset.status = PCITOOL_OUT_OF_RANGE;
281 		rval = ENOTSUP;
282 		goto done_set_intr;
283 	}
284 
285 	if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) {
286 		iset.status = PCITOOL_IO_ERROR;
287 		rval = ENOTSUP;
288 		goto done_set_intr;
289 	}
290 
291 	iset.status = PCITOOL_IO_ERROR;
292 
293 	if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
294 		px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
295 		pci_msi_valid_state_t	msi_state;
296 
297 		if ((iset.msi < msi_state_p->msi_1st_msinum) ||
298 		    (iset.msi >= (msi_state_p->msi_1st_msinum +
299 		    msi_state_p->msi_cnt))) {
300 			iset.status = PCITOOL_INVALID_MSI;
301 			rval = EINVAL;
302 			goto done_set_intr;
303 		}
304 
305 		if ((px_lib_msi_getvalid(dip, iset.msi,
306 		    &msi_state) != DDI_SUCCESS) ||
307 		    (msi_state != PCI_MSI_VALID))
308 			goto done_set_intr;
309 
310 		if (px_lib_msi_getmsiq(dip, iset.msi,
311 		    &msiq_id) != DDI_SUCCESS)
312 			goto done_set_intr;
313 
314 		iset.ino = px_msiqid_to_devino(px_p, msiq_id);
315 	} else {
316 		iset.msi = (uint32_t)-1;
317 	}
318 
319 	/* Validate input argument. */
320 	if (iset.ino > pxtool_num_inos) {
321 		iset.status = PCITOOL_INVALID_INO;
322 		rval = EINVAL;
323 		goto done_set_intr;
324 	}
325 
326 	/* Convert leaf-wide intr to system-wide intr */
327 	if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) !=
328 	    DDI_SUCCESS)
329 		goto done_set_intr;
330 
331 	/* Operate only on inos which are already enabled. */
332 	if ((px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
333 	    DDI_SUCCESS) || (intr_valid_state == INTR_NOTVALID))
334 		goto done_set_intr;
335 
336 	/*
337 	 * Consider all valid inos: those mapped to the root complex itself
338 	 * as well as those mapped to devices.
339 	 */
340 	if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS)
341 		goto done_set_intr;
342 
343 	if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
344 		ddi_intr_handle_impl_t	hdle;
345 
346 		bzero(&hdle, sizeof (ddi_intr_handle_impl_t));
347 		if (pxtool_ib_get_msi_info(px_p, iset.ino, iset.msi,
348 		    &hdle) != DDI_SUCCESS) {
349 			iset.status = PCITOOL_INVALID_MSI;
350 			rval = EINVAL;
351 			goto done_set_intr;
352 		}
353 
354 		if ((ret = px_ib_set_msix_target(px_p, &hdle, iset.msi,
355 		    iset.cpu_id)) == DDI_SUCCESS) {
356 			(void) px_lib_msi_getmsiq(dip, iset.msi, &msiq_id);
357 			iset.ino = px_msiqid_to_devino(px_p, msiq_id);
358 			iset.cpu_id = old_cpu_id;
359 			iset.status = PCITOOL_SUCCESS;
360 			rval = SUCCESS;
361 			goto done_set_intr;
362 		}
363 	} else {
364 		if ((ret = px_ib_set_intr_target(px_p, iset.ino,
365 		    iset.cpu_id)) == DDI_SUCCESS) {
366 			iset.cpu_id = old_cpu_id;
367 			iset.status = PCITOOL_SUCCESS;
368 			rval = SUCCESS;
369 			goto done_set_intr;
370 		}
371 	}
372 
373 	switch (ret) {
374 	case DDI_EPENDING:
375 		iset.status = PCITOOL_PENDING_INTRTIMEOUT;
376 		rval = ETIME;
377 		break;
378 	case DDI_EINVAL:
379 		iset.status = PCITOOL_INVALID_CPUID;
380 		rval = EINVAL;
381 		break;
382 	default:
383 		iset.status = PCITOOL_IO_ERROR;
384 		rval = EIO;
385 		break;
386 	}
387 
388 done_set_intr:
389 	iset.drvr_version = PCITOOL_VERSION;
390 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
391 		rval = EFAULT;
392 
393 	return (rval);
394 }
395 
396 
397 /* Main function for handling interrupt CPU binding requests and queries. */
398 int
399 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
400 {
401 	int rval = SUCCESS;
402 
403 	switch (cmd) {
404 
405 	/* Get system interrupt information. */
406 	case PCITOOL_SYSTEM_INTR_INFO:
407 		rval = pxtool_intr_info(dip, arg, mode);
408 		break;
409 
410 	/* Get interrupt information for a given ino. */
411 	case PCITOOL_DEVICE_GET_INTR:
412 		rval = pxtool_get_intr(dip, arg, mode);
413 		break;
414 
415 	/* Associate a new CPU with a given ino. */
416 	case PCITOOL_DEVICE_SET_INTR:
417 		rval = pxtool_set_intr(dip, arg, mode);
418 		break;
419 
420 	default:
421 		rval = ENOTTY;
422 	}
423 
424 	return (rval);
425 }
426 
427 
428 static int
429 pxtool_validate_barnum_bdf(pcitool_reg_t *prg)
430 {
431 	int rval = SUCCESS;
432 
433 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
434 		prg->status = PCITOOL_OUT_OF_RANGE;
435 		rval = EINVAL;
436 
437 	/* Validate address arguments of bus / dev / func */
438 	} else if (((prg->bus_no &
439 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
440 	    ((prg->dev_no &
441 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
442 	    ((prg->func_no &
443 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
444 		prg->status = PCITOOL_INVALID_ADDRESS;
445 		rval = EINVAL;
446 	}
447 
448 	return (rval);
449 }
450 
451 /*
452  * px_p defines which leaf, space defines which space in that leaf, offset
453  * defines the offset within the specified space.
454  *
455  * This returns the physical address of the corresponding location.
456  */
457 static uintptr_t
458 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset)
459 {
460 	uint64_t range_base;
461 	int rval;
462 	pci_regspec_t dev_regspec;
463 	struct regspec xlated_regspec;
464 	dev_info_t *dip = px_p->px_dip;
465 
466 	/*
467 	 * Assume that requested entity is small enough to be on the same page.
468 	 * PCItool checks alignment so that this will be true for single
469 	 * accesses.
470 	 */
471 	dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT;
472 	if (space == PCI_CONFIG_SPACE) {
473 		dev_regspec.pci_phys_hi +=
474 		    (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M));
475 		dev_regspec.pci_phys_low = offset & PCI_REG_REG_M;
476 		dev_regspec.pci_phys_mid = 0;	/* Not used */
477 	} else {
478 		dev_regspec.pci_phys_mid = offset >> 32;
479 		dev_regspec.pci_phys_low = offset & 0xffffffff;
480 	}
481 	dev_regspec.pci_size_hi = 0;	/* Not used. */
482 
483 	/* Note: object is guaranteed to be within a page. */
484 	dev_regspec.pci_size_low = 4;
485 
486 	rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec);
487 
488 	DBG(DBG_TOOLS, dip,
489 	    "space:0x%d, offset:0x%" PRIx64 "\n", space, offset);
490 
491 	if (rval != DDI_SUCCESS)
492 		return (NULL);
493 
494 	/* Bustype here returns the high order address bits. */
495 	xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p);
496 
497 	range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) +
498 	    xlated_regspec.regspec_addr;
499 	DBG(DBG_TOOLS, dip,
500 	    "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n",
501 	    xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr,
502 	    xlated_regspec.regspec_size, range_base);
503 
504 	return ((uintptr_t)range_base);
505 }
506 
507 
508 static int
509 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p,
510     uint32_t *space_p)
511 {
512 	int rval;
513 	uint64_t off_in_space;
514 	pcitool_reg_t cfg_prg = *prg_p;	/* Make local copy. */
515 	dev_info_t *dip = px_p->px_dip;
516 
517 	*space_p = PCI_MEM32_SPACE;
518 	*bar_p = 0;
519 
520 	/*
521 	 * Translate BAR number into offset of the BAR in
522 	 * the device's config space.
523 	 */
524 	cfg_prg.acc_attr =
525 	    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
526 
527 	/*
528 	 * Note: sun4u acc function uses phys_addr which includes offset.
529 	 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset.
530 	 */
531 	cfg_prg.offset = PCI_BAR_OFFSET((*prg_p));
532 	off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset;
533 	cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE,
534 	    off_in_space);
535 
536 	DBG(DBG_TOOLS, dip,
537 	    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n",
538 	    off_in_space, cfg_prg.phys_addr, cfg_prg.barnum);
539 
540 	/*
541 	 * Get Bus Address Register (BAR) from config space.
542 	 * cfg_prg.offset is the offset into config space of the
543 	 * BAR desired.  prg_p->status is modified on error.
544 	 */
545 	rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
546 
547 	if (rval != SUCCESS) {
548 		prg_p->status = cfg_prg.status;
549 		return (rval);
550 	}
551 
552 	DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p);
553 
554 	/*
555 	 * BAR has bits saying this space is IO space, unless
556 	 * this is the ROM address register.
557 	 */
558 	if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) &&
559 	    (cfg_prg.offset != PCI_CONF_ROM)) {
560 		*space_p = PCI_IO_SPACE;
561 		*bar_p &= PCI_BASE_IO_ADDR_M;
562 
563 	/*
564 	 * BAR has bits saying this space is 64 bit memory
565 	 * space, unless this is the ROM address register.
566 	 *
567 	 * The 64 bit address stored in two BAR cells is not
568 	 * necessarily aligned on an 8-byte boundary.
569 	 * Need to keep the first 4 bytes read,
570 	 * and do a separate read of the high 4 bytes.
571 	 */
572 	} else if ((PCI_BASE_TYPE_ALL & *bar_p) &&
573 	    (cfg_prg.offset != PCI_CONF_ROM)) {
574 
575 		uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL);
576 
577 		/* Don't try to read the next 4 bytes past the end of BARs. */
578 		if (cfg_prg.offset >= PCI_CONF_BASE5) {
579 			prg_p->status = PCITOOL_OUT_OF_RANGE;
580 			return (EIO);
581 		}
582 
583 		/* Access device.  prg_p->status is modified on error. */
584 		cfg_prg.phys_addr += sizeof (uint32_t);
585 		cfg_prg.offset += sizeof (uint32_t);
586 
587 		rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
588 		if (rval != SUCCESS) {
589 			prg_p->status = cfg_prg.status;
590 			return (rval);
591 		}
592 
593 		/*
594 		 * Honor the 64 bit BAR as such, only when the upper 32 bits
595 		 * store a non-zero value.
596 		 */
597 		if (*bar_p) {
598 			*space_p = PCI_MEM64_SPACE;
599 			*bar_p = (*bar_p << 32) | low_bytes;
600 		} else
601 			*bar_p = low_bytes;
602 
603 	} else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */
604 
605 		/*
606 		 * ROM enabled. Filter ROM enable bit from the BAR.
607 		 * Treat as Mem32 henceforth.
608 		 */
609 		if (!(*bar_p & PCI_BASE_ROM_ENABLE))
610 			*bar_p ^= PCI_BASE_ROM_ENABLE;
611 
612 		else {	/* ROM disabled. */
613 			prg_p->status = PCITOOL_ROM_DISABLED;
614 			return (EIO);
615 		}
616 	}
617 
618 	/* Accept a bar of 0 only for IO space. */
619 	if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) {
620 		prg_p->status = PCITOOL_INVALID_ADDRESS;
621 		return (EINVAL);
622 	}
623 
624 	return (SUCCESS);
625 }
626 
627 
628 /* Perform register accesses on PCI leaf devices. */
629 int
630 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
631 {
632 	pcitool_reg_t	prg;
633 	uint64_t	bar;
634 	uint32_t	space;
635 	uint64_t	off_in_space;
636 	boolean_t	write_flag = B_FALSE;
637 	px_t		*px_p = DIP_TO_STATE(dip);
638 	int		rval = 0;
639 
640 	if (cmd == PCITOOL_DEVICE_SET_REG)
641 		write_flag = B_TRUE;
642 
643 	DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n");
644 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
645 	    mode) != DDI_SUCCESS) {
646 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
647 		return (EFAULT);
648 	}
649 
650 	if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) {
651 		goto done_reg;
652 	}
653 
654 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
655 	    prg.bus_no, prg.dev_no, prg.func_no);
656 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
657 	    prg.barnum, prg.offset, prg.acc_attr);
658 
659 	if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS)
660 		goto done_reg;
661 
662 	if (prg.barnum == 0) {	/* Proper config space desired. */
663 
664 		/* Enforce offset limits. */
665 		if (prg.offset >= DEV_CFG_SPACE_SIZE) {
666 			DBG(DBG_TOOLS, dip,
667 			    "Config space offset 0x%" PRIx64 " out of range\n",
668 			    prg.offset);
669 			prg.status = PCITOOL_OUT_OF_RANGE;
670 			rval = EINVAL;
671 			goto done_reg;
672 		}
673 
674 		/*
675 		 * For sun4v, config space base won't be known.
676 		 * pxtool_get_phys_addr will return zero.
677 		 * Note that for sun4v, phys_addr isn't
678 		 * used for making config space accesses.
679 		 *
680 		 * For sun4u, assume that phys_addr will come back valid.
681 		 */
682 		/*
683 		 * Accessed entity is assumed small enough to be on one page.
684 		 *
685 		 * Since config space is less than a page and is aligned to
686 		 * 0x1000, a device's entire config space will be on a single
687 		 * page.  Pass the device's base config space address here,
688 		 * then add the offset within that space later.  This works
689 		 * around an issue in px_xlate_reg (called by
690 		 * pxtool_get_phys_addr) which accepts only a 256 byte
691 		 * range within a device.
692 		 */
693 		off_in_space = PX_GET_BDF(&prg);
694 		prg.phys_addr =
695 		    pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space);
696 		prg.phys_addr += prg.offset;
697 
698 		DBG(DBG_TOOLS, dip,
699 		    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", "
700 		    "end:%s\n", off_in_space, prg.phys_addr,
701 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl");
702 
703 		/*
704 		 * Access device.  pr.status is modified.
705 		 * BDF is assumed valid at this point.
706 		 */
707 		rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag);
708 		goto done_reg;
709 	}
710 
711 	/* IO/ MEM/ MEM64 space. */
712 
713 	if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS)
714 		goto done_reg;
715 
716 	switch (space) {
717 	case PCI_MEM32_SPACE:
718 
719 		DBG(DBG_TOOLS, dip, "32 bit mem space\n");
720 
721 		/* Can't write to ROM */
722 		if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) {
723 			prg.status = PCITOOL_ROM_WRITE;
724 			rval = EIO;
725 			goto done_reg;
726 		}
727 		break;
728 
729 	case PCI_IO_SPACE:
730 		DBG(DBG_TOOLS, dip, "IO space\n");
731 		break;
732 
733 	case PCI_MEM64_SPACE:
734 		DBG(DBG_TOOLS, dip,
735 		    "64 bit mem space.  64-bit bar is 0x%" PRIx64 "\n", bar);
736 		break;
737 
738 	default:
739 		DBG(DBG_TOOLS, dip, "Unknown space!\n");
740 		prg.status = PCITOOL_IO_ERROR;
741 		rval = EIO;
742 		goto done_reg;
743 	}
744 
745 	/*
746 	 * Common code for all IO/MEM range spaces.
747 	 *
748 	 * Use offset provided by caller to index into desired space.
749 	 * Note that prg.status is modified on error.
750 	 */
751 	off_in_space = bar + prg.offset;
752 	prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space);
753 
754 	DBG(DBG_TOOLS, dip,
755 	    "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", "
756 	    "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr);
757 
758 	rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag);
759 
760 done_reg:
761 	prg.drvr_version = PCITOOL_VERSION;
762 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
763 	    mode) != DDI_SUCCESS) {
764 		DBG(DBG_TOOLS, dip, "Error returning arguments.\n");
765 		rval = EFAULT;
766 	}
767 	return (rval);
768 }
769 
770 
771 int
772 pxtool_init(dev_info_t *dip)
773 {
774 	int instance = ddi_get_instance(dip);
775 
776 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
777 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
778 	    DDI_NT_REGACC, 0) != DDI_SUCCESS) {
779 		return (DDI_FAILURE);
780 	}
781 
782 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
783 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
784 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
785 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
786 		return (DDI_FAILURE);
787 	}
788 
789 	return (DDI_SUCCESS);
790 }
791 
792 
793 void
794 pxtool_uninit(dev_info_t *dip)
795 {
796 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
797 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
798 }
799