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