/*
* 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
* or 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 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 <stddef.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <locale.h>
#include <langinfo.h>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/dditypes.h>
#include <sys/modctl.h>
#include <sys/obpdefs.h>
#include <sys/fhc.h>
#include <sys/sysctrl.h>
#include <sys/openpromio.h>
#ifdef SIM
#include <sys/stat.h>
#endif
#define CFGA_PLUGIN_LIB
#include <config_admin.h>
#ifdef DEBUG
#define DBG printf
#define DBG1 printf
#define DBG3 printf
#define DBG4 printf
#else
#define DBG(a, b)
#define DBG1(a)
#define DBG3(a, b, c)
#define DBG4(a, b, c, d)
#endif
#define BD_CPU 1
#define BD_MEM 2
#define BD_IO_2SBUS 3
#define BD_IO_SBUS_FFB 4
#define BD_IO_PCI 5
#define BD_DISK 6
#define BD_IO_2SBUS_SOCPLUS 7
#define BD_IO_SBUS_FFB_SOCPLUS 8
#define BD_UNKNOWN 9
#define CMD_GETSTAT 10
#define CMD_LIST 11
#define CMD_CONNECT 12
#define CMD_DISCONNECT 13
#define CMD_CONFIGURE 14
#define CMD_UNCONFIGURE 15
#define CMD_QUIESCE 16
#define CMD_INSERT 17
#define CMD_REMOVE 18
#define CMD_SET_COND 19
#define OPT_ENABLE 20
#define OPT_DISABLE 21
#define ERR_PROM_OPEN 22
#define ERR_PROM_GETPROP 23
#define ERR_PROM_SETPROP 24
#define ERR_TRANS 25
#define ERR_CMD_INVAL 26
#define ERR_OPT_INVAL 27
#define ERR_AP_INVAL 28
#define ERR_DISABLED 29
#define DIAG_FORCE 30
#define DIAG_TRANS_OK 31
#define DIAG_FAILED 32
#define DIAG_WAS_ENABLED 33
#define DIAG_WAS_DISABLED 34
#define DIAG_WILL_ENABLE 35
#define DIAG_WILL_DISABLE 36
#define HELP_HEADER 37
#define HELP_QUIESCE 38
#define HELP_INSERT 39
#define HELP_REMOVE 40
#define HELP_SET_COND 41
#define HELP_ENABLE 42
#define HELP_DISABLE 43
#define HELP_UNKNOWN 44
#define ASK_CONNECT 45
#define STR_BD 46
#define STR_COL 47
#define COND_UNKNOWN 48
#define COND_OK 49
#define COND_FAILING 50
#define COND_FAILED 51
#define COND_UNUSABLE 52
#define SYSC_COOLING 53
#define SYSC_POWER 54
#define SYSC_PRECHARGE 55
#define SYSC_INTRANS 56
#define SYSC_UTHREAD 57
#define SYSC_KTHREAD 58
#define SYSC_DEV_ATTACH 59
#define SYSC_DEV_DETACH 60
#define SYSC_NDI_ATTACH 61
#define SYSC_NDI_DETACH 62
#define SYSC_CORE_RESOURCE 63
#define SYSC_OSTATE 64
#define SYSC_RSTATE 65
#define SYSC_COND 66
#define SYSC_PROM 67
#define SYSC_NOMEM 68
#define SYSC_HOTPLUG 69
#define SYSC_HW_COMPAT 70
#define SYSC_NON_DR_PROM 71
#define SYSC_SUSPEND 72
#define SYSC_RESUME 73
#define SYSC_UNKNOWN 74
#define SYSC_DEVSTR 75
/*
* The string table contains all the strings used by the platform
* library. The comment next to each string specifies whether the
* string should be internationalized (y) or not (n).
* Note that there are calls to dgettext() with strings other than
* the ones below, they are marked by the li18 symbol.
*/
static char *
cfga_strs[] = {
/* */ NULL,
/* n */ "cpu/mem ",
/* n */ "mem ",
/* n */ "dual-sbus ",
/* n */ "sbus-upa ",
/* n */ "dual-pci ",
/* n */ "disk ",
/* n */ "soc+sbus ",
/* n */ "soc+upa ",
/* n */ "unknown ",
/* n */ "get-status",
/* n */ "list",
/* n */ "connect",
/* n */ "disconnect",
/* n */ "configure",
/* n */ "unconfigure",
/* n */ "quiesce-test",
/* n */ "insert-test",
/* n */ "remove-test",
/* n */ "set-condition-test",
/* n */ "enable-at-boot",
/* n */ "disable-at-boot",
/* n */ "prom open",
/* n */ "prom getprop",
/* n */ "prom setprop",
/* y */ "invalid transition",
/* y */ "invalid command: ",
/* y */ "invalid option: ",
/* y */ "invalid attachment point: ",
/* y */ "board is disabled: must override with ",
/* n */ "[-f][-o enable-at-boot]",
/* y */ "transition succeeded but ",
/* y */ " failed: ",
/* y */ "was already enabled at boot time",
/* y */ "was already disabled at boot time",
/* y */ "will be enabled at boot time",
/* y */ "will be disabled at boot time",
/* y */ "\nSysctrl specific commands/options:",
/* n */ "\t-x quiesce-test ap_id [ap_id...]",
/* n */ "\t-x insert-test ap_id [ap_id...]",
/* n */ "\t-x remove-test ap_id [ap_id...]",
/* n */ "\t-x set-condition-test=<condition>",
/* n */ "\t-o enable-at-boot",
/* n */ "\t-o disable-at-boot",
/* y */ "\tunknown command or option: ",
/* y */
"system will be temporarily suspended to connect a board: proceed",
/* y */ "board ",
/* y */ ": ",
/* n */ "unknown",
/* n */ "ok",
/* n */ "failing",
/* n */ "failed",
/* n */ "unusable",
/* y */ "not enough cooling for a new board",
/* y */ "not enough power for a new board",
/* y */ "not enough precharge power for a new board",
/* y */ "configuration operation already in progress on this board",
/* y */ "could not suspend user process: ",
/* y */ "could not suspend system processes",
/* y */ "device did not attach",
/* y */ "device did not detach",
/* y */ "nexus error during attach",
/* y */ "nexus error during detach",
/* y */ "attempt to remove core system resource",
/* y */ "invalid occupant state",
/* y */ "invalid receptacle state",
/* y */ "insufficient condition",
/* y */ "firmware operation error",
/* y */ "not enough memory",
/* y */ "hotplug feature unavailable on this machine",
/* y */ "board does not support dynamic reconfiguration",
/* y */ "firmware does not support dynamic reconfiguration",
/* y */ "system suspend error",
/* y */ "system resume error",
/* y */ "unknown system error",
/* */ NULL
};
#define cfga_str(i) cfga_strs[(i)]
#define cfga_eid(a, b) (((a) << 8) + (b))
/*
*
* Translation table for mapping from an <errno,sysc_err>
* pair to an error string.
*
*
* SYSC_COOLING, EAGAIN, SYSC_ERR_COOLING
* SYSC_POWER, EAGAIN, SYSC_ERR_POWER
* SYSC_PRECHARGE, EAGAIN, SYSC_ERR_PRECHARGE
* SYSC_INTRANS, EBUSY, SYSC_ERR_INTRANS
* SYSC_KTHREAD, EBUSY, SYSC_ERR_KTHREAD
* SYSC_DEV_ATTACH, EBUSY, SYSC_ERR_NDI_ATTACH
* SYSC_DEV_DETACH, EBUSY, SYSC_ERR_NDI_DETACH
* SYSC_NDI_ATTACH, EFAULT, SYSC_ERR_NDI_ATTACH
* SYSC_NDI_DETACH, EFAULT, SYSC_ERR_NDI_DETACH
* SYSC_CORE_RESOURCE, EINVAL, SYSC_ERR_CORE_RESOURCE
* SYSC_OSTATE, EINVAL, SYSC_ERR_OSTATE
* SYSC_RSTATE, EINVAL, SYSC_ERR_RSTATE
* SYSC_COND, EINVAL, SYSC_ERR_COND
* SYSC_PROM, EIO, SYSC_ERR_PROM
* SYSC_NOMEM, ENOMEM, SYSC_ERR_DR_INIT
* SYSC_NOMEM, ENOMEM, SYSC_ERR_NDI_ATTACH
* SYSC_NOMEM, ENOMEM, SYSC_ERR_NDI_DETACH
* SYSC_HOTPLUG, ENOTSUP, SYSC_ERR_HOTPLUG
* SYSC_HW_COMPAT, ENOTSUP, SYSC_ERR_HW_COMPAT
* SYSC_NON_DR_PROM, ENOTSUP, SYSC_ERR_NON_DR_PROM
* SYSC_SUSPEND, ENXIO, SYSC_ERR_SUSPEND
* SYSC_RESUME, ENXIO, SYSC_ERR_RESUME
* SYSC_UTHREAD, ESRCH, SYSC_ERR_UTHREAD
*/
static int
cfga_sid(int err, int scerr)
{
if (scerr == SYSC_ERR_DEFAULT)
return (SYSC_UNKNOWN);
switch (cfga_eid(err, scerr)) {
case cfga_eid(EAGAIN, SYSC_ERR_COOLING):
return (SYSC_COOLING);
case cfga_eid(EAGAIN, SYSC_ERR_POWER):
return (SYSC_POWER);
case cfga_eid(EAGAIN, SYSC_ERR_PRECHARGE):
return (SYSC_PRECHARGE);
case cfga_eid(EBUSY, SYSC_ERR_INTRANS):
return (SYSC_INTRANS);
case cfga_eid(EBUSY, SYSC_ERR_KTHREAD):
return (SYSC_KTHREAD);
case cfga_eid(EBUSY, SYSC_ERR_NDI_ATTACH):
return (SYSC_DEV_ATTACH);
case cfga_eid(EBUSY, SYSC_ERR_NDI_DETACH):
return (SYSC_DEV_DETACH);
case cfga_eid(EFAULT, SYSC_ERR_NDI_ATTACH):
return (SYSC_NDI_ATTACH);
case cfga_eid(EFAULT, SYSC_ERR_NDI_DETACH):
return (SYSC_NDI_DETACH);
case cfga_eid(EINVAL, SYSC_ERR_CORE_RESOURCE):
return (SYSC_CORE_RESOURCE);
case cfga_eid(EINVAL, SYSC_ERR_OSTATE):
return (SYSC_OSTATE);
case cfga_eid(EINVAL, SYSC_ERR_RSTATE):
return (SYSC_RSTATE);
case cfga_eid(EINVAL, SYSC_ERR_COND):
return (SYSC_COND);
case cfga_eid(EIO, SYSC_ERR_PROM):
return (SYSC_PROM);
case cfga_eid(ENOMEM, SYSC_ERR_DR_INIT):
return (SYSC_NOMEM);
case cfga_eid(ENOMEM, SYSC_ERR_NDI_ATTACH):
return (SYSC_NOMEM);
case cfga_eid(ENOMEM, SYSC_ERR_NDI_DETACH):
return (SYSC_NOMEM);
case cfga_eid(ENOTSUP, SYSC_ERR_HOTPLUG):
return (SYSC_HOTPLUG);
case cfga_eid(ENOTSUP, SYSC_ERR_HW_COMPAT):
return (SYSC_HW_COMPAT);
case cfga_eid(ENOTSUP, SYSC_ERR_NON_DR_PROM):
return (SYSC_NON_DR_PROM);
case cfga_eid(ENXIO, SYSC_ERR_SUSPEND):
return (SYSC_SUSPEND);
case cfga_eid(ENXIO, SYSC_ERR_RESUME):
return (SYSC_RESUME);
case cfga_eid(ESRCH, SYSC_ERR_UTHREAD):
return (SYSC_UTHREAD);
default:
break;
}
return (SYSC_UNKNOWN);
}
static void
sysc_cmd_init(sysc_cfga_cmd_t *sc, char *outputstr, int force)
{
sc->force = force;
sc->outputstr = outputstr;
sc->errtype = SYSC_ERR_DEFAULT;
(void) memset((void *)outputstr, 0, sizeof (outputstr));
cfga_str(SYSC_DEVSTR) = outputstr;
}
/*
* cfga_err() accepts a variable number of message IDs and constructs
* a corresponding error string which is returned via the errstring argument.
* cfga_err() calls dgettext() to internationalize proper messages.
*/
static void
cfga_err(sysc_cfga_cmd_t *sc, char **errstring, ...)
{
int a;
int i;
int n;
int len;
int flen;
char *p;
char *q;
char *s[32];
char *failed;
va_list ap;
char syserr_num[20];
/*
* If errstring is null it means user in not interested in getting
* error status. So we don't do all the work
*/
if (errstring == NULL) {
return;
}
va_start(ap, errstring);
failed = dgettext(TEXT_DOMAIN, cfga_str(DIAG_FAILED));
flen = strlen(failed);
for (n = len = 0; (a = va_arg(ap, int)) != 0; n++) {
switch (a) {
case ERR_PROM_OPEN:
case ERR_PROM_GETPROP:
case ERR_PROM_SETPROP:
case CMD_GETSTAT:
case CMD_LIST:
case CMD_CONNECT:
case CMD_DISCONNECT:
case CMD_CONFIGURE:
case CMD_UNCONFIGURE:
case CMD_QUIESCE:
case CMD_INSERT:
case CMD_REMOVE:
case CMD_SET_COND:
p = cfga_str(a);
len += (strlen(p) + flen);
s[n] = p;
s[++n] = failed;
DBG("<%s>", p);
DBG("<%s>", failed);
break;
case OPT_ENABLE:
case OPT_DISABLE:
p = dgettext(TEXT_DOMAIN, cfga_str(DIAG_TRANS_OK));
q = cfga_str(a);
len += (strlen(p) + strlen(q) + flen);
s[n] = p;
s[++n] = q;
s[++n] = failed;
DBG("<%s>", p);
DBG("<%s>", q);
DBG("<%s>", failed);
break;
case ERR_CMD_INVAL:
case ERR_AP_INVAL:
case ERR_OPT_INVAL:
p = dgettext(TEXT_DOMAIN, cfga_str(a));
q = va_arg(ap, char *);
len += (strlen(p) + strlen(q));
s[n] = p;
s[++n] = q;
DBG("<%s>", p);
DBG("<%s>", q);
break;
case ERR_TRANS:
case ERR_DISABLED:
p = dgettext(TEXT_DOMAIN, cfga_str(a));
len += strlen(p);
s[n] = p;
DBG("<%s>", p);
break;
case DIAG_FORCE:
default:
p = cfga_str(a);
len += strlen(p);
s[n] = p;
DBG("<%s>", p);
break;
}
}
DBG1("\n");
va_end(ap);
if (errno) {
if (sc)
i = cfga_sid(errno, (int)sc->errtype);
else
i = SYSC_UNKNOWN;
DBG4("cfga_sid(%d,%d)=%d\n", errno, sc->errtype, i);
if (i == SYSC_UNKNOWN) {
p = strerror(errno);
if (p == NULL) {
(void) sprintf(syserr_num, "errno=%d", errno);
p = syserr_num;
}
} else
p = dgettext(TEXT_DOMAIN, cfga_str(i));
len += strlen(p);
s[n++] = p;
p = cfga_str(SYSC_DEVSTR);
if (p && p[0]) {
q = cfga_str(STR_COL);
len += strlen(q);
s[n++] = q;
len += strlen(p);
s[n++] = p;
}
}
if ((p = (char *)calloc(len, 1)) == NULL)
return;
for (i = 0; i < n; i++)
(void) strcat(p, s[i]);
*errstring = p;
#ifdef SIM_MSG
printf("%s\n", *errstring);
#endif
}
/*
* This routine accepts a variable number of message IDs and constructs
* a corresponding error string which is printed via the message print routine
* argument. The HELP_UNKNOWN message ID has an argument string (the unknown
* help topic) that follows.
*/
static void
cfga_msg(struct cfga_msg *msgp, ...)
{
int a;
int i;
int n;
int len;
char *p;
char *s[32];
va_list ap;
va_start(ap, msgp);
for (n = len = 0; (a = va_arg(ap, int)) != 0; n++) {
DBG("<%d>", a);
p = dgettext(TEXT_DOMAIN, cfga_str(a));
len += strlen(p);
s[n] = p;
if (a == HELP_UNKNOWN) {
p = va_arg(ap, char *);
len += strlen(p);
s[++n] = p;
}
}
va_end(ap);
if ((p = (char *)calloc(len + 1, 1)) == NULL)
return;
for (i = 0; i < n; i++)
(void) strcat(p, s[i]);
(void) strcat(p, "\n");
#ifdef SIM_MSG
printf("%s", p);
#else
(*msgp->message_routine)(msgp->appdata_ptr, p);
#endif
free(p);
}
static sysc_cfga_stat_t *
sysc_stat(const char *ap_id, int *fdp)
{
int fd;
static sysc_cfga_stat_t sc_list[MAX_BOARDS];
if ((fd = open(ap_id, O_RDWR, 0)) == -1)
return (NULL);
else if (ioctl(fd, SYSC_CFGA_CMD_GETSTATUS, sc_list) == -1) {
(void) close(fd);
return (NULL);
} else if (fdp)
*fdp = fd;
else
(void) close(fd);
return (sc_list);
}
/*
* This code implementes the simulation of the ioctls that transition state.
* The GETSTAT ioctl is not simulated. In this way a snapshot of the system
* state is read and manipulated by the simulation routines. It is basically
* a useful debugging tool.
*/
#ifdef SIM
static int sim_idx;
static int sim_fd = -1;
static int sim_size = MAX_BOARDS * sizeof (sysc_cfga_stat_t);
static sysc_cfga_stat_t sim_sc_list[MAX_BOARDS];
static sysc_cfga_stat_t *
sim_sysc_stat(const char *ap_id, int *fdp)
{
int fd;
struct stat buf;
if (sim_fd != -1)
return (sim_sc_list);
if ((sim_fd = open("/tmp/cfga_simdata", O_RDWR|O_CREAT)) == -1) {
perror("sim_open");
exit(1);
} else if (fstat(sim_fd, &buf) == -1) {
perror("sim_stat");
exit(1);
}
if (buf.st_size) {
if (buf.st_size != sim_size) {
perror("sim_size");
exit(1);
} else if (read(sim_fd, sim_sc_list, sim_size) == -1) {
perror("sim_read");
exit(1);
}
} else if ((fd = open(ap_id, O_RDWR, 0)) == -1)
return (NULL);
else if (ioctl(fd, SYSC_CFGA_CMD_GETSTATUS, sim_sc_list) == -1) {
(void) close(fd);
return (NULL);
} else if (fdp)
*fdp = fd;
return (sim_sc_list);
}
static int
sim_open(char *a, int b, int c)
{
printf("sim_open(%s)\n", a);
if (strcmp(a, "/dev/openprom") == 0)
return (open(a, b, c));
return (0);
}
static int
sim_close(int a) { return (0); }
static int
sim_ioctl(int fd, int cmd, void *a)
{
printf("sim_ioctl(%d)\n", sim_idx);
switch (cmd) {
case SYSC_CFGA_CMD_CONNECT:
sim_sc_list[sim_idx].rstate = SYSC_CFGA_RSTATE_CONNECTED;
break;
case SYSC_CFGA_CMD_CONFIGURE:
sim_sc_list[sim_idx].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
break;
case SYSC_CFGA_CMD_UNCONFIGURE:
sim_sc_list[sim_idx].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
break;
case SYSC_CFGA_CMD_DISCONNECT:
sim_sc_list[sim_idx].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
break;
case SYSC_CFGA_CMD_QUIESCE_TEST:
case SYSC_CFGA_CMD_TEST:
return (0);
case OPROMGETOPT:
return (ioctl(fd, OPROMGETOPT, a));
case OPROMSETOPT:
return (ioctl(fd, OPROMSETOPT, a));
}
if (lseek(sim_fd, SEEK_SET, 0) == -1) {
perror("sim_seek");
exit(1);
}
if (write(sim_fd, sim_sc_list, sim_size) == -1) {
perror("sim_write");
exit(1);
}
return (0);
}
#define open(a, b, c) sim_open((char *)(a), (int)(b), (int)(c))
#define close(a) sim_close(a)
#define ioctl(a, b, c) sim_ioctl((int)(a), (int)(b), (void *)(c))
#define sysc_stat(a, b) sim_sysc_stat(a, b)
#endif /* SIM */
static char *promdev = "/dev/openprom";
static char *dlprop = "disabled-board-list";
#define BUFSIZE 128
typedef union {
char buf[BUFSIZE];
struct openpromio opp;
} oppbuf_t;
static int
prom_get_prop(int prom_fd, char *var, char **val)
{
static oppbuf_t oppbuf;
struct openpromio *opp = &(oppbuf.opp);
(void) strncpy(opp->oprom_array, var, OBP_MAXPROPNAME);
opp->oprom_array[OBP_MAXPROPNAME + 1] = '\0';
opp->oprom_size = BUFSIZE;
DBG3("getprop(%s, %d)\n", opp->oprom_array, opp->oprom_size);
if (ioctl(prom_fd, OPROMGETOPT, opp) < 0)
return (ERR_PROM_GETPROP);
else if (opp->oprom_size > 0)
*val = opp->oprom_array;
else
*val = NULL;
return (0);
}
static cfga_err_t
prom_set_prop(int prom_fd, char *var, char *val)
{
oppbuf_t oppbuf;
struct openpromio *opp = &(oppbuf.opp);
int varlen = strlen(var) + 1;
int vallen = strlen(val);
DBG("prom_set_prop(%s)\n", val);
(void) strcpy(opp->oprom_array, var);
(void) strcpy(opp->oprom_array + varlen, val);
opp->oprom_size = varlen + vallen;
if (ioctl(prom_fd, OPROMSETOPT, opp) < 0)
return (ERR_PROM_SETPROP);
return (0);
}
static int
dlist_find(int board, char **dlist, int *disabled)
{
int i;
int err;
int prom_fd;
char *p;
char *dl;
char b[2];
if ((prom_fd = open(promdev, O_RDWR, 0)) < 0)
return (ERR_PROM_OPEN);
else if (err = prom_get_prop(prom_fd, dlprop, dlist)) {
(void) close(prom_fd);
return (err);
} else
(void) close(prom_fd);
b[1] = 0;
*disabled = 0;
if ((dl = *dlist) != NULL) {
int len = strlen(dl);
for (i = 0; i < len; i++) {
int bd;
b[0] = dl[i];
bd = strtol(b, &p, 16);
if (p != b && bd == board)
(*disabled)++;
}
}
return (0);
}
static int
dlist_update(int board, int disable, char *dlist, struct cfga_msg *msgp,
int verbose)
{
int i, j, n;
int err;
int found;
int update;
int prom_fd;
char *p;
char b[2];
char ndlist[64];
b[1] = 0;
ndlist[0] = 0;
j = 0;
found = 0;
update = 0;
if ((prom_fd = open(promdev, O_RDWR, 0)) < 0)
return (ERR_PROM_OPEN);
if (dlist) {
int len = strlen(dlist);
for (i = 0; i < len; i++) {
int bd;
b[0] = dlist[i];
bd = strtol(b, &p, 16);
if (p != b && bd == board) {
found++;
if (disable) {
if (verbose)
cfga_msg(msgp, STR_BD,
DIAG_WAS_DISABLED, 0);
} else {
if (verbose)
cfga_msg(msgp, STR_BD,
DIAG_WILL_ENABLE, 0);
update++;
continue;
}
}
ndlist[j++] = dlist[i];
}
ndlist[j] = 0;
}
if (!found)
if (disable) {
if (verbose)
cfga_msg(msgp, STR_BD, DIAG_WILL_DISABLE, 0);
p = &ndlist[j];
n = sprintf(p, "%x", board);
p[n] = 0;
update++;
} else {
if (verbose)
cfga_msg(msgp, STR_BD, DIAG_WAS_ENABLED, 0);
}
if (update)
err = prom_set_prop(prom_fd, dlprop, ndlist);
else
err = 0;
(void) close(prom_fd);
return (err);
}
static int
ap_idx(const char *ap_id)
{
int id;
char *s;
static char *slot = "slot";
DBG("ap_idx(%s)\n", ap_id);
if ((s = strstr(ap_id, slot)) == NULL)
return (-1);
else {
int n;
s += strlen(slot);
n = strlen(s);
DBG3("ap_idx: s=%s, n=%d\n", s, n);
switch (n) {
case 2:
if (!isdigit(s[1]))
return (-1);
/* FALLTHROUGH */
case 1:
if (!isdigit(s[0]))
return (-1);
break;
default:
return (-1);
}
}
if ((id = atoi(s)) > MAX_BOARDS)
return (-1);
DBG3("ap_idx(%s)=%d\n", s, id);
return (id);
}
/*ARGSUSED*/
cfga_err_t
cfga_change_state(
cfga_cmd_t state_change_cmd,
const char *ap_id,
const char *options,
struct cfga_confirm *confp,
struct cfga_msg *msgp,
char **errstring,
cfga_flags_t flags)
{
int fd;
int idx;
int err;
int force;
int verbose;
int opterr;
int disable;
int disabled;
cfga_err_t rc;
sysc_cfga_stat_t *ss;
sysc_cfga_cmd_t *sc, sysc_cmd;
sysc_cfga_rstate_t rs;
sysc_cfga_ostate_t os;
char *dlist;
char outputstr[SYSC_OUTPUT_LEN];
if (errstring != NULL)
*errstring = NULL;
rc = CFGA_ERROR;
if (options) {
disable = 0;
if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
disable++;
else if (strcmp(options, cfga_str(OPT_ENABLE))) {
cfga_err(NULL, errstring, ERR_OPT_INVAL, options, 0);
return (rc);
}
}
if ((idx = ap_idx(ap_id)) == -1) {
cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
return (rc);
} else if ((ss = sysc_stat(ap_id, &fd)) == NULL) {
cfga_err(NULL, errstring, CMD_GETSTAT, 0);
return (rc);
}
#ifdef SIM
sim_idx = idx;
#endif
/*
* We disallow connecting on the disabled list unless
* either the FORCE flag or the enable-at-boot option
* is set. The check is made further below
*/
if (opterr = dlist_find(idx, &dlist, &disabled)) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
cfga_err(NULL, errstring, err, opterr, 0);
(void) close(fd);
return (rc);
} else
force = flags & CFGA_FLAG_FORCE;
rs = ss[idx].rstate;
os = ss[idx].ostate;
sc = &sysc_cmd;
sysc_cmd_init(sc, outputstr, force);
verbose = flags & CFGA_FLAG_VERBOSE;
switch (state_change_cmd) {
case CFGA_CMD_CONNECT:
if (rs != SYSC_CFGA_RSTATE_DISCONNECTED)
cfga_err(NULL, errstring, ERR_TRANS, 0);
else if (disabled && !(force || (options && !disable)))
cfga_err(NULL, errstring, CMD_CONNECT,
ERR_DISABLED, DIAG_FORCE, 0);
else if (!(*confp->confirm)(confp->appdata_ptr,
cfga_str(ASK_CONNECT))) {
(void) close(fd);
return (CFGA_NACK);
} else if (ioctl(fd, SYSC_CFGA_CMD_CONNECT, sc) == -1)
cfga_err(sc, errstring, CMD_CONNECT, 0);
else if (options && (opterr = dlist_update(idx, disable,
dlist, msgp, verbose))) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
cfga_err(NULL, errstring, err, opterr, 0);
} else
rc = CFGA_OK;
break;
case CFGA_CMD_DISCONNECT:
if ((os == SYSC_CFGA_OSTATE_CONFIGURED) &&
(ioctl(fd, SYSC_CFGA_CMD_UNCONFIGURE, sc) == -1)) {
cfga_err(sc, errstring, CMD_UNCONFIGURE, 0);
(void) close(fd);
return (CFGA_ERROR);
} else
sysc_cmd_init(sc, outputstr, force);
if (rs == SYSC_CFGA_RSTATE_CONNECTED) {
if (ioctl(fd, SYSC_CFGA_CMD_DISCONNECT, sc) == -1)
cfga_err(sc, errstring, CMD_DISCONNECT, 0);
else if (options && (opterr = dlist_update(idx, disable,
dlist, msgp, verbose))) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
cfga_err(NULL, errstring, err, opterr, 0);
} else
rc = CFGA_OK;
} else
cfga_err(NULL, errstring, ERR_TRANS, 0);
break;
case CFGA_CMD_CONFIGURE:
if (rs == SYSC_CFGA_RSTATE_DISCONNECTED)
if (disabled && !(force || (options && !disable))) {
cfga_err(NULL, errstring, CMD_CONFIGURE,
ERR_DISABLED, DIAG_FORCE, 0);
(void) close(fd);
return (CFGA_ERROR);
} else if (!(*confp->confirm)(confp->appdata_ptr,
cfga_str(ASK_CONNECT))) {
(void) close(fd);
return (CFGA_NACK);
} else if (ioctl(fd, SYSC_CFGA_CMD_CONNECT, sc) == -1) {
cfga_err(sc, errstring, CMD_CONNECT, 0);
(void) close(fd);
return (CFGA_ERROR);
} else
sysc_cmd_init(sc, outputstr, force);
if (os == SYSC_CFGA_OSTATE_UNCONFIGURED) {
if (ioctl(fd, SYSC_CFGA_CMD_CONFIGURE, sc) == -1)
cfga_err(sc, errstring, CMD_CONFIGURE, 0);
else if (options && (opterr = dlist_update(idx,
disable, dlist, msgp, verbose))) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
cfga_err(NULL, errstring, err, opterr, 0);
} else
rc = CFGA_OK;
} else
cfga_err(NULL, errstring, ERR_TRANS, 0);
break;
case CFGA_CMD_UNCONFIGURE:
if (os != SYSC_CFGA_OSTATE_CONFIGURED)
cfga_err(NULL, errstring, ERR_TRANS, 0);
else if (ioctl(fd, SYSC_CFGA_CMD_UNCONFIGURE, sc) == -1)
cfga_err(sc, errstring, CMD_UNCONFIGURE, 0);
else if (options && (opterr = dlist_update(idx, disable,
dlist, msgp, verbose))) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
cfga_err(NULL, errstring, err, opterr, 0);
} else
rc = CFGA_OK;
break;
default:
rc = CFGA_OPNOTSUPP;
break;
}
(void) close(fd);
return (rc);
}
static int
str2cond(const char *cond)
{
int c;
if (strcmp(cond, cfga_str(COND_UNKNOWN)) == 0)
c = SYSC_CFGA_COND_UNKNOWN;
else if (strcmp(cond, cfga_str(COND_OK)) == 0)
c = SYSC_CFGA_COND_OK;
else if (strcmp(cond, cfga_str(COND_FAILING)) == 0)
c = SYSC_CFGA_COND_FAILING;
else if (strcmp(cond, cfga_str(COND_FAILED)) == 0)
c = SYSC_CFGA_COND_FAILED;
else if (strcmp(cond, cfga_str(COND_UNUSABLE)) == 0)
c = SYSC_CFGA_COND_UNUSABLE;
else
c = -1;
return (c);
}
/*ARGSUSED*/
cfga_err_t
cfga_private_func(
const char *function,
const char *ap_id,
const char *options,
struct cfga_confirm *confp,
struct cfga_msg *msgp,
char **errstring,
cfga_flags_t flags)
{
int fd;
int idx;
int len;
int cmd;
int cond;
int err;
int opterr;
int verbose;
int disable;
int disabled;
cfga_err_t rc;
char *str;
char *dlist;
char outputstr[SYSC_OUTPUT_LEN];
sysc_cfga_cmd_t *sc, sysc_cmd;
if (errstring != NULL)
*errstring = NULL;
verbose = flags & CFGA_FLAG_VERBOSE;
rc = CFGA_ERROR;
if (options) {
disable = 0;
if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
disable++;
else if (strcmp(options, cfga_str(OPT_ENABLE))) {
cfga_err(NULL, errstring, ERR_OPT_INVAL, options, 0);
return (rc);
}
}
sc = &sysc_cmd;
str = cfga_str(CMD_SET_COND);
len = strlen(str);
if ((strncmp(function, str, len) == 0) && (function[len++] == '=') &&
((cond = (str2cond(&function[len]))) != -1)) {
cmd = SYSC_CFGA_CMD_TEST_SET_COND;
err = CMD_SET_COND;
sc->arg = cond;
} else if (strcmp(function, cfga_str(CMD_QUIESCE)) == 0) {
cmd = SYSC_CFGA_CMD_QUIESCE_TEST;
err = CMD_QUIESCE;
} else if (strcmp(function, cfga_str(CMD_INSERT)) == 0) {
cmd = SYSC_CFGA_CMD_TEST;
err = CMD_INSERT;
} else if (strcmp(function, cfga_str(CMD_REMOVE)) == 0) {
cmd = SYSC_CFGA_CMD_TEST;
err = CMD_REMOVE;
} else {
cfga_err(NULL, errstring, ERR_CMD_INVAL, (char *)function, 0);
return (rc);
}
sysc_cmd_init(sc, outputstr, 0);
if ((idx = ap_idx(ap_id)) == -1)
cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
else if (((fd = open(ap_id, O_RDWR, 0)) == -1) ||
(ioctl(fd, cmd, sc) == -1))
cfga_err(NULL, errstring, err, 0);
else
rc = CFGA_OK;
if (options) {
opterr = (dlist_find(idx, &dlist, &disabled) ||
dlist_update(idx, disable, dlist, msgp, verbose));
if (opterr) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
if (verbose)
cfga_msg(msgp, err, opterr, 0);
}
}
(void) close(fd);
return (rc);
}
/*ARGSUSED*/
cfga_err_t
cfga_test(
const char *ap_id,
const char *options,
struct cfga_msg *msgp,
char **errstring,
cfga_flags_t flags)
{
if (errstring != NULL)
*errstring = NULL;
return (CFGA_OPNOTSUPP);
}
static cfga_stat_t
rstate_cvt(sysc_cfga_rstate_t rs)
{
cfga_stat_t cs;
switch (rs) {
case SYSC_CFGA_RSTATE_EMPTY:
cs = CFGA_STAT_EMPTY;
break;
case SYSC_CFGA_RSTATE_DISCONNECTED:
cs = CFGA_STAT_DISCONNECTED;
break;
case SYSC_CFGA_RSTATE_CONNECTED:
cs = CFGA_STAT_CONNECTED;
break;
default:
cs = CFGA_STAT_NONE;
break;
}
return (cs);
}
static cfga_stat_t
ostate_cvt(sysc_cfga_ostate_t os)
{
cfga_stat_t cs;
switch (os) {
case SYSC_CFGA_OSTATE_UNCONFIGURED:
cs = CFGA_STAT_UNCONFIGURED;
break;
case SYSC_CFGA_OSTATE_CONFIGURED:
cs = CFGA_STAT_CONFIGURED;
break;
default:
cs = CFGA_STAT_NONE;
break;
}
return (cs);
}
static cfga_cond_t
cond_cvt(sysc_cfga_cond_t sc)
{
cfga_cond_t cc;
switch (sc) {
case SYSC_CFGA_COND_OK:
cc = CFGA_COND_OK;
break;
case SYSC_CFGA_COND_FAILING:
cc = CFGA_COND_FAILING;
break;
case SYSC_CFGA_COND_FAILED:
cc = CFGA_COND_FAILED;
break;
case SYSC_CFGA_COND_UNUSABLE:
cc = CFGA_COND_UNUSABLE;
break;
case SYSC_CFGA_COND_UNKNOWN:
default:
cc = CFGA_COND_UNKNOWN;
break;
}
return (cc);
}
static char *
type_str(enum board_type type)
{
char *type_str;
switch (type) {
case MEM_BOARD:
type_str = cfga_str(BD_MEM);
break;
case CPU_BOARD:
type_str = cfga_str(BD_CPU);
break;
case IO_2SBUS_BOARD:
type_str = cfga_str(BD_IO_2SBUS);
break;
case IO_SBUS_FFB_BOARD:
type_str = cfga_str(BD_IO_SBUS_FFB);
break;
case IO_PCI_BOARD:
type_str = cfga_str(BD_IO_PCI);
break;
case DISK_BOARD:
type_str = cfga_str(BD_DISK);
break;
case IO_2SBUS_SOCPLUS_BOARD:
type_str = cfga_str(BD_IO_2SBUS_SOCPLUS);
break;
case IO_SBUS_FFB_SOCPLUS_BOARD:
type_str = cfga_str(BD_IO_SBUS_FFB_SOCPLUS);
break;
case UNKNOWN_BOARD:
default:
type_str = cfga_str(BD_UNKNOWN);
break;
}
return (type_str);
}
static void
info_set(sysc_cfga_stat_t *sc, cfga_info_t info, int disabled)
{
int i;
struct cpu_info *cpu;
union bd_un *bd = &sc->bd;
*info = NULL;
switch (sc->type) {
case CPU_BOARD:
for (i = 0, cpu = bd->cpu; i < 2; i++, cpu++) {
if (cpu->cpu_speed > 1) {
info += sprintf(info, "cpu %d: ", i);
info += sprintf(info, "%3d MHz ",
cpu->cpu_speed);
if (cpu->cache_size)
info += sprintf(info, "%0.1fM ",
(float)cpu->cache_size /
(float)(1024 * 1024));
}
}
break;
case IO_SBUS_FFB_BOARD:
switch (bd->io2.ffb_size) {
case FFB_SINGLE:
info += sprintf(info, "single buffered ffb ");
break;
case FFB_DOUBLE:
info += sprintf(info, "double buffered ffb ");
break;
case FFB_NOT_FOUND:
#ifdef FFB_DR_SUPPORT
info += sprintf(info, "no ffb installed ");
#endif
break;
default:
info += sprintf(info, "illegal ffb size ");
break;
}
break;
case DISK_BOARD:
for (i = 0; i < 2; i++)
if (bd->dsk.disk_pres[i])
info += sprintf(info, "target: %2d ",
bd->dsk.disk_id[i]);
else
info += sprintf(info, "no disk ");
break;
}
if (disabled)
info += sprintf(info, "disabled at boot ");
if (sc->no_detach)
info += sprintf(info, "non-detachable ");
if (sc->plus_board)
info += sprintf(info, "100 MHz capable ");
}
static void
sysc_cvt(sysc_cfga_stat_t *sc, cfga_stat_data_t *cs, int disabled)
{
(void) strcpy(cs->ap_type, type_str(sc->type));
cs->ap_r_state = rstate_cvt(sc->rstate);
cs->ap_o_state = ostate_cvt(sc->ostate);
cs->ap_cond = cond_cvt(sc->condition);
cs->ap_busy = (cfga_busy_t)sc->in_transition;
cs->ap_status_time = sc->last_change;
info_set(sc, cs->ap_info, disabled);
cs->ap_log_id[0] = NULL;
cs->ap_phys_id[0] = NULL;
}
/*ARGSUSED*/
cfga_err_t
cfga_list(
const char *ap_id,
cfga_stat_data_t **ap_list,
int *nlist,
const char *options,
char **errstring)
{
int i;
cfga_err_t rc;
sysc_cfga_stat_t *sc;
cfga_stat_data_t *cs;
if (errstring != NULL)
*errstring = NULL;
rc = CFGA_ERROR;
if (ap_idx(ap_id) == -1)
cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
else if ((sc = sysc_stat(ap_id, NULL)) == NULL)
cfga_err(NULL, errstring, CMD_LIST, 0);
else if (!(cs = (cfga_stat_data_t *)malloc(MAX_BOARDS * sizeof (*cs))))
cfga_err(NULL, errstring, CMD_LIST, 0);
else {
*ap_list = cs;
for (*nlist = 0, i = 0; i < MAX_BOARDS; i++, sc++) {
if (sc->board == -1)
continue;
sysc_cvt(sc, cs++, 0); /* XXX - disable */
(*nlist)++;
}
rc = CFGA_OK;
}
return (rc);
}
/*ARGSUSED*/
cfga_err_t
cfga_stat(
const char *ap_id,
struct cfga_stat_data *cs,
const char *options,
char **errstring)
{
cfga_err_t rc;
int idx;
int err;
int opterr;
int disable;
int disabled;
char *dlist;
sysc_cfga_stat_t *sc;
if (errstring != NULL)
*errstring = NULL;
rc = CFGA_ERROR;
if (options && options[0]) {
disable = 0;
if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
disable++;
else if (strcmp(options, cfga_str(OPT_ENABLE))) {
cfga_err(NULL, errstring, ERR_OPT_INVAL, options, 0);
return (rc);
}
}
if ((idx = ap_idx(ap_id)) == -1)
cfga_err(NULL, errstring, ERR_AP_INVAL, ap_id, 0);
else if ((sc = sysc_stat(ap_id, NULL)) == NULL)
cfga_err(NULL, errstring, CMD_GETSTAT, 0);
else {
opterr = dlist_find(idx, &dlist, &disabled);
sysc_cvt(sc + idx, cs, disabled);
rc = CFGA_OK;
if (options && options[0] && ((opterr != 0) ||
((opterr = dlist_update(idx, disable, dlist, NULL, 0))
!= 0))) {
err = disable ? OPT_DISABLE : OPT_ENABLE;
cfga_err(NULL, errstring, err, opterr, 0);
}
}
return (rc);
}
/*ARGSUSED*/
cfga_err_t
cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
{
int help = 0;
if (options) {
if (strcmp(options, cfga_str(OPT_DISABLE)) == 0)
help = HELP_DISABLE;
else if (strcmp(options, cfga_str(OPT_ENABLE)) == 0)
help = HELP_ENABLE;
else if (strcmp(options, cfga_str(CMD_INSERT)) == 0)
help = HELP_INSERT;
else if (strcmp(options, cfga_str(CMD_REMOVE)) == 0)
help = HELP_REMOVE;
else if (strcmp(options, cfga_str(CMD_QUIESCE)) == 0)
help = HELP_QUIESCE;
else
help = HELP_UNKNOWN;
}
if (help) {
if (help == HELP_UNKNOWN)
cfga_msg(msgp, help, options, 0);
else
cfga_msg(msgp, help, 0);
} else {
cfga_msg(msgp, HELP_HEADER, 0);
cfga_msg(msgp, HELP_DISABLE, 0);
cfga_msg(msgp, HELP_ENABLE, 0);
cfga_msg(msgp, HELP_INSERT, 0);
cfga_msg(msgp, HELP_REMOVE, 0);
cfga_msg(msgp, HELP_QUIESCE, 0);
cfga_msg(msgp, HELP_SET_COND, 0);
}
return (CFGA_OK);
}
/*
* cfga_ap_id_cmp -- use default_ap_id_cmp() in libcfgadm
*/