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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/promif.h> 27 #include <sys/promimpl.h> 28 29 #ifdef DPRINTF 30 #define dprintf prom_printf 31 #endif 32 33 /* 34 * Check if the prom is 64-bit ready. 35 */ 36 37 /* 38 * Table listing the minimum prom versions supported by this kernel. 39 * The model value is expected to match the model in the flashprom node. 40 */ 41 static struct obp_rev_table { 42 char *model; 43 char *version; 44 } obp_min_revs[] = { 45 {"SUNW,525-1414", "OBP 3.11.2 1997/12/05 10:25"}, /* pulsar */ 46 {"SUNW,525-1672", "OBP 3.7.107 1998/02/19 17:54"}, /* tazmo */ 47 {"SUNW,525-1431", "OBP 3.2.16 1998/06/08 16:58"}, /* sunfire */ 48 { NULL, NULL} 49 }; 50 51 #define NMINS 60 52 #define NHOURS 24 53 #define NDAYS 31 54 #define NMONTHS 12 55 56 #define YEAR(y) ((y-1) * (NMONTHS * NDAYS * NHOURS * NMINS)) 57 #define MONTH(m) ((m-1) * (NDAYS * NHOURS * NMINS)) 58 #define DAY(d) ((d-1) * (NHOURS * NMINS)) 59 #define HOUR(h) ((h) * (NMINS)) 60 #define MINUTE(m) (m) 61 62 static int 63 strtoi(char *str, char **pos) 64 { 65 int c; 66 int val = 0; 67 68 for (c = *str++; c >= '0' && c <= '9'; c = *str++) { 69 val *= 10; 70 val += c - '0'; 71 } 72 if (pos) 73 *pos = str; 74 return (val); 75 } 76 77 /* 78 * obp_timestamp: based on the OBP flashprom version string of the 79 * format "OBP x.y.z YYYY/MM/DD HH:MM" calculate a timestamp based 80 * on the year, month, day, hour and minute by turning that into 81 * a number of minutes. 82 */ 83 static int 84 obp_timestamp(char *v) 85 { 86 char *c; 87 int maj, year, month, day, hour, min; 88 89 if (v[0] != 'O' || v[1] != 'B' || v[2] != 'P') 90 return (-1); 91 92 c = v + 3; 93 94 /* Find first non-space character after OBP */ 95 while (*c != '\0' && (*c == ' ' || *c == '\t')) 96 c++; 97 if (prom_strlen(c) < 5) /* need at least "x.y.z" */ 98 return (-1); 99 100 maj = strtoi(c, &c); 101 if (maj < 3) 102 return (-1); 103 104 #if 0 /* XXX - not used */ 105 dot = dotdot = 0; 106 if (*c == '.') { 107 dot = strtoi(c + 1, &c); 108 109 /* optional? dot-dot release */ 110 if (*c == '.') 111 dotdot = strtoi(c + 1, &c); 112 } 113 #endif 114 115 /* Find space at the end of version number */ 116 while (*c != '\0' && *c != ' ') 117 c++; 118 if (prom_strlen(c) < 11) /* need at least " xxxx/xx/xx" */ 119 return (-1); 120 121 /* Point to first character of date */ 122 c++; 123 124 /* Validate date format */ 125 if (c[4] != '/' || c[7] != '/') 126 return (-1); 127 128 year = strtoi(c, NULL); 129 month = strtoi(c + 5, NULL); 130 day = strtoi(c + 8, NULL); 131 132 if (year < 1995 || month == 0 || day == 0) 133 return (-1); 134 135 /* 136 * Find space at the end of date number 137 */ 138 c += 10; 139 while (*c != '\0' && *c != ' ') 140 c++; 141 if (prom_strlen(c) < 6) /* need at least " xx:xx" */ 142 return (-1); 143 144 /* Point to first character of time */ 145 c++; 146 147 if (c[2] != ':') 148 return (-1); 149 150 hour = strtoi(c, NULL); 151 min = strtoi(c + 3, NULL); 152 153 return (YEAR(year) + MONTH(month) + 154 DAY(day) + HOUR(hour) + MINUTE(min)); 155 } 156 157 /* 158 * Check the prom against the obp_min_revs table and complain if 159 * the system has an older prom installed. The actual major/minor/ 160 * dotdot numbers are not checked, only the date/time stamp. 161 */ 162 163 static struct obp_rev_table *flashprom_ortp; 164 static pnode_t flashprom_node; 165 static int flashprom_checked; 166 static int flashprom_return_code; 167 168 int 169 check_timestamp(char *model, int tstamp) 170 { 171 int min_tstamp; 172 struct obp_rev_table *ortp; 173 174 for (ortp = obp_min_revs; ortp->model != NULL; ortp++) { 175 if (prom_strcmp(model, ortp->model) == 0) { 176 min_tstamp = obp_timestamp(ortp->version); 177 if (min_tstamp == -1) { 178 #ifdef DEBUG 179 prom_printf("prom_version_check: " 180 "invalid OBP version string in table " 181 " (entry %d)", (int)(ortp - obp_min_revs)); 182 #endif 183 continue; 184 } 185 if (tstamp < min_tstamp) { 186 #ifdef DPRINTF 187 dprintf("prom_version_check: " 188 "Down-rev OBP detected. " 189 "Please update to at least:\n\t%s\n\n", 190 ortp->version); 191 #endif 192 flashprom_ortp = ortp; 193 return (1); 194 } 195 } 196 } /* for each obp_rev_table entry */ 197 198 return (0); 199 } 200 201 static pnode_t 202 visit(pnode_t node) 203 { 204 int tstamp, plen, i; 205 char vers[512], model[64]; 206 static pnode_t openprom_node; 207 static char version[] = "version"; 208 static char model_name[] = "model"; 209 static char flashprom[] = "flashprom"; 210 211 /* 212 * if name isn't 'flashprom', continue. 213 */ 214 if (prom_getproplen(node, OBP_NAME) != sizeof (flashprom)) 215 return ((pnode_t)0); 216 (void) prom_getprop(node, OBP_NAME, model); 217 if (prom_strncmp(model, flashprom, sizeof (flashprom)) != 0) 218 return ((pnode_t)0); 219 220 plen = prom_getproplen(node, version); 221 if (plen <= 0 || plen > sizeof (vers)) 222 return ((pnode_t)0); 223 (void) prom_getprop(node, version, vers); 224 vers[plen] = '\0'; 225 226 /* Make sure it's an OBP flashprom */ 227 if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') 228 return ((pnode_t)0); 229 230 plen = prom_getproplen(node, model_name); 231 if (plen <= 0 || plen > sizeof (model)) 232 return ((pnode_t)0); 233 (void) prom_getprop(node, model_name, model); 234 model[plen] = '\0'; 235 236 tstamp = obp_timestamp(vers); 237 if (tstamp == -1) { 238 prom_printf("prom_version_check: node contains " 239 "improperly formatted version property,\n" 240 "\tnot checking prom version"); 241 return ((pnode_t)0); 242 } 243 244 i = check_timestamp(model, tstamp); 245 246 if (i == 0) 247 return ((pnode_t)0); 248 249 /* 250 * We know that "node"'s flashprom image contains downrev firmware, 251 * however, a multi-board server might be running correct firmware. 252 * Check for that case by looking at the "/openprom" node, 253 * which always contains the running version. (We needed the 254 * "model" value to be able to do this, so we can use it as 255 * an index value into the table.) 256 * 257 * If it turns out we're running 'current' firmware, 258 * but detect down-rev firmware, use a different return code. 259 */ 260 261 flashprom_return_code = PROM_VER64_UPGRADE; 262 263 openprom_node = prom_finddevice("/openprom"); 264 if (openprom_node == OBP_BADNODE) 265 return (node); 266 267 plen = prom_getproplen(node, version); 268 if (plen <= 0 || plen > sizeof (vers)) 269 return (node); 270 (void) prom_getprop(node, version, vers); 271 vers[plen] = '\0'; 272 273 if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') { 274 prom_printf("prom_version_check: " 275 "unknown <version> string in </openprom>\n"); 276 return (node); 277 } 278 279 tstamp = obp_timestamp(vers); 280 if (tstamp == -1) { 281 prom_printf("prom_version_check: " 282 "</openprom> node <version> property: bad tstamp\n"); 283 return (node); 284 } 285 286 i = check_timestamp(model, tstamp); 287 /* 288 * If that returned zero, then the running version is 289 * adequate ... so we can 'suggest' instead of 'require'. 290 */ 291 if (i == 0) 292 flashprom_return_code = PROM_VER64_SUGGEST; 293 294 return (node); 295 } 296 297 /* 298 * visit each node in the device tree, until we get a non-null answer 299 */ 300 static pnode_t 301 walk(pnode_t node) 302 { 303 pnode_t id; 304 305 if (visit(node)) 306 return (node); 307 308 for (node = prom_childnode(node); node; node = prom_nextnode(node)) 309 if ((id = walk(node)) != (pnode_t)0) 310 return (id); 311 312 return ((pnode_t)0); 313 } 314 315 /* 316 * Check if the prom is 64-bit ready. 317 * 318 * If it's ready (or the test doesn't apply), return PROM_VER64_OK. 319 * If downrev firmware is running, return PROM_VER64_UPGRADE. 320 * If downrev firmware is detected (but not running), return PROM_VER64_SUGGEST. 321 * 322 * For PROM_VER64_UPGRADE and PROM_VER64_SUGGEST return code values: 323 * Return the nodeid of the flashprom node in *nodeid. 324 * and a printable message in *buf, buflen. 325 */ 326 int 327 prom_version_check(char *buf, size_t buflen, pnode_t *nodeid) 328 { 329 char *p; 330 pnode_t node = flashprom_node; 331 size_t i; 332 333 /* 334 * If we already checked, we already know the answer. 335 */ 336 if (flashprom_checked == 0) { 337 flashprom_node = node = walk(prom_rootnode()); 338 flashprom_checked = 1; 339 } 340 341 if (nodeid) 342 *nodeid = node; 343 344 if (node == (pnode_t)0) { 345 if (buf && buflen) 346 *buf = '\0'; 347 return (PROM_VER64_OK); 348 } 349 350 /* bzero the callers buffer */ 351 for (i = buflen, p = buf; i != 0; --i, ++p) 352 *p = '\0'; 353 354 /* 355 * Do a bounded copy of the output string into the callers buffer 356 */ 357 if (buflen <= 1) 358 return (flashprom_return_code); 359 360 (void) prom_strncpy(buf, flashprom_ortp->version, buflen - 1); 361 return (flashprom_return_code); 362 } 363