xref: /openbmc/hiomapd/mtd/backend.c (revision 8cef63e3a3652b25f6a310800c1e0bf09aeed4c6)
1 // 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 
34 static 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 
105 out:
106 	return rc;
107 
108 cleanup_priv:
109 	free(priv);
110 	return rc;
111 }
112 
113 static 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 
124 int 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  */
138 static 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  */
158 static 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  */
185 static 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  */
257 static 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  */
297 static 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  */
336 static 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 
343 static 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,
352 	.align_offset = NULL,
353 };
354 
355 struct backend backend_get_mtd(void)
356 {
357 	struct backend be = {0};
358 
359 	be.ops = &mtd_ops;
360 
361 	return be;
362 }
363 
364 int 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 }
373