/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SIM #include #endif #define CFGA_PLUGIN_LIB #include #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=", /* 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 * 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 = '\0'; 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] = '\0'; cs->ap_phys_id[0] = '\0'; } /*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 */