1 /* string.c - IPMI string handling
2 * Copyright (C) 2012 MontaVista Software.
3 * Corey Minyard <cminyard@mvista.com>
4 *
5 * This file is part of the IPMI Interface (IPMIIF).
6 *
7 * This is for handling strings in SDRs and FRU data.
8 *
9 * This software is available to you under a choice of one of two
10 * licenses. You may choose to be licensed under the terms of the GNU
11 * Lesser General Public License (GPL) Version 2 or the modified BSD
12 * license below. The following disclamer applies to both licenses:
13 *
14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
20 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
22 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * GNU Lesser General Public Licence
26 *
27 * This program is free software; you can redistribute it and/or
28 * modify it under the terms of the GNU Lesser General Public License
29 * as published by the Free Software Foundation; either version 2 of
30 * the License, or (at your option) any later version.
31 *
32 * You should have received a copy of the GNU Lesser General Public
33 * License along with this program; if not, write to the Free
34 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *
36 * Modified BSD Licence
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 *
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above
45 * copyright notice, this list of conditions and the following
46 * disclaimer in the documentation and/or other materials provided
47 * with the distribution.
48 * 3. The name of the author may not be used to endorse or promote
49 * products derived from this software without specific prior
50 * written permission.
51 */
52
53 #include <string.h>
54 #include <errno.h>
55
56 #include <OpenIPMI/ipmi_string.h>
57
58 static int
ipmi_get_unicode(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)59 ipmi_get_unicode(unsigned int len,
60 unsigned char **d, unsigned int in_len,
61 char *out, unsigned int out_len)
62 {
63 if (in_len < len)
64 return -1;
65 if (out_len < len)
66 return -1;
67
68 memcpy(out, *d, len);
69 *d += len;
70 return len;
71 }
72
73 static int
ipmi_get_bcd_plus(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)74 ipmi_get_bcd_plus(unsigned int len,
75 unsigned char **d, unsigned int in_len,
76 char *out, unsigned int out_len)
77 {
78 static char table[16] = {
79 '0', '1', '2', '3', '4', '5', '6', '7',
80 '8', '9', ' ', '-', '.', ':', ',', '_'
81 };
82 unsigned int bo;
83 unsigned int val = 0;
84 unsigned int i;
85 unsigned int real_length;
86 char *out_s = out;
87
88 real_length = (in_len * 8) / 4;
89 if (len > real_length)
90 return -1;
91 if (len > out_len)
92 return -1;
93
94 bo = 0;
95 for (i=0; i<len; i++) {
96 switch (bo) {
97 case 0:
98 val = **d & 0xf;
99 bo = 4;
100 break;
101 case 4:
102 val = (**d >> 4) & 0xf;
103 (*d)++;
104 bo = 0;
105 break;
106 }
107 *out = table[val];
108 out++;
109 }
110
111 if (bo != 0)
112 (*d)++;
113
114 return out - out_s;
115 }
116
117 static int
ipmi_get_6_bit_ascii(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)118 ipmi_get_6_bit_ascii(unsigned int len,
119 unsigned char **d, unsigned int in_len,
120 char *out, unsigned int out_len)
121 {
122 static char table[64] = {
123 ' ', '!', '"', '#', '$', '%', '&', '\'',
124 '(', ')', '*', '+', ',', '-', '.', '/',
125 '0', '1', '2', '3', '4', '5', '6', '7',
126 '8', '9', ':', ';', '<', '=', '>', '?',
127 '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
128 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
129 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
130 'X', 'Y', 'Z', '[', '\\', ']', '^', '_'
131 };
132 unsigned int bo;
133 unsigned int val = 0;
134 unsigned int i;
135 unsigned int real_length;
136 char *out_s = out;
137
138 real_length = (in_len * 8) / 6;
139 if (len > real_length)
140 return -1;
141 if (len > out_len)
142 return -1;
143
144 bo = 0;
145 for (i=0; i<len; i++) {
146 switch (bo) {
147 case 0:
148 val = **d & 0x3f;
149 bo = 6;
150 break;
151 case 2:
152 val = (**d >> 2) & 0x3f;
153 (*d)++;
154 bo = 0;
155 break;
156 case 4:
157 val = (**d >> 4) & 0xf;
158 (*d)++;
159 val |= (**d & 0x3) << 4;
160 bo = 2;
161 break;
162 case 6:
163 val = (**d >> 6) & 0x3;
164 (*d)++;
165 val |= (**d & 0xf) << 2;
166 bo = 4;
167 break;
168 }
169 *out = table[val];
170 out++;
171 }
172
173 if (bo != 0)
174 (*d)++;
175
176 return out - out_s;
177 }
178
179 static int
ipmi_get_8_bit_ascii(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)180 ipmi_get_8_bit_ascii(unsigned int len,
181 unsigned char **d, unsigned int in_len,
182 char *out, unsigned int out_len)
183 {
184 unsigned int j;
185
186 if (len > in_len)
187 return -1;
188 if (len > out_len)
189 return -1;
190
191 for (j=0; j<len; j++) {
192 *out = **d;
193 out++;
194 (*d)++;
195 }
196 return len;
197 };
198
199 int
ipmi_get_device_string(unsigned char ** const pinput,unsigned int in_len,char * output,int semantics,int force_unicode,enum ipmi_str_type_e * stype,unsigned int max_out_len,unsigned int * out_len)200 ipmi_get_device_string(unsigned char ** const pinput,
201 unsigned int in_len,
202 char *output,
203 int semantics,
204 int force_unicode,
205 enum ipmi_str_type_e *stype,
206 unsigned int max_out_len,
207 unsigned int *out_len)
208 {
209 int type;
210 int len;
211 int olen;
212
213 if (max_out_len == 0)
214 return 0;
215
216 if (in_len <= 0) {
217 *output = '\0';
218 return 0;
219 }
220
221 #if 0
222 /* Note that this is technically correct, but commonly ignored.
223 0xc1 is invalid, but some FRU and SDR data still uses it. Grr.
224 The FRU stuff has to handle the end-of-area marker c1 itself,
225 anyway, so this is relatively safe. In a "correct" system you
226 should never see a 0xc1 here, anyway. */
227 if (**pinput == 0xc1) {
228 *output = '\0';
229 (*pinput)++;
230 return 0;
231 }
232 #endif
233
234 type = (**pinput >> 6) & 3;
235
236 /* Special case for FRU data, type 3 is unicode if the language is
237 non-english. */
238 if ((force_unicode) && (type == 3)) {
239 type = 0;
240 force_unicode = 0;
241 }
242
243 len = **pinput & 0x3f;
244 (*pinput)++;
245 in_len--;
246 *stype = IPMI_ASCII_STR;
247 switch (type)
248 {
249 case 0: /* Unicode */
250 olen = ipmi_get_unicode(len, pinput, in_len, output, max_out_len);
251 if (semantics == IPMI_STR_FRU_SEMANTICS)
252 *stype = IPMI_BINARY_STR;
253 else
254 *stype = IPMI_UNICODE_STR;
255 break;
256 case 1: /* BCD Plus */
257 olen = ipmi_get_bcd_plus(len, pinput, in_len, output, max_out_len);
258 break;
259 case 2: /* 6-bit ASCII */
260 olen=ipmi_get_6_bit_ascii(len, pinput, in_len, output, max_out_len);
261 break;
262 case 3: /* 8-bit ASCII */
263 olen=ipmi_get_8_bit_ascii(len, pinput, in_len, output, max_out_len);
264 break;
265 default:
266 olen = 0;
267 }
268
269 if (olen < 0)
270 return EINVAL;
271
272 *out_len = olen;
273 return 0;
274 }
275
276 /* Element will be zero if not present, n-1 if present. */
277 static char table_4_bit[256] =
278 {
279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
282 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
284 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0d, 0x00,
285 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
286 0x09, 0x0a, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
289 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
291 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
296 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
297 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
299 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311 };
312
313 /* Element will be zero if not present, n-1 if present. */
314 static char table_6_bit[256] =
315 {
316 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x21, 0x08,
321 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
322 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
323 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
324 0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
325 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
326 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
327 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 };
349
350 static void
ipmi_set_bcdplus(const char * input,unsigned int in_len,unsigned char * output,unsigned int * out_len)351 ipmi_set_bcdplus(const char *input,
352 unsigned int in_len,
353 unsigned char *output,
354 unsigned int *out_len)
355 {
356 unsigned int len = *out_len;
357 const char *s = input;
358 unsigned int pos = 0;
359 unsigned int bit = 0;
360 unsigned int count = 0;
361
362 while (in_len > 0) {
363 switch(bit) {
364 case 0:
365 pos++;
366 if (pos >= len)
367 goto out_overflow;
368 output[pos] = table_4_bit[(int) *s] - 1;
369 bit = 4;
370 break;
371
372 case 4:
373 output[pos] |= (table_4_bit[(int) *s] - 1) << 4;
374 bit = 0;
375 break;
376 }
377 count++;
378 in_len--;
379 s++;
380 }
381 pos++;
382 out_overflow:
383 output[0] = (0x01 << 6) | count;
384 *out_len = pos;
385 }
386
387 static void
ipmi_set_6_bit_ascii(const char * input,unsigned int in_len,unsigned char * output,unsigned int * out_len)388 ipmi_set_6_bit_ascii(const char *input,
389 unsigned int in_len,
390 unsigned char *output,
391 unsigned int *out_len)
392 {
393 unsigned int len = *out_len;
394 const char *s = input;
395 unsigned int pos = 0;
396 unsigned int bit = 0;
397 unsigned int count = 0;
398 unsigned int cval;
399 unsigned int oval;
400
401 while (in_len > 0) {
402 cval = *s;
403 s++;
404 oval = table_6_bit[cval] - 1;
405 switch(bit) {
406 case 0:
407 pos++;
408 if (pos >= len)
409 goto out_overflow;
410 output[pos] = oval;
411 bit = 6;
412 break;
413
414 case 2:
415 output[pos] |= oval << 2;
416 bit = 0;
417 break;
418
419 case 4:
420 output[pos] |= oval << 4;
421 pos++;
422 if (pos >= len)
423 goto out_overflow;
424 output[pos] = (oval >> 4) & 0x3;
425 bit = 2;
426 break;
427
428 case 6:
429 output[pos] |= oval << 6;
430 pos++;
431 if (pos >= len)
432 goto out_overflow;
433 output[pos] = (oval >> 2) & 0xf;
434 bit = 4;
435 break;
436 }
437 count++;
438 in_len--;
439 }
440 pos++;
441 out_overflow:
442 output[0] = (0x02 << 6) | count;
443 *out_len = pos;
444 }
445
446 static void
ipmi_set_8_bit_ascii(const char * input,unsigned int in_len,unsigned char * output,unsigned int * out_len)447 ipmi_set_8_bit_ascii(const char *input,
448 unsigned int in_len,
449 unsigned char *output,
450 unsigned int *out_len)
451 {
452 char tmp[2];
453 /* truncate if necessary. */
454 if (in_len > (*out_len - 1))
455 in_len = *out_len - 1;
456
457 /* A length of 1 is invalid, make it 2 with a nil char */
458 if (in_len == 1) {
459 tmp[0] = input[0];
460 tmp[1] = '\0';
461 input = tmp;
462 in_len++;
463 }
464
465 *out_len = in_len + 1;
466
467 memcpy(output+1, input, in_len);
468 output[0] = (0x03 << 6) | in_len;
469 }
470
471 void
ipmi_set_device_string2(const char * input,enum ipmi_str_type_e type,unsigned int in_len,unsigned char * output,int force_unicode,unsigned int * out_len,unsigned int options)472 ipmi_set_device_string2(const char *input,
473 enum ipmi_str_type_e type,
474 unsigned int in_len,
475 unsigned char *output,
476 int force_unicode,
477 unsigned int *out_len,
478 unsigned int options)
479 {
480 const char *s = input;
481 int bsize = 0; /* Start with 4-bit. */
482 unsigned int i;
483
484 /* Max size is 64 (63 bytes + the type byte). */
485 if (*out_len > 64)
486 *out_len = 64;
487 /* Truncate */
488 if (in_len > 63)
489 in_len = 63;
490
491 if (type == IPMI_ASCII_STR) {
492 if (options && IPMI_STRING_OPTION_8BIT_ONLY)
493 bsize = 2;
494 else {
495 for (i=0; i<in_len; i++) {
496 if (table_4_bit[(int) *s] == 0) {
497 bsize |= 1;
498 if (table_6_bit[(int) *s] == 0) {
499 bsize |= 2;
500 break;
501 }
502 }
503 s++;
504 }
505 }
506 if (bsize == 0) {
507 /* We can encode it in 4-bit BCD+ */
508 ipmi_set_bcdplus(input, in_len, output, out_len);
509 } else if (bsize == 1) {
510 /* We can encode it in 6-bit ASCII. */
511 ipmi_set_6_bit_ascii(input, in_len, output, out_len);
512 } else {
513 ipmi_set_8_bit_ascii(input, in_len, output, out_len);
514 }
515 } else {
516 /* The input and output are unicode. */
517 if (in_len > *out_len-1)
518 in_len = *out_len-1;
519 if ((force_unicode) && (type == IPMI_UNICODE_STR))
520 *output = (0x3 << 6) | in_len;
521 else
522 *output = in_len;
523 memcpy(output+1, input, in_len);
524 *out_len = in_len + 1;
525 }
526 }
527
528 void
ipmi_set_device_string(const char * input,enum ipmi_str_type_e type,unsigned int in_len,unsigned char * output,int force_unicode,unsigned int * out_len)529 ipmi_set_device_string(const char *input,
530 enum ipmi_str_type_e type,
531 unsigned int in_len,
532 unsigned char *output,
533 int force_unicode,
534 unsigned int *out_len)
535 {
536 ipmi_set_device_string2(input, type, in_len, output, force_unicode,
537 out_len, IPMI_STRING_OPTION_NONE);
538 }
539