xref: /titanic_52/usr/src/uts/i86pc/io/pci/pci_tools.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/mkdev.h>
31 #include <sys/stat.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <vm/seg_kmem.h>
35 #include <sys/machparam.h>
36 #include <sys/ontrap.h>
37 #include <sys/pci.h>
38 #include <sys/hotplug/pci/pcihp.h>
39 #include <sys/pci_cfgspace.h>
40 #include <sys/pci_tools.h>
41 #include "pci_tools_ext.h"
42 #include "pci_var.h"
43 #include <sys/promif.h>
44 
45 #define	PCIEX_BDF_OFFSET_DELTA	4
46 #define	PCIEX_REG_FUNC_SHIFT	(PCI_REG_FUNC_SHIFT + PCIEX_BDF_OFFSET_DELTA)
47 #define	PCIEX_REG_DEV_SHIFT	(PCI_REG_DEV_SHIFT + PCIEX_BDF_OFFSET_DELTA)
48 #define	PCIEX_REG_BUS_SHIFT	(PCI_REG_BUS_SHIFT + PCIEX_BDF_OFFSET_DELTA)
49 
50 #define	SUCCESS	0
51 
52 int pcitool_debug = 0;
53 
54 /*
55  * Offsets of BARS in config space.  First entry of 0 means config space.
56  * Entries here correlate to pcitool_bars_t enumerated type.
57  */
58 static uint8_t pci_bars[] = {
59 	0x0,
60 	PCI_CONF_BASE0,
61 	PCI_CONF_BASE1,
62 	PCI_CONF_BASE2,
63 	PCI_CONF_BASE3,
64 	PCI_CONF_BASE4,
65 	PCI_CONF_BASE5,
66 	PCI_CONF_ROM
67 };
68 
69 /* Max offset allowed into config space for a particular device. */
70 static uint64_t max_cfg_size = PCI_CONF_HDR_SIZE;
71 
72 static uint64_t pcitool_swap_endian(uint64_t data, int size);
73 static int pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg,
74     boolean_t write_flag);
75 static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg,
76     boolean_t write_flag);
77 static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg,
78     boolean_t write_flag);
79 static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg,
80     uint64_t virt_addr, boolean_t write_flag);
81 static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages);
82 static void pcitool_unmap(uint64_t virt_addr, size_t num_pages);
83 
84 int
85 pcitool_init(dev_info_t *dip, boolean_t is_pciex)
86 {
87 	int instance = ddi_get_instance(dip);
88 
89 	/* Create pcitool nodes for register access and interrupt routing. */
90 
91 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
92 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
93 	    DDI_NT_REGACC, 0) != DDI_SUCCESS) {
94 		return (DDI_FAILURE);
95 	}
96 
97 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
98 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
99 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
100 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
101 		return (DDI_FAILURE);
102 	}
103 
104 	if (is_pciex)
105 		max_cfg_size = PCIE_CONF_HDR_SIZE;
106 
107 	return (DDI_SUCCESS);
108 }
109 
110 void
111 pcitool_uninit(dev_info_t *dip)
112 {
113 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
114 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
115 }
116 
117 
118 /*
119  * A note about ontrap handling:
120  *
121  * X86 systems on which this module was tested return FFs instead of bus errors
122  * when accessing devices with invalid addresses.  Ontrap handling, which
123  * gracefully handles kernel bus errors, is installed anyway, in case future
124  * X86 platforms require it.
125  */
126 
127 /*
128  * Main function for handling interrupt CPU binding requests and queries.
129  * Need to implement later
130  */
131 /*ARGSUSED*/
132 int
133 pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode)
134 {
135 	return (ENOTSUP);
136 }
137 
138 
139 /*
140  * Perform register accesses on the nexus device itself.
141  * No explicit PCI nexus device for X86, so not applicable.
142  */
143 /*ARGSUSED*/
144 int
145 pcitool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
146 {
147 	return (ENOTSUP);
148 }
149 
150 /* Swap endianness. */
151 static uint64_t
152 pcitool_swap_endian(uint64_t data, int size)
153 {
154 	typedef union {
155 		uint64_t data64;
156 		uint8_t data8[8];
157 	} data_split_t;
158 
159 	data_split_t orig_data;
160 	data_split_t returned_data;
161 	int i;
162 
163 	orig_data.data64 = data;
164 	returned_data.data64 = 0;
165 
166 	for (i = 0; i < size; i++) {
167 		returned_data.data8[i] = orig_data.data8[size - 1 - i];
168 	}
169 
170 	return (returned_data.data64);
171 }
172 
173 
174 /*
175  * Access device.  prg is modified.
176  *
177  * Extended config space is available only through memory-mapped access.
178  * Standard config space on pci express devices is available either way,
179  * so do it memory-mapped here too, for simplicity.
180  */
181 /*ARGSUSED*/
182 static int
183 pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg,
184     boolean_t write_flag)
185 {
186 	int rval = SUCCESS;
187 	uint64_t virt_addr;
188 	size_t	num_virt_pages;
189 
190 	prg->status = PCITOOL_SUCCESS;
191 
192 	/* XXX replace e0000000 value below with 0 once FW changes are made */
193 	prg->phys_addr = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0,
194 	    "ecfga-base-address", 0xe00000000);
195 	if (prg->phys_addr == 0) {
196 		prg->status = PCITOOL_IO_ERROR;
197 		return (EIO);
198 	}
199 
200 	prg->phys_addr += prg->offset +
201 	    ((prg->bus_no << PCIEX_REG_BUS_SHIFT) |
202 	    (prg->dev_no << PCIEX_REG_DEV_SHIFT) |
203 	    (prg->func_no << PCIEX_REG_FUNC_SHIFT));
204 
205 	virt_addr = pcitool_map(prg->phys_addr,
206 	    PCITOOL_ACC_ATTR_SIZE(prg->acc_attr), &num_virt_pages);
207 	if (virt_addr == NULL) {
208 		prg->status = PCITOOL_IO_ERROR;
209 		return (EIO);
210 	}
211 
212 	rval = pcitool_mem_access(dip, prg, virt_addr, write_flag);
213 	pcitool_unmap(virt_addr, num_virt_pages);
214 	return (rval);
215 }
216 
217 /* Access device.  prg is modified. */
218 /*ARGSUSED*/
219 static int
220 pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag)
221 {
222 	int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
223 	boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
224 	int rval = SUCCESS;
225 	uint64_t local_data;
226 
227 	/*
228 	 * NOTE: there is no way to verify whether or not the address is valid.
229 	 * The put functions return void and the get functions return ff on
230 	 * error.
231 	 */
232 	prg->status = PCITOOL_SUCCESS;
233 
234 	if (write_flag) {
235 
236 		if (big_endian) {
237 			local_data = pcitool_swap_endian(prg->data, size);
238 		} else {
239 			local_data = prg->data;
240 		}
241 
242 		switch (size) {
243 		case 1:
244 			(*pci_putb_func)(prg->bus_no, prg->dev_no,
245 			    prg->func_no, prg->offset, local_data);
246 			break;
247 		case 2:
248 			(*pci_putw_func)(prg->bus_no, prg->dev_no,
249 			    prg->func_no, prg->offset, local_data);
250 			break;
251 		case 4:
252 			(*pci_putl_func)(prg->bus_no, prg->dev_no,
253 			    prg->func_no, prg->offset, local_data);
254 			break;
255 		default:
256 			rval = ENOTSUP;
257 			prg->status = PCITOOL_INVALID_SIZE;
258 			break;
259 		}
260 	} else {
261 		switch (size) {
262 		case 1:
263 			local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no,
264 			    prg->func_no, prg->offset);
265 			break;
266 		case 2:
267 			local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no,
268 			    prg->func_no, prg->offset);
269 			break;
270 		case 4:
271 			local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no,
272 			    prg->func_no, prg->offset);
273 			break;
274 		default:
275 			rval = ENOTSUP;
276 			prg->status = PCITOOL_INVALID_SIZE;
277 			break;
278 		}
279 
280 		if (rval == SUCCESS) {
281 			if (big_endian) {
282 				prg->data =
283 				    pcitool_swap_endian(local_data, size);
284 			} else {
285 				prg->data = local_data;
286 			}
287 		}
288 	}
289 	prg->phys_addr = 0;	/* Config space is not memory mapped on X86. */
290 	return (rval);
291 }
292 
293 
294 /*ARGSUSED*/
295 static int
296 pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag)
297 {
298 	int port = (int)prg->phys_addr;
299 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
300 	boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
301 	int rval = SUCCESS;
302 	on_trap_data_t otd;
303 	uint64_t local_data;
304 
305 
306 	/*
307 	 * on_trap works like setjmp.
308 	 *
309 	 * A non-zero return here means on_trap has returned from an error.
310 	 *
311 	 * A zero return here means that on_trap has just returned from setup.
312 	 */
313 	if (on_trap(&otd, OT_DATA_ACCESS)) {
314 		no_trap();
315 		if (pcitool_debug)
316 			prom_printf(
317 			    "pcitool_mem_access: on_trap caught an error...\n");
318 		prg->status = PCITOOL_INVALID_ADDRESS;
319 		return (EFAULT);
320 	}
321 
322 	if (write_flag) {
323 
324 		if (big_endian) {
325 			local_data = pcitool_swap_endian(prg->data, size);
326 		} else {
327 			local_data = prg->data;
328 		}
329 
330 		if (pcitool_debug)
331 			prom_printf("Writing %ld byte(s) to port 0x%x\n",
332 			    size, port);
333 
334 		switch (size) {
335 		case 1:
336 			outb(port, (uint8_t)local_data);
337 			break;
338 		case 2:
339 			outw(port, (uint16_t)local_data);
340 			break;
341 		case 4:
342 			outl(port, (uint32_t)local_data);
343 			break;
344 		default:
345 			rval = ENOTSUP;
346 			prg->status = PCITOOL_INVALID_SIZE;
347 			break;
348 		}
349 	} else {
350 		if (pcitool_debug)
351 			prom_printf("Reading %ld byte(s) from port 0x%x\n",
352 			    size, port);
353 
354 		switch (size) {
355 		case 1:
356 			local_data = inb(port);
357 			break;
358 		case 2:
359 			local_data = inw(port);
360 			break;
361 		case 4:
362 			local_data = inl(port);
363 			break;
364 		default:
365 			rval = ENOTSUP;
366 			prg->status = PCITOOL_INVALID_SIZE;
367 			break;
368 		}
369 
370 		if (rval == SUCCESS) {
371 			if (big_endian) {
372 				prg->data =
373 				    pcitool_swap_endian(local_data, size);
374 			} else {
375 				prg->data = local_data;
376 			}
377 		}
378 	}
379 
380 	no_trap();
381 	return (rval);
382 }
383 
384 /*ARGSUSED*/
385 static int
386 pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr,
387 	boolean_t write_flag)
388 {
389 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
390 	boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
391 	int rval = DDI_SUCCESS;
392 	on_trap_data_t otd;
393 	uint64_t local_data;
394 
395 	/*
396 	 * on_trap works like setjmp.
397 	 *
398 	 * A non-zero return here means on_trap has returned from an error.
399 	 *
400 	 * A zero return here means that on_trap has just returned from setup.
401 	 */
402 	if (on_trap(&otd, OT_DATA_ACCESS)) {
403 		no_trap();
404 		if (pcitool_debug)
405 			prom_printf(
406 			    "pcitool_mem_access: on_trap caught an error...\n");
407 		prg->status = PCITOOL_INVALID_ADDRESS;
408 		return (EFAULT);
409 	}
410 
411 	if (write_flag) {
412 
413 		if (big_endian) {
414 			local_data = pcitool_swap_endian(prg->data, size);
415 		} else {
416 			local_data = prg->data;
417 		}
418 
419 		switch (size) {
420 		case 1:
421 			*((uint8_t *)(uintptr_t)virt_addr) = local_data;
422 			break;
423 		case 2:
424 			*((uint16_t *)(uintptr_t)virt_addr) = local_data;
425 			break;
426 		case 4:
427 			*((uint32_t *)(uintptr_t)virt_addr) = local_data;
428 			break;
429 		case 8:
430 			*((uint64_t *)(uintptr_t)virt_addr) = local_data;
431 			break;
432 		default:
433 			rval = ENOTSUP;
434 			prg->status = PCITOOL_INVALID_SIZE;
435 			break;
436 		}
437 	} else {
438 		switch (size) {
439 		case 1:
440 			local_data = *((uint8_t *)(uintptr_t)virt_addr);
441 			break;
442 		case 2:
443 			local_data = *((uint16_t *)(uintptr_t)virt_addr);
444 			break;
445 		case 4:
446 			local_data = *((uint32_t *)(uintptr_t)virt_addr);
447 			break;
448 		case 8:
449 			local_data = *((uint64_t *)(uintptr_t)virt_addr);
450 			break;
451 		default:
452 			rval = ENOTSUP;
453 			prg->status = PCITOOL_INVALID_SIZE;
454 			break;
455 		}
456 
457 		if (rval == SUCCESS) {
458 			if (big_endian) {
459 				prg->data =
460 				    pcitool_swap_endian(local_data, size);
461 			} else {
462 				prg->data = local_data;
463 			}
464 		}
465 	}
466 
467 	no_trap();
468 	return (rval);
469 }
470 
471 /*
472  * Map up to 2 pages which contain the address we want to access.
473  *
474  * Mapping should span no more than 8 bytes.  With X86 it is possible for an
475  * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary.
476  * We'll never have to map more than two pages.
477  */
478 
479 static uint64_t
480 pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages)
481 {
482 
483 	uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET;
484 	uint64_t offset = phys_addr & MMU_PAGEOFFSET;
485 	void *virt_base;
486 	uint64_t returned_addr;
487 
488 	if (pcitool_debug)
489 		prom_printf("pcitool_map: Called with PA:0x%p\n",
490 		    (uint8_t *)(uintptr_t)phys_addr);
491 
492 	*num_pages = 1;
493 
494 	/* Desired mapping would span more than two pages. */
495 	if ((offset + size) > (MMU_PAGESIZE * 2)) {
496 		if (pcitool_debug)
497 			prom_printf("boundary violation: "
498 			    "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%lx\n",
499 			    offset, (uintptr_t)size, (uintptr_t)MMU_PAGESIZE);
500 		return (NULL);
501 
502 	} else if ((offset + size) > MMU_PAGESIZE) {
503 		(*num_pages)++;
504 	}
505 
506 	/* Get page(s) of virtual space. */
507 	virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP);
508 	if (virt_base == NULL) {
509 		if (pcitool_debug)
510 			prom_printf("Couldn't get virtual base address.\n");
511 		return (NULL);
512 	}
513 
514 	if (pcitool_debug)
515 		prom_printf("Got base virtual address:0x%p\n", virt_base);
516 
517 	/* Now map the allocated virtual space to the physical address. */
518 	hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages),
519 	    mmu_btop(page_base), PROT_READ | PROT_WRITE | HAT_STRICTORDER,
520 	    HAT_LOAD_LOCK);
521 
522 	returned_addr = ((uintptr_t)(virt_base)) + offset;
523 
524 	if (pcitool_debug)
525 		prom_printf("pcitool_map: returning VA:0x%p\n",
526 		    (void *)(uintptr_t)returned_addr);
527 
528 	return (returned_addr);
529 }
530 
531 /* Unmap the mapped page(s). */
532 static void
533 pcitool_unmap(uint64_t virt_addr, size_t num_pages)
534 {
535 	void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET);
536 
537 	hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages),
538 	    HAT_UNLOAD_UNLOCK);
539 	vmem_free(heap_arena, base_virt_addr, ptob(num_pages));
540 }
541 
542 
543 /* Perform register accesses on PCI leaf devices. */
544 int
545 pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
546 {
547 	boolean_t	write_flag = B_FALSE;
548 	int		rval = 0;
549 	pcitool_reg_t	prg;
550 	uint8_t		size;
551 
552 	uint64_t	base_addr;
553 	uint64_t	virt_addr;
554 	size_t		num_virt_pages;
555 
556 	switch (cmd) {
557 	case (PCITOOL_DEVICE_SET_REG):
558 		write_flag = B_TRUE;
559 
560 	/*FALLTHRU*/
561 	case (PCITOOL_DEVICE_GET_REG):
562 		if (pcitool_debug)
563 			prom_printf("pci_dev_reg_ops set/get reg\n");
564 		if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
565 		    DDI_SUCCESS) {
566 			if (pcitool_debug)
567 				prom_printf("Error reading arguments\n");
568 			return (EFAULT);
569 		}
570 
571 		if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
572 			prg.status = PCITOOL_OUT_OF_RANGE;
573 			rval = EINVAL;
574 			goto done_reg;
575 		}
576 
577 		if (pcitool_debug)
578 			prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n",
579 			    prg.bus_no, prg.dev_no, prg.func_no);
580 		/* Validate address arguments of bus / dev / func */
581 		if (((prg.bus_no &
582 		    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) !=
583 		    prg.bus_no) ||
584 		    ((prg.dev_no &
585 		    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) !=
586 		    prg.dev_no) ||
587 		    ((prg.func_no &
588 		    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) !=
589 		    prg.func_no)) {
590 			prg.status = PCITOOL_INVALID_ADDRESS;
591 			rval = EINVAL;
592 			goto done_reg;
593 		}
594 
595 		size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
596 
597 		/* Proper config space desired. */
598 		if (prg.barnum == 0) {
599 
600 			if (pcitool_debug)
601 				prom_printf(
602 				    "config access: offset:0x%" PRIx64 ", "
603 				    "phys_addr:0x%" PRIx64 "\n",
604 				    prg.offset, prg.phys_addr);
605 
606 			if (prg.offset >= max_cfg_size) {
607 				prg.status = PCITOOL_OUT_OF_RANGE;
608 				rval = EINVAL;
609 				goto done_reg;
610 			}
611 
612 			/* Access device.  prg is modified. */
613 			if (max_cfg_size == PCIE_CONF_HDR_SIZE)
614 				rval = pcitool_pciex_cfg_access(dip, &prg,
615 				    write_flag);
616 			else
617 				rval = pcitool_cfg_access(dip, &prg,
618 				    write_flag);
619 
620 			if (pcitool_debug)
621 				prom_printf(
622 				    "config access: data:0x%" PRIx64 "\n",
623 				    prg.data);
624 
625 		/* IO/ MEM/ MEM64 space. */
626 		} else {
627 
628 			pcitool_reg_t	prg2;
629 			bcopy(&prg, &prg2, sizeof (pcitool_reg_t));
630 
631 			/*
632 			 * Translate BAR number into offset of the BAR in
633 			 * the device's config space.
634 			 */
635 			prg2.offset = pci_bars[prg2.barnum];
636 			prg2.acc_attr =
637 			    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
638 
639 			if (pcitool_debug)
640 				prom_printf(
641 				    "barnum:%d, bar_offset:0x%" PRIx64 "\n",
642 				    prg2.barnum, prg2.offset);
643 			/*
644 			 * Get Bus Address Register (BAR) from config space.
645 			 * prg2.offset is the offset into config space of the
646 			 * BAR desired.  prg.status is modified on error.
647 			 */
648 			rval = pcitool_cfg_access(dip, &prg2, B_FALSE);
649 			if (rval != SUCCESS) {
650 				if (pcitool_debug)
651 					prom_printf("BAR access failed\n");
652 				prg.status = prg2.status;
653 				goto done_reg;
654 			}
655 			/*
656 			 * Reference proper PCI space based on the BAR.
657 			 * If 64 bit MEM space, need to load other half of the
658 			 * BAR first.
659 			 */
660 
661 			if (pcitool_debug)
662 				prom_printf("bar returned is 0x%" PRIx64 "\n",
663 				    prg2.data);
664 			if (!prg2.data) {
665 				if (pcitool_debug)
666 					prom_printf("BAR data == 0\n");
667 				rval = EINVAL;
668 				prg.status = PCITOOL_INVALID_ADDRESS;
669 				goto done_reg;
670 			}
671 			if (prg2.data == 0xffffffff) {
672 				if (pcitool_debug)
673 					prom_printf("BAR data == -1\n");
674 				rval = EINVAL;
675 				prg.status = PCITOOL_INVALID_ADDRESS;
676 				goto done_reg;
677 			}
678 
679 			/*
680 			 * BAR has bits saying this space is IO space, unless
681 			 * this is the ROM address register.
682 			 */
683 			if (((PCI_BASE_SPACE_M & prg2.data) ==
684 			    PCI_BASE_SPACE_IO) &&
685 			    (prg2.offset != PCI_CONF_ROM)) {
686 				if (pcitool_debug)
687 					prom_printf("IO space\n");
688 
689 				prg2.data &= PCI_BASE_IO_ADDR_M;
690 				prg.phys_addr = prg2.data + prg.offset;
691 
692 				rval = pcitool_io_access(dip, &prg, write_flag);
693 				if ((rval != SUCCESS) && (pcitool_debug))
694 					prom_printf("IO access failed\n");
695 
696 				goto done_reg;
697 
698 
699 			/*
700 			 * BAR has bits saying this space is 64 bit memory
701 			 * space, unless this is the ROM address register.
702 			 *
703 			 * The 64 bit address stored in two BAR cells is not
704 			 * necessarily aligned on an 8-byte boundary.
705 			 * Need to keep the first 4 bytes read,
706 			 * and do a separate read of the high 4 bytes.
707 			 */
708 
709 			} else if ((PCI_BASE_TYPE_ALL & prg2.data) &&
710 			    (prg2.offset != PCI_CONF_ROM)) {
711 
712 				uint32_t low_bytes =
713 				    (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL);
714 
715 				/*
716 				 * Don't try to read the next 4 bytes
717 				 * past the end of BARs.
718 				 */
719 				if (prg2.offset >= PCI_CONF_BASE5) {
720 					prg.status = PCITOOL_OUT_OF_RANGE;
721 					rval = EIO;
722 					goto done_reg;
723 				}
724 
725 				/*
726 				 * Access device.
727 				 * prg2.status is modified on error.
728 				 */
729 				prg2.offset += 4;
730 				rval = pcitool_cfg_access(dip, &prg2, B_FALSE);
731 				if (rval != SUCCESS) {
732 					prg.status = prg2.status;
733 					goto done_reg;
734 				}
735 
736 				if (prg2.data == 0xffffffff) {
737 					prg.status = PCITOOL_INVALID_ADDRESS;
738 					prg.status = EFAULT;
739 					goto done_reg;
740 				}
741 
742 				prg2.data = (prg2.data << 32) + low_bytes;
743 				if (pcitool_debug)
744 					prom_printf(
745 					    "64 bit mem space.  "
746 					    "64-bit bar is 0x%" PRIx64 "\n",
747 					    prg2.data);
748 
749 			/* Mem32 space, including ROM */
750 			} else {
751 
752 				if (prg2.offset == PCI_CONF_ROM) {
753 					if (pcitool_debug)
754 						prom_printf(
755 						    "Additional ROM "
756 						    "checking\n");
757 					/* Can't write to ROM */
758 					if (write_flag) {
759 						prg.status = PCITOOL_ROM_WRITE;
760 						rval = EIO;
761 						goto done_reg;
762 
763 					/* ROM disabled for reading */
764 					} else if (!(prg2.data & 0x00000001)) {
765 						prg.status =
766 						    PCITOOL_ROM_DISABLED;
767 						rval = EIO;
768 						goto done_reg;
769 					}
770 				}
771 
772 				if (pcitool_debug)
773 					prom_printf("32 bit mem space\n");
774 			}
775 
776 			/* Common code for all IO/MEM range spaces. */
777 
778 			base_addr = prg2.data;
779 			if (pcitool_debug)
780 				prom_printf(
781 				    "addr portion of bar is 0x%" PRIx64 ", "
782 				    "base=0x%" PRIx64 ", "
783 				    "offset:0x%" PRIx64 "\n",
784 				    prg2.data, base_addr, prg.offset);
785 			/*
786 			 * Use offset provided by caller to index into
787 			 * desired space, then access.
788 			 * Note that prg.status is modified on error.
789 			 */
790 			prg.phys_addr = base_addr + prg.offset;
791 
792 			virt_addr = pcitool_map(prg.phys_addr, size,
793 			    &num_virt_pages);
794 			if (virt_addr == NULL) {
795 				prg.status = PCITOOL_IO_ERROR;
796 				rval = EIO;
797 				goto done_reg;
798 			}
799 
800 			rval = pcitool_mem_access(dip, &prg, virt_addr,
801 			    write_flag);
802 			pcitool_unmap(virt_addr, num_virt_pages);
803 		}
804 done_reg:
805 		if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
806 		    DDI_SUCCESS) {
807 			if (pcitool_debug)
808 				prom_printf("Error returning arguments.\n");
809 			rval = EFAULT;
810 		}
811 		break;
812 	default:
813 		rval = ENOTTY;
814 		break;
815 	}
816 	return (rval);
817 }
818