px_tools.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <sys/ddi_implfuncs.h>
#include <px_csr.h>
#include <px_regs.h> /* XXX - remove it later */
#include <px_obj.h>
#include <px_lib4u.h>
#include <px_asm.h>
#include <px_tools.h>
#include <px_tools_var.h>
/*
* Extract 64 bit parent or size values from 32 bit cells of
* px_ranges_t property.
*
* Only bits 42-32 are relevant in parent_high.
*/
/* Big and little endian as boolean values. */
#define SUCCESS 0
/*
* PX hardware shifts bus / dev / function bits 4 to the left of their
* normal PCI placement.
*/
#define PX_PCI_BDF_OFFSET_DELTA 4
/* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
typedef union {
/*
* Offsets of BARS in config space. First entry of 0 means config space.
* Entries here correlate to pcitool_bars_t enumerated type.
*/
0x0,
};
static int px_validate_cpuid(uint32_t);
static int px_intr_get_max_ino(uint32_t *, int);
/*
* Safe C wrapper around assy language routine px_phys_peek
*
* Type is TRUE for big endian, FALSE for little endian.
* Size is 1, 2, 4 or 8 bytes.
* paddr is the physical address in IO space to access read.
* value_p is where the value is returned.
*/
static int
{
int err = DDI_SUCCESS;
/* Set up trap handling to make the access safe. */
/*
* on_trap works like setjmp.
* Set it up to not panic on data access error,
* but to call peek_fault instead.
* Call px_phys_peek after trap handling is setup.
* When on_trap returns FALSE, it has been setup.
* When it returns TRUE, an it has caught an error.
*/
} else {
err = DDI_FAILURE;
}
no_trap();
if (err != DDI_FAILURE) {
switch (size) {
case 8:
break;
case 4:
break;
case 2:
break;
case 1:
break;
default:
err = DDI_FAILURE;
}
}
return (err);
}
/*
* Safe C wrapper around assy language routine px_phys_poke
*
* Type is TRUE for big endian, FALSE for little endian.
* Size is 1,2,4 or 8 bytes.
* paddr is the physical address in IO space to access read.
* value contains the value to be written.
*/
static int
{
int err = DDI_SUCCESS;
switch (size) {
case 8:
break;
case 4:
break;
case 2:
break;
case 1:
break;
default:
return (DDI_FAILURE);
}
/*
* on_trap works like setjmp.
* Set it up to not panic on data access error,
* but to call poke_fault instead.
* Call px_phys_poke after trap handling is setup.
* When on_trap returns FALSE, it has been setup.
* When it returns TRUE, an it has caught an error.
*/
} else {
err = DDI_FAILURE;
}
no_trap();
return (err);
}
/*
* Validate the cpu_id passed in.
* A value of 1 will be returned for success and zero for failure.
*/
static int
{
int rval = 1;
rval = 0;
rval = 0;
rval = 0;
}
return (rval);
}
/*
* Return the dips or number of dips associated with a given interrupt block.
* Size of dips array arg is passed in as dips_ret arg.
* Number of dips returned is returned in dips_ret arg.
* Array of dips gets returned in the dips argument.
* Function returns number of dips existing for the given interrupt block.
*
*/
static uint8_t
{
int i;
}
*devs_ret = i;
}
return (num_devs);
}
/* Return the number of interrupts on a pci bus. */
static int
{
DDI_SUCCESS) {
return (EFAULT);
} else {
return (SUCCESS);
}
}
/*
* Get interrupt information for a given ino.
* Returns info only for inos mapped to devices.
*
* Returned info is valid only when iget.num_devs is returned > 0.
* If ino is not enabled or is not mapped to a device,
* iget.num_devs will be returned as = 0.
*/
/*ARGSUSED*/
static int
{
/* Array part isn't used here, but oh well... */
int copyout_rval;
/* Read in just the header part, no array section. */
return (EFAULT);
}
/* Validate argument. */
goto done_get_intr;
}
/* Caller wants device information returned. */
if (num_devs_ret > 0) {
/*
* Allocate room.
* Note if num_devs == 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);
}
}
/*
* Read "valid" bit. If set, interrupts are enabled.
* This bit happens to be the same on Fire and Tomatillo.
*/
/*
* The following looks up the px_ib_ino_info and returns
* info of devices mapped to this ino.
*/
/*
* Consider only inos mapped to devices (as opposed to
* inos mapped to the bridge itself.
*/
/*
* These 2 items are platform specific,
* extracted from the bridge.
*/
}
}
if (iget_kmem_alloc_size > 0) {
}
if (copyout_rval != DDI_SUCCESS) {
}
return (rval);
}
/*
* Associate a new CPU with a given ino.
*
* Operate only on inos which are already mapped to devices.
*/
static int
{
DDI_SUCCESS) {
return (EFAULT);
}
/* Validate input argument. */
goto done_set_intr;
}
/* Validate that ino given belongs to a device. */
goto done_set_intr;
}
/* Save original mapreg value. */
/* Is this request a noop? */
goto done_set_intr;
}
/* Operate only on inos which are already enabled. */
goto done_set_intr;
}
/* Wait until there are no more pending interrupts. */
start_time = gethrtime();
continue;
/* Timed out waiting. */
} else {
goto done_set_intr;
}
}
"after disabling intr, mapreg value: 0x%llx\n", new_imregval);
/* Prepare new mapreg value with interrupts enabled and new cpu_id. */
/*
* Get lock, validate cpu and write new mapreg value.
* Return original cpu value to caller via iset.cpu.
*/
/* Invalid cpu. Restore original register image. */
} else {
"Invalid cpuid: writing orig mapreg value\n");
}
DDI_SUCCESS) {
}
return (rval);
}
/* Main function for handling interrupt CPU binding requests and queries. */
int
{
switch (cmd) {
/* Return the number of interrupts supported by a PCI bus. */
case PCITOOL_DEVICE_NUM_INTR:
break;
/* Get interrupt information for a given ino. */
case PCITOOL_DEVICE_GET_INTR:
break;
/* Associate a new CPU with a given ino. */
case PCITOOL_DEVICE_SET_INTR:
break;
default:
}
return (rval);
}
/*
* Wrapper around px_safe_phys_peek/poke.
*
* Validates arguments and calls px_safe_phys_peek/poke appropriately.
*
* Dip is of the nexus,
* phys_addr is the address to write in physical space,
* max_addr is the upper bound on the physical space used for bounds checking,
* pcitool_status returns more detailed status in addition to a more generic
* errno-style function return value.
* other args are self-explanatory.
*/
static int
{
/* Upper bounds checking. */
"Phys addr 0x%llx out of range (max 0x%llx).\n",
/* Alignment checking. */
/* Made it through checks. Do the access. */
} else if (write) {
"%d byte %s px_safe_phys_poke at addr 0x%llx\n",
DDI_SUCCESS) {
"%d byte %s px_safe_phys_poke at addr "
"0x%llx failed\n",
}
/* Read */
} else {
"%d byte %s px_safe_phys_peek at addr 0x%llx\n",
DDI_SUCCESS) {
"%d byte %s px_safe_phys_peek at addr "
"0x%llx failed\n",
}
}
return (rval);
}
/*
* Perform register accesses on the nexus device itself.
*/
int
{
switch (cmd) {
case PCITOOL_NEXUS_SET_REG:
write_flag = B_TRUE;
/*FALLTHRU*/
case PCITOOL_NEXUS_GET_REG:
/* Read data from userland. */
mode) != DDI_SUCCESS) {
return (EFAULT);
}
/*
* Read reg property which contains starting addr
* and size of banks.
*/
if (((reglen * sizeof (int)) %
sizeof (px_nexus_regspec_t)) != 0) {
"reg prop not well-formed");
goto done;
}
}
numbanks =
(reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
/* Bounds check the bank number. */
goto done;
}
"px_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
"addr:0x%llx, max_addr:0x%llx\n",
/* Access device. pr.status is modified. */
break;
default:
return (ENOTTY);
}
done:
mode) != DDI_SUCCESS) {
return (EFAULT);
}
return (rval);
}
/* 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):
mode) != DDI_SUCCESS) {
return (EFAULT);
}
goto done_reg;
}
/* 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;
}
/* Get config space first. */
/*
* Build device address based on base addr from range prop, and
* bus, dev and func values passed in. This address is where
* config space begins.
*/
/* BDF bits are shifted left addl bits with fire hardware. */
goto done_reg;
}
"range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
"func:0x%x, addr:0x%x",
/* Proper config space desired. */
/* Access config space and we're done. */
"config access: base:0x%llx, offset:0x%llx, "
"phys_addr:0x%llx, end:%s\n",
/* Access device. pr.status is modified. */
/* IO/ MEM/ MEM64 space. */
} else {
/*
* Translate BAR number into offset of the BAR in
* the device's config space.
*/
/*
* Get Bus Address Register (BAR) from config space.
* bar_offset is the offset into config space of the
* BAR desired. prg.status is modified on error.
*/
4, /* 4 bytes. */
B_FALSE, /* Read */
B_FALSE, /* Little endian. */
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 (!bar) {
goto done_reg;
}
/*
* BAR has bits saying this space is IO space, unless
* this is the ROM address register.
*/
(bar_offset != PCI_CONF_ROM)) {
/* Reposition to focus on IO space. */
/*
* 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.
*/
} else if ((PCI_BASE_TYPE_ALL & bar) &&
(bar_offset != PCI_CONF_ROM)) {
/*
* Don't try to read the next 4 bytes
* past the end of BARs.
*/
if (bar_offset >= PCI_CONF_BASE5) {
goto done_reg;
}
/*
* Access device.
* prg.status is modified on error.
*/
4, /* 4 bytes. */
B_FALSE, /* Read */
B_FALSE, /* Little endian. */
goto done_reg;
}
"64 bit mem space. 64-bit bar is 0x%llx\n",
bar);
/* Reposition to MEM64 range space. */
/* Mem32 space, including ROM */
} else {
if (bar_offset == PCI_CONF_ROM) {
"Additional ROM checking\n");
/* Can't write to ROM */
if (write_flag) {
goto done_reg;
/* ROM disabled for reading */
} else if (!(bar & 0x00000001)) {
goto done_reg;
}
}
}
"addr portion of bar is 0x%llx, base=0x%llx, "
/*
* Use offset provided by caller to index into
* desired space, then access.
* Note that prg.status is modified on error.
*/
}
mode) != DDI_SUCCESS) {
}
break;
default:
break;
}
return (rval);
}