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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * cpr functions for supported sparc platforms
31 #include <sys/types.h>
32 #include <sys/systm.h>
35 #include <sys/errno.h>
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.
43 static cdef_t
*new_def_info
;
45 static cdef_t orig_def_info
= {
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] */
55 * since the above array is the only place where cprop_t content
56 * is specified, these defines are provided for quick/direct access.
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 */
64 #define CPR_PROP_PTR(dfp, idx) &(dfp)->props[idx]
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;
75 cpr_get_options_node(pnode_t
*nodep
)
77 *nodep
= prom_optionsnode();
78 if (*nodep
== OBP_NONODE
|| *nodep
== OBP_BADNODE
) {
79 cpr_err(CE_WARN
, "cannot get \"options\" node");
88 * returns non-zero on error, otherwise returns 0 and
89 * sets the result code based on (prop value == "true")
92 cpr_get_bool_prop(char *name
, int *result
)
94 char value
[PROP_BOOL_LEN
];
98 if (err
= cpr_get_options_node(&node
))
100 len
= prom_getproplen(node
, name
);
101 if (len
< 0 || len
>= sizeof (value
))
103 bzero(value
, sizeof (value
));
104 if (prom_getprop(node
, name
, value
) != len
)
106 *result
= (strcmp(value
, "true") == 0);
112 * write new or original values to nvram
115 cpr_update_nvram(cprop_t
*props
)
121 if (rc
= cpr_get_options_node(&node
))
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
);
131 if (props
->mod
== PROP_NOMOD
)
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!
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
);
152 * update nvram with the new or original nvram values;
153 * this routine provides local access to both sets
156 cpr_set_properties(int new)
160 props
= new ? new_def_info
->props
: orig_def_info
.props
;
161 return (cpr_update_nvram(props
));
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
172 cpr_prop_update(int index
, char *str
)
176 prop
= CPR_PROP_PTR(&orig_def_info
, index
);
177 prop
->mod
= PROP_MOD
;
179 prop
= CPR_PROP_PTR(new_def_info
, index
);
180 prop
->mod
= PROP_MOD
;
181 (void) strcpy(prop
->value
, str
);
186 * setup new property values within new_def_info;
187 * these are used later to udpate nvram
192 int len
, err
, ds_ival
, dev_idx
, file_idx
;
193 char bootdev
[OBP_MAXPATHLEN
], bootfile
[OBP_MAXPATHLEN
];
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.
202 cpr_abbreviate_devpath(prom_bootpath(), bootdev
);
205 * create a new boot-file value; flags get appended when
206 * not reusable and when the statefile is a block device
208 (void) strcpy(bootfile
, CPRBOOT
);
209 if (!cpr_reusable_mode
&& cpr_statefile_is_spec())
214 (void) strcat(bootfile
, sp
);
215 len
= strlen(bootfile
);
216 sp
= cpr_get_statefile_prom_path();
217 cpr_abbreviate_devpath(sp
, &bootfile
[len
]);
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
225 if (err
= cpr_get_bool_prop("diag-switch?", &ds_ival
))
227 else if (ds_ival
== 0) {
228 dev_idx
= CPR_BD_IDX
;
229 file_idx
= CPR_BF_IDX
;
231 dev_idx
= CPR_DD_IDX
;
232 file_idx
= CPR_DF_IDX
;
234 cpr_prop_update(dev_idx
, bootdev
);
236 if (!cpr_reusable_mode
)
237 cpr_prop_update(file_idx
, bootfile
);
240 * check/set auto-boot?
242 sp
= orig_def_info
.props
[CPR_AB_IDX
].value
;
245 cpr_prop_update(CPR_AB_IDX
, cp
);
252 * setup the original and new sets of property names/values
255 cpr_default_setup(int alloc
)
257 cprop_t
*orig
, *new, *tail
;
263 ASSERT(new_def_info
);
264 kmem_free(new_def_info
, sizeof (*new_def_info
));
269 if (err
= cpr_get_options_node(&node
))
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.
277 ASSERT(new_def_info
== NULL
);
278 new_def_info
= kmem_zalloc(sizeof (*new_def_info
), KM_SLEEP
);
279 new = new_def_info
->props
;
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\"";
289 bzero(orig
->value
, sizeof (orig
->value
));
290 if (prom_getprop(node
, orig
->name
, orig
->value
) < 0) {
291 fmt
= "cannot get \"%s\" value";
296 new->mod
= orig
->mod
= PROP_NOMOD
;
297 (void) strcpy(new->name
, orig
->name
);
301 kmem_free(new_def_info
, sizeof (*new_def_info
));
303 cpr_err(CE_WARN
, fmt
, orig
->name
);
305 err
= cpr_prop_setup();
312 cpr_validate_definfo(int reusable
)
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
));
321 cpr_send_notice(void)
323 static char cstr
[] = "\014" "\033[1P" "\033[18;21H";
326 prom_printf("Saving System State. Please Wait... ");
330 cpr_spinning_bar(void)
332 static char *spin_strings
[] = { "|\b", "/\b", "-\b", "\\\b" };
335 prom_printf(spin_strings
[idx
]);
341 cpr_resume_notice(void)
343 static char cstr
[] = "\014" "\033[1P" "\033[18;21H";
346 prom_printf("Restoring System State. Please Wait... ");
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.
356 cpr_abbreviate_devpath(char *in_path
, char *out_path
)
358 static pnode_t cur_node
;
359 char *position
= in_path
+ 1; /* Skip the leading slash. */
362 cur_node
= prom_nextnode(0);
365 while ((cmpt
= cpr_next_component(&position
)) != NULL
) {
366 pnode_t long_match
= NULL
;
367 pnode_t short_match
= NULL
;
370 char *prefix
= cpr_get_prefix(cmpt
);
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
);
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.
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
;
392 } while ((cur_node
= prom_nextnode(cur_node
)) != 0);
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.
402 (void) strcat(out_path
, "/");
403 if (short_hits
== 1) {
404 (void) strcat(out_path
, prefix
);
405 cur_node
= short_match
;
409 (void) strcat(out_path
, cmpt
);
410 cur_node
= long_match
;
412 (void) strcpy(out_path
, in_path
);
416 /* We need to copy the target and slice info manually. */
417 (void) strcat(out_path
, strrchr(in_path
, '@'));
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.
426 cpr_next_component(char **path
)
428 static char obuf
[64];
430 int len
= strlen(*path
);
435 if ((slash
= strchr(*path
, '/'))) {
437 (void) strncpy(obuf
, *path
, len
);
439 *path
+= len
+ 1; /* Position beyond the slash. */
441 (void) strcpy(obuf
, *path
);
442 *path
+= len
; /* Position at the terminal NULL. */
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 @.
453 cpr_get_prefix(char *cmpt
)
455 static char prefix
[OBP_MAXDRVNAME
];
456 char *at_sign
= strchr(cmpt
, '@');
457 int len
= at_sign
? at_sign
- cmpt
: strlen(cmpt
);
459 (void) strncpy(prefix
, cmpt
, len
);
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.
471 cpr_build_nodename(pnode_t node
)
473 static char name
[OBP_MAXPATHLEN
];
475 char buf
[32]; /* must contain expansion of @%x,%x */
476 int prop_len
= prom_getproplen(node
, OBP_NAME
);
478 if (prop_len
< 0 || prop_len
>= sizeof (name
) ||
479 prom_getprop(node
, OBP_NAME
, name
) < 0)
481 name
[prop_len
] = '\0';
483 if ((prop_len
= prom_getproplen(node
, OBP_REG
)) <
484 2 * sizeof (int) || prop_len
>= sizeof (reg
))
487 if (prom_getprop(node
, OBP_REG
, (caddr_t
)reg
) < 0)
490 (void) sprintf(buf
, "@%x,%x", reg
[0], reg
[1]);
491 (void) strcat(name
, buf
);
497 * Makes a printable list of prom_prop names for error messages
498 * Caller must free space.
501 cpr_enumerate_promprops(char **bufp
, size_t *len
)
503 cprop_t
*prop
, *tail
;
504 size_t size
= 2; /* for "." */
507 tail
= &orig_def_info
.props
[CPR_MAXPROP
];
508 for (prop
= orig_def_info
.props
; prop
< tail
; prop
++)
509 size
+= strlen(prop
->name
) + 2; /* + ", " */
511 buf
= kmem_alloc(size
, KM_SLEEP
);
514 for (prop
= orig_def_info
.props
; prop
< tail
; prop
++) {
516 (void) strcat(buf
, ", ");
517 (void) strcat(buf
, prop
->name
);
519 (void) strcat(buf
, ".");