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