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