1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <assert.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <macros.h>
33 #include <dirent.h>
34 #include <libgen.h>
35 #include <libdevinfo.h>
36 #define	CFGA_PLUGIN_LIB
37 #include <config_admin.h>
38 #include "ap.h"
39 
40 #ifdef	__x86
41 #include <libscf_priv.h>
42 
43 static int fastreboot_disabled;
44 #endif	/* __x86 */
45 
46 static cfga_err_t
ap_suspend_check(apd_t * a,int cmd,int first,int last,int * suspend)47 ap_suspend_check(apd_t *a, int cmd, int first, int last, int *suspend)
48 {
49 	int c;
50 	int rc;
51 	int skip;
52 	int check;
53 
54 	skip = a->opts.skip;
55 
56 	/*
57 	 * Check if any of the steps in the sequence
58 	 * may require a suspension of service and ask
59 	 * the user to confirm.
60 	 */
61 	for (check = 0, c = first; c <= last; c++)
62 		if (mask(c) & skip)
63 			continue;
64 		else if ((rc = ap_suspend_query(a, c, &check)) != CFGA_OK)
65 			return (rc);
66 
67 	*suspend = check;
68 
69 	/*
70 	 * If a suspend is required, ask for user confirmation.
71 	 * The force flag overrides the user confirmation.
72 	 */
73 	if (check && (!ap_getopt(a, OPT_FORCE)) && (!ap_confirm(a))) {
74 		ap_err(a, ERR_CMD_NACK, cmd);
75 		return (CFGA_NACK);
76 	}
77 
78 	return (CFGA_OK);
79 }
80 
81 #define	AP_SEQ_OK	0
82 #define	AP_SEQ_NULL	1
83 #define	AP_SEQ_FAIL	-1
84 
85 /*
86  * Sequence a cfgadm state change command into driver commands.
87  * The rstate and ostate of the AP are needed at this point
88  * in order to compute the proper sequence.
89  */
90 static int
ap_seq_get(apd_t * a,int cmd,int * first,int * last)91 ap_seq_get(apd_t *a, int cmd, int *first, int *last)
92 {
93 	int done = 0;
94 	int f = CMD_NONE;
95 	int l = CMD_NONE;
96 	cfga_stat_t rs, os;
97 
98 	ap_state(a, &rs, &os);
99 
100 	switch (rs) {
101 	case CFGA_STAT_EMPTY:
102 		switch (os) {
103 		case CFGA_STAT_UNCONFIGURED:
104 			switch (cmd) {
105 			case CMD_UNCONFIGURE:
106 				done++;
107 				break;
108 			default:
109 				break;
110 			}
111 			break;
112 		default:
113 			break;
114 		}
115 		break;
116 	case CFGA_STAT_DISCONNECTED:
117 		switch (os) {
118 		case CFGA_STAT_UNCONFIGURED:
119 			switch (cmd) {
120 			case CMD_DISCONNECT:
121 				/*
122 				 * skip the disconnect command since
123 				 * the rstate is already disconnected
124 				 */
125 				f = CMD_DISCONNECT;
126 				a->opts.skip |= mask(CMD_DISCONNECT);
127 				l = CMD_UNASSIGN;
128 				break;
129 			case CMD_UNCONFIGURE:
130 				done++;
131 				break;
132 			case CMD_CONNECT:
133 				f = CMD_ASSIGN;
134 				l = CMD_CONNECT;
135 				break;
136 			case CMD_CONFIGURE:
137 				f = CMD_ASSIGN;
138 				l = CMD_RCM_CAP_ADD;
139 				break;
140 			default:
141 				break;
142 			}
143 			break;
144 		default:
145 			break;
146 		}
147 		break;
148 	case CFGA_STAT_CONNECTED:
149 		switch (os) {
150 		case CFGA_STAT_UNCONFIGURED:
151 			switch (cmd) {
152 			case CMD_CONNECT:
153 			case CMD_UNCONFIGURE:
154 				done++;
155 				break;
156 			case CMD_DISCONNECT:
157 				f = CMD_DISCONNECT;
158 				l = CMD_UNASSIGN;
159 				break;
160 			case CMD_CONFIGURE:
161 				f = CMD_CONFIGURE;
162 				l = CMD_RCM_CAP_ADD;
163 				break;
164 			default:
165 				break;
166 			}
167 			break;
168 		case CFGA_STAT_CONFIGURED:
169 			switch (cmd) {
170 			case CMD_CONNECT:
171 				done++;
172 				break;
173 			case CMD_DISCONNECT:
174 				f = CMD_SUSPEND_CHECK;
175 				l = CMD_UNASSIGN;
176 				break;
177 			case CMD_CONFIGURE:
178 				f = CMD_CONFIGURE;
179 				l = CMD_RCM_CAP_ADD;
180 				break;
181 			case CMD_UNCONFIGURE:
182 				f = CMD_SUSPEND_CHECK;
183 				l = CMD_RCM_CAP_NOTIFY;
184 				break;
185 			default:
186 				break;
187 			}
188 			break;
189 		default:
190 			break;
191 		}
192 		break;
193 	default:
194 		break;
195 	}
196 
197 	if (f == CMD_NONE) {
198 		if (done)
199 			return (AP_SEQ_NULL);
200 		ap_err(a, ERR_TRANS_INVAL, cmd);
201 		return (AP_SEQ_FAIL);
202 	}
203 
204 	*first = f;
205 	*last = l;
206 
207 	DBG("ap_seq(%d, %d, %d, %p, %p) = (%d, %d)\n",
208 	    rs, os, cmd, (void *)first, (void *)last, f, l);
209 
210 	return (AP_SEQ_OK);
211 }
212 
213 #define	DBG_RECOVER_MSG(f, l) \
214 	DBG("Sequencing recovery: first = %s, last = %s\n", \
215 	ap_cmd_name(f), ap_cmd_name(l))
216 
217 cfga_err_t
ap_seq_exec(apd_t * a,int cmd,int first,int last)218 ap_seq_exec(apd_t *a, int cmd, int first, int last)
219 {
220 	int c;
221 	int skip;
222 	int suspend;
223 	int resume;
224 	cfga_err_t rc;
225 	int recover_f = CMD_NONE;	/* first recovery cmd */
226 	int recover_l = CMD_NONE;	/* last recovery cmd */
227 
228 
229 	suspend = 0;
230 	resume = 0;
231 
232 	skip = a->opts.skip;
233 
234 	/*
235 	 * The unassign step is skipped unless explicity requested
236 	 * either by a -x request or as an option to a disconnect
237 	 * request.
238 	 */
239 	if (cmd != CMD_UNASSIGN && ap_getopt(a, OPT_UNASSIGN) == 0)
240 		skip |= mask(CMD_UNASSIGN);
241 
242 	/*
243 	 * Check for platform options
244 	 */
245 	rc = ap_platopts_check(a, first, last);
246 
247 	if (rc != CFGA_OK) {
248 		goto done;
249 	}
250 
251 	for (c = first; c <= last; c++) {
252 		if (mask(c) & skip) {
253 			ap_msg(a, MSG_SKIP, c, a->target);
254 			continue;
255 		}
256 
257 		DBG("exec %s\n", ap_cmd_name(c));
258 
259 		/*
260 		 * If the suspend operation does not
261 		 * succeed, resume any devices already
262 		 * suspended as well as the device on
263 		 * which the operation failed.
264 		 */
265 		switch (c) {
266 		case CMD_SUSPEND_CHECK:
267 			/*
268 			 * Check whether the user allows a suspend
269 			 * operation if the suspend is required.
270 			 * Next step is to allow RCM clients to
271 			 * interpose on the suspend operation.
272 			 */
273 			rc = ap_suspend_check(a, cmd,
274 			    first + 1, last, &suspend);
275 			break;
276 		case CMD_RCM_SUSPEND:
277 			if (suspend && ((rc = ap_rcm_ctl(a, c)) == CFGA_OK)) {
278 				/*
279 				 * Mark the fact that a suspend operation
280 				 * is required, and that RCM clients have
281 				 * allowed the suspend.
282 				 */
283 				ap_setopt(a, OPT_SUSPEND_OK);
284 				resume++;
285 			}
286 			break;
287 		case CMD_RCM_RESUME:
288 			if (resume) {
289 				(void) ap_rcm_ctl(a, c);
290 				resume--;
291 			}
292 			break;
293 		case CMD_RCM_OFFLINE:
294 		case CMD_RCM_CAP_DEL:
295 			rc = ap_rcm_ctl(a, c);
296 			break;
297 		case CMD_RCM_ONLINE:
298 		case CMD_RCM_CAP_ADD:
299 		case CMD_RCM_REMOVE:
300 		case CMD_RCM_CAP_NOTIFY:
301 			(void) ap_rcm_ctl(a, c);
302 			break;
303 
304 #ifdef	__x86
305 		/*
306 		 * Disable fast reboot if a CPU/MEM/IOH hotplug event happens.
307 		 * Note: this is a temporary solution and will be revised when
308 		 * fast reboot can support CPU/MEM/IOH DR operations in the
309 		 * future.
310 		 *
311 		 * ACPI BIOS generates some static ACPI tables, such as MADT,
312 		 * SRAT and SLIT, to describe the system hardware configuration
313 		 * on power-on. When a CPU/MEM/IOH hotplug event happens, those
314 		 * static tables won't be updated and will become stale.
315 		 *
316 		 * If we reset the system by fast reboot, BIOS will have no
317 		 * chance to regenerate those staled static tables. Fast reboot
318 		 * can't tolerate such inconsistency between staled ACPI tables
319 		 * and real hardware configuration yet.
320 		 *
321 		 * A temporary solution is introduced to disable fast reboot if
322 		 * CPU/MEM/IOH hotplug event happens. This solution should be
323 		 * revised when fast reboot is enhanced to support CPU/MEM/IOH
324 		 * DR operations.
325 		 */
326 		case CMD_ASSIGN:
327 		case CMD_POWERON:
328 		case CMD_POWEROFF:
329 		case CMD_UNASSIGN:
330 			if (!fastreboot_disabled &&
331 			    scf_fastreboot_default_set_transient(B_FALSE) ==
332 			    SCF_SUCCESS) {
333 				fastreboot_disabled = 1;
334 			}
335 #endif	/* __x86 */
336 			/* FALLTHROUGH */
337 
338 		default:
339 			rc = ap_ioctl(a, c);
340 			break;
341 		}
342 
343 		if (rc != CFGA_OK)
344 			break;
345 
346 	}
347 done:
348 
349 	if (resume)
350 		(void) ap_rcm_ctl(a, CMD_RCM_RESUME);
351 
352 	/*
353 	 * Check if any operations failed. If so, attempt to rollback
354 	 * to previously known states.
355 	 * Note: The rollback is currently limited to RCM operations.
356 	 */
357 	if (rc != CFGA_OK) {
358 		if (c == CMD_UNCONFIGURE ||
359 		    c == CMD_RCM_OFFLINE ||
360 		    c == CMD_RCM_CAP_DEL) {
361 			DBG("ap_seq_exec: %s failed\n", ap_cmd_name(c));
362 
363 			switch (c) {
364 			case CMD_UNCONFIGURE:
365 				/*
366 				 * If the unconfigure operation fails, perform
367 				 * an RCM_ONLINE and RCM_CAP_NOTIFY only. This
368 				 * keeps RCM clients consistent with the domain.
369 				 */
370 				recover_f = CMD_RCM_ONLINE;
371 				recover_l = CMD_RCM_ONLINE;
372 				DBG_RECOVER_MSG(recover_f, recover_l);
373 				(void) ap_seq_exec(a, cmd, recover_f,
374 				    recover_l);
375 
376 				recover_f = CMD_RCM_CAP_NOTIFY;
377 				recover_l = CMD_RCM_CAP_NOTIFY;
378 				DBG_RECOVER_MSG(recover_f, recover_l);
379 				(void) ap_seq_exec(a, cmd, recover_f,
380 				    recover_l);
381 				break;
382 			case CMD_RCM_OFFLINE:
383 				recover_f = CMD_RCM_ONLINE;
384 				recover_l = CMD_RCM_CAP_ADD;
385 				DBG_RECOVER_MSG(recover_f, recover_l);
386 				(void) ap_seq_exec(a, cmd, recover_f,
387 				    recover_l);
388 				break;
389 			case CMD_RCM_CAP_DEL:
390 				recover_f = CMD_RCM_CAP_ADD;
391 				recover_l = CMD_RCM_CAP_ADD;
392 				DBG_RECOVER_MSG(recover_f, recover_l);
393 				(void) ap_seq_exec(a, cmd, recover_f,
394 				    recover_l);
395 				break;
396 			default:
397 				break;
398 			}
399 
400 			DBG("recovery complete!\n");
401 		}
402 	}
403 	return (rc);
404 }
405 
406 cfga_err_t
ap_cmd_exec(apd_t * a,int cmd)407 ap_cmd_exec(apd_t *a, int cmd)
408 {
409 	return (ap_seq_exec(a, cmd, cmd, cmd));
410 }
411 
412 cfga_err_t
ap_cmd_seq(apd_t * a,int cmd)413 ap_cmd_seq(apd_t *a, int cmd)
414 {
415 	int first, last;
416 	cfga_err_t rc;
417 
418 	switch (ap_seq_get(a, cmd, &first, &last)) {
419 	case AP_SEQ_OK:
420 		rc = ap_seq_exec(a, cmd, first, last);
421 		break;
422 	case AP_SEQ_NULL:
423 		rc = CFGA_OK;
424 		break;
425 	case AP_SEQ_FAIL:
426 	default:
427 		rc = CFGA_LIB_ERROR;
428 		break;
429 	}
430 
431 	return (rc);
432 }
433