diff options
Diffstat (limited to 'drivers/firmware/google/vpd_decode.c')
-rw-r--r-- | drivers/firmware/google/vpd_decode.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/drivers/firmware/google/vpd_decode.c b/drivers/firmware/google/vpd_decode.c new file mode 100644 index 000000000..6c7ab2ba8 --- /dev/null +++ b/drivers/firmware/google/vpd_decode.c @@ -0,0 +1,108 @@ +/* + * vpd_decode.c + * + * Google VPD decoding routines. + * + * Copyright 2017 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/export.h> + +#include "vpd_decode.h" + +static int vpd_decode_len(const u32 max_len, const u8 *in, + u32 *length, u32 *decoded_len) +{ + u8 more; + int i = 0; + + if (!length || !decoded_len) + return VPD_FAIL; + + *length = 0; + do { + if (i >= max_len) + return VPD_FAIL; + + more = in[i] & 0x80; + *length <<= 7; + *length |= in[i] & 0x7f; + ++i; + } while (more); + + *decoded_len = i; + return VPD_OK; +} + +static int vpd_decode_entry(const u32 max_len, const u8 *input_buf, + u32 *_consumed, const u8 **entry, u32 *entry_len) +{ + u32 decoded_len; + u32 consumed = *_consumed; + + if (vpd_decode_len(max_len - consumed, &input_buf[consumed], + entry_len, &decoded_len) != VPD_OK) + return VPD_FAIL; + if (max_len - consumed < decoded_len) + return VPD_FAIL; + + consumed += decoded_len; + *entry = input_buf + consumed; + + /* entry_len is untrusted data and must be checked again. */ + if (max_len - consumed < *entry_len) + return VPD_FAIL; + + consumed += *entry_len; + *_consumed = consumed; + return VPD_OK; +} + +int vpd_decode_string(const u32 max_len, const u8 *input_buf, u32 *consumed, + vpd_decode_callback callback, void *callback_arg) +{ + int type; + u32 key_len; + u32 value_len; + const u8 *key; + const u8 *value; + + /* type */ + if (*consumed >= max_len) + return VPD_FAIL; + + type = input_buf[*consumed]; + + switch (type) { + case VPD_TYPE_INFO: + case VPD_TYPE_STRING: + (*consumed)++; + + if (vpd_decode_entry(max_len, input_buf, consumed, &key, + &key_len) != VPD_OK) + return VPD_FAIL; + + if (vpd_decode_entry(max_len, input_buf, consumed, &value, + &value_len) != VPD_OK) + return VPD_FAIL; + + if (type == VPD_TYPE_STRING) + return callback(key, key_len, value, value_len, + callback_arg); + break; + + default: + return VPD_FAIL; + } + + return VPD_OK; +} |