/**
* $Id: pciinspect.c 743 2012-06-18 06:10:35Z elkner $
*
* * CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* http://www.opensolaris.org/os/licensing.
* 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.
* 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]
*
* All Rights reserved.
* CDDL HEADER END
*/
/*
* Copyright (c) 2011,2012 by Jens Elkner,
* Otto-von-Guericke Universitaet Magdeburg. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <pciaccess.h>
#include <time.h>
#include <errno.h>
static void
convert(uint32_t val, char *res) {
char buf[40] = "ffff-ffff-ffff-ffff-ffff-ffff-ffff-ffff";
uint32_t save = val;
int i = 39;
buf[i--] = '\0';
for (; i >= 0; i--) {
buf[i] = '0' + (val & 1L);
if (i > 0 && i % 5 == 0) {
buf[--i] = '.';
}
val >>= 1L;
}
sprintf(res,"%10u = 0x%08x = %s", save, save, buf);
}
static void
mask(char *res, int to, int from) {
int i = strlen(res) - 1;
int b = 0;
for (;i >= 0 && b != from; i--, b++) {
if (res[i] == '.') {
res[i] = ' ';
i--;
}
res[i] = ' ';
}
if (i > 0 && res[i] == '.') {
res[i--] = ' ';
}
to++;
for (;i >= 0 && b < to; i--, b++) {
if (res[i] == '.') {
i--;
}
}
for (; i >= 0 && res[i] != '='; i--) {
res[i] = ' ';
}
}
#define PFREG "r RegisterAddr ... # print the content at the given register address(es)\n"
#define XTRACT "x Num From To # extract bits From:To from Num and print them out\n"
#define CONV "c Num # print the given Num in dec, hex, and bin format\n"
#define QUIT "q # exit program\n"
static void
usage() {
fprintf(stderr, "Usage: pciinspect vendorId deviceId\n\n"
"PCI config base inspector. Following commands are supported:\n\n"
PFREG
XTRACT
CONV
QUIT
"\nNumbers might be entered as decimal, hexadecimal (prefix '0x'),\n"
"or binary (suffix 'b').\n");
}
boolean_t
getNum(char *in, uint32_t *num) {
char *idx;
char *k;
if (in == NULL) {
return B_FALSE;
}
if ((idx = strchr(in, 'x')) != NULL) {
return sscanf(++idx, "%x", num) != 0;
} else if ((idx = strchr(in, 'b')) != NULL) {
*num = 0;
for (k=in; k < idx; k++) {
if (*k == '.') {
continue;
}
if (*k != '0' && *k != '1') {
return B_FALSE;
}
*num <<= 1;
*num |= *k - '0';
}
return B_TRUE;
}
return sscanf(in, "%d", num) != 0;
}
typedef struct pci_device *pci_device_t;
#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
static void
loop(int count, pci_device_t *devices) {
char buf[80], buf2[80];
char *token, *token2, *token3, *token4;
char *ws = " \t\n";
uint32_t op1, op2, op3, i, run = 1;
uint32_t data = 0;
while(run) {
fprintf(stdout, "> ");
token = fgets(buf, 80, stdin);
if (token == NULL) {
break;
}
token = strtok(buf, ws);
if (token == NULL) {
continue;
}
if (strlen(token) == 1) {
switch (token[0]) {
case 'r':
token2 = strtok(NULL, ws);
while ((token2 != NULL) && getNum(token2, &op1)) {
for (i=0; i < count; i++) {
data = 0;
buf2[0] = '\0';
pci_device_cfg_read_u32(devices[i], &data, op1);
convert(data, buf2);
fprintf(stdout, "[%d] 0x%02x %s\n", i, op1, buf2);
}
token2 = strtok(NULL, ws);
}
break;
case 'x':
token2 = strtok(NULL, ws);
if (token2 == NULL) { fprintf(stdout, PFREG); break; }
token3 = strtok(NULL, ws);
if (token3 == NULL) { fprintf(stdout, PFREG); break; }
token4 = strtok(NULL, ws);
if (token4 == NULL) { fprintf(stdout, PFREG); break; }
if (!(getNum(token2, &op1) && getNum(token3, &op2)
&& getNum(token4, &op3)))
{
fprintf(stdout, PFREG); break;
}
if (op3 > op2) {
i = op3; op3 = op2; op2 = i;
}
buf2[0] = '\0';
convert(op1, buf2);
fprintf(stdout, "%s (orig)\n", buf2);
data = BITX(op1, op2, op3);
convert(data << op3, buf2);
mask(buf2, op2, op3);
fprintf(stdout, "%s\n", buf2);
convert(data, buf2);
fprintf(stdout, "%s (lshift %d)\n", buf2, op3);
break;
case 'c':
token2 = strtok(NULL, ws);
if (token2 == NULL) { fprintf(stdout, PFREG); break; }
if (!getNum(token2, &op1)) { fprintf(stdout, PFREG); break; }
convert(op1, buf2);
fprintf(stdout, "%s\n", buf2);
break;
case 'q':
run = 0;
break;
}
}
}
fprintf(stdout, "\n");
}
int
main(int argc, char** argv)
{
struct pci_id_match match;
struct pci_device_iterator *iter;
struct pci_device * dev;
uint32_t vendorId, deviceId, val, count = 0;
pci_device_t *devices = NULL;
char buf[80];
if (argc < 3) {
usage();
exit(1);
}
if (!(getNum(argv[1], &vendorId) && getNum(argv[2], &deviceId))) {
usage();
exit(2);
}
val = pci_system_init();
if (val) {
perror("Couldn't initialize PCI system");
exit(EXIT_FAILURE);
}
match.device_id = deviceId;
match.vendor_id = vendorId;
match.subdevice_id = PCI_MATCH_ANY;
match.subvendor_id = PCI_MATCH_ANY;
match.device_class = 0;
match.device_class_mask = 0;
errno = 0;
iter = pci_id_match_iterator_create(&match);
if (iter == NULL || errno != 0) {
exit(3);
}
fprintf(stdout, "Scanning for device 0x%04x,0x%04x ...\n", vendorId, deviceId);
while ((dev = pci_device_next(iter)) != NULL) {
pci_device_t *tmp = realloc(devices, sizeof(pci_device_t) * (count+1));
if (*tmp != NULL) {
devices = tmp;
devices[count] = dev;
count++;
} else {
perror("allocating space for device");
break;
}
}
if (count == 0) {
fprintf(stdout, "No device found.\n");
} else {
if (count == 1) {
fprintf(stdout, "1 device found\n");
} else {
fprintf(stdout, "%d devices found\n", count);
}
loop(count, devices);
}
pci_iterator_destroy(iter);
pci_system_cleanup();
return (EXIT_SUCCESS);
}