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 */
43static cdef_t *new_def_info;
44
45static 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
67static char *cpr_next_component(char **);
68static char *cpr_get_prefix(char *);
69static char *cpr_build_nodename(pnode_t);
70static void cpr_abbreviate_devpath(char *, char *);
71static int cpr_show_props = 0;
72
73
74static int
75cpr_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 */
91static int
92cpr_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 */
114int
115cpr_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 */
155int
156cpr_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 */
171static void
172cpr_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 */
189static int
190cpr_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 */
254int
255cpr_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
311int
312cpr_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
320void
321cpr_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
329void
330cpr_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
340void
341cpr_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 */
355static void
356cpr_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 */
425static char *
426cpr_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 */
452static char *
453cpr_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 */
470static char *
471cpr_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 */
500char *
501cpr_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