1 /* Copyright 2013-2017 IBM Corp.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * 	http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <skiboot.h>
18 #include <vpd.h>
19 #include <string.h>
20 #include "spira.h"
21 #include "hdata.h"
22 #include <device.h>
23 #include "hdata.h"
24 #include <inttypes.h>
25 #include <mem_region-malloc.h>
26 
27 struct card_info {
28 	const char *ccin; 	/* Customer card identification number */
29 	const char *description;
30 };
31 
32 static const struct card_info card_table[] = {
33 	{"2B06", "System planar 2S4U"},
34 	{"2B07", "System planar 1S4U"},
35 	{"2B2E", "System planar 2S2U"},
36 	{"2B2F", "System planar 1S2U"},
37 	{"2CD4", "System planar 2S4U"},
38 	{"2CD5", "System planar 1S4U"},
39 	{"2CD6", "System planar 2S2U"},
40 	{"2CD7", "System planar 1S2U"},
41 	{"2CD7", "System planar 1S2U"},
42 	{"2B09", "Base JBOD, RAID and Backplane HD"},
43 	{"57D7", "Split JBOD, RAID Card"},
44 	{"2B0B", "Native I/O Card"},
45 
46 	/* Anchor cards */
47 	{"52FE", "System Anchor Card - IBM Power 824"},
48 	{"52F2", "System Anchor Card - IBM Power 814"},
49 	{"52F5", "System Anchor Card - IBM Power 822"},
50 	{"561A", "System Anchor Card - IBM Power 824L"},
51 	{"524D", "System Anchor Card - IBM Power 822L"},
52 	{"560F", "System Anchor Card - IBM Power 812L"},
53 	{"561C", "System Anchor Card - DS8870"},
54 
55 	/* Memory DIMMs */
56 	{"31E0", "16GB CDIMM"},
57 	{"31E8", "16GB CDIMM"},
58 	{"31E1", "32GB CDIMM"},
59 	{"31E9", "32GB CDIMM"},
60 	{"31E2", "64GB CDIMM"},
61 	{"31EA", "64GB CDIMM"},
62 
63 	/* Power supplies */
64 	{"2B1D", "Power Supply 900W AC"},
65 	{"2B1E", "Power Supply 1400W AC"},
66 	{"2B75", "Power Supply 1400W HVDC"},
67 
68 	/* Fans */
69 	{"2B1F", "Fan 4U (A1, A2, A3, A4)"},
70 	{"2B29", "Fan 2U (A1, A2, A3, A4, A5, A6)"},
71 
72 	/* Other cards */
73 };
74 
75 struct vpd_key_map {
76 	const char *keyword;		/* 2 char keyword  */
77 	const char *description;
78 };
79 
80 static const struct vpd_key_map vpd_key_table[] = {
81 	{"AA", "ac-power-supply"},
82 	{"AM", "air-mover"},
83 	{"AV", "anchor-card"},
84 
85 	{"BA", "bus-adapter-card"},
86 	{"BC", "battery-charger"},
87 	{"BD", "bus-daughter-card"},
88 	{"BE", "bus-expansion-card"},
89 	{"BP", "backplane"},
90 	{"BR", "backplane-riser"},
91 	{"BX", "backplane-extender"},
92 
93 	{"CA", "calgary-bridge"},
94 	{"CB", "infiniband-connector"},
95 	{"CC", "clock-card"},
96 	{"CD", "card-connector"},
97 	{"CE", "ethernet-connector"},
98 	{"CL", "calgary-phb"},
99 	{"CI", "capacity-card"},
100 	{"CO", "sma-connector"},
101 	{"CP", "processor-capacity-card"},
102 	{"CR", "rio-connector"},
103 	{"CS", "serial-connector"},
104 	{"CU", "usb-connector"},
105 
106 	{"DB", "dasd-backplane"},
107 	{"DC", "drawer-card"},
108 	{"DE", "drawer-extension"},
109 	{"DI", "drawer-interposer"},
110 	{"DL", "p7ih-dlink-connector"},
111 	{"DT", "legacy-pci-card"},
112 	{"DV", "media-drawer-led"},
113 
114 	{"EI", "enclosure-led"},
115 	{"EF", "enclosure-fault-led"},
116 	{"ES", "embedded-sas"},
117 	{"ET", "ethernet-riser"},
118 	{"EV", "enclosure"},
119 
120 	{"FM", "frame"},
121 
122 	{"HB", "host-rio-pci-card"},
123 	{"HD", "high-speed-card"},
124 	{"HM", "hmc-connector"},
125 
126 	{"IB", "io-backplane"},
127 	{"IC", "io-card"},
128 	{"ID", "ide-connector"},
129 	{"II", "io-drawer-led"},
130 	{"IP", "interplane-card"},
131 	{"IS", "smp-vbus-cable"},
132 	{"IT", "enclosure-cable"},
133 	{"IV", "io-enclosure"},
134 
135 	{"KV", "keyboard-led"},
136 
137 	{"L2", "l2-cache-module"},
138 	{"L3", "l3-cache-module"},
139 	{"LC", "squadrons-light-connector"},
140 	{"LR", "p7ih-connector"},
141 	{"LO", "system-locate-led"},
142 	{"LT", "squadrons-light-strip"},
143 
144 	{"MB", "media-backplane"},
145 	{"ME", "map-extension"},
146 	{"MM", "mip-meter"},
147 	{"MS", "ms-dimm"},
148 
149 	{"NB", "nvram-battery"},
150 	{"NC", "sp-node-controller"},
151 	{"ND", "numa-dimm"},
152 
153 	{"OD", "cuod-card"},
154 	{"OP", "op-panel"},
155 	{"OS", "oscillator"},
156 
157 	{"P2", "ioc"},
158 	{"P5", "ioc-bridge"},
159 	{"PB", "io-drawer-backplane"},
160 	{"PC", "power-capacitor"},
161 	{"PD", "processor-card"},
162 	{"PF", "processor"},
163 	{"PI", "ioc-phb"},
164 	{"PO", "spcn"},
165 	{"PN", "spcn-connector"},
166 	{"PR", "pci-riser-card"},
167 	{"PS", "power-supply"},
168 	{"PT", "pass-through-card"},
169 	{"PX", "psc-sync-card"},
170 	{"PW", "power-connector"},
171 
172 	{"RG", "regulator"},
173 	{"RI", "riser"},
174 	{"RK", "rack-indicator"},
175 	{"RW", "riscwatch-connector"},
176 
177 	{"SA", "sys-attn-led"},
178 	{"SB", "backup-sysvpd"},
179 	{"SC", "scsi-connector"},
180 	{"SD", "sas-connector"},
181 	{"SI", "scsi-ide-converter"},
182 	{"SL", "phb-slot"},
183 	{"SP", "service-processor"},
184 	{"SR", "service-card"},
185 	{"SS", "soft-switch"},
186 	{"SV", "system-vpd"},
187 	{"SY", "legacy-sysvpd"},
188 
189 	{"TD", "tod-clock"},
190 	{"TI", "torrent-pcie-phb"},
191 	{"TL", "torrent-riser"},
192 	{"TM", "thermal-sensor"},
193 	{"TP", "tpmd-adapter"},
194 	{"TR", "torrent-bridge"},
195 
196 	{"VV", "root-node-vpd"},
197 
198 	{"WD", "water_device"},
199 };
200 
vpd_map_name(const char * vpd_name)201 static const char *vpd_map_name(const char *vpd_name)
202 {
203 	int i;
204 
205 	for (i = 0; i < ARRAY_SIZE(vpd_key_table); i++)
206 		if (!strcmp(vpd_key_table[i].keyword, vpd_name))
207 			return vpd_key_table[i].description;
208 
209 	prlog(PR_WARNING, "VPD: Could not map FRU ID %s to a known name\n",
210 	      vpd_name);
211 
212 	return "Unknown";
213 }
214 
card_info_lookup(char * ccin)215 static const struct card_info *card_info_lookup(char *ccin)
216 {
217 	int i;
218 	for(i = 0; i < ARRAY_SIZE(card_table); i++)
219 		/* CCIN is always 4 bytes in size */
220 		if (!strncmp(card_table[i].ccin, ccin, 4))
221 			return &card_table[i];
222 	return NULL;
223 }
224 
225 /* Discard trailing spaces and populate device tree */
dt_add_prop_sanitize_val(struct dt_node * node,const char * name,const char * val,int vlen)226 static struct dt_property *dt_add_prop_sanitize_val(struct dt_node *node,
227 			     const char *name, const char *val, int vlen)
228 {
229 	char *prop = zalloc(vlen + 1);
230 	int i;
231 	struct dt_property *p = NULL;
232 
233 	if (!prop)
234 		return p;
235 
236 	memcpy(prop, val, vlen);
237 	for (i = vlen - 1; i >= 0; i--) {
238 		if (prop[i] != 0x20) {
239 			prop[i + 1] = '\0';
240 			break;
241 		}
242 	}
243 
244 	if (i >= 0 && !dt_find_property(node, name))
245 		p = dt_add_property_string(node, name, prop);
246 
247 	free(prop);
248 	return p;
249 }
250 
251 /*
252  * OpenPower system does not provide processor vendor name under FRU VPD.
253  * Parse processor module VPD to get vendor detail
254  */
dt_add_proc_vendor(struct dt_node * proc_node,const void * mvpd,unsigned int mvpd_sz)255 void dt_add_proc_vendor(struct dt_node *proc_node,
256 			const void *mvpd, unsigned int mvpd_sz)
257 {
258 	const void *kw;
259 	uint8_t sz;
260 
261 	kw = vpd_find(mvpd, mvpd_sz, "VINI", "VN", &sz);
262 	if (kw)
263 		dt_add_prop_sanitize_val(proc_node, "vendor", kw, sz);
264 }
265 
266 /*
267  * For OpenPOWER, we only decipher OPFR records. While OP HDAT have VINI
268  * records too, populating the fields in there is optional. Also, there
269  * is an overlap in the fields contained therein.
270  */
vpd_opfr_parse(struct dt_node * node,const void * fruvpd,unsigned int fruvpd_sz)271 static void vpd_opfr_parse(struct dt_node *node,
272 		const void *fruvpd, unsigned int fruvpd_sz)
273 {
274 	const void *kw;
275 	uint8_t sz;
276 
277 	/* Vendor Name */
278 	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VN", &sz);
279 	if (kw)
280 		dt_add_prop_sanitize_val(node, "vendor", kw, sz);
281 
282 	/* FRU Description */
283 	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "DR", &sz);
284 	if (kw)
285 		dt_add_prop_sanitize_val(node, "description", kw, sz);
286 
287 	/* Part number */
288 	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VP", &sz);
289 	if (kw)
290 		dt_add_prop_sanitize_val(node, "part-number", kw, sz);
291 
292 	/* Serial number */
293 	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VS", &sz);
294 	if (kw)
295 		dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
296 
297 	/* Build date in BCD */
298 	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "MB", &sz);
299 	if (kw)
300 		dt_add_prop_sanitize_val(node, "build-date", kw, sz);
301 
302 	return;
303 }
304 
305 /*
306  * For CPUs, parse the VRML data.
307  */
vpd_vrml_parse(struct dt_node * node,const void * fruvpd,unsigned int fruvpd_sz)308 static void vpd_vrml_parse(struct dt_node *node,
309 		const void *fruvpd, unsigned int fruvpd_sz)
310 {
311 	const void *kw;
312 	uint8_t sz;
313 
314 	/* Part number */
315 	kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "PN", &sz);
316 	if (kw)
317 		dt_add_prop_sanitize_val(node, "part-number", kw, sz);
318 
319 	/* Serial number */
320 	kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "SN", &sz);
321 	if (kw)
322 		dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
323 
324 	return;
325 }
326 
vpd_vini_parse(struct dt_node * node,const void * fruvpd,unsigned int fruvpd_sz)327 static void vpd_vini_parse(struct dt_node *node,
328 			   const void *fruvpd, unsigned int fruvpd_sz)
329 {
330 	const void *kw;
331 	const char *desc;
332 	uint8_t sz;
333 	const struct card_info *cinfo;
334 
335 	/* FRU Stocking Part Number */
336 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &sz);
337 	if (kw)
338 		dt_add_prop_sanitize_val(node, "fru-number", kw, sz);
339 
340 	/* Serial Number */
341 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &sz);
342 	if (kw)
343 		dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
344 
345 	/* Part Number */
346 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &sz);
347 	if (kw)
348 		dt_add_prop_sanitize_val(node, "part-number", kw, sz);
349 
350 	/* Vendor Name */
351 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "VN", &sz);
352 	if (kw)
353 		dt_add_prop_sanitize_val(node, "vendor", kw, sz);
354 
355 	/* CCIN Extension */
356 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CE", &sz);
357 	if (kw)
358 		dt_add_prop_sanitize_val(node, "ccin-extension", kw, sz);
359 
360 	/* HW Version info */
361 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "HW", &sz);
362 	if (kw)
363 		dt_add_prop_sanitize_val(node, "hw-version", kw, sz);
364 
365 	/* Card type info */
366 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CT", &sz);
367 	if (kw)
368 		dt_add_prop_sanitize_val(node, "card-type", kw, sz);
369 
370 	/* HW characteristics info */
371 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "B3", &sz);
372 	if (kw)
373 		dt_add_prop_sanitize_val(node, "hw-characteristics", kw, sz);
374 
375 	/* Customer Card Identification Number (CCIN) */
376 	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &sz);
377 	if (kw) {
378 		dt_add_prop_sanitize_val(node, "ccin", kw, sz);
379 
380 		cinfo = card_info_lookup((char *)kw);
381 		if (cinfo) {
382 			dt_add_property_string(node,
383 				       "description", cinfo->description);
384 		} else {
385 			desc = vpd_find(fruvpd, fruvpd_sz, "VINI", "DR", &sz);
386 			if (desc) {
387 				dt_add_prop_sanitize_val(node,
388 						     "description", desc, sz);
389 			} else {
390 				dt_add_property_string(node, "description", "Unknown");
391 				prlog(PR_WARNING,
392 				      "VPD: CCIN desc not available for: %s\n",
393 				      (char*)kw);
394 			}
395 		}
396 	}
397 
398 	return;
399 }
400 
valid_child_entry(const struct slca_entry * entry)401 static bool valid_child_entry(const struct slca_entry *entry)
402 {
403 	if ((entry->install_indic == SLCA_INSTALL_INSTALLED) &&
404 		(entry->vpd_collected == SLCA_VPD_COLLECTED))
405 		return true;
406 
407 	return false;
408 }
409 
vpd_data_parse(struct dt_node * node,const void * fruvpd,u32 fruvpd_sz)410 void vpd_data_parse(struct dt_node *node, const void *fruvpd, u32 fruvpd_sz)
411 {
412 	if (vpd_find_record(fruvpd, fruvpd_sz, "OPFR", NULL))
413 		vpd_opfr_parse(node, fruvpd, fruvpd_sz);
414 	else if (vpd_find_record(fruvpd, fruvpd_sz, "VRML", NULL))
415 		vpd_vrml_parse(node, fruvpd, fruvpd_sz);
416 	else
417 		vpd_vini_parse(node, fruvpd, fruvpd_sz);
418 }
419 
420 /* Create the /vpd node and add its children */
dt_init_vpd_node(void)421 void dt_init_vpd_node(void)
422 {
423 	const char *name, *p_name;
424 	int count, index;
425 	uint64_t addr, p_addr;
426 	struct dt_node *dt_vpd;
427 	struct HDIF_common_hdr *slca_hdr;
428 	struct dt_node *parent, *node;
429 	const struct slca_entry *entry, *p_entry;
430 
431 	dt_vpd = dt_new(dt_root, "vpd");
432 	assert(dt_vpd);
433 	dt_add_property_string(dt_vpd, "compatible", "ibm,opal-v3-vpd");
434 	dt_add_property_cells(dt_vpd, "#size-cells", 0);
435 	dt_add_property_cells(dt_vpd, "#address-cells", 1);
436 
437 	slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG);
438 	if (!slca_hdr) {
439 		prerror("SLCA Invalid\n");
440 		return;
441 	}
442 
443 	count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY);
444 	if (count < 0) {
445 		prerror("SLCA: Can't find SLCA array size!\n");
446 		return;
447 	}
448 
449 	for (index = 0; index < count; index++) {
450 		/* Get SLCA entry */
451 		entry = slca_get_entry(index);
452 		if (!entry)
453 			continue;
454 
455 		/*
456 		 * A child entry is valid if all of the following criteria is met
457 		 *   a. SLCA_INSTALL_INSTALLED is set in s_entry->install_indic
458 		 *   b. SLCA_VPD_COLLECTED is set in s_entry->vpd_collected
459 		 *   c. The SLCA is not a duplicate entry.
460 		 */
461 		if (!valid_child_entry(entry))
462 			goto next_entry;
463 
464 		name = vpd_map_name(entry->fru_id);
465 		addr = be16_to_cpu(entry->rsrc_id);
466 		/* Check node is already created or not */
467 		if (dt_find_by_name_addr(dt_vpd, name, addr))
468 			goto next_entry;
469 
470 		/* Get parent node */
471 		if (index == SLCA_ROOT_INDEX) {
472 			parent = dt_vpd;
473 		} else {
474 			p_entry = slca_get_entry(be16_to_cpu(entry->parent_index));
475 			if (!p_entry)
476 				goto next_entry;
477 			p_name = vpd_map_name(p_entry->fru_id);
478 			p_addr = be16_to_cpu(p_entry->rsrc_id);
479 			parent = dt_find_by_name_addr(dt_vpd, p_name, p_addr);
480 		}
481 		if (!parent)
482 			goto next_entry;
483 
484 		node = dt_new_addr(parent, name, addr);
485 		if (!node) {
486 			prerror("VPD: Creating node at %s@%"PRIx64" failed\n",
487 				name, addr);
488 			goto next_entry;
489 		}
490 
491 		/* Add location code */
492 		slca_vpd_add_loc_code(node, be16_to_cpu(entry->my_index));
493 		/* Add FRU label */
494 		dt_add_property(node, "fru-type", entry->fru_id, 2);
495 		dt_add_property_cells(node, "reg", addr);
496 		dt_add_property_cells(node, "#size-cells", 0);
497 		dt_add_property_cells(node, "#address-cells", 1);
498 
499 next_entry:
500 		/* Skip dups -- dups are contiguous */
501 		index += entry->nr_dups;
502 	}
503 }
504 
dt_add_vpd_node(const struct HDIF_common_hdr * hdr,int indx_fru,int indx_vpd)505 struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr,
506 				int indx_fru, int indx_vpd)
507 {
508 	const struct spira_fru_id *fru_id;
509 	unsigned int fruvpd_sz, fru_id_sz;
510 	const struct slca_entry *entry;
511 	struct dt_node *dt_vpd, *node;
512 	const void *fruvpd;
513 	const char *name;
514 	uint64_t addr;
515 
516 	fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz);
517 	if (!fru_id)
518 		return NULL;
519 
520 	fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz);
521 	if (!CHECK_SPPTR(fruvpd))
522 		return NULL;
523 
524 	dt_vpd = dt_find_by_path(dt_root, "/vpd");
525 	if (!dt_vpd)
526 		return NULL;
527 
528 	entry = slca_get_entry(be16_to_cpu(fru_id->slca_index));
529 	if (!entry)
530 		return NULL;
531 
532 	name = vpd_map_name(entry->fru_id);
533 	addr = be16_to_cpu(entry->rsrc_id);
534 	/* Get the node already created */
535 	node = dt_find_by_name_addr(dt_vpd, name, addr);
536 	/*
537 	 * It is unlikely that node not found because vpd nodes have the
538 	 * corresponding slca entry which we would have used to populate the vpd
539 	 * tree during the 'first' pass above so that we just need to perform
540 	 * VINI parse and add the vpd data..
541 	 */
542 	if (!node)
543 		return NULL;
544 
545 	/* Parse VPD fields, ensure that it has not been added already */
546 	if (vpd_valid(fruvpd, fruvpd_sz)
547 	    && !dt_find_property(node, "ibm,vpd")) {
548 		dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz);
549 		vpd_data_parse(node, fruvpd, fruvpd_sz);
550 	}
551 
552 	return node;
553 }
554 
dt_add_model_name(void)555 static void dt_add_model_name(void)
556 {
557 	const char *model_name = NULL;
558 	const struct machine_info *mi;
559 	const struct iplparams_sysparams *p;
560 	const struct HDIF_common_hdr *iplp;
561 	const struct dt_property *model;
562 
563 	model = dt_find_property(dt_root, "model");
564 
565 	iplp = get_hdif(&spira.ntuples.ipl_parms, "IPLPMS");
566 	if (!iplp)
567 		goto def_model;
568 
569 	p = HDIF_get_idata(iplp, IPLPARAMS_SYSPARAMS, NULL);
570 	if (!CHECK_SPPTR(p))
571 		goto def_model;
572 
573 	if (be16_to_cpu(iplp->version) >= 0x60)
574 		model_name = p->sys_type_str;
575 
576 def_model:
577 	if ((!model_name || model_name[0] == '\0') && model) {
578 		mi = machine_info_lookup(model->prop);
579 		if (mi)
580 			model_name = mi->name;
581 	}
582 
583 	if(model_name)
584 		dt_add_property_string(dt_root, "model-name", model_name);
585 }
586 
sysvpd_parse_opp(const void * sysvpd,unsigned int sysvpd_sz)587 static void sysvpd_parse_opp(const void *sysvpd, unsigned int sysvpd_sz)
588 {
589 	const char *v;
590 	uint8_t sz;
591 
592 	v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "MM", &sz);
593 	if (v)
594 		dt_add_prop_sanitize_val(dt_root, "model", v, sz);
595 	else
596 		dt_add_property_string(dt_root, "model", "Unknown");
597 
598 	v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "SS", &sz);
599 	if (v)
600 		dt_add_prop_sanitize_val(dt_root, "system-id", v, sz);
601 	else
602 		dt_add_property_string(dt_root, "system-id", "Unknown");
603 }
604 
605 
sysvpd_parse_legacy(const void * sysvpd,unsigned int sysvpd_sz)606 static void sysvpd_parse_legacy(const void *sysvpd, unsigned int sysvpd_sz)
607 {
608 	const char *model;
609 	const char *system_id;
610 	const char *brand;
611 	uint8_t sz;
612 
613 	model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz);
614 	if (model)
615 		dt_add_prop_sanitize_val(dt_root, "model", model, sz);
616 	else
617 		dt_add_property_string(dt_root, "model", "Unknown");
618 
619 	system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz);
620 	if (system_id)
621 		dt_add_prop_sanitize_val(dt_root, "system-id", system_id, sz);
622 	else
623 		dt_add_property_string(dt_root, "system-id", "Unknown");
624 
625 	brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz);
626 	if (brand)
627 		dt_add_prop_sanitize_val(dt_root, "system-brand", brand, sz);
628 	else
629 		dt_add_property_string(dt_root, "brand", "Unknown");
630 }
631 
sysvpd_parse(void)632 static void sysvpd_parse(void)
633 {
634 	const void *sysvpd;
635 	unsigned int sysvpd_sz;
636 	unsigned int fru_id_sz;
637 	struct dt_node *dt_vpd;
638 	const struct spira_fru_id *fru_id;
639 	struct HDIF_common_hdr *sysvpd_hdr;
640 
641 	sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG);
642 	if (!sysvpd_hdr)
643 		return;
644 
645 	fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz);
646 	if (!fru_id)
647 		return;
648 
649 	sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz);
650 	if (!CHECK_SPPTR(sysvpd))
651 		return;
652 
653 	/* Add system VPD */
654 	dt_vpd = dt_find_by_path(dt_root, "/vpd");
655 	if (dt_vpd) {
656 		dt_add_property(dt_vpd, "ibm,vpd", sysvpd, sysvpd_sz);
657 		slca_vpd_add_loc_code(dt_vpd, be16_to_cpu(fru_id->slca_index));
658 	}
659 
660 	/* Look for the new OpenPower "OSYS" first */
661 	if (vpd_find_record(sysvpd, sysvpd_sz, "OSYS", NULL))
662 		sysvpd_parse_opp(sysvpd, sysvpd_sz);
663 	else
664 		sysvpd_parse_legacy(sysvpd, sysvpd_sz);
665 
666 	dt_add_model_name();
667 }
668 
iokid_vpd_parse(const struct HDIF_common_hdr * iohub_hdr)669 static void iokid_vpd_parse(const struct HDIF_common_hdr *iohub_hdr)
670 {
671 	const struct HDIF_child_ptr *iokids;
672 	const struct HDIF_common_hdr *iokid;
673 	unsigned int i;
674 
675 	iokids = HDIF_child_arr(iohub_hdr, CECHUB_CHILD_IO_KIDS);
676 	if (!CHECK_SPPTR(iokids)) {
677 		prerror("VPD: No IOKID child array\n");
678 		return;
679 	}
680 
681 	for (i = 0; i < be32_to_cpu(iokids->count); i++) {
682 		iokid = HDIF_child(iohub_hdr, iokids, i,
683 					IOKID_FRU_HDIF_SIG);
684 		/* IO KID VPD structure layout is similar to FRUVPD */
685 		if (iokid)
686 			dt_add_vpd_node(iokid,
687 				FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
688 	}
689 }
690 
iohub_vpd_parse(void)691 static void iohub_vpd_parse(void)
692 {
693 	const struct HDIF_common_hdr *iohub_hdr;
694 	const struct cechub_hub_fru_id *fru_id_data;
695 	unsigned int i, vpd_sz, fru_id_sz;
696 
697 	if (!get_hdif(&spira.ntuples.cec_iohub_fru, CECHUB_FRU_HDIF_SIG)) {
698 		prerror("VPD: Could not find IO HUB FRU data\n");
699 		return;
700 	}
701 
702 	for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, iohub_hdr,
703 					i, CECHUB_FRU_HDIF_SIG) {
704 
705 		fru_id_data = HDIF_get_idata(iohub_hdr,
706 					     CECHUB_FRU_ID_DATA_AREA,
707 					     &fru_id_sz);
708 
709 		/* P8, IO HUB is on processor card and we have a
710 		 * daughter card array
711 		 */
712 		if (fru_id_data &&
713 		    be32_to_cpu(fru_id_data->card_type) == CECHUB_FRU_TYPE_CPU_CARD) {
714 			iokid_vpd_parse(iohub_hdr);
715 			continue;
716 		}
717 
718 		/* On P7, the keyword VPD will not be NULL */
719 		if (HDIF_get_idata(iohub_hdr,
720 				   CECHUB_ASCII_KEYWORD_VPD, &vpd_sz))
721 			dt_add_vpd_node(iohub_hdr, CECHUB_FRU_ID_DATA,
722 					CECHUB_ASCII_KEYWORD_VPD);
723 	}
724 }
725 
_vpd_parse(struct spira_ntuple tuple)726 static void _vpd_parse(struct spira_ntuple tuple)
727 {
728 	const struct HDIF_common_hdr *fruvpd_hdr;
729 	unsigned int i;
730 
731 	if (!get_hdif(&tuple, FRUVPD_HDIF_SIG))
732 		return;
733 
734 	for_each_ntuple_idx(&tuple, fruvpd_hdr, i, FRUVPD_HDIF_SIG)
735 		dt_add_vpd_node(fruvpd_hdr,
736 				FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
737 }
738 
vpd_parse(void)739 void vpd_parse(void)
740 {
741 	const struct HDIF_common_hdr *fruvpd_hdr;
742 
743 	/* System VPD uses the VSYS record, so its special */
744 	sysvpd_parse();
745 
746 	/* Enclosure */
747 	_vpd_parse(spira.ntuples.nt_enclosure_vpd);
748 
749 	/* Backplane */
750 	_vpd_parse(spira.ntuples.backplane_vpd);
751 
752 	/* clock card -- does this use the FRUVPD sig? */
753 	_vpd_parse(spira.ntuples.clock_vpd);
754 
755 	/* Anchor card */
756 	_vpd_parse(spira.ntuples.anchor_vpd);
757 
758 	/* Op panel -- does this use the FRUVPD sig? */
759 	_vpd_parse(spira.ntuples.op_panel_vpd);
760 
761 	/* External cache FRU vpd -- does this use the FRUVPD sig? */
762 	_vpd_parse(spira.ntuples.ext_cache_fru_vpd);
763 
764 	/* Misc CEC FRU */
765 	_vpd_parse(spira.ntuples.misc_cec_fru_vpd);
766 
767 	/* CEC IO HUB FRU */
768 	iohub_vpd_parse();
769 
770 	/*
771 	 * SP subsystem -- while the rest of the SPINFO structure is
772 	 * different, the FRU ID data and pointer pair to keyword VPD
773 	 * are the same offset as a FRUVPD entry. So reuse it
774 	 */
775 	fruvpd_hdr = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG);
776 	if (fruvpd_hdr)
777 		dt_add_vpd_node(fruvpd_hdr,
778 				FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
779 }
780