2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <ctype.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <unistd.h>
2N/A#include <macros.h>
2N/A#include <dirent.h>
2N/A#include <libgen.h>
2N/A#include <libdevinfo.h>
2N/A#define CFGA_PLUGIN_LIB
2N/A#include <config_admin.h>
2N/A#include "ap.h"
2N/A
2N/A#ifdef __x86
2N/A#include <libscf_priv.h>
2N/A
2N/Astatic int fastreboot_disabled;
2N/A#endif /* __x86 */
2N/A
2N/Astatic cfga_err_t
2N/Aap_suspend_check(apd_t *a, int cmd, int first, int last, int *suspend)
2N/A{
2N/A int c;
2N/A int rc;
2N/A int skip;
2N/A int check;
2N/A
2N/A skip = a->opts.skip;
2N/A
2N/A /*
2N/A * Check if any of the steps in the sequence
2N/A * may require a suspension of service and ask
2N/A * the user to confirm.
2N/A */
2N/A for (check = 0, c = first; c <= last; c++)
2N/A if (mask(c) & skip)
2N/A continue;
2N/A else if ((rc = ap_suspend_query(a, c, &check)) != CFGA_OK)
2N/A return (rc);
2N/A
2N/A *suspend = check;
2N/A
2N/A /*
2N/A * If a suspend is required, ask for user confirmation.
2N/A * The force flag overrides the user confirmation.
2N/A */
2N/A if (check && (!ap_getopt(a, OPT_FORCE)) && (!ap_confirm(a))) {
2N/A ap_err(a, ERR_CMD_NACK, cmd);
2N/A return (CFGA_NACK);
2N/A }
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/A#define AP_SEQ_OK 0
2N/A#define AP_SEQ_NULL 1
2N/A#define AP_SEQ_FAIL -1
2N/A
2N/A/*
2N/A * Sequence a cfgadm state change command into driver commands.
2N/A * The rstate and ostate of the AP are needed at this point
2N/A * in order to compute the proper sequence.
2N/A */
2N/Astatic int
2N/Aap_seq_get(apd_t *a, int cmd, int *first, int *last)
2N/A{
2N/A int done = 0;
2N/A int f = CMD_NONE;
2N/A int l = CMD_NONE;
2N/A cfga_stat_t rs, os;
2N/A
2N/A ap_state(a, &rs, &os);
2N/A
2N/A switch (rs) {
2N/A case CFGA_STAT_EMPTY:
2N/A switch (os) {
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A switch (cmd) {
2N/A case CMD_UNCONFIGURE:
2N/A done++;
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A case CFGA_STAT_DISCONNECTED:
2N/A switch (os) {
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A switch (cmd) {
2N/A case CMD_DISCONNECT:
2N/A /*
2N/A * skip the disconnect command since
2N/A * the rstate is already disconnected
2N/A */
2N/A f = CMD_DISCONNECT;
2N/A a->opts.skip |= mask(CMD_DISCONNECT);
2N/A l = CMD_UNASSIGN;
2N/A break;
2N/A case CMD_UNCONFIGURE:
2N/A done++;
2N/A break;
2N/A case CMD_CONNECT:
2N/A f = CMD_ASSIGN;
2N/A l = CMD_CONNECT;
2N/A break;
2N/A case CMD_CONFIGURE:
2N/A f = CMD_ASSIGN;
2N/A l = CMD_RCM_CAP_ADD;
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A case CFGA_STAT_CONNECTED:
2N/A switch (os) {
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A switch (cmd) {
2N/A case CMD_CONNECT:
2N/A case CMD_UNCONFIGURE:
2N/A done++;
2N/A break;
2N/A case CMD_DISCONNECT:
2N/A f = CMD_DISCONNECT;
2N/A l = CMD_UNASSIGN;
2N/A break;
2N/A case CMD_CONFIGURE:
2N/A f = CMD_CONFIGURE;
2N/A l = CMD_RCM_CAP_ADD;
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A case CFGA_STAT_CONFIGURED:
2N/A switch (cmd) {
2N/A case CMD_CONNECT:
2N/A done++;
2N/A break;
2N/A case CMD_DISCONNECT:
2N/A f = CMD_SUSPEND_CHECK;
2N/A l = CMD_UNASSIGN;
2N/A break;
2N/A case CMD_CONFIGURE:
2N/A f = CMD_CONFIGURE;
2N/A l = CMD_RCM_CAP_ADD;
2N/A break;
2N/A case CMD_UNCONFIGURE:
2N/A f = CMD_SUSPEND_CHECK;
2N/A l = CMD_RCM_CAP_NOTIFY;
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A if (f == CMD_NONE) {
2N/A if (done)
2N/A return (AP_SEQ_NULL);
2N/A ap_err(a, ERR_TRANS_INVAL, cmd);
2N/A return (AP_SEQ_FAIL);
2N/A }
2N/A
2N/A *first = f;
2N/A *last = l;
2N/A
2N/A DBG("ap_seq(%d, %d, %d, %p, %p) = (%d, %d)\n",
2N/A rs, os, cmd, (void *)first, (void *)last, f, l);
2N/A
2N/A return (AP_SEQ_OK);
2N/A}
2N/A
2N/A#define DBG_RECOVER_MSG(f, l) \
2N/A DBG("Sequencing recovery: first = %s, last = %s\n", \
2N/A ap_cmd_name(f), ap_cmd_name(l))
2N/A
2N/Acfga_err_t
2N/Aap_seq_exec(apd_t *a, int cmd, int first, int last)
2N/A{
2N/A int c;
2N/A int skip;
2N/A int suspend;
2N/A int resume;
2N/A cfga_err_t rc;
2N/A int recover_f = CMD_NONE; /* first recovery cmd */
2N/A int recover_l = CMD_NONE; /* last recovery cmd */
2N/A
2N/A
2N/A suspend = 0;
2N/A resume = 0;
2N/A
2N/A skip = a->opts.skip;
2N/A
2N/A /*
2N/A * The unassign step is skipped unless explicity requested
2N/A * either by a -x request or as an option to a disconnect
2N/A * request.
2N/A */
2N/A if (cmd != CMD_UNASSIGN && ap_getopt(a, OPT_UNASSIGN) == 0)
2N/A skip |= mask(CMD_UNASSIGN);
2N/A
2N/A /*
2N/A * Check for platform options
2N/A */
2N/A rc = ap_platopts_check(a, first, last);
2N/A
2N/A if (rc != CFGA_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A for (c = first; c <= last; c++) {
2N/A if (mask(c) & skip) {
2N/A ap_msg(a, MSG_SKIP, c, a->target);
2N/A continue;
2N/A }
2N/A
2N/A DBG("exec %s\n", ap_cmd_name(c));
2N/A
2N/A /*
2N/A * If the suspend operation does not
2N/A * succeed, resume any devices already
2N/A * suspended as well as the device on
2N/A * which the operation failed.
2N/A */
2N/A switch (c) {
2N/A case CMD_SUSPEND_CHECK:
2N/A /*
2N/A * Check whether the user allows a suspend
2N/A * operation if the suspend is required.
2N/A * Next step is to allow RCM clients to
2N/A * interpose on the suspend operation.
2N/A */
2N/A rc = ap_suspend_check(a, cmd,
2N/A first + 1, last, &suspend);
2N/A break;
2N/A case CMD_RCM_SUSPEND:
2N/A if (suspend && ((rc = ap_rcm_ctl(a, c)) == CFGA_OK)) {
2N/A /*
2N/A * Mark the fact that a suspend operation
2N/A * is required, and that RCM clients have
2N/A * allowed the suspend.
2N/A */
2N/A ap_setopt(a, OPT_SUSPEND_OK);
2N/A resume++;
2N/A }
2N/A break;
2N/A case CMD_RCM_RESUME:
2N/A if (resume) {
2N/A (void) ap_rcm_ctl(a, c);
2N/A resume--;
2N/A }
2N/A break;
2N/A case CMD_RCM_OFFLINE:
2N/A case CMD_RCM_CAP_DEL:
2N/A rc = ap_rcm_ctl(a, c);
2N/A break;
2N/A case CMD_RCM_ONLINE:
2N/A case CMD_RCM_CAP_ADD:
2N/A case CMD_RCM_REMOVE:
2N/A case CMD_RCM_CAP_NOTIFY:
2N/A (void) ap_rcm_ctl(a, c);
2N/A break;
2N/A
2N/A#ifdef __x86
2N/A /*
2N/A * Disable fast reboot if a CPU/MEM/IOH hotplug event happens.
2N/A * Note: this is a temporary solution and will be revised when
2N/A * fast reboot can support CPU/MEM/IOH DR operations in the
2N/A * future.
2N/A *
2N/A * ACPI BIOS generates some static ACPI tables, such as MADT,
2N/A * SRAT and SLIT, to describe the system hardware configuration
2N/A * on power-on. When a CPU/MEM/IOH hotplug event happens, those
2N/A * static tables won't be updated and will become stale.
2N/A *
2N/A * If we reset the system by fast reboot, BIOS will have no
2N/A * chance to regenerate those staled static tables. Fast reboot
2N/A * can't tolerate such inconsistency between staled ACPI tables
2N/A * and real hardware configuration yet.
2N/A *
2N/A * A temporary solution is introduced to disable fast reboot if
2N/A * CPU/MEM/IOH hotplug event happens. This solution should be
2N/A * revised when fast reboot is enhanced to support CPU/MEM/IOH
2N/A * DR operations.
2N/A */
2N/A case CMD_ASSIGN:
2N/A case CMD_POWERON:
2N/A case CMD_POWEROFF:
2N/A case CMD_UNASSIGN:
2N/A if (!fastreboot_disabled &&
2N/A scf_fastreboot_default_set_transient(B_FALSE) ==
2N/A SCF_SUCCESS) {
2N/A fastreboot_disabled = 1;
2N/A }
2N/A /* FALLTHROUGH */
2N/A#endif /* __x86 */
2N/A
2N/A default:
2N/A rc = ap_ioctl(a, c);
2N/A break;
2N/A }
2N/A
2N/A if (rc != CFGA_OK)
2N/A break;
2N/A
2N/A }
2N/Adone:
2N/A
2N/A if (resume)
2N/A (void) ap_rcm_ctl(a, CMD_RCM_RESUME);
2N/A
2N/A /*
2N/A * Check if any operations failed. If so, attempt to rollback
2N/A * to previously known states.
2N/A * Note: The rollback is currently limited to RCM operations.
2N/A */
2N/A if (rc != CFGA_OK) {
2N/A if (c == CMD_UNCONFIGURE ||
2N/A c == CMD_RCM_OFFLINE ||
2N/A c == CMD_RCM_CAP_DEL) {
2N/A DBG("ap_seq_exec: %s failed\n", ap_cmd_name(c));
2N/A
2N/A switch (c) {
2N/A case CMD_UNCONFIGURE:
2N/A /*
2N/A * If the unconfigure operation fails, perform
2N/A * an RCM_ONLINE and RCM_CAP_NOTIFY only. This
2N/A * keeps RCM clients consistent with the domain.
2N/A */
2N/A recover_f = CMD_RCM_ONLINE;
2N/A recover_l = CMD_RCM_ONLINE;
2N/A DBG_RECOVER_MSG(recover_f, recover_l);
2N/A (void) ap_seq_exec(a, cmd, recover_f,
2N/A recover_l);
2N/A
2N/A recover_f = CMD_RCM_CAP_NOTIFY;
2N/A recover_l = CMD_RCM_CAP_NOTIFY;
2N/A DBG_RECOVER_MSG(recover_f, recover_l);
2N/A (void) ap_seq_exec(a, cmd, recover_f,
2N/A recover_l);
2N/A break;
2N/A case CMD_RCM_OFFLINE:
2N/A recover_f = CMD_RCM_ONLINE;
2N/A recover_l = CMD_RCM_CAP_ADD;
2N/A DBG_RECOVER_MSG(recover_f, recover_l);
2N/A (void) ap_seq_exec(a, cmd, recover_f,
2N/A recover_l);
2N/A break;
2N/A case CMD_RCM_CAP_DEL:
2N/A recover_f = CMD_RCM_CAP_ADD;
2N/A recover_l = CMD_RCM_CAP_ADD;
2N/A DBG_RECOVER_MSG(recover_f, recover_l);
2N/A (void) ap_seq_exec(a, cmd, recover_f,
2N/A recover_l);
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A DBG("recovery complete!\n");
2N/A }
2N/A }
2N/A return (rc);
2N/A}
2N/A
2N/Acfga_err_t
2N/Aap_cmd_exec(apd_t *a, int cmd)
2N/A{
2N/A return (ap_seq_exec(a, cmd, cmd, cmd));
2N/A}
2N/A
2N/Acfga_err_t
2N/Aap_cmd_seq(apd_t *a, int cmd)
2N/A{
2N/A int first, last;
2N/A cfga_err_t rc;
2N/A
2N/A switch (ap_seq_get(a, cmd, &first, &last)) {
2N/A case AP_SEQ_OK:
2N/A rc = ap_seq_exec(a, cmd, first, last);
2N/A break;
2N/A case AP_SEQ_NULL:
2N/A rc = CFGA_OK;
2N/A break;
2N/A case AP_SEQ_FAIL:
2N/A default:
2N/A rc = CFGA_LIB_ERROR;
2N/A break;
2N/A }
2N/A
2N/A return (rc);
2N/A}