blob: 10f5703e6a3e36f287a93903e788b36ae1df4c48 [file] [log] [blame]
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +08001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
Jonathan Zhangb5392f92019-07-16 14:37:23 -07007#include <arch/early_variables.h>
8#include <assert.h>
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +08009#include <console/console.h>
Hung-Te Linb15a0d02015-07-13 15:49:57 +080010#include <cbmem.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050011#include <fmap.h>
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080012#include <stdlib.h>
13#include <string.h>
Julius Werner4f7a3612016-01-20 18:01:15 -080014#include <timestamp.h>
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080015
Patrick Rudolph28cee592018-03-08 15:43:12 +010016#include "vpd.h"
Hung-Te Linc3455702019-05-27 11:02:00 +080017#include "vpd_decode.h"
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080018#include "vpd_tables.h"
19
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080020struct vpd_gets_arg {
21 const uint8_t *key;
22 const uint8_t *value;
23 int32_t key_len, value_len;
24 int matched;
25};
26
Jonathan Zhangb5392f92019-07-16 14:37:23 -070027struct vpd_blob g_vpd_blob CAR_GLOBAL = {0};
Hung-Te Linb15a0d02015-07-13 15:49:57 +080028
Jonathan Zhangb5392f92019-07-16 14:37:23 -070029/*
30 * returns the size of data in a VPD 2.0 formatted fmap region, or 0.
31 * Also sets *base as the region's base address.
32 */
Hung-Te Linb15a0d02015-07-13 15:49:57 +080033static int32_t get_vpd_size(const char *fmap_name, int32_t *base)
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080034{
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080035 struct google_vpd_info info;
Aaron Durbin0424c952015-03-28 23:56:22 -050036 struct region_device vpd;
Hung-Te Linb15a0d02015-07-13 15:49:57 +080037 int32_t size;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080038
Hung-Te Linb15a0d02015-07-13 15:49:57 +080039 if (fmap_locate_area_as_rdev(fmap_name, &vpd)) {
40 printk(BIOS_ERR, "%s: No %s FMAP section.\n", __func__,
41 fmap_name);
42 return 0;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080043 }
44
Hung-Te Linb15a0d02015-07-13 15:49:57 +080045 size = region_device_sz(&vpd);
Aaron Durbin0424c952015-03-28 23:56:22 -050046
Hung-Te Linb15a0d02015-07-13 15:49:57 +080047 if ((size < GOOGLE_VPD_2_0_OFFSET + sizeof(info)) ||
Aaron Durbin0424c952015-03-28 23:56:22 -050048 rdev_chain(&vpd, &vpd, GOOGLE_VPD_2_0_OFFSET,
Hung-Te Linb15a0d02015-07-13 15:49:57 +080049 size - GOOGLE_VPD_2_0_OFFSET)) {
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080050 printk(BIOS_ERR, "%s: Too small (%d) for Google VPD 2.0.\n",
Hung-Te Linb15a0d02015-07-13 15:49:57 +080051 __func__, size);
52 return 0;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080053 }
54
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080055 /* Try if we can find a google_vpd_info, otherwise read whole VPD. */
Julius Wernerffc22602016-01-21 11:12:38 -080056 if (rdev_readat(&vpd, &info, *base, sizeof(info)) != sizeof(info)) {
57 printk(BIOS_ERR, "ERROR: Failed to read %s header.\n",
58 fmap_name);
59 return 0;
60 }
61
62 if (memcmp(info.header.magic, VPD_INFO_MAGIC, sizeof(info.header.magic))
Hung-Te Linb15a0d02015-07-13 15:49:57 +080063 == 0 && size >= info.size + sizeof(info)) {
64 *base += sizeof(info);
65 size = info.size;
Julius Wernerffc22602016-01-21 11:12:38 -080066 } else if (info.header.tlv.type == VPD_TYPE_TERMINATOR ||
67 info.header.tlv.type == VPD_TYPE_IMPLICIT_TERMINATOR) {
68 printk(BIOS_WARNING, "WARNING: %s is uninitialized or empty.\n",
69 fmap_name);
70 size = 0;
Hung-Te Linb15a0d02015-07-13 15:49:57 +080071 } else {
72 size -= GOOGLE_VPD_2_0_OFFSET;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080073 }
74
Hung-Te Linb15a0d02015-07-13 15:49:57 +080075 return size;
76}
77
Jonathan Zhangb5392f92019-07-16 14:37:23 -070078static void vpd_get_blob(void)
Hung-Te Linb15a0d02015-07-13 15:49:57 +080079{
Jonathan Zhangb5392f92019-07-16 14:37:23 -070080 int32_t ro_vpd_base = 0;
81 int32_t rw_vpd_base = 0;
82 int32_t ro_vpd_size = get_vpd_size("RO_VPD", &ro_vpd_base);
83 int32_t rw_vpd_size = get_vpd_size("RW_VPD", &rw_vpd_base);
84
85 /* Return if no VPD at all */
86 if (ro_vpd_size == 0 && rw_vpd_size == 0)
87 return;
88
89 struct vpd_blob *blob = car_get_var_ptr(&g_vpd_blob);
90 if (!blob)
91 return;
92 blob->ro_base = NULL;
93 blob->ro_size = 0;
94 blob->rw_base = NULL;
95 blob->rw_size = 0;
96
Hung-Te Linb15a0d02015-07-13 15:49:57 +080097 struct region_device vpd;
Hung-Te Linb15a0d02015-07-13 15:49:57 +080098
99 if (ro_vpd_size) {
100 if (fmap_locate_area_as_rdev("RO_VPD", &vpd)) {
101 /* shouldn't happen, but let's be extra defensive */
102 printk(BIOS_ERR, "%s: No RO_VPD FMAP section.\n",
103 __func__);
104 return;
105 }
106 rdev_chain(&vpd, &vpd, GOOGLE_VPD_2_0_OFFSET,
107 region_device_sz(&vpd) - GOOGLE_VPD_2_0_OFFSET);
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700108 blob->ro_base = (uint8_t *)(rdev_mmap_full(&vpd) +
109 sizeof(struct google_vpd_info));
110 blob->ro_size = ro_vpd_size;
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800111 }
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800112 if (rw_vpd_size) {
113 if (fmap_locate_area_as_rdev("RW_VPD", &vpd)) {
114 /* shouldn't happen, but let's be extra defensive */
115 printk(BIOS_ERR, "%s: No RW_VPD FMAP section.\n",
116 __func__);
117 return;
118 }
119 rdev_chain(&vpd, &vpd, GOOGLE_VPD_2_0_OFFSET,
120 region_device_sz(&vpd) - GOOGLE_VPD_2_0_OFFSET);
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700121 blob->rw_base = (uint8_t *)(rdev_mmap_full(&vpd) +
122 sizeof(struct google_vpd_info));
123 blob->rw_size = rw_vpd_size;
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800124 }
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700125 blob->initialized = true;
126}
127
128const struct vpd_blob *vpd_load_blob(void)
129{
130 struct vpd_blob *blob = NULL;
131
132 blob = car_get_var_ptr(&g_vpd_blob);
133
134 if (blob && blob->initialized == false)
135 vpd_get_blob();
136
137 return blob;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800138}
139
Hung-Te Linc3455702019-05-27 11:02:00 +0800140static int vpd_gets_callback(const uint8_t *key, uint32_t key_len,
141 const uint8_t *value, uint32_t value_len,
142 void *arg)
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800143{
144 struct vpd_gets_arg *result = (struct vpd_gets_arg *)arg;
145 if (key_len != result->key_len ||
146 memcmp(key, result->key, key_len) != 0)
Hung-Te Linc3455702019-05-27 11:02:00 +0800147 /* Returns VPD_DECODE_OK to continue parsing. */
148 return VPD_DECODE_OK;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800149
150 result->matched = 1;
151 result->value = value;
152 result->value_len = value_len;
Hung-Te Linc3455702019-05-27 11:02:00 +0800153 /* Returns VPD_DECODE_FAIL to stop parsing. */
154 return VPD_DECODE_FAIL;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800155}
156
Patrick Rudolph28cee592018-03-08 15:43:12 +0100157const void *vpd_find(const char *key, int *size, enum vpd_region region)
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800158{
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700159 struct vpd_blob blob = {0};
160
161 vpd_get_buffers(&blob);
162 if (blob.ro_size == 0 && blob.rw_size == 0)
163 return NULL;
164
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800165 struct vpd_gets_arg arg = {0};
Hung-Te Linc3455702019-05-27 11:02:00 +0800166 uint32_t consumed = 0;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800167
168 arg.key = (const uint8_t *)key;
169 arg.key_len = strlen(key);
170
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700171 if ((region == VPD_ANY || region == VPD_RO) && blob.ro_size != 0) {
172 while (vpd_decode_string(blob.ro_size, blob.ro_base,
173 &consumed, vpd_gets_callback, &arg) == VPD_DECODE_OK) {
174 /* Iterate until found or no more entries. */
Patrick Rudolph28cee592018-03-08 15:43:12 +0100175 }
Hung-Te Linc3455702019-05-27 11:02:00 +0800176 }
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700177
178 if ((!arg.matched && region != VPD_RO) && blob.rw_size != 0) {
179 while (vpd_decode_string(blob.rw_size, blob.rw_base,
180 &consumed, vpd_gets_callback, &arg) == VPD_DECODE_OK) {
181 /* Iterate until found or no more entries. */
Patrick Rudolph28cee592018-03-08 15:43:12 +0100182 }
Hung-Te Linc3455702019-05-27 11:02:00 +0800183 }
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800184
185 if (!arg.matched)
186 return NULL;
187
Vadim Bendebury127c3392014-10-22 17:39:24 -0700188 *size = arg.value_len;
189 return arg.value;
190}
191
Patrick Rudolph28cee592018-03-08 15:43:12 +0100192char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region)
Vadim Bendebury127c3392014-10-22 17:39:24 -0700193{
194 const void *string_address;
195 int string_size;
196
Patrick Rudolph28cee592018-03-08 15:43:12 +0100197 string_address = vpd_find(key, &string_size, region);
Vadim Bendebury127c3392014-10-22 17:39:24 -0700198
199 if (!string_address)
200 return NULL;
201
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700202 assert(size > 0);
203 int copy_size = MIN(size - 1, string_size);
204 memcpy(buffer, string_address, copy_size);
205 buffer[copy_size] = '\0';
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800206 return buffer;
207}
208
Jonathan Zhangb5392f92019-07-16 14:37:23 -0700209/*
210 * Find value of boolean type vpd key.
211 *
212 * During the process, necessary checking is done, such as making
213 * sure the value length is 1, and value is either '1' or '0'.
214 */
215bool vpd_get_bool(const char *key, enum vpd_region region, uint8_t *val)
216{
217 int size;
218 const char *value;
219
220 value = vpd_find(key, &size, region);
221 if (!value) {
222 printk(BIOS_CRIT, "problem returning from vpd_find.\n");
223 return false;
224 }
225
226 if (size != 1)
227 return false;
228
229 /* Make sure the value is either '1' or '0' */
230 if (*value == '1') {
231 *val = 1;
232 return true;
233 } else if (*value == '0') {
234 *val = 0;
235 return true;
236 } else
237 return false;
238}