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
cpr_get_options_node(pnode_t * nodep)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
cpr_get_bool_prop(char * name,int * result)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
cpr_update_nvram(cprop_t * props)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
cpr_set_properties(int new)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
cpr_prop_update(int index,char * str)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
cpr_prop_setup(void)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
cpr_default_setup(int alloc)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
cpr_validate_definfo(int reusable)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
cpr_send_notice(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
cpr_spinning_bar(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
cpr_resume_notice(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
cpr_abbreviate_devpath(char * in_path,char * out_path)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 *
cpr_next_component(char ** path)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 *
cpr_get_prefix(char * cmpt)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 *
cpr_build_nodename(pnode_t node)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 *
cpr_enumerate_promprops(char ** bufp,size_t * len)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