1 /* SPDX-License-Identifier: BSD-3-Clause */
4 #include <console/console.h>
8 #include <program_loading.h>
10 #include <timestamp.h>
14 #include "vpd_decode.h"
15 #include "vpd_tables.h"
17 /* Currently we only support Google VPD 2.0, which has a fixed offset. */
19 CROSVPD_CBMEM_MAGIC
= 0x43524f53,
20 CROSVPD_CBMEM_VERSION
= 0x0001,
29 /* The blob contains both RO and RW data. It starts with RO (0 ..
30 * ro_size) and then RW (ro_size .. ro_size+rw_size).
37 int32_t key_len
, value_len
;
41 static struct region_device ro_vpd
, rw_vpd
;
44 * Initializes a region_device to represent the requested VPD 2.0 formatted
45 * region on flash. On errors rdev->size will be set to 0.
47 static void init_vpd_rdev(const char *fmap_name
, struct region_device
*rdev
)
49 struct google_vpd_info info
;
52 if (fmap_locate_area_as_rdev(fmap_name
, rdev
)) {
53 printk(BIOS_WARNING
, "%s: No %s FMAP section.\n", __func__
,
58 size
= region_device_sz(rdev
);
60 if ((size
< GOOGLE_VPD_2_0_OFFSET
+ sizeof(info
)) ||
61 rdev_chain(rdev
, rdev
, GOOGLE_VPD_2_0_OFFSET
,
62 size
- GOOGLE_VPD_2_0_OFFSET
)) {
63 printk(BIOS_ERR
, "%s: Too small (%d) for Google VPD 2.0.\n",
68 /* Try if we can find a google_vpd_info, otherwise read whole VPD. */
69 if (rdev_readat(rdev
, &info
, 0, sizeof(info
)) != sizeof(info
)) {
70 printk(BIOS_ERR
, "Failed to read %s header.\n",
75 if (memcmp(info
.header
.magic
, VPD_INFO_MAGIC
, sizeof(info
.header
.magic
))
77 if (rdev_chain(rdev
, rdev
, sizeof(info
), info
.size
)) {
78 printk(BIOS_ERR
, "%s info size too large.\n",
82 } else if (info
.header
.tlv
.type
== VPD_TYPE_TERMINATOR
||
83 info
.header
.tlv
.type
== VPD_TYPE_IMPLICIT_TERMINATOR
) {
84 printk(BIOS_WARNING
, "%s is uninitialized or empty.\n",
92 memset(rdev
, 0, sizeof(*rdev
));
95 static int init_vpd_rdevs_from_cbmem(void)
100 struct vpd_cbmem
*cbmem
= cbmem_find(CBMEM_ID_VPD
);
104 rdev_chain_mem(&ro_vpd
, cbmem
->blob
, cbmem
->ro_size
);
105 rdev_chain_mem(&rw_vpd
, cbmem
->blob
+ cbmem
->ro_size
, cbmem
->rw_size
);
110 static void init_vpd_rdevs(void)
112 static bool done
= false;
117 if (init_vpd_rdevs_from_cbmem() != 0) {
118 init_vpd_rdev("RO_VPD", &ro_vpd
);
119 init_vpd_rdev("RW_VPD", &rw_vpd
);
125 static void cbmem_add_cros_vpd(int is_recovery
)
127 struct vpd_cbmem
*cbmem
;
129 timestamp_add_now(TS_COPYVPD_START
);
133 /* Return if no VPD at all */
134 if (region_device_sz(&ro_vpd
) == 0 && region_device_sz(&rw_vpd
) == 0)
137 size_t ro_size
= region_device_sz(&ro_vpd
);
138 size_t rw_size
= region_device_sz(&rw_vpd
);
140 cbmem
= cbmem_add(CBMEM_ID_VPD
, sizeof(*cbmem
) + ro_size
+ rw_size
);
142 printk(BIOS_ERR
, "%s: Failed to allocate CBMEM (%zu+%zu).\n",
143 __func__
, ro_size
, rw_size
);
147 cbmem
->magic
= CROSVPD_CBMEM_MAGIC
;
148 cbmem
->version
= CROSVPD_CBMEM_VERSION
;
149 cbmem
->ro_size
= ro_size
;
150 cbmem
->rw_size
= rw_size
;
153 if (rdev_readat(&ro_vpd
, cbmem
->blob
, 0, ro_size
) != ro_size
) {
154 printk(BIOS_ERR
, "Couldn't read RO VPD\n");
155 cbmem
->ro_size
= ro_size
= 0;
157 timestamp_add_now(TS_COPYVPD_RO_END
);
161 if (rdev_readat(&rw_vpd
, cbmem
->blob
+ ro_size
, 0, rw_size
)
163 printk(BIOS_ERR
, "Couldn't read RW VPD\n");
164 cbmem
->rw_size
= rw_size
= 0;
166 timestamp_add_now(TS_COPYVPD_RW_END
);
169 init_vpd_rdevs_from_cbmem();
172 static int vpd_gets_callback(const uint8_t *key
, uint32_t key_len
,
173 const uint8_t *value
, uint32_t value_len
,
176 struct vpd_gets_arg
*result
= (struct vpd_gets_arg
*)arg
;
177 if (key_len
!= result
->key_len
||
178 memcmp(key
, result
->key
, key_len
) != 0)
179 /* Returns VPD_DECODE_OK to continue parsing. */
180 return VPD_DECODE_OK
;
183 result
->value
= value
;
184 result
->value_len
= value_len
;
185 /* Returns VPD_DECODE_FAIL to stop parsing. */
186 return VPD_DECODE_FAIL
;
189 static void vpd_find_in(struct region_device
*rdev
, struct vpd_gets_arg
*arg
)
191 if (region_device_sz(rdev
) == 0)
194 uint32_t consumed
= 0;
195 void *mapping
= rdev_mmap_full(rdev
);
196 while (vpd_decode_string(region_device_sz(rdev
), mapping
,
197 &consumed
, vpd_gets_callback
, arg
) == VPD_DECODE_OK
) {
198 /* Iterate until found or no more entries. */
200 rdev_munmap(rdev
, mapping
);
203 const void *vpd_find(const char *key
, int *size
, enum vpd_region region
)
205 struct vpd_gets_arg arg
= {0};
207 arg
.key
= (const uint8_t *)key
;
208 arg
.key_len
= strlen(key
);
212 if (region
== VPD_RW_THEN_RO
)
213 vpd_find_in(&rw_vpd
, &arg
);
215 if (!arg
.matched
&& (region
== VPD_RO
|| region
== VPD_RO_THEN_RW
||
216 region
== VPD_RW_THEN_RO
))
217 vpd_find_in(&ro_vpd
, &arg
);
219 if (!arg
.matched
&& (region
== VPD_RW
|| region
== VPD_RO_THEN_RW
))
220 vpd_find_in(&rw_vpd
, &arg
);
225 *size
= arg
.value_len
;
229 char *vpd_gets(const char *key
, char *buffer
, int size
, enum vpd_region region
)
231 const void *string_address
;
234 string_address
= vpd_find(key
, &string_size
, region
);
240 int copy_size
= MIN(size
- 1, string_size
);
241 memcpy(buffer
, string_address
, copy_size
);
242 buffer
[copy_size
] = '\0';
247 * Find value of boolean type vpd key.
249 * During the process, necessary checking is done, such as making
250 * sure the value length is 1, and value is either '1' or '0'.
252 bool vpd_get_bool(const char *key
, enum vpd_region region
, uint8_t *val
)
257 value
= vpd_find(key
, &size
, region
);
259 printk(BIOS_CRIT
, "problem returning from vpd_find.\n");
266 /* Make sure the value is either '1' or '0' */
270 } else if (*value
== '0') {
278 * Find value of integer type by vpd key.
280 * Expects to find a decimal string, trailing chars are ignored.
281 * Returns true if the key is found and the value is not too long and
282 * starts with a decimal digit. Leaves `val` untouched if unsuccessful.
284 bool vpd_get_int(const char *const key
, const enum vpd_region region
, int *const val
)
288 if (!vpd_gets(key
, value
, sizeof(value
), region
))
291 if (!isdigit(*value
))
294 *val
= (int)atol(value
);
298 CBMEM_CREATION_HOOK(cbmem_add_cros_vpd
);