blob: 833407df4e37f6cf8589f5d7e76250ddcd7624af [file] [log] [blame]
Patrick Georgi9360fea2018-03-14 21:11:21 +01001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2018 The Chromium OS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <boot_device.h>
17#include <cbfs.h>
Arthur Heymans8b04dc72018-12-01 15:28:03 +010018#include <fmap.h>
Elyes HAOUAS2195f7a2019-06-21 07:20:12 +020019#include <commonlib/helpers.h>
Patrick Georgi9360fea2018-03-14 21:11:21 +010020#include <commonlib/region.h>
21#include <console/console.h>
22#include <smmstore.h>
Patrick Georgi9360fea2018-03-14 21:11:21 +010023
24/*
25 * The region format is still not finalized, but so far it looks like this:
26 * (
27 * uint32le_t key_sz
28 * uint32le_t value_sz
29 * uint8_t key[key_sz]
30 * uint8_t value[value_sz]
31 * uint8_t active
32 * align to 4 bytes
Elyes HAOUASa342f392018-10-17 10:56:26 +020033 * )*
Patrick Georgi9360fea2018-03-14 21:11:21 +010034 * uint32le_t endmarker = 0xffffffff
35 *
36 * active needs to be set to 0x00 for the entry to be valid. This satisfies
37 * the constraint that entries are either complete or will be ignored, as long
38 * as flash is written sequentially and into a fully erased block.
39 *
40 * Future additions to the format will split the region in half with an active
41 * block marker to allow safe compaction (ie. write the new data in the unused
42 * region, mark it active after the write completed). Otherwise a well-timed
43 * crash/reboot could clear out all variables.
44 */
45
46/*
47 * Return a region device that points into the store file.
48 *
49 * It's the image builder's responsibility to make it block aligned so that
50 * erase works without destroying other data.
51 *
52 * It doesn't cache the location to cope with flash changing underneath (eg
53 * due to an update)
54 *
55 * returns 0 on success, -1 on failure
56 * outputs the valid store rdev in rstore
57 */
58static int lookup_store(struct region_device *rstore)
59{
60 struct cbfsf file;
Julius Wernercd49cce2019-03-05 16:53:33 -080061 if (CONFIG(SMMSTORE_IN_CBFS)) {
Arthur Heymans8b04dc72018-12-01 15:28:03 +010062 if (cbfs_locate_file_in_region(&file,
63 CONFIG_SMMSTORE_REGION,
64 CONFIG_SMMSTORE_FILENAME, NULL) < 0) {
65 printk(BIOS_WARNING, "smm store: "
66 "Unable to find SMM store file in region '%s'\n",
67 CONFIG_SMMSTORE_REGION);
68 return -1;
69 }
Patrick Georgi9360fea2018-03-14 21:11:21 +010070
Arthur Heymans8b04dc72018-12-01 15:28:03 +010071 cbfs_file_data(rstore, &file);
72 } else {
73 if (fmap_locate_area_as_rdev_rw(CONFIG_SMMSTORE_REGION, rstore)) {
74 printk(BIOS_WARNING,
75 "smm store: Unable to find SMM store FMAP region '%s'\n",
76 CONFIG_SMMSTORE_REGION);
77 return -1;
78 }
79 }
Patrick Georgi9360fea2018-03-14 21:11:21 +010080
81 return 0;
82}
83
84/*
85 * Read entire store into user provided buffer
86 *
87 * returns 0 on success, -1 on failure
88 * writes up to `*bufsize` bytes into `buf` and updates `*bufsize`
89 */
90int smmstore_read_region(void *buf, ssize_t *bufsize)
91{
92 struct region_device store;
93
94 if (bufsize == NULL)
95 return -1;
96
97 *bufsize = 0;
98 if (lookup_store(&store) < 0) {
99 printk(BIOS_WARNING, "reading region failed\n");
100 return -1;
101 }
102
103 ssize_t tx = min(*bufsize, region_device_sz(&store));
104 *bufsize = rdev_readat(&store, buf, 0, tx);
105
106 if (*bufsize < 0)
107 return -1;
108
109 return 0;
110}
111
112/*
113 * Append data to region
114 *
115 * Returns 0 on success, -1 on failure
116 */
117int smmstore_append_data(void *key, uint32_t key_sz,
118 void *value, uint32_t value_sz)
119{
120 struct region_device store;
121
122 if (lookup_store(&store) < 0) {
123 printk(BIOS_WARNING, "reading region failed\n");
124 return -1;
125 }
126
127 ssize_t data_sz = region_device_sz(&store);
128
129 /* scan for end */
130 ssize_t end = 0;
131 uint32_t k_sz, v_sz;
132 while (end < data_sz) {
133 /* make odd corner cases identifiable, eg. invalid v_sz */
134 k_sz = 0;
135
136 if (rdev_readat(&store, &k_sz, end, sizeof(k_sz)) < 0) {
137 printk(BIOS_WARNING, "failed reading key size\n");
138 return -1;
139 }
140
141 /* found the end */
142 if (k_sz == 0xffffffff)
143 break;
144
145 /* something is fishy here:
146 * Avoid wrapping (since data_size < MAX_UINT32_T / 2) while
147 * other problems are covered by the loop condition
148 */
149 if (k_sz > data_sz) {
150 printk(BIOS_WARNING, "key size out of bounds\n");
151 return -1;
152 }
153
154 if (rdev_readat(&store, &v_sz, end + 4, sizeof(v_sz)) < 0) {
155 printk(BIOS_WARNING, "failed reading value size\n");
156 return -1;
157 }
158
159 if (v_sz > data_sz) {
160 printk(BIOS_WARNING, "value size out of bounds\n");
161 return -1;
162 }
163
164 end += 8 + k_sz + v_sz + 1;
165 end = ALIGN_UP(end, sizeof(uint32_t));
166 }
167
168 printk(BIOS_WARNING, "used smm store size might be 0x%zx bytes\n", end);
169
170 if (k_sz != 0xffffffff) {
171 printk(BIOS_WARNING,
172 "eof of data marker looks invalid: 0x%x\n", k_sz);
173 return -1;
174 }
175
176 printk(BIOS_WARNING, "used size looks legit\n");
177
178 printk(BIOS_WARNING, "open (%zx, %zx) for writing\n",
179 region_device_offset(&store), region_device_sz(&store));
180 if (boot_device_rw_subregion(&store.region, &store) < 0) {
181 printk(BIOS_WARNING, "couldn't open store for writing\n");
182 return -1;
183 }
184
185 uint32_t record_sz = 8 + key_sz + value_sz + 1;
186 if (end + record_sz >= data_sz) {
187 printk(BIOS_WARNING, "not enough space for new data\n");
188 return -1;
189 }
190
191 if (rdev_writeat(&store, &key_sz, end, 4) != 4) {
192 printk(BIOS_WARNING, "failed writing key size\n");
193 }
194 end += 4;
195 if (rdev_writeat(&store, &value_sz, end, 4) != 4) {
196 printk(BIOS_WARNING, "failed writing value size\n");
197 }
198 end += 4;
199 if (rdev_writeat(&store, key, end, key_sz) != key_sz) {
200 printk(BIOS_WARNING, "failed writing key data\n");
201 }
202 end += key_sz;
203 if (rdev_writeat(&store, value, end, value_sz) != value_sz) {
204 printk(BIOS_WARNING, "failed writing value data\n");
205 }
206 end += value_sz;
207 uint8_t nul = 0;
208 if (rdev_writeat(&store, &nul, end, 1) != 1) {
209 printk(BIOS_WARNING, "failed writing termination\n");
210 }
211
212 return 0;
213}
214
215/*
216 * Clear region
217 *
218 * Returns 0 on success, -1 on failure, including partial erase
219 */
220int smmstore_clear_region(void)
221{
222 struct region_device store;
223
224 if (lookup_store(&store) < 0) {
225 printk(BIOS_WARNING, "smm store: reading region failed\n");
226 return -1;
227 }
228
229 ssize_t res = rdev_eraseat(&store, 0, region_device_sz(&store));
230 if (res != region_device_sz(&store)) {
231 printk(BIOS_WARNING, "smm store: erasing region failed\n");
232 return -1;
233 }
234
235 return 0;
236}