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