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