GNU Linux-libre 4.14.328-gnu1
[releases.git] / drivers / firmware / google / vpd_decode.c
1 /*
2  * vpd_decode.c
3  *
4  * Google VPD decoding routines.
5  *
6  * Copyright 2017 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License v2.0 as published by
10  * the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 #include <linux/export.h>
19
20 #include "vpd_decode.h"
21
22 static int vpd_decode_len(const u32 max_len, const u8 *in,
23                           u32 *length, u32 *decoded_len)
24 {
25         u8 more;
26         int i = 0;
27
28         if (!length || !decoded_len)
29                 return VPD_FAIL;
30
31         *length = 0;
32         do {
33                 if (i >= max_len)
34                         return VPD_FAIL;
35
36                 more = in[i] & 0x80;
37                 *length <<= 7;
38                 *length |= in[i] & 0x7f;
39                 ++i;
40         } while (more);
41
42         *decoded_len = i;
43         return VPD_OK;
44 }
45
46 static int vpd_decode_entry(const u32 max_len, const u8 *input_buf,
47                             u32 *_consumed, const u8 **entry, u32 *entry_len)
48 {
49         u32 decoded_len;
50         u32 consumed = *_consumed;
51
52         if (vpd_decode_len(max_len - consumed, &input_buf[consumed],
53                            entry_len, &decoded_len) != VPD_OK)
54                 return VPD_FAIL;
55         if (max_len - consumed < decoded_len)
56                 return VPD_FAIL;
57
58         consumed += decoded_len;
59         *entry = input_buf + consumed;
60
61         /* entry_len is untrusted data and must be checked again. */
62         if (max_len - consumed < *entry_len)
63                 return VPD_FAIL;
64
65         consumed += *entry_len;
66         *_consumed = consumed;
67         return VPD_OK;
68 }
69
70 int vpd_decode_string(const u32 max_len, const u8 *input_buf, u32 *consumed,
71                       vpd_decode_callback callback, void *callback_arg)
72 {
73         int type;
74         u32 key_len;
75         u32 value_len;
76         const u8 *key;
77         const u8 *value;
78
79         /* type */
80         if (*consumed >= max_len)
81                 return VPD_FAIL;
82
83         type = input_buf[*consumed];
84
85         switch (type) {
86         case VPD_TYPE_INFO:
87         case VPD_TYPE_STRING:
88                 (*consumed)++;
89
90                 if (vpd_decode_entry(max_len, input_buf, consumed, &key,
91                                      &key_len) != VPD_OK)
92                         return VPD_FAIL;
93
94                 if (vpd_decode_entry(max_len, input_buf, consumed, &value,
95                                      &value_len) != VPD_OK)
96                         return VPD_FAIL;
97
98                 if (type == VPD_TYPE_STRING)
99                         return callback(key, key_len, value, value_len,
100                                         callback_arg);
101                 break;
102
103         default:
104                 return VPD_FAIL;
105         }
106
107         return VPD_OK;
108 }