1 /* Copyright 2013-2014 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 <stdlib.h>
19 #include <string.h>
20 #include <ipmi.h>
21 #include <lock.h>
22 #include <opal.h>
23 #include <device.h>
24 
25 struct product_info {
26 	char *manufacturer;
27 	char *product;
28 	char *part_no;
29 	char *version;
30 	char *serial_no;
31 	char *asset_tag;
32 };
33 
34 struct common_header {
35 	u8 version;
36 	u8 internal_offset;
37 	u8 chassis_offset;
38 	u8 board_offset;
39 	u8 product_offset;
40 	u8 multirecord_offset;
41 	u8 pad;
42 	u8 checksum;
43 } __packed;
44 
45 #define min(x,y) ((x) < (y) ? x : y)
46 
47 /* The maximum amount of FRU data we can store. */
48 #define FRU_DATA_SIZE 256
49 
50 /* We allocate two bytes at these locations in the data array to track
51  * state. */
52 #define WRITE_INDEX 256
53 #define REMAINING 257
54 
55 /* The ASCII string encoding used only has 5 bits to encode length
56  * hence the maximum is 31 characters. */
57 #define MAX_STR_LEN 31
58 
59 static u8 fru_dev_id = 0;
60 
fru_insert_string(u8 * buf,char * str)61 static int fru_insert_string(u8 *buf, char *str)
62 {
63 	int len = strlen(str);
64 
65 	/* The ASCII type/length format only supports a string length
66 	 * between 2 and 31 characters. Zero characters is ok though
67 	 * as it indicates no data present. */
68 	if (len == 1 || len > MAX_STR_LEN)
69 		return OPAL_PARAMETER;
70 
71 	buf[0] = 0xc0 | len;
72 	memcpy(&buf[1], str, len);
73 
74 	return len + 1;
75 }
76 
fru_checksum(u8 * buf,int len)77 static u8 fru_checksum(u8 *buf, int len)
78 {
79 	int i;
80 	u8 checksum = 0;
81 
82 	for(i = 0; i < len; i++) {
83 		checksum += buf[i];
84 	}
85 	checksum = ~checksum + 1;
86 	return checksum;
87 }
88 
89 #define FRU_INSERT_STRING(x, y)						\
90 	({ rc = fru_insert_string(x, y);				\
91 		if (rc < 1) return OPAL_PARAMETER; rc; })
92 
fru_fill_product_info(u8 * buf,struct product_info * info,size_t size)93 static int fru_fill_product_info(u8 *buf, struct product_info *info, size_t size)
94 {
95 	size_t total_size = 11;
96 	int index = 0;
97 	int rc;
98 
99 	total_size += strlen(info->manufacturer);
100 	total_size += strlen(info->product);
101 	total_size += strlen(info->part_no);
102 	total_size += strlen(info->version);
103 	total_size += strlen(info->serial_no);
104 	total_size += strlen(info->asset_tag);
105 	total_size += (8 - (total_size % 8)) % 8;
106 	if (total_size > size)
107 		return OPAL_PARAMETER;
108 
109 	buf[index++] = 0x1;		/* Version */
110 	buf[index++] = total_size / 8;	/* Size */
111 	buf[index++] = 0;		/* Language code (English) */
112 
113 	index += FRU_INSERT_STRING(&buf[index], info->manufacturer);
114 	index += FRU_INSERT_STRING(&buf[index], info->product);
115 	index += FRU_INSERT_STRING(&buf[index], info->part_no);
116 	index += FRU_INSERT_STRING(&buf[index], info->version);
117 	index += FRU_INSERT_STRING(&buf[index], info->serial_no);
118 	index += FRU_INSERT_STRING(&buf[index], info->asset_tag);
119 
120 	buf[index++] = 0xc1;		/* End of data marker */
121 	memset(&buf[index], 0, total_size - index - 1);
122 	index += total_size - index - 1;
123 	buf[index] = fru_checksum(buf, index);
124 	assert(index == total_size - 1);
125 
126 	return total_size;
127 }
128 
fru_add(u8 * buf,int size)129 static int fru_add(u8 *buf, int size)
130 {
131 	int len;
132 	char short_version[MAX_STR_LEN + 1];
133 	struct common_header common_hdr;
134 	struct product_info info = {
135 		.manufacturer = (char *) "IBM",
136 		.product = (char *) "skiboot",
137 		.part_no = (char *) "",
138 		.serial_no = (char *) "",
139 		.asset_tag = (char *) "",
140 	};
141 
142 	if (size < sizeof(common_hdr))
143 		return OPAL_PARAMETER;
144 
145 	/* We currently only support adding the version number at the
146 	 * product information offset. We choose an offset of 64 bytes
147 	 * because that's what the standard recommends. */
148 	common_hdr.version = 1;
149 	common_hdr.internal_offset = 0;
150 	common_hdr.chassis_offset = 0;
151 	common_hdr.board_offset = 0;
152 	common_hdr.product_offset = 64/8;
153 	common_hdr.multirecord_offset = 0;
154 	common_hdr.pad = 0;
155 	common_hdr.checksum = fru_checksum((u8 *) &common_hdr, sizeof(common_hdr) - 1);
156 	memcpy(buf, &common_hdr, sizeof(common_hdr));
157 
158 	info.version = short_version;
159 	if (!strncmp(version, "skiboot-", 8))
160 		strncpy(info.version, &version[8], MAX_STR_LEN + 1);
161 	else
162 		strncpy(info.version, version, MAX_STR_LEN + 1);
163 
164 	if (info.version[MAX_STR_LEN] != '\0')
165 		info.version[MAX_STR_LEN - 1] = '+';
166 	info.version[MAX_STR_LEN] = '\0';
167 
168 	len = fru_fill_product_info(&buf[64], &info, size - 64);
169 	if (len < 0)
170 		return OPAL_PARAMETER;
171 
172 	return len + 64;
173 }
174 
fru_write_complete(struct ipmi_msg * msg)175 static void fru_write_complete(struct ipmi_msg *msg)
176 {
177 	u8 write_count = msg->data[0];
178 	u16 offset;
179 
180 	msg->data[WRITE_INDEX] += write_count;
181 	msg->data[REMAINING] -= write_count;
182 	if (msg->data[REMAINING] == 0)
183 		goto out;
184 
185 	offset = msg->data[WRITE_INDEX];
186 	ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU,
187 		      fru_write_complete, NULL,
188 		      MIN(msg->data[REMAINING] + 3, IPMI_MAX_REQ_SIZE), 2);
189 
190 	memmove(&msg->data[3], &msg->data[offset + 3], msg->req_size - 3);
191 
192 	msg->data[0] = fru_dev_id;     		/* FRU Device ID */
193 	msg->data[1] = offset & 0xff;		/* Offset LSB */
194 	msg->data[2] = (offset >> 8) & 0xff;	/* Offset MSB */
195 
196 	ipmi_queue_msg(msg);
197 
198 	return;
199 
200 out:
201 	ipmi_free_msg(msg);
202 }
203 
fru_write(void)204 static int fru_write(void)
205 {
206 	struct ipmi_msg *msg;
207 	int len;
208 
209 	/* We allocate FRU_DATA_SIZE + 5 bytes for the message:
210 	 * - 3 bytes for the the write FRU command header
211 	 * - FRU_DATA_SIZE bytes for FRU data
212 	 * - 2 bytes for offset & bytes remaining count
213 	 */
214 	msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU,
215 			 fru_write_complete, NULL, NULL, FRU_DATA_SIZE + 5, 2);
216 	if (!msg)
217 		return OPAL_RESOURCE;
218 
219 	msg->data[0] = fru_dev_id;	/* FRU Device ID */
220 	msg->data[1] = 0x0;		/* Offset LSB (we always write a new common header) */
221 	msg->data[2] = 0x0;		/* Offset MSB */
222 	len = fru_add(&msg->data[3], FRU_DATA_SIZE);
223 
224 	if (len < 0)
225 		return len;
226 
227 	/* Three bytes for the actual FRU Data Command */
228 	msg->data[WRITE_INDEX] = 0;
229 	msg->data[REMAINING] = len;
230 	msg->req_size = min(len + 3, IPMI_MAX_REQ_SIZE);
231 	return ipmi_queue_msg(msg);
232 }
233 
ipmi_fru_init(u8 dev_id)234 void ipmi_fru_init(u8 dev_id)
235 {
236 	fru_dev_id = dev_id;
237 	fru_write();
238 
239 	return;
240 }
241