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 	struct common_header common_hdr;
133 	char *short_version;
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 	short_version = strdup(version);
159 	info.version = short_version;
160 	if (!strncmp(version, "skiboot-", 8))
161 		info.version = &short_version[8];
162 
163 	if (strlen(info.version) >= MAX_STR_LEN) {
164 		if (info.version[MAX_STR_LEN] != '\0')
165 			info.version[MAX_STR_LEN - 1] = '+';
166 		info.version[MAX_STR_LEN] = '\0';
167 	}
168 
169 	len = fru_fill_product_info(&buf[64], &info, size - 64);
170 	free(short_version);
171 	if (len < 0)
172 		return OPAL_PARAMETER;
173 
174 	return len + 64;
175 }
176 
fru_write_complete(struct ipmi_msg * msg)177 static void fru_write_complete(struct ipmi_msg *msg)
178 {
179 	u8 write_count = msg->data[0];
180 	u16 offset;
181 
182 	msg->data[WRITE_INDEX] += write_count;
183 	msg->data[REMAINING] -= write_count;
184 	if (msg->data[REMAINING] == 0)
185 		goto out;
186 
187 	offset = msg->data[WRITE_INDEX];
188 	ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU,
189 		      fru_write_complete, NULL,
190 		      MIN(msg->data[REMAINING] + 3, IPMI_MAX_REQ_SIZE), 2);
191 
192 	memmove(&msg->data[3], &msg->data[offset + 3], msg->req_size - 3);
193 
194 	msg->data[0] = fru_dev_id;     		/* FRU Device ID */
195 	msg->data[1] = offset & 0xff;		/* Offset LSB */
196 	msg->data[2] = (offset >> 8) & 0xff;	/* Offset MSB */
197 
198 	ipmi_queue_msg(msg);
199 
200 	return;
201 
202 out:
203 	ipmi_free_msg(msg);
204 }
205 
fru_write(void)206 static int fru_write(void)
207 {
208 	struct ipmi_msg *msg;
209 	int len;
210 
211 	/* We allocate FRU_DATA_SIZE + 5 bytes for the message:
212 	 * - 3 bytes for the the write FRU command header
213 	 * - FRU_DATA_SIZE bytes for FRU data
214 	 * - 2 bytes for offset & bytes remaining count
215 	 */
216 	msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU,
217 			 fru_write_complete, NULL, NULL, FRU_DATA_SIZE + 5, 2);
218 	if (!msg)
219 		return OPAL_RESOURCE;
220 
221 	msg->data[0] = fru_dev_id;	/* FRU Device ID */
222 	msg->data[1] = 0x0;		/* Offset LSB (we always write a new common header) */
223 	msg->data[2] = 0x0;		/* Offset MSB */
224 	len = fru_add(&msg->data[3], FRU_DATA_SIZE);
225 
226 	if (len < 0)
227 		return len;
228 
229 	/* Three bytes for the actual FRU Data Command */
230 	msg->data[WRITE_INDEX] = 0;
231 	msg->data[REMAINING] = len;
232 	msg->req_size = min(len + 3, IPMI_MAX_REQ_SIZE);
233 	return ipmi_queue_msg(msg);
234 }
235 
ipmi_fru_init(u8 dev_id)236 void ipmi_fru_init(u8 dev_id)
237 {
238 	fru_dev_id = dev_id;
239 	fru_write();
240 
241 	return;
242 }
243