pci_tools.c revision 649d4cce0c8ba57f2c399df82be95a0395cfeef2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <vm/seg_kmem.h>
#include <sys/machparam.h>
#include <sys/pci_cfgspace.h>
#include <sys/pci_tools.h>
#include <sys/x86_archext.h>
#define PCIEX_BDF_OFFSET_DELTA 4
#define SUCCESS 0
int pcitool_debug = 0;
/*
* Offsets of BARS in config space. First entry of 0 means config space.
* Entries here correlate to pcitool_bars_t enumerated type.
*/
0x0,
};
/* Max offset allowed into config space for a particular device. */
/* Extern decalrations */
psm_intr_op_t, int *);
int
{
/* Create pcitool nodes for register access and interrupt routing. */
DDI_NT_REGACC, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (is_pciex)
return (DDI_SUCCESS);
}
void
{
}
/* Return the number of interrupts on a pci bus. */
static int
{
return (EFAULT);
else
return (SUCCESS);
}
/*ARGSUSED*/
static int
{
return (EFAULT);
goto done_set_intr;
}
goto done_set_intr;
}
/*
* For this locally-declared and used handle, ih_private will contain a
* CPU value, not an ihdl_plat_t as used for global interrupt handling.
*/
if (ret != PSM_SUCCESS) {
switch (result) {
case EIO: /* Error making the change */
break;
case ENXIO: /* Couldn't convert vector to irq */
break;
case EINVAL: /* CPU out of range */
break;
}
}
/* Return original CPU. */
return (rval);
}
/* It is assumed that dip != NULL */
static void
{
}
/*ARGSUSED*/
static int
{
/* Array part isn't used here, but oh well... */
int copyout_rval;
int circ;
int i;
/* Read in just the header part, no array section. */
return (EFAULT);
/* Validate argument. */
partial_iget.num_devs_ret = 0;
goto done_get_intr;
}
/*
* For this locally-declared and used handle, ih_private will contain a
* pointer to apic_get_intr_t, not an ihdl_plat_t as used for
* global interrupt handling.
*/
/* Caller wants device information returned. */
if (num_devs_ret > 0) {
/*
* Allocate room.
* If num_devs_ret == 0 iget remains pointing to partial_iget.
*/
/* Read in whole structure to verify there's room. */
SUCCESS) {
/* Be consistent and just return EFAULT here. */
return (EFAULT);
}
}
/*
* Lock device tree branch from the pci root nexus on down if info will
* be extracted from dips returned from the tree.
*/
}
/* Call psm_intr_ops(PSM_INTR_OP_GET_INTR) to get information. */
iget->num_devs_ret = 0;
goto done_get_intr;
}
/*
* Fill in the pcitool_intr_get_t to be returned,
* with the CPU, num_devs_ret and num_devs.
*/
/* Number of devices returned by apic. */
/* Device info was returned. */
/*
* num devs returned is num devs ret by apic,
* space permitting.
*/
/*
* Loop thru list of dips and extract driver, name and instance.
* Fill in the pcitool_intr_dev_t's with this info.
*/
for (i = 0; i < iget->num_devs_ret; i++)
/* Free kmem_alloc'ed memory of the apic_get_intr_t */
}
}
if (iget_kmem_alloc_size > 0)
if (copyout_rval != DDI_SUCCESS)
return (rval);
}
/*
* Main function for handling interrupt CPU binding requests and queries.
* Need to implement later
*/
/*ARGSUSED*/
int
{
int rval;
switch (cmd) {
/* Associate a new CPU with a given vector */
case PCITOOL_DEVICE_SET_INTR:
break;
case PCITOOL_DEVICE_GET_INTR:
break;
case PCITOOL_DEVICE_NUM_INTR:
break;
default:
}
return (rval);
}
/*
* A note about ontrap handling:
*
* X86 systems on which this module was tested return FFs instead of bus errors
* when accessing devices with invalid addresses. Ontrap handling, which
* gracefully handles kernel bus errors, is installed anyway, in case future
* X86 platforms require it.
*/
/*
* Perform register accesses on the nexus device itself.
* No explicit PCI nexus device for X86, so not applicable.
*/
/*ARGSUSED*/
int
{
return (ENOTSUP);
}
/* Swap endianness. */
static uint64_t
{
typedef union {
} data_split_t;
int i;
returned_data.data64 = 0;
for (i = 0; i < size; i++) {
}
return (returned_data.data64);
}
/*
* Access device. prg is modified.
*
* Extended config space is available only through memory-mapped access.
* Standard config space on pci express devices is available either way,
* so do it memory-mapped here too, for simplicity.
*/
/*ARGSUSED*/
static int
{
"ecfga-base-address", 0);
return (EIO);
}
return (EIO);
}
return (rval);
}
/* Access device. prg is modified. */
/*ARGSUSED*/
static int
{
/*
* NOTE: there is no way to verify whether or not the address is valid.
* The put functions return void and the get functions return ff on
* error.
*/
if (write_flag) {
if (big_endian) {
} else {
}
switch (size) {
case 1:
break;
case 2:
break;
case 4:
break;
default:
break;
}
} else {
switch (size) {
case 1:
break;
case 2:
break;
case 4:
break;
default:
break;
}
if (big_endian) {
} else {
}
}
}
return (rval);
}
/*ARGSUSED*/
static int
{
/*
* on_trap works like setjmp.
*
* A non-zero return here means on_trap has returned from an error.
*
* A zero return here means that on_trap has just returned from setup.
*/
no_trap();
if (pcitool_debug)
"pcitool_mem_access: on_trap caught an error...\n");
return (EFAULT);
}
if (write_flag) {
if (big_endian) {
} else {
}
if (pcitool_debug)
prom_printf("Writing %ld byte(s) to port 0x%x\n",
switch (size) {
case 1:
break;
case 2:
break;
case 4:
break;
default:
break;
}
} else {
if (pcitool_debug)
prom_printf("Reading %ld byte(s) from port 0x%x\n",
switch (size) {
case 1:
break;
case 2:
break;
case 4:
break;
default:
break;
}
if (big_endian) {
} else {
}
}
}
no_trap();
return (rval);
}
/*ARGSUSED*/
static int
{
int rval = DDI_SUCCESS;
/*
* on_trap works like setjmp.
*
* A non-zero return here means on_trap has returned from an error.
*
* A zero return here means that on_trap has just returned from setup.
*/
no_trap();
if (pcitool_debug)
"pcitool_mem_access: on_trap caught an error...\n");
return (EFAULT);
}
if (write_flag) {
if (big_endian) {
} else {
}
switch (size) {
case 1:
break;
case 2:
break;
case 4:
break;
case 8:
break;
default:
break;
}
} else {
switch (size) {
case 1:
break;
case 2:
break;
case 4:
break;
case 8:
break;
default:
break;
}
if (big_endian) {
} else {
}
}
}
no_trap();
return (rval);
}
/*
* Map up to 2 pages which contain the address we want to access.
*
* Mapping should span no more than 8 bytes. With X86 it is possible for an
* 8 byte value to start on a 4 byte boundary, so it can cross a page boundary.
* We'll never have to map more than two pages.
*/
static uint64_t
{
void *virt_base;
if (pcitool_debug)
prom_printf("pcitool_map: Called with PA:0x%p\n",
*num_pages = 1;
/* Desired mapping would span more than two pages. */
if (pcitool_debug)
prom_printf("boundary violation: "
return (NULL);
(*num_pages)++;
}
/* Get page(s) of virtual space. */
if (pcitool_debug)
prom_printf("Couldn't get virtual base address.\n");
return (NULL);
}
if (pcitool_debug)
/* Now map the allocated virtual space to the physical address. */
if (pcitool_debug)
prom_printf("pcitool_map: returning VA:0x%p\n",
(void *)(uintptr_t)returned_addr);
return (returned_addr);
}
/* Unmap the mapped page(s). */
static void
{
}
/* Perform register accesses on PCI leaf devices. */
int
{
int rval = 0;
switch (cmd) {
case (PCITOOL_DEVICE_SET_REG):
write_flag = B_TRUE;
/*FALLTHRU*/
case (PCITOOL_DEVICE_GET_REG):
if (pcitool_debug)
prom_printf("pci_dev_reg_ops set/get reg\n");
DDI_SUCCESS) {
if (pcitool_debug)
prom_printf("Error reading arguments\n");
return (EFAULT);
}
goto done_reg;
}
if (pcitool_debug)
prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n",
/* Validate address arguments of bus / dev / func */
(PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) !=
(PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) !=
(PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) !=
goto done_reg;
}
/* Proper config space desired. */
if (pcitool_debug)
goto done_reg;
}
/*
* Access device. prg is modified.
* First, check for AMD northbridges for I/O access
* (This fix will move in future to pcitool user-land)
* Next, check for PCIe devices and do
* memory-mapped access
* Lastly, check for PCI devices and do I/O access
*/
} else if (max_cfg_size == PCIE_CONF_HDR_SIZE)
else
if (pcitool_debug)
/* IO/ MEM/ MEM64 space. */
} else {
/*
* Translate BAR number into offset of the BAR in
* the device's config space.
*/
if (pcitool_debug)
/*
* Get Bus Address Register (BAR) from config space.
* prg2.offset is the offset into config space of the
* BAR desired. prg.status is modified on error.
*/
if (pcitool_debug)
prom_printf("BAR access failed\n");
goto done_reg;
}
/*
* Reference proper PCI space based on the BAR.
* If 64 bit MEM space, need to load other half of the
* BAR first.
*/
if (pcitool_debug)
if (pcitool_debug)
prom_printf("BAR data == 0\n");
goto done_reg;
}
if (pcitool_debug)
prom_printf("BAR data == -1\n");
goto done_reg;
}
/*
* BAR has bits saying this space is IO space, unless
* this is the ROM address register.
*/
if (pcitool_debug)
prom_printf("IO space\n");
prom_printf("IO access failed\n");
goto done_reg;
/*
* BAR has bits saying this space is 64 bit memory
* space, unless this is the ROM address register.
*
* The 64 bit address stored in two BAR cells is not
* necessarily aligned on an 8-byte boundary.
* Need to keep the first 4 bytes read,
* and do a separate read of the high 4 bytes.
*/
/*
* Don't try to read the next 4 bytes
* past the end of BARs.
*/
goto done_reg;
}
/*
* Access device.
* prg2.status is modified on error.
*/
goto done_reg;
}
goto done_reg;
}
if (pcitool_debug)
"64 bit mem space. "
/* Mem32 space, including ROM */
} else {
if (pcitool_debug)
"Additional ROM "
"checking\n");
/* Can't write to ROM */
if (write_flag) {
goto done_reg;
/* ROM disabled for reading */
goto done_reg;
}
}
if (pcitool_debug)
prom_printf("32 bit mem space\n");
}
if (pcitool_debug)
/*
* Use offset provided by caller to index into
* desired space, then access.
* Note that prg.status is modified on error.
*/
goto done_reg;
}
}
DDI_SUCCESS) {
if (pcitool_debug)
prom_printf("Error returning arguments.\n");
}
break;
default:
break;
}
return (rval);
}