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