David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 2 | /* |
| 3 | * vpd_decode.c |
| 4 | * |
| 5 | * Google VPD decoding routines. |
| 6 | * |
| 7 | * Copyright 2017 Google Inc. |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 8 | */ |
| 9 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 10 | #include "vpd_decode.h" |
| 11 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 12 | static int vpd_decode_len(const u32 max_len, const u8 *in, |
| 13 | u32 *length, u32 *decoded_len) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 14 | { |
| 15 | u8 more; |
| 16 | int i = 0; |
| 17 | |
| 18 | if (!length || !decoded_len) |
| 19 | return VPD_FAIL; |
| 20 | |
| 21 | *length = 0; |
| 22 | do { |
| 23 | if (i >= max_len) |
| 24 | return VPD_FAIL; |
| 25 | |
| 26 | more = in[i] & 0x80; |
| 27 | *length <<= 7; |
| 28 | *length |= in[i] & 0x7f; |
| 29 | ++i; |
| 30 | } while (more); |
| 31 | |
| 32 | *decoded_len = i; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 33 | return VPD_OK; |
| 34 | } |
| 35 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 36 | static int vpd_decode_entry(const u32 max_len, const u8 *input_buf, |
| 37 | u32 *_consumed, const u8 **entry, u32 *entry_len) |
| 38 | { |
| 39 | u32 decoded_len; |
| 40 | u32 consumed = *_consumed; |
| 41 | |
| 42 | if (vpd_decode_len(max_len - consumed, &input_buf[consumed], |
| 43 | entry_len, &decoded_len) != VPD_OK) |
| 44 | return VPD_FAIL; |
| 45 | if (max_len - consumed < decoded_len) |
| 46 | return VPD_FAIL; |
| 47 | |
| 48 | consumed += decoded_len; |
| 49 | *entry = input_buf + consumed; |
| 50 | |
| 51 | /* entry_len is untrusted data and must be checked again. */ |
| 52 | if (max_len - consumed < *entry_len) |
| 53 | return VPD_FAIL; |
| 54 | |
| 55 | consumed += *entry_len; |
| 56 | *_consumed = consumed; |
| 57 | return VPD_OK; |
| 58 | } |
| 59 | |
| 60 | int vpd_decode_string(const u32 max_len, const u8 *input_buf, u32 *consumed, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 61 | vpd_decode_callback callback, void *callback_arg) |
| 62 | { |
| 63 | int type; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 64 | u32 key_len; |
| 65 | u32 value_len; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 66 | const u8 *key; |
| 67 | const u8 *value; |
| 68 | |
| 69 | /* type */ |
| 70 | if (*consumed >= max_len) |
| 71 | return VPD_FAIL; |
| 72 | |
| 73 | type = input_buf[*consumed]; |
| 74 | |
| 75 | switch (type) { |
| 76 | case VPD_TYPE_INFO: |
| 77 | case VPD_TYPE_STRING: |
| 78 | (*consumed)++; |
| 79 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 80 | if (vpd_decode_entry(max_len, input_buf, consumed, &key, |
| 81 | &key_len) != VPD_OK) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 82 | return VPD_FAIL; |
| 83 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 84 | if (vpd_decode_entry(max_len, input_buf, consumed, &value, |
| 85 | &value_len) != VPD_OK) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 86 | return VPD_FAIL; |
| 87 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 88 | if (type == VPD_TYPE_STRING) |
| 89 | return callback(key, key_len, value, value_len, |
| 90 | callback_arg); |
| 91 | break; |
| 92 | |
| 93 | default: |
| 94 | return VPD_FAIL; |
| 95 | } |
| 96 | |
| 97 | return VPD_OK; |
| 98 | } |