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