linux/drivers/firmware/google/vpd_decode.c
<<
>>
Prefs
   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
  22static int vpd_decode_len(const s32 max_len, const u8 *in,
  23                          s32 *length, s32 *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
  44        return VPD_OK;
  45}
  46
  47int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
  48                      vpd_decode_callback callback, void *callback_arg)
  49{
  50        int type;
  51        int res;
  52        s32 key_len;
  53        s32 value_len;
  54        s32 decoded_len;
  55        const u8 *key;
  56        const u8 *value;
  57
  58        /* type */
  59        if (*consumed >= max_len)
  60                return VPD_FAIL;
  61
  62        type = input_buf[*consumed];
  63
  64        switch (type) {
  65        case VPD_TYPE_INFO:
  66        case VPD_TYPE_STRING:
  67                (*consumed)++;
  68
  69                /* key */
  70                res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
  71                                     &key_len, &decoded_len);
  72                if (res != VPD_OK || *consumed + decoded_len >= max_len)
  73                        return VPD_FAIL;
  74
  75                *consumed += decoded_len;
  76                key = &input_buf[*consumed];
  77                *consumed += key_len;
  78
  79                /* value */
  80                res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
  81                                     &value_len, &decoded_len);
  82                if (res != VPD_OK || *consumed + decoded_len > max_len)
  83                        return VPD_FAIL;
  84
  85                *consumed += decoded_len;
  86                value = &input_buf[*consumed];
  87                *consumed += value_len;
  88
  89                if (type == VPD_TYPE_STRING)
  90                        return callback(key, key_len, value, value_len,
  91                                        callback_arg);
  92                break;
  93
  94        default:
  95                return VPD_FAIL;
  96        }
  97
  98        return VPD_OK;
  99}
 100