blob: d0e2cc1ef9069a11189e994788696289ca1bb6e4 [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
7#include <console/console.h>
8
Hung-Te Linb15a0d02015-07-13 15:49:57 +08009#include <cbmem.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050010#include <fmap.h>
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080011#include <stdlib.h>
12#include <string.h>
Julius Werner4f7a3612016-01-20 18:01:15 -080013#include <timestamp.h>
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080014
15#include "cros_vpd.h"
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080016#include "lib_vpd.h"
17#include "vpd_tables.h"
18
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080019/* Currently we only support Google VPD 2.0, which has a fixed offset. */
20enum {
21 GOOGLE_VPD_2_0_OFFSET = 0x600,
Hung-Te Linb15a0d02015-07-13 15:49:57 +080022 CROSVPD_CBMEM_MAGIC = 0x43524f53,
23 CROSVPD_CBMEM_VERSION = 0x0001,
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080024};
25
26struct vpd_gets_arg {
27 const uint8_t *key;
28 const uint8_t *value;
29 int32_t key_len, value_len;
30 int matched;
31};
32
Hung-Te Linb15a0d02015-07-13 15:49:57 +080033struct vpd_cbmem {
34 uint32_t magic;
35 uint32_t version;
36 uint32_t ro_size;
37 uint32_t rw_size;
38 uint8_t blob[0];
39 /* The blob contains both RO and RW data. It starts with RO (0 ..
40 * ro_size) and then RW (ro_size .. ro_size+rw_size).
41 */
42};
43
44/* returns the size of data in a VPD 2.0 formatted fmap region, or 0 */
45static int32_t get_vpd_size(const char *fmap_name, int32_t *base)
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080046{
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080047 struct google_vpd_info info;
Aaron Durbin0424c952015-03-28 23:56:22 -050048 struct region_device vpd;
Hung-Te Linb15a0d02015-07-13 15:49:57 +080049 int32_t size;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080050
Hung-Te Linb15a0d02015-07-13 15:49:57 +080051 if (fmap_locate_area_as_rdev(fmap_name, &vpd)) {
52 printk(BIOS_ERR, "%s: No %s FMAP section.\n", __func__,
53 fmap_name);
54 return 0;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080055 }
56
Hung-Te Linb15a0d02015-07-13 15:49:57 +080057 size = region_device_sz(&vpd);
Aaron Durbin0424c952015-03-28 23:56:22 -050058
Hung-Te Linb15a0d02015-07-13 15:49:57 +080059 if ((size < GOOGLE_VPD_2_0_OFFSET + sizeof(info)) ||
Aaron Durbin0424c952015-03-28 23:56:22 -050060 rdev_chain(&vpd, &vpd, GOOGLE_VPD_2_0_OFFSET,
Hung-Te Linb15a0d02015-07-13 15:49:57 +080061 size - GOOGLE_VPD_2_0_OFFSET)) {
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080062 printk(BIOS_ERR, "%s: Too small (%d) for Google VPD 2.0.\n",
Hung-Te Linb15a0d02015-07-13 15:49:57 +080063 __func__, size);
64 return 0;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080065 }
66
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080067 /* Try if we can find a google_vpd_info, otherwise read whole VPD. */
Julius Wernerffc22602016-01-21 11:12:38 -080068 if (rdev_readat(&vpd, &info, *base, sizeof(info)) != sizeof(info)) {
69 printk(BIOS_ERR, "ERROR: Failed to read %s header.\n",
70 fmap_name);
71 return 0;
72 }
73
74 if (memcmp(info.header.magic, VPD_INFO_MAGIC, sizeof(info.header.magic))
Hung-Te Linb15a0d02015-07-13 15:49:57 +080075 == 0 && size >= info.size + sizeof(info)) {
76 *base += sizeof(info);
77 size = info.size;
Julius Wernerffc22602016-01-21 11:12:38 -080078 } else if (info.header.tlv.type == VPD_TYPE_TERMINATOR ||
79 info.header.tlv.type == VPD_TYPE_IMPLICIT_TERMINATOR) {
80 printk(BIOS_WARNING, "WARNING: %s is uninitialized or empty.\n",
81 fmap_name);
82 size = 0;
Hung-Te Linb15a0d02015-07-13 15:49:57 +080083 } else {
84 size -= GOOGLE_VPD_2_0_OFFSET;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +080085 }
86
Hung-Te Linb15a0d02015-07-13 15:49:57 +080087 return size;
88}
89
90static void cbmem_add_cros_vpd(int is_recovery)
91{
92 struct region_device vpd;
93 struct vpd_cbmem *cbmem;
94 int32_t ro_vpd_base = 0, rw_vpd_base = 0;
95 int32_t ro_vpd_size, rw_vpd_size;
96
Julius Werner4f7a3612016-01-20 18:01:15 -080097 timestamp_add_now(TS_START_COPYVPD);
98
Hung-Te Linb15a0d02015-07-13 15:49:57 +080099 ro_vpd_size = get_vpd_size("RO_VPD", &ro_vpd_base);
100 rw_vpd_size = get_vpd_size("RW_VPD", &rw_vpd_base);
101
102 /* no VPD at all? nothing to do then */
103 if ((ro_vpd_size == 0) && (rw_vpd_size == 0))
104 return;
105
106 cbmem = cbmem_add(CBMEM_ID_VPD, sizeof(*cbmem) + ro_vpd_size +
107 rw_vpd_size);
108 if (!cbmem) {
109 printk(BIOS_ERR, "%s: Failed to allocate CBMEM (%u+%u).\n",
110 __func__, ro_vpd_size, rw_vpd_size);
111 return;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800112 }
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800113
114 cbmem->magic = CROSVPD_CBMEM_MAGIC;
115 cbmem->version = CROSVPD_CBMEM_VERSION;
116 cbmem->ro_size = 0;
117 cbmem->rw_size = 0;
118
119 if (ro_vpd_size) {
120 if (fmap_locate_area_as_rdev("RO_VPD", &vpd)) {
121 /* shouldn't happen, but let's be extra defensive */
122 printk(BIOS_ERR, "%s: No RO_VPD FMAP section.\n",
123 __func__);
124 return;
125 }
126 rdev_chain(&vpd, &vpd, GOOGLE_VPD_2_0_OFFSET,
127 region_device_sz(&vpd) - GOOGLE_VPD_2_0_OFFSET);
128
129
130 if (rdev_readat(&vpd, cbmem->blob, ro_vpd_base, ro_vpd_size) ==
131 ro_vpd_size) {
132 cbmem->ro_size = ro_vpd_size;
133 } else {
134 printk(BIOS_ERR,
135 "%s: Reading RO_VPD FMAP section failed.\n",
136 __func__);
137 ro_vpd_size = 0;
138 }
Julius Werner4f7a3612016-01-20 18:01:15 -0800139 timestamp_add_now(TS_END_COPYVPD_RO);
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800140 }
141
142 if (rw_vpd_size) {
143 if (fmap_locate_area_as_rdev("RW_VPD", &vpd)) {
144 /* shouldn't happen, but let's be extra defensive */
145 printk(BIOS_ERR, "%s: No RW_VPD FMAP section.\n",
146 __func__);
147 return;
148 }
149 rdev_chain(&vpd, &vpd, GOOGLE_VPD_2_0_OFFSET,
150 region_device_sz(&vpd) - GOOGLE_VPD_2_0_OFFSET);
151
152 if (rdev_readat(&vpd, cbmem->blob + ro_vpd_size, rw_vpd_base,
153 rw_vpd_size) == rw_vpd_size) {
154 cbmem->rw_size = rw_vpd_size;
155 } else {
156 printk(BIOS_ERR,
157 "%s: Reading RW_VPD FMAP section failed.\n",
158 __func__);
159 }
Julius Werner4f7a3612016-01-20 18:01:15 -0800160 timestamp_add_now(TS_END_COPYVPD_RW);
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800161 }
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800162}
163
164static int vpd_gets_callback(const uint8_t *key, int32_t key_len,
165 const uint8_t *value, int32_t value_len,
166 void *arg)
167{
168 struct vpd_gets_arg *result = (struct vpd_gets_arg *)arg;
169 if (key_len != result->key_len ||
170 memcmp(key, result->key, key_len) != 0)
171 /* Returns VPD_OK to continue parsing. */
172 return VPD_OK;
173
174 result->matched = 1;
175 result->value = value;
176 result->value_len = value_len;
177 /* Returns VPD_FAIL to stop parsing. */
178 return VPD_FAIL;
179}
180
Vadim Bendebury127c3392014-10-22 17:39:24 -0700181const void *cros_vpd_find(const char *key, int *size)
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800182{
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800183 struct vpd_gets_arg arg = {0};
184 int consumed = 0;
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800185 const struct vpd_cbmem *vpd;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800186
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800187 vpd = cbmem_find(CBMEM_ID_VPD);
188 if (!vpd || !vpd->ro_size)
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800189 return NULL;
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800190
191 arg.key = (const uint8_t *)key;
192 arg.key_len = strlen(key);
193
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800194 while (VPD_OK == decodeVpdString(vpd->ro_size, vpd->blob, &consumed,
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800195 vpd_gets_callback, &arg)) {
196 /* Iterate until found or no more entries. */
197 }
198
199 if (!arg.matched)
200 return NULL;
201
Vadim Bendebury127c3392014-10-22 17:39:24 -0700202 *size = arg.value_len;
203 return arg.value;
204}
205
206char *cros_vpd_gets(const char *key, char *buffer, int size)
207{
208 const void *string_address;
209 int string_size;
210
211 string_address = cros_vpd_find(key, &string_size);
212
213 if (!string_address)
214 return NULL;
215
216 if (size > (string_size + 1)) {
Stephen Barber31f8a2f2015-06-19 12:56:34 -0700217 memcpy(buffer, string_address, string_size);
218 buffer[string_size] = '\0';
Vadim Bendebury127c3392014-10-22 17:39:24 -0700219 } else {
220 memcpy(buffer, string_address, size - 1);
221 buffer[size - 1] = '\0';
222 }
Hung-Te Lin6eaaafa2014-02-21 16:21:00 +0800223 return buffer;
224}
225
Hung-Te Linb15a0d02015-07-13 15:49:57 +0800226RAMSTAGE_CBMEM_INIT_HOOK(cbmem_add_cros_vpd)