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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 * 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