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