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