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