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 /* 341 * Convert a full device path to its shortest unambiguous equivalent. 342 * For example, a path which starts out /iommu@x,y/sbus@i,j/espdma . . . 343 * might be converted to /iommu/sbus/espdma . . . If we encounter 344 * problems at any point, just output the unabbreviated path. 345 */ 346 static void 347 cpr_abbreviate_devpath(char *in_path, char *out_path) 348 { 349 static pnode_t cur_node; 350 char *position = in_path + 1; /* Skip the leading slash. */ 351 char *cmpt; 352 353 cur_node = prom_nextnode(0); 354 *out_path = '\0'; 355 356 while ((cmpt = cpr_next_component(&position)) != NULL) { 357 pnode_t long_match = NULL; 358 pnode_t short_match = NULL; 359 int short_hits = 0; 360 char *name; 361 char *prefix = cpr_get_prefix(cmpt); 362 363 /* Go to next tree level by getting first child. */ 364 if ((cur_node = prom_childnode(cur_node)) == 0) { 365 (void) strcpy(out_path, in_path); 366 return; 367 } 368 369 /* 370 * Traverse the current level and remember the node (if any) 371 * where we match on the fully qualified component name. 372 * Also remember the node of the most recent prefix match 373 * and the number of such matches. 374 */ 375 do { 376 name = cpr_build_nodename(cur_node); 377 if (strcmp(name, cmpt) == 0) 378 long_match = cur_node; 379 if (strncmp(prefix, name, strlen(prefix)) == 0) { 380 short_match = cur_node; 381 short_hits++; 382 } 383 } while ((cur_node = prom_nextnode(cur_node)) != 0); 384 385 /* 386 * We don't want to be too dependent on what we know 387 * about how the names are stored. We just assume that 388 * if there is only one match on the prefix, we can 389 * use it, otherwise we need to use a fully qualified 390 * name. In the "impossible" cases we just give up 391 * and use the complete input devpath. 392 */ 393 (void) strcat(out_path, "/"); 394 if (short_hits == 1) { 395 (void) strcat(out_path, prefix); 396 cur_node = short_match; 397 } 398 else 399 if (long_match) { 400 (void) strcat(out_path, cmpt); 401 cur_node = long_match; 402 } else { 403 (void) strcpy(out_path, in_path); 404 return; 405 } 406 } 407 /* We need to copy the target and slice info manually. */ 408 (void) strcat(out_path, strrchr(in_path, '@')); 409 } 410 411 /* 412 * Return a pointer to the next component of a device path or NULL if 413 * the entire path has been consumed. Note that we update the caller's 414 * pointer to the current position in the full pathname buffer. 415 */ 416 static char * 417 cpr_next_component(char **path) 418 { 419 static char obuf[64]; 420 char *slash; 421 int len = strlen(*path); 422 423 if (len == 0) 424 return (NULL); 425 426 if ((slash = strchr(*path, '/'))) { 427 len = slash - *path; 428 (void) strncpy(obuf, *path, len); 429 obuf[len] = '\0'; 430 *path += len + 1; /* Position beyond the slash. */ 431 } else { 432 (void) strcpy(obuf, *path); 433 *path += len; /* Position at the terminal NULL. */ 434 } 435 436 return (obuf); 437 } 438 439 /* 440 * Return a pointer to the prefix (i.e., the basic unqualified node name) 441 * Basically, this is the part of the fully qualified name before the @. 442 */ 443 static char * 444 cpr_get_prefix(char *cmpt) 445 { 446 static char prefix[OBP_MAXDRVNAME]; 447 char *at_sign = strchr(cmpt, '@'); 448 int len = at_sign ? at_sign - cmpt : strlen(cmpt); 449 450 (void) strncpy(prefix, cmpt, len); 451 prefix[len] = '\0'; 452 453 return (prefix); 454 } 455 456 /* 457 * Build the unambiguous name for the current node, like iommu@f,e10000000. 458 * The prefix is just the "name" property, and the qualifier is constructed 459 * from the first two (binary) words of the "reg" property. 460 */ 461 static char * 462 cpr_build_nodename(pnode_t node) 463 { 464 static char name[OBP_MAXPATHLEN]; 465 int reg[512]; 466 char buf[32]; /* must contain expansion of @%x,%x */ 467 int prop_len = prom_getproplen(node, OBP_NAME); 468 469 if (prop_len < 0 || prop_len >= sizeof (name) || 470 prom_getprop(node, OBP_NAME, name) < 0) 471 return (""); 472 name[prop_len] = '\0'; 473 474 if ((prop_len = prom_getproplen(node, OBP_REG)) < 475 2 * sizeof (int) || prop_len >= sizeof (reg)) 476 return (name); 477 478 if (prom_getprop(node, OBP_REG, (caddr_t)reg) < 0) 479 return (name); 480 481 (void) sprintf(buf, "@%x,%x", reg[0], reg[1]); 482 (void) strcat(name, buf); 483 484 return (name); 485 } 486 487 /* 488 * Makes a printable list of prom_prop names for error messages 489 * Caller must free space. 490 */ 491 char * 492 cpr_enumerate_promprops(char **bufp, size_t *len) 493 { 494 cprop_t *prop, *tail; 495 size_t size = 2; /* for "." */ 496 char *buf; 497 498 tail = &orig_def_info.props[CPR_MAXPROP]; 499 for (prop = orig_def_info.props; prop < tail; prop++) 500 size += strlen(prop->name) + 2; /* + ", " */ 501 502 buf = kmem_alloc(size, KM_SLEEP); 503 *buf = '\0'; 504 505 for (prop = orig_def_info.props; prop < tail; prop++) { 506 if (strlen(buf)) 507 (void) strcat(buf, ", "); 508 (void) strcat(buf, prop->name); 509 } 510 (void) strcat(buf, "."); 511 512 *bufp = buf; 513 *len = size; 514 return (buf); 515 } 516