blob: 921c041eeaf16b750e8f12e2cf59756bdf5a20da [file] [log] [blame]
Evan Lojewskif1e547c2019-03-14 14:34:33 +10301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
3
4#define _GNU_SOURCE
5#include <assert.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <getopt.h>
9#include <inttypes.h>
10#include <limits.h>
11#include <mtd/mtd-abi.h>
12#include <poll.h>
13#include <signal.h>
14#include <stdbool.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <sys/ioctl.h>
20#include <sys/mman.h>
21#include <sys/stat.h>
22#include <sys/timerfd.h>
23#include <sys/types.h>
24#include <syslog.h>
25#include <time.h>
26#include <unistd.h>
27
28#include "common.h"
29#include "backend.h"
30#include "lpc.h"
31#include "mboxd.h"
32#include "mtd/backend.h"
33
34static int mtd_dev_init(struct backend *backend, void *data)
35{
36 const char *path = data;
37 struct mtd_data *priv;
38 int rc = 0;
39
40 if (!path) {
41 MSG_INFO("Discovering PNOR MTD\n");
42 path = get_dev_mtd();
43 }
44
45 priv = malloc(sizeof(*priv));
46 if (!priv) {
47 rc = -errno;
48 goto out;
49 }
50
51 MSG_DBG("Opening %s\n", path);
52
53 priv->fd = open(path, O_RDWR);
54 if (priv->fd < 0) {
55 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", path,
56 strerror(errno));
57 rc = -errno;
58 goto cleanup_priv;
59 }
60
61 /* If the file does not support MEMGETINFO it's not an mtd device */
62 if (ioctl(priv->fd, MEMGETINFO, &priv->mtd_info) == -1) {
63 rc = -errno;
64 close(priv->fd);
65 goto cleanup_priv;
66 }
67
68 if (backend->flash_size == 0) {
69 /*
70 * PNOR images for current OpenPOWER systems are at most 64MB
71 * despite the PNOR itself sometimes being as big as 128MB. To
72 * ensure the image read from the PNOR is exposed in the LPC
73 * address space at the location expected by the host firmware,
74 * it is required that the image size be used for
75 * context->flash_size, and not the size of the flash device.
76 *
77 * However, the test cases specify the flash size via special
78 * test APIs (controlling flash behaviour) which don't have
79 * access to the mbox context. Rather than requiring
80 * error-prone assignments in every test case, we instead rely
81 * on context->flash_size being set to the size reported by the
82 * MEMINFO ioctl().
83 *
84 * As this case should never be hit in production (i.e. outside
85 * the test environment), log an error. As a consequence, this
86 * error is expected in the test case output.
87 */
88 MSG_ERR(
89 "Flash size MUST be supplied on the commandline. However, "
90 "continuing by assuming flash is %u bytes\n",
91 priv->mtd_info.size);
92 backend->flash_size = priv->mtd_info.size;
93 }
94
95 /* We know the erase size so we can allocate the flash_erased bytemap */
96 backend->erase_size_shift = log_2(priv->mtd_info.erasesize);
97 backend->block_size_shift = backend->erase_size_shift;
98 priv->flash_bmap = calloc(backend->flash_size
99 >> backend->erase_size_shift,
100 sizeof(*priv->flash_bmap));
101 MSG_DBG("Flash erase size: 0x%.8x\n", priv->mtd_info.erasesize);
102
103 backend->priv = priv;
104
105out:
106 return rc;
107
108cleanup_priv:
109 free(priv);
110 return rc;
111}
112
113static void mtd_dev_free(struct backend *backend)
114{
115 struct mtd_data *priv = backend->priv;
116
117 free(priv->flash_bmap);
118 close(priv->fd);
119 free(priv);
120}
121
122/* Flash Functions */
123
124int flash_validate(struct mbox_context *context, uint32_t offset,
125 uint32_t size, bool ro)
126{
127 /* Default behaviour is all accesses are valid */
128 return 0;
129}
130
131/*
132 * mtd_is_erased() - Check if an offset into flash is erased
133 * @context: The mbox context pointer
134 * @offset: The flash offset to check (bytes)
135 *
136 * Return: true if erased otherwise false
137 */
138static inline bool mtd_is_erased(struct backend *backend, uint32_t offset)
139{
140 const off_t index = offset >> backend->erase_size_shift;
141 struct mtd_data *priv = backend->priv;
142
143 return priv->flash_bmap[index] == FLASH_ERASED;
144}
145
146/*
147 * mtd_set_bytemap() - Set the flash erased bytemap
148 * @context: The backend context pointer
149 * @offset: The flash offset to set (bytes)
150 * @count: Number of bytes to set
151 * @val: Value to set the bytemap to
152 *
153 * The flash bytemap only tracks the erased status at the erase block level so
154 * this will update the erased state for an (or many) erase blocks
155 *
156 * Return: 0 if success otherwise negative error code
157 */
158static int mtd_set_bytemap(struct backend *backend, uint32_t offset,
159 uint32_t count, uint8_t val)
160{
161 struct mtd_data *priv = backend->priv;
162
163 if ((offset + count) > backend->flash_size) {
164 return -EINVAL;
165 }
166
167 MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n", offset, count,
168 val ? "ERASED" : "DIRTY");
169 memset(priv->flash_bmap + (offset >> backend->erase_size_shift),
170 val,
171 align_up(count, 1 << backend->erase_size_shift) >>
172 backend->erase_size_shift);
173
174 return 0;
175}
176
177/*
178 * mtd_erase() - Erase the flash
179 * @context: The mbox context pointer
180 * @offset: The flash offset to erase (bytes)
181 * @size: The number of bytes to erase
182 *
183 * Return: 0 on success otherwise negative error code
184 */
185static int mtd_erase(struct backend *backend, uint32_t offset, uint32_t count)
186{
187 const uint32_t erase_size = 1 << backend->erase_size_shift;
188 struct mtd_data *priv = backend->priv;
189 struct erase_info_user erase_info = {0};
190 int rc;
191
192 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count);
193
194 /*
195 * We have an erased_bytemap for the flash so we want to avoid erasing
196 * blocks which we already know to be erased. Look for runs of blocks
197 * which aren't erased and erase the entire run at once to avoid how
198 * often we have to call the erase ioctl. If the block is already
199 * erased then there's nothing we need to do.
200 */
201 while (count) {
202 if (!mtd_is_erased(backend, offset)) { /* Need to erase */
203 if (!erase_info.length) { /* Start of not-erased run */
204 erase_info.start = offset;
205 }
206 erase_info.length += erase_size;
207 } else if (erase_info.length) { /* Already erased|end of run? */
208 /* Erase the previous run which just ended */
209 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n",
210 erase_info.start, erase_info.length);
211 rc = ioctl(priv->fd, MEMERASE, &erase_info);
212 if (rc < 0) {
213 MSG_ERR("Couldn't erase flash at 0x%.8x\n",
214 erase_info.start);
215 return -errno;
216 }
217 /* Mark ERASED where we just erased */
218 mtd_set_bytemap(backend, erase_info.start,
219 erase_info.length, FLASH_ERASED);
220 erase_info.start = 0;
221 erase_info.length = 0;
222 }
223
224 offset += erase_size;
225 count -= erase_size;
226 }
227
228 if (erase_info.length) {
229 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", erase_info.start,
230 erase_info.length);
231 rc = ioctl(priv->fd, MEMERASE, &erase_info);
232 if (rc < 0) {
233 MSG_ERR("Couldn't erase flash at 0x%.8x\n",
234 erase_info.start);
235 return -errno;
236 }
237 /* Mark ERASED where we just erased */
238 mtd_set_bytemap(backend, erase_info.start, erase_info.length,
239 FLASH_ERASED);
240 }
241
242 return 0;
243}
244
245#define CHUNKSIZE (64 * 1024)
246
247/*
248 * mtd_copy() - Copy data from the flash device into a provided buffer
249 * @context: The backend context pointer
250 * @offset: The flash offset to copy from (bytes)
251 * @mem: The buffer to copy into (must be of atleast 'size' bytes)
252 * @size: The number of bytes to copy
253 * Return: Number of bytes copied on success, otherwise negative error
254 * code. mtd_copy will copy at most 'size' bytes, but it may
255 * copy less.
256 */
257static int64_t mtd_copy(struct backend *backend, uint32_t offset,
258 void *mem, uint32_t size)
259{
260 struct mtd_data *priv = backend->priv;
261 int32_t size_read;
262 void *start = mem;
263
264 MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n", mem,
265 size, offset);
266 if (lseek(priv->fd, offset, SEEK_SET) != offset) {
267 MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
268 strerror(errno));
269 return -errno;
270 }
271
272 do {
273 size_read = read(priv->fd, mem,
274 min_u32(CHUNKSIZE, size));
275 if (size_read < 0) {
276 MSG_ERR("Couldn't copy mtd into ram: %s\n",
277 strerror(errno));
278 return -errno;
279 }
280
281 size -= size_read;
282 mem += size_read;
283 } while (size && size_read);
284
285 return size_read ? mem - start : -EIO;
286}
287
288/*
289 * mtd_write() - Write the flash from a provided buffer
290 * @context: The mbox context pointer
291 * @offset: The flash offset to write to (bytes)
292 * @buf: The buffer to write from (must be of atleast size)
293 * @size: The number of bytes to write
294 *
295 * Return: 0 on success otherwise negative error code
296 */
297static int mtd_write(struct backend *backend, uint32_t offset, void *buf,
298 uint32_t count)
299{
300 struct mtd_data *priv = backend->priv;
301 uint32_t buf_offset = 0;
302 int rc;
303
304 MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count,
305 buf);
306
307 if (lseek(priv->fd, offset, SEEK_SET) != offset) {
308 MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
309 strerror(errno));
310 return -errno;
311 }
312
313 while (count) {
314 rc = write(priv->fd, buf + buf_offset, count);
315 if (rc < 0) {
316 MSG_ERR("Couldn't write to flash, write lost: %s\n",
317 strerror(errno));
318 return -errno;
319 }
320 /* Mark *NOT* erased where we just wrote */
321 mtd_set_bytemap(backend, offset + buf_offset, rc, FLASH_DIRTY);
322 count -= rc;
323 buf_offset += rc;
324 }
325
326 return 0;
327}
328
329/*
330 * mtd_reset() - Reset the lpc bus mapping
331 * @context: The mbox context pointer
332 *
333 * Return: A value from enum backend_reset_mode, otherwise a negative
334 * error code
335 */
336static int mtd_reset(struct backend *backend,
337 void *buf __attribute__((unused)),
338 uint32_t count __attribute__((unused)))
339{
340 return reset_lpc_flash;
341}
342
343static const struct backend_ops mtd_ops = {
344 .init = mtd_dev_init,
345 .free = mtd_dev_free,
346 .copy = mtd_copy,
347 .set_bytemap = mtd_set_bytemap,
348 .erase = mtd_erase,
349 .write = mtd_write,
350 .validate = NULL,
351 .reset = mtd_reset,
Alvin Wang8cef63e2019-10-15 23:23:38 +0800352 .align_offset = NULL,
Evan Lojewskif1e547c2019-03-14 14:34:33 +1030353};
354
355struct backend backend_get_mtd(void)
356{
357 struct backend be = {0};
358
359 be.ops = &mtd_ops;
360
361 return be;
362}
363
364int backend_probe_mtd(struct backend *master, const char *path)
365{
366 struct backend with;
367
368 assert(master);
369 with = backend_get_mtd();
370
371 return backend_init(master, &with, (void *)path);
372}