/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #ifdef DPRINTF #define dprintf prom_printf #endif /* * Check if the prom is 64-bit ready. */ /* * Table listing the minimum prom versions supported by this kernel. * The model value is expected to match the model in the flashprom node. */ static struct obp_rev_table { char *model; char *version; } obp_min_revs[] = { {"SUNW,525-1414", "OBP 3.11.2 1997/12/05 10:25"}, /* pulsar */ {"SUNW,525-1672", "OBP 3.7.107 1998/02/19 17:54"}, /* tazmo */ {"SUNW,525-1431", "OBP 3.2.16 1998/06/08 16:58"}, /* sunfire */ { NULL, NULL} }; #define NMINS 60 #define NHOURS 24 #define NDAYS 31 #define NMONTHS 12 #define YEAR(y) ((y-1) * (NMONTHS * NDAYS * NHOURS * NMINS)) #define MONTH(m) ((m-1) * (NDAYS * NHOURS * NMINS)) #define DAY(d) ((d-1) * (NHOURS * NMINS)) #define HOUR(h) ((h) * (NMINS)) #define MINUTE(m) (m) static int strtoi(char *str, char **pos) { int c; int val = 0; for (c = *str++; c >= '0' && c <= '9'; c = *str++) { val *= 10; val += c - '0'; } if (pos) *pos = str; return (val); } /* * obp_timestamp: based on the OBP flashprom version string of the * format "OBP x.y.z YYYY/MM/DD HH:MM" calculate a timestamp based * on the year, month, day, hour and minute by turning that into * a number of minutes. */ static int obp_timestamp(char *v) { char *c; int maj, year, month, day, hour, min; if (v[0] != 'O' || v[1] != 'B' || v[2] != 'P') return (-1); c = v + 3; /* Find first non-space character after OBP */ while (*c != '\0' && (*c == ' ' || *c == '\t')) c++; if (prom_strlen(c) < 5) /* need at least "x.y.z" */ return (-1); maj = strtoi(c, &c); if (maj < 3) return (-1); #if 0 /* XXX - not used */ dot = dotdot = 0; if (*c == '.') { dot = strtoi(c + 1, &c); /* optional? dot-dot release */ if (*c == '.') dotdot = strtoi(c + 1, &c); } #endif /* Find space at the end of version number */ while (*c != '\0' && *c != ' ') c++; if (prom_strlen(c) < 11) /* need at least " xxxx/xx/xx" */ return (-1); /* Point to first character of date */ c++; /* Validate date format */ if (c[4] != '/' || c[7] != '/') return (-1); year = strtoi(c, NULL); month = strtoi(c + 5, NULL); day = strtoi(c + 8, NULL); if (year < 1995 || month == 0 || day == 0) return (-1); /* * Find space at the end of date number */ c += 10; while (*c != '\0' && *c != ' ') c++; if (prom_strlen(c) < 6) /* need at least " xx:xx" */ return (-1); /* Point to first character of time */ c++; if (c[2] != ':') return (-1); hour = strtoi(c, NULL); min = strtoi(c + 3, NULL); return (YEAR(year) + MONTH(month) + DAY(day) + HOUR(hour) + MINUTE(min)); } /* * Check the prom against the obp_min_revs table and complain if * the system has an older prom installed. The actual major/minor/ * dotdot numbers are not checked, only the date/time stamp. */ static struct obp_rev_table *flashprom_ortp; static pnode_t flashprom_node; static int flashprom_checked; static int flashprom_return_code; int check_timestamp(char *model, int tstamp) { int min_tstamp; struct obp_rev_table *ortp; for (ortp = obp_min_revs; ortp->model != NULL; ortp++) { if (prom_strcmp(model, ortp->model) == 0) { min_tstamp = obp_timestamp(ortp->version); if (min_tstamp == -1) { #ifdef DEBUG prom_printf("prom_version_check: " "invalid OBP version string in table " " (entry %d)", (int)(ortp - obp_min_revs)); #endif continue; } if (tstamp < min_tstamp) { #ifdef DPRINTF dprintf("prom_version_check: " "Down-rev OBP detected. " "Please update to at least:\n\t%s\n\n", ortp->version); #endif flashprom_ortp = ortp; return (1); } } } /* for each obp_rev_table entry */ return (0); } static pnode_t visit(pnode_t node) { int tstamp, plen, i; char vers[512], model[64]; static pnode_t openprom_node; static char version[] = "version"; static char model_name[] = "model"; static char flashprom[] = "flashprom"; /* * if name isn't 'flashprom', continue. */ if (prom_getproplen(node, OBP_NAME) != sizeof (flashprom)) return ((pnode_t)0); (void) prom_getprop(node, OBP_NAME, model); if (prom_strncmp(model, flashprom, sizeof (flashprom)) != 0) return ((pnode_t)0); plen = prom_getproplen(node, version); if (plen <= 0 || plen > sizeof (vers)) return ((pnode_t)0); (void) prom_getprop(node, version, vers); vers[plen] = '\0'; /* Make sure it's an OBP flashprom */ if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') return ((pnode_t)0); plen = prom_getproplen(node, model_name); if (plen <= 0 || plen > sizeof (model)) return ((pnode_t)0); (void) prom_getprop(node, model_name, model); model[plen] = '\0'; tstamp = obp_timestamp(vers); if (tstamp == -1) { prom_printf("prom_version_check: node contains " "improperly formatted version property,\n" "\tnot checking prom version"); return ((pnode_t)0); } i = check_timestamp(model, tstamp); if (i == 0) return ((pnode_t)0); /* * We know that "node"'s flashprom image contains downrev firmware, * however, a multi-board server might be running correct firmware. * Check for that case by looking at the "/openprom" node, * which always contains the running version. (We needed the * "model" value to be able to do this, so we can use it as * an index value into the table.) * * If it turns out we're running 'current' firmware, * but detect down-rev firmware, use a different return code. */ flashprom_return_code = PROM_VER64_UPGRADE; openprom_node = prom_finddevice("/openprom"); if (openprom_node == OBP_BADNODE) return (node); plen = prom_getproplen(node, version); if (plen <= 0 || plen > sizeof (vers)) return (node); (void) prom_getprop(node, version, vers); vers[plen] = '\0'; if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') { prom_printf("prom_version_check: " "unknown string in \n"); return (node); } tstamp = obp_timestamp(vers); if (tstamp == -1) { prom_printf("prom_version_check: " " node property: bad tstamp\n"); return (node); } i = check_timestamp(model, tstamp); /* * If that returned zero, then the running version is * adequate ... so we can 'suggest' instead of 'require'. */ if (i == 0) flashprom_return_code = PROM_VER64_SUGGEST; return (node); } /* * visit each node in the device tree, until we get a non-null answer */ static pnode_t walk(pnode_t node) { pnode_t id; if (visit(node)) return (node); for (node = prom_childnode(node); node; node = prom_nextnode(node)) if ((id = walk(node)) != (pnode_t)0) return (id); return ((pnode_t)0); } /* * Check if the prom is 64-bit ready. * * If it's ready (or the test doesn't apply), return PROM_VER64_OK. * If downrev firmware is running, return PROM_VER64_UPGRADE. * If downrev firmware is detected (but not running), return PROM_VER64_SUGGEST. * * For PROM_VER64_UPGRADE and PROM_VER64_SUGGEST return code values: * Return the nodeid of the flashprom node in *nodeid. * and a printable message in *buf, buflen. */ int prom_version_check(char *buf, size_t buflen, pnode_t *nodeid) { char *p; pnode_t node = flashprom_node; size_t i; /* * If we already checked, we already know the answer. */ if (flashprom_checked == 0) { flashprom_node = node = walk(prom_rootnode()); flashprom_checked = 1; } if (nodeid) *nodeid = node; if (node == (pnode_t)0) { if (buf && buflen) *buf = '\0'; return (PROM_VER64_OK); } /* bzero the callers buffer */ for (i = buflen, p = buf; i != 0; --i, ++p) *p = '\0'; /* * Do a bounded copy of the output string into the callers buffer */ if (buflen <= 1) return (flashprom_return_code); (void) prom_strncpy(buf, flashprom_ortp->version, buflen - 1); return (flashprom_return_code); }