xref: /titanic_51/usr/src/psm/promif/ieee1275/sun4/prom_vercheck.c (revision bb1fad37c75defa7a6ae25f00c1d4b356713b734)
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