xref: /illumos-gate/usr/src/uts/sparc/os/cpr_sparc.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * cpr functions for supported sparc platforms
30  */
31 #include <sys/types.h>
32 #include <sys/systm.h>
33 #include <sys/cpr.h>
34 #include <sys/kmem.h>
35 #include <sys/errno.h>
36 
37 /*
38  * new_def_info is used as tmp space to store new values and write them
39  * to nvram.  orig_def_info gets filled with the original nvram values,
40  * gets written to disk, and later used by cprboot to restore the
41  * original nvram values.
42  */
43 static cdef_t *new_def_info;
44 
45 static cdef_t orig_def_info = {
46 	0, 0,
47 	0, "boot-file",    "",		/* props[0] */
48 	0, "boot-device",  "",		/* props[1] */
49 	0, "auto-boot?",   "",		/* props[2] */
50 	0, "diag-file",    "",		/* props[3] */
51 	0, "diag-device",  "",		/* props[4] */
52 };
53 
54 /*
55  * since the above array is the only place where cprop_t content
56  * is specified, these defines are provided for quick/direct access.
57  */
58 #define	CPR_BF_IDX	0		/* index for boot-file */
59 #define	CPR_BD_IDX	1		/* index for boot-device */
60 #define	CPR_AB_IDX	2		/* index for auto-boot? */
61 #define	CPR_DF_IDX	3		/* index for diag-file */
62 #define	CPR_DD_IDX	4		/* index for diag-device */
63 
64 #define	CPR_PROP_PTR(dfp, idx)	&(dfp)->props[idx]
65 
66 
67 static char *cpr_next_component(char **);
68 static char *cpr_get_prefix(char *);
69 static char *cpr_build_nodename(pnode_t);
70 static void cpr_abbreviate_devpath(char *, char *);
71 static int cpr_show_props = 0;
72 
73 
74 static int
75 cpr_get_options_node(pnode_t *nodep)
76 {
77 	*nodep = prom_optionsnode();
78 	if (*nodep == OBP_NONODE || *nodep == OBP_BADNODE) {
79 		cpr_err(CE_WARN, "cannot get \"options\" node");
80 		return (ENOENT);
81 	}
82 
83 	return (0);
84 }
85 
86 
87 /*
88  * returns non-zero on error, otherwise returns 0 and
89  * sets the result code based on (prop value == "true")
90  */
91 static int
92 cpr_get_bool_prop(char *name, int *result)
93 {
94 	char value[PROP_BOOL_LEN];
95 	pnode_t node;
96 	int len, err;
97 
98 	if (err = cpr_get_options_node(&node))
99 		return (err);
100 	len = prom_getproplen(node, name);
101 	if (len < 0 || len >= sizeof (value))
102 		return (ENXIO);
103 	bzero(value, sizeof (value));
104 	if (prom_getprop(node, name, value) != len)
105 		return (ENOENT);
106 	*result = (strcmp(value, "true") == 0);
107 	return (0);
108 }
109 
110 
111 /*
112  * write new or original values to nvram
113  */
114 int
115 cpr_update_nvram(cprop_t *props)
116 {
117 	cprop_t *tail;
118 	pnode_t node;
119 	int len, rc;
120 
121 	if (rc = cpr_get_options_node(&node))
122 		return (rc);
123 
124 	if (cpr_show_props)
125 		prom_printf("\ncpr_show_props:\n");
126 	for (tail = props + CPR_MAXPROP; props < tail; props++) {
127 		if (cpr_show_props) {
128 			prom_printf("mod=%c, name \"%s\",\tvalue \"%s\"\n",
129 			    props->mod, props->name, props->value);
130 		}
131 		if (props->mod == PROP_NOMOD)
132 			continue;
133 		/*
134 		 * Note: When doing a prom_setprop you must include the
135 		 * trailing NULL in the length argument, but when calling
136 		 * prom_getproplen() the NULL is excluded from the count!
137 		 */
138 		len = strlen(props->value);
139 		rc = prom_setprop(node, props->name, props->value, len + 1);
140 		if (rc < 0 || prom_getproplen(node, props->name) != len) {
141 			cpr_err(CE_WARN, "cannot set nvram \"%s\" to \"%s\"",
142 			    props->name, props->value);
143 			return (ENXIO);
144 		}
145 	}
146 
147 	return (0);
148 }
149 
150 
151 /*
152  * update nvram with the new or original nvram values;
153  * this routine provides local access to both sets
154  */
155 int
156 cpr_set_properties(int new)
157 {
158 	cprop_t *props;
159 
160 	props = new ? new_def_info->props : orig_def_info.props;
161 	return (cpr_update_nvram(props));
162 }
163 
164 
165 
166 /*
167  * update the .mod field in both new_def_info and orig_def_info;
168  * this tells cpr and cprboot which properties to set/reset.
169  * then copy the arg str into a new property value at index
170  */
171 static void
172 cpr_prop_update(int index, char *str)
173 {
174 	cprop_t *prop;
175 
176 	prop = CPR_PROP_PTR(&orig_def_info, index);
177 	prop->mod = PROP_MOD;
178 
179 	prop = CPR_PROP_PTR(new_def_info, index);
180 	prop->mod = PROP_MOD;
181 	(void) strcpy(prop->value, str);
182 }
183 
184 
185 /*
186  * setup new property values within new_def_info;
187  * these are used later to udpate nvram
188  */
189 static int
190 cpr_prop_setup(void)
191 {
192 	int len, err, ds_ival, dev_idx, file_idx;
193 	char bootdev[OBP_MAXPATHLEN], bootfile[OBP_MAXPATHLEN];
194 	char *cp, *sp;
195 
196 	/*
197 	 * create a new boot-device value.  for some older prom revs,
198 	 * a fully qualified device path can be truncated when stored
199 	 * to nvram.  this call generates the shortest equivalent.
200 	 * using devaliases could be simpler in most cases.
201 	 */
202 	cpr_abbreviate_devpath(prom_bootpath(), bootdev);
203 
204 	/*
205 	 * create a new boot-file value; flags get appended when
206 	 * not reusable and when the statefile is a block device
207 	 */
208 	(void) strcpy(bootfile, CPRBOOT);
209 	if (!cpr_reusable_mode && cpr_statefile_is_spec())
210 		sp = " -S ";
211 	else
212 		sp = NULL;
213 	if (sp) {
214 		(void) strcat(bootfile, sp);
215 		len = strlen(bootfile);
216 		sp = cpr_get_statefile_prom_path();
217 		cpr_abbreviate_devpath(sp, &bootfile[len]);
218 	}
219 
220 	/*
221 	 * record property info for booting with cprboot based on
222 	 * the value of diag-switch?.  when "false", set boot-device
223 	 * and boot-file; when "true", set diag-device and diag-file
224 	 */
225 	if (err = cpr_get_bool_prop("diag-switch?", &ds_ival))
226 		return (err);
227 	else if (ds_ival == 0) {
228 		dev_idx  = CPR_BD_IDX;
229 		file_idx = CPR_BF_IDX;
230 	} else {
231 		dev_idx  = CPR_DD_IDX;
232 		file_idx = CPR_DF_IDX;
233 	}
234 	cpr_prop_update(dev_idx,  bootdev);
235 
236 	if (!cpr_reusable_mode)
237 		cpr_prop_update(file_idx, bootfile);
238 
239 	/*
240 	 * check/set auto-boot?
241 	 */
242 	sp = orig_def_info.props[CPR_AB_IDX].value;
243 	cp = "true";
244 	if (strcmp(sp, cp))
245 		cpr_prop_update(CPR_AB_IDX, cp);
246 
247 	return (0);
248 }
249 
250 
251 /*
252  * setup the original and new sets of property names/values
253  */
254 int
255 cpr_default_setup(int alloc)
256 {
257 	cprop_t *orig, *new, *tail;
258 	int len, err = 0;
259 	pnode_t node;
260 	char *fmt;
261 
262 	if (alloc == 0) {
263 		ASSERT(new_def_info);
264 		kmem_free(new_def_info, sizeof (*new_def_info));
265 		new_def_info = NULL;
266 		return (0);
267 	}
268 
269 	if (err = cpr_get_options_node(&node))
270 		return (err);
271 
272 	/*
273 	 * allocate space for new properties, get the original nvram
274 	 * property values, mark both property sets with PROP_NOMOD,
275 	 * and copy the original prop names to the new set.
276 	 */
277 	ASSERT(new_def_info == NULL);
278 	new_def_info = kmem_zalloc(sizeof (*new_def_info), KM_SLEEP);
279 	new = new_def_info->props;
280 
281 	for (orig = orig_def_info.props, tail = orig + CPR_MAXPROP;
282 	    orig < tail; orig++, new++) {
283 		len = prom_getproplen(node, orig->name);
284 		if (len < 0 || len >= (int)sizeof (orig->value)) {
285 			fmt = "invalid property or length for \"%s\"";
286 			err = ENXIO;
287 			break;
288 		}
289 		bzero(orig->value, sizeof (orig->value));
290 		if (prom_getprop(node, orig->name, orig->value) < 0) {
291 			fmt = "cannot get \"%s\" value";
292 			err = ENXIO;
293 			break;
294 		}
295 
296 		new->mod = orig->mod = PROP_NOMOD;
297 		(void) strcpy(new->name, orig->name);
298 	}
299 
300 	if (err) {
301 		kmem_free(new_def_info, sizeof (*new_def_info));
302 		new_def_info = NULL;
303 		cpr_err(CE_WARN, fmt, orig->name);
304 	} else
305 		err = cpr_prop_setup();
306 
307 	return (err);
308 }
309 
310 
311 int
312 cpr_validate_definfo(int reusable)
313 {
314 	orig_def_info.mini.magic = CPR->c_cprboot_magic = CPR_DEFAULT_MAGIC;
315 	orig_def_info.mini.reusable = reusable;
316 	return (cpr_write_deffile(&orig_def_info));
317 }
318 
319 
320 void
321 cpr_send_notice(void)
322 {
323 	static char cstr[] = "\014" "\033[1P" "\033[18;21H";
324 
325 	prom_printf(cstr);
326 	prom_printf("Saving System State. Please Wait... ");
327 }
328 
329 void
330 cpr_spinning_bar(void)
331 {
332 	static char *spin_strings[] = { "|\b", "/\b", "-\b", "\\\b" };
333 	static int idx;
334 
335 	prom_printf(spin_strings[idx]);
336 	if (++idx == 4)
337 		idx = 0;
338 }
339 
340 void
341 cpr_resume_notice(void)
342 {
343 	static char cstr[] = "\014" "\033[1P" "\033[18;21H";
344 
345 	prom_printf(cstr);
346 	prom_printf("Restoring System State. Please Wait... ");
347 }
348 
349 /*
350  * Convert a full device path to its shortest unambiguous equivalent.
351  * For example, a path which starts out /iommu@x,y/sbus@i,j/espdma . . .
352  * might be converted to /iommu/sbus/espdma . . .  If we encounter
353  * problems at any point, just output the unabbreviated path.
354  */
355 static void
356 cpr_abbreviate_devpath(char *in_path, char *out_path)
357 {
358 	static pnode_t cur_node;
359 	char *position = in_path + 1;	/* Skip the leading slash. */
360 	char *cmpt;
361 
362 	cur_node = prom_nextnode(0);
363 	*out_path = '\0';
364 
365 	while ((cmpt = cpr_next_component(&position)) != NULL) {
366 		pnode_t long_match = NULL;
367 		pnode_t short_match = NULL;
368 		int short_hits = 0;
369 		char *name;
370 		char *prefix = cpr_get_prefix(cmpt);
371 
372 		/* Go to next tree level by getting first child. */
373 		if ((cur_node = prom_childnode(cur_node)) == 0) {
374 			(void) strcpy(out_path, in_path);
375 			return;
376 		}
377 
378 		/*
379 		 * Traverse the current level and remember the node (if any)
380 		 * where we match on the fully qualified component name.
381 		 * Also remember the node of the most recent prefix match
382 		 * and the number of such matches.
383 		 */
384 		do {
385 			name = cpr_build_nodename(cur_node);
386 			if (strcmp(name, cmpt) == 0)
387 				long_match = cur_node;
388 			if (strncmp(prefix, name, strlen(prefix)) == 0) {
389 				short_match = cur_node;
390 				short_hits++;
391 			}
392 		} while ((cur_node = prom_nextnode(cur_node)) != 0);
393 
394 		/*
395 		 * We don't want to be too dependent on what we know
396 		 * about how the names are stored.  We just assume that
397 		 * if there is only one match on the prefix, we can
398 		 * use it, otherwise we need to use a fully qualified
399 		 * name.  In the "impossible" cases we just give up
400 		 * and use the complete input devpath.
401 		 */
402 		(void) strcat(out_path, "/");
403 		if (short_hits == 1) {
404 			(void) strcat(out_path, prefix);
405 			cur_node = short_match;
406 		}
407 		else
408 			if (long_match) {
409 				(void) strcat(out_path, cmpt);
410 				cur_node = long_match;
411 			} else {
412 				(void) strcpy(out_path, in_path);
413 				return;
414 			}
415 	}
416 	/* We need to copy the target and slice info manually. */
417 	(void) strcat(out_path, strrchr(in_path, '@'));
418 }
419 
420 /*
421  * Return a pointer to the next component of a device path or NULL if
422  * the entire path has been consumed.  Note that we update the caller's
423  * pointer to the current position in the full pathname buffer.
424  */
425 static char *
426 cpr_next_component(char **path)
427 {
428 	static char obuf[64];
429 	char *slash;
430 	int len = strlen(*path);
431 
432 	if (len == 0)
433 		return (NULL);
434 
435 	if ((slash = strchr(*path, '/'))) {
436 		len = slash - *path;
437 		(void) strncpy(obuf, *path, len);
438 		obuf[len] = '\0';
439 		*path += len + 1;	/* Position beyond the slash. */
440 	} else {
441 		(void) strcpy(obuf, *path);
442 		*path += len;		/* Position at the terminal NULL. */
443 	}
444 
445 	return (obuf);
446 }
447 
448 /*
449  * Return a pointer to the prefix (i.e., the basic unqualified node name)
450  * Basically, this is the part of the fully qualified name before the @.
451  */
452 static char *
453 cpr_get_prefix(char *cmpt)
454 {
455 	static char	prefix[OBP_MAXDRVNAME];
456 	char		*at_sign = strchr(cmpt, '@');
457 	int		len = at_sign ? at_sign - cmpt : strlen(cmpt);
458 
459 	(void) strncpy(prefix, cmpt, len);
460 	prefix[len] = '\0';
461 
462 	return (prefix);
463 }
464 
465 /*
466  * Build the unambiguous name for the current node, like iommu@f,e10000000.
467  * The prefix is just the "name" property, and the qualifier is constructed
468  * from the first two (binary) words of the "reg" property.
469  */
470 static char *
471 cpr_build_nodename(pnode_t node)
472 {
473 	static char	name[OBP_MAXPATHLEN];
474 	int		reg[512];
475 	char		buf[32]; /* must contain expansion of @%x,%x */
476 	int		prop_len = prom_getproplen(node, OBP_NAME);
477 
478 	if (prop_len < 0 || prop_len >= sizeof (name) ||
479 	    prom_getprop(node, OBP_NAME, name) < 0)
480 		return ("");
481 	name[prop_len] = '\0';
482 
483 	if ((prop_len = prom_getproplen(node, OBP_REG)) <
484 	    2 * sizeof (int) || prop_len >= sizeof (reg))
485 		return (name);
486 
487 	if (prom_getprop(node, OBP_REG, (caddr_t)reg) < 0)
488 		return (name);
489 
490 	(void) sprintf(buf, "@%x,%x", reg[0], reg[1]);
491 	(void) strcat(name, buf);
492 
493 	return (name);
494 }
495 
496 /*
497  * Makes a printable list of prom_prop names for error messages
498  * Caller must free space.
499  */
500 char *
501 cpr_enumerate_promprops(char **bufp, size_t *len)
502 {
503 	cprop_t *prop, *tail;
504 	size_t size = 2;	/* for "." */
505 	char *buf;
506 
507 	tail = &orig_def_info.props[CPR_MAXPROP];
508 	for (prop = orig_def_info.props; prop < tail; prop++)
509 		size += strlen(prop->name) + 2;	/* + ", " */
510 
511 	buf = kmem_alloc(size, KM_SLEEP);
512 	*buf = '\0';
513 
514 	for (prop = orig_def_info.props; prop < tail; prop++) {
515 		if (strlen(buf))
516 			(void) strcat(buf, ", ");
517 		(void) strcat(buf, prop->name);
518 	}
519 	(void) strcat(buf, ".");
520 
521 	*bufp = buf;
522 	*len = size;
523 	return (buf);
524 }
525