xref: /openbmc/phosphor-mboxd/mboxd_flash.c (revision 53c21aaa803e148c9c79cb3f5e0252d911506b10)
1*53c21aaaSAndrew Jeffery // SPDX-License-Identifier: Apache-2.0
2*53c21aaaSAndrew Jeffery // Copyright (C) 2018 IBM Corp.
3*53c21aaaSAndrew Jeffery 
4*53c21aaaSAndrew Jeffery #define _GNU_SOURCE
5*53c21aaaSAndrew Jeffery #include <assert.h>
6*53c21aaaSAndrew Jeffery #include <errno.h>
7*53c21aaaSAndrew Jeffery #include <fcntl.h>
8*53c21aaaSAndrew Jeffery #include <getopt.h>
9*53c21aaaSAndrew Jeffery #include <limits.h>
10*53c21aaaSAndrew Jeffery #include <poll.h>
11*53c21aaaSAndrew Jeffery #include <stdbool.h>
12*53c21aaaSAndrew Jeffery #include <stdint.h>
13*53c21aaaSAndrew Jeffery #include <stdio.h>
14*53c21aaaSAndrew Jeffery #include <stdlib.h>
15*53c21aaaSAndrew Jeffery #include <string.h>
16*53c21aaaSAndrew Jeffery #include <syslog.h>
17*53c21aaaSAndrew Jeffery #include <signal.h>
18*53c21aaaSAndrew Jeffery #include <sys/ioctl.h>
19*53c21aaaSAndrew Jeffery #include <sys/mman.h>
20*53c21aaaSAndrew Jeffery #include <sys/stat.h>
21*53c21aaaSAndrew Jeffery #include <sys/timerfd.h>
22*53c21aaaSAndrew Jeffery #include <sys/types.h>
23*53c21aaaSAndrew Jeffery #include <time.h>
24*53c21aaaSAndrew Jeffery #include <unistd.h>
25*53c21aaaSAndrew Jeffery #include <inttypes.h>
26*53c21aaaSAndrew Jeffery #include <errno.h>
27*53c21aaaSAndrew Jeffery #include <mtd/mtd-abi.h>
28*53c21aaaSAndrew Jeffery 
29*53c21aaaSAndrew Jeffery #include "mbox.h"
30*53c21aaaSAndrew Jeffery #include "common.h"
31*53c21aaaSAndrew Jeffery #include "mboxd_flash.h"
32*53c21aaaSAndrew Jeffery 
init_flash_dev(struct mbox_context * context)33*53c21aaaSAndrew Jeffery int init_flash_dev(struct mbox_context *context)
34*53c21aaaSAndrew Jeffery {
35*53c21aaaSAndrew Jeffery 	char *filename = get_dev_mtd();
36*53c21aaaSAndrew Jeffery 	int fd, rc = 0;
37*53c21aaaSAndrew Jeffery 
38*53c21aaaSAndrew Jeffery 	if (!filename) {
39*53c21aaaSAndrew Jeffery 		MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n");
40*53c21aaaSAndrew Jeffery 		return -1;
41*53c21aaaSAndrew Jeffery 	}
42*53c21aaaSAndrew Jeffery 
43*53c21aaaSAndrew Jeffery 	MSG_DBG("Opening %s\n", filename);
44*53c21aaaSAndrew Jeffery 
45*53c21aaaSAndrew Jeffery 	/* Open Flash Device */
46*53c21aaaSAndrew Jeffery 	fd = open(filename, O_RDWR);
47*53c21aaaSAndrew Jeffery 	if (fd < 0) {
48*53c21aaaSAndrew Jeffery 		MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
49*53c21aaaSAndrew Jeffery 			filename, strerror(errno));
50*53c21aaaSAndrew Jeffery 		rc = -errno;
51*53c21aaaSAndrew Jeffery 		goto out;
52*53c21aaaSAndrew Jeffery 	}
53*53c21aaaSAndrew Jeffery 	context->fds[MTD_FD].fd = fd;
54*53c21aaaSAndrew Jeffery 
55*53c21aaaSAndrew Jeffery 	/* Read the Flash Info */
56*53c21aaaSAndrew Jeffery 	if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) {
57*53c21aaaSAndrew Jeffery 		MSG_ERR("Couldn't get information about MTD: %s\n",
58*53c21aaaSAndrew Jeffery 			strerror(errno));
59*53c21aaaSAndrew Jeffery 		rc = -1;
60*53c21aaaSAndrew Jeffery 		goto out;
61*53c21aaaSAndrew Jeffery 	}
62*53c21aaaSAndrew Jeffery 
63*53c21aaaSAndrew Jeffery 	if (context->flash_size == 0) {
64*53c21aaaSAndrew Jeffery 		/*
65*53c21aaaSAndrew Jeffery 		 * PNOR images for current OpenPOWER systems are at most 64MB
66*53c21aaaSAndrew Jeffery 		 * despite the PNOR itself sometimes being as big as 128MB. To
67*53c21aaaSAndrew Jeffery 		 * ensure the image read from the PNOR is exposed in the LPC
68*53c21aaaSAndrew Jeffery 		 * address space at the location expected by the host firmware,
69*53c21aaaSAndrew Jeffery 		 * it is required that the image size be used for
70*53c21aaaSAndrew Jeffery 		 * context->flash_size, and not the size of the flash device.
71*53c21aaaSAndrew Jeffery 		 *
72*53c21aaaSAndrew Jeffery 		 * However, the test cases specify the flash size via special
73*53c21aaaSAndrew Jeffery 		 * test APIs (controlling flash behaviour) which don't have
74*53c21aaaSAndrew Jeffery 		 * access to the mbox context. Rather than requiring
75*53c21aaaSAndrew Jeffery 		 * error-prone assignments in every test case, we instead rely
76*53c21aaaSAndrew Jeffery 		 * on context->flash_size being set to the size reported by the
77*53c21aaaSAndrew Jeffery 		 * MEMINFO ioctl().
78*53c21aaaSAndrew Jeffery 		 *
79*53c21aaaSAndrew Jeffery 		 * As this case should never be hit in production (i.e. outside
80*53c21aaaSAndrew Jeffery 		 * the test environment), log an error. As a consequence, this
81*53c21aaaSAndrew Jeffery 		 * error is expected in the test case output.
82*53c21aaaSAndrew Jeffery 		 */
83*53c21aaaSAndrew Jeffery 		MSG_ERR("Flash size MUST be supplied on the commandline. However, continuing by assuming flash is %u bytes\n",
84*53c21aaaSAndrew Jeffery 				context->mtd_info.size);
85*53c21aaaSAndrew Jeffery 		context->flash_size = context->mtd_info.size;
86*53c21aaaSAndrew Jeffery 	}
87*53c21aaaSAndrew Jeffery 
88*53c21aaaSAndrew Jeffery 	/* We know the erase size so we can allocate the flash_erased bytemap */
89*53c21aaaSAndrew Jeffery 	context->erase_size_shift = log_2(context->mtd_info.erasesize);
90*53c21aaaSAndrew Jeffery 	context->flash_bmap = calloc(context->flash_size >>
91*53c21aaaSAndrew Jeffery 				     context->erase_size_shift,
92*53c21aaaSAndrew Jeffery 				     sizeof(*context->flash_bmap));
93*53c21aaaSAndrew Jeffery 	MSG_DBG("Flash erase size: 0x%.8x\n", context->mtd_info.erasesize);
94*53c21aaaSAndrew Jeffery 
95*53c21aaaSAndrew Jeffery out:
96*53c21aaaSAndrew Jeffery 	free(filename);
97*53c21aaaSAndrew Jeffery 	return rc;
98*53c21aaaSAndrew Jeffery }
99*53c21aaaSAndrew Jeffery 
free_flash_dev(struct mbox_context * context)100*53c21aaaSAndrew Jeffery void free_flash_dev(struct mbox_context *context)
101*53c21aaaSAndrew Jeffery {
102*53c21aaaSAndrew Jeffery 	free(context->flash_bmap);
103*53c21aaaSAndrew Jeffery 	close(context->fds[MTD_FD].fd);
104*53c21aaaSAndrew Jeffery }
105*53c21aaaSAndrew Jeffery 
106*53c21aaaSAndrew Jeffery /* Flash Functions */
107*53c21aaaSAndrew Jeffery 
108*53c21aaaSAndrew Jeffery /*
109*53c21aaaSAndrew Jeffery  * flash_is_erased() - Check if an offset into flash is erased
110*53c21aaaSAndrew Jeffery  * @context:	The mbox context pointer
111*53c21aaaSAndrew Jeffery  * @offset:	The flash offset to check (bytes)
112*53c21aaaSAndrew Jeffery  *
113*53c21aaaSAndrew Jeffery  * Return:	true if erased otherwise false
114*53c21aaaSAndrew Jeffery  */
flash_is_erased(struct mbox_context * context,uint32_t offset)115*53c21aaaSAndrew Jeffery static inline bool flash_is_erased(struct mbox_context *context,
116*53c21aaaSAndrew Jeffery 				   uint32_t offset)
117*53c21aaaSAndrew Jeffery {
118*53c21aaaSAndrew Jeffery 	return context->flash_bmap[offset >> context->erase_size_shift]
119*53c21aaaSAndrew Jeffery 			== FLASH_ERASED;
120*53c21aaaSAndrew Jeffery }
121*53c21aaaSAndrew Jeffery 
122*53c21aaaSAndrew Jeffery /*
123*53c21aaaSAndrew Jeffery  * set_flash_bytemap() - Set the flash erased bytemap
124*53c21aaaSAndrew Jeffery  * @context:	The mbox context pointer
125*53c21aaaSAndrew Jeffery  * @offset:	The flash offset to set (bytes)
126*53c21aaaSAndrew Jeffery  * @count:	Number of bytes to set
127*53c21aaaSAndrew Jeffery  * @val:	Value to set the bytemap to
128*53c21aaaSAndrew Jeffery  *
129*53c21aaaSAndrew Jeffery  * The flash bytemap only tracks the erased status at the erase block level so
130*53c21aaaSAndrew Jeffery  * this will update the erased state for an (or many) erase blocks
131*53c21aaaSAndrew Jeffery  *
132*53c21aaaSAndrew Jeffery  * Return:	0 if success otherwise negative error code
133*53c21aaaSAndrew Jeffery  */
set_flash_bytemap(struct mbox_context * context,uint32_t offset,uint32_t count,uint8_t val)134*53c21aaaSAndrew Jeffery int set_flash_bytemap(struct mbox_context *context, uint32_t offset,
135*53c21aaaSAndrew Jeffery 		      uint32_t count, uint8_t val)
136*53c21aaaSAndrew Jeffery {
137*53c21aaaSAndrew Jeffery 	if ((offset + count) > context->flash_size) {
138*53c21aaaSAndrew Jeffery 		return -MBOX_R_PARAM_ERROR;
139*53c21aaaSAndrew Jeffery 	}
140*53c21aaaSAndrew Jeffery 
141*53c21aaaSAndrew Jeffery 	MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n",
142*53c21aaaSAndrew Jeffery 		offset, count, val ? "ERASED" : "DIRTY");
143*53c21aaaSAndrew Jeffery 	memset(context->flash_bmap + (offset >> context->erase_size_shift),
144*53c21aaaSAndrew Jeffery 	       val,
145*53c21aaaSAndrew Jeffery 	       align_up(count, 1 << context->erase_size_shift) >>
146*53c21aaaSAndrew Jeffery 	       context->erase_size_shift);
147*53c21aaaSAndrew Jeffery 
148*53c21aaaSAndrew Jeffery 	return 0;
149*53c21aaaSAndrew Jeffery }
150*53c21aaaSAndrew Jeffery 
151*53c21aaaSAndrew Jeffery /*
152*53c21aaaSAndrew Jeffery  * erase_flash() - Erase the flash
153*53c21aaaSAndrew Jeffery  * @context:	The mbox context pointer
154*53c21aaaSAndrew Jeffery  * @offset:	The flash offset to erase (bytes)
155*53c21aaaSAndrew Jeffery  * @size:	The number of bytes to erase
156*53c21aaaSAndrew Jeffery  *
157*53c21aaaSAndrew Jeffery  * Return:	0 on success otherwise negative error code
158*53c21aaaSAndrew Jeffery  */
erase_flash(struct mbox_context * context,uint32_t offset,uint32_t count)159*53c21aaaSAndrew Jeffery int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count)
160*53c21aaaSAndrew Jeffery {
161*53c21aaaSAndrew Jeffery 	const uint32_t erase_size = 1 << context->erase_size_shift;
162*53c21aaaSAndrew Jeffery 	struct erase_info_user erase_info = { 0 };
163*53c21aaaSAndrew Jeffery 	int rc;
164*53c21aaaSAndrew Jeffery 
165*53c21aaaSAndrew Jeffery 	MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count);
166*53c21aaaSAndrew Jeffery 
167*53c21aaaSAndrew Jeffery 	/*
168*53c21aaaSAndrew Jeffery 	 * We have an erased_bytemap for the flash so we want to avoid erasing
169*53c21aaaSAndrew Jeffery 	 * blocks which we already know to be erased. Look for runs of blocks
170*53c21aaaSAndrew Jeffery 	 * which aren't erased and erase the entire run at once to avoid how
171*53c21aaaSAndrew Jeffery 	 * often we have to call the erase ioctl. If the block is already
172*53c21aaaSAndrew Jeffery 	 * erased then there's nothing we need to do.
173*53c21aaaSAndrew Jeffery 	 */
174*53c21aaaSAndrew Jeffery 	while (count) {
175*53c21aaaSAndrew Jeffery 		if (!flash_is_erased(context, offset)) { /* Need to erase */
176*53c21aaaSAndrew Jeffery 			if (!erase_info.length) { /* Start of not-erased run */
177*53c21aaaSAndrew Jeffery 				erase_info.start = offset;
178*53c21aaaSAndrew Jeffery 			}
179*53c21aaaSAndrew Jeffery 			erase_info.length += erase_size;
180*53c21aaaSAndrew Jeffery 		} else if (erase_info.length) { /* Already erased|end of run? */
181*53c21aaaSAndrew Jeffery 			/* Erase the previous run which just ended */
182*53c21aaaSAndrew Jeffery 			MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n",
183*53c21aaaSAndrew Jeffery 				erase_info.start, erase_info.length);
184*53c21aaaSAndrew Jeffery 			rc = ioctl(context->fds[MTD_FD].fd, MEMERASE,
185*53c21aaaSAndrew Jeffery 				   &erase_info);
186*53c21aaaSAndrew Jeffery 			if (rc < 0) {
187*53c21aaaSAndrew Jeffery 				MSG_ERR("Couldn't erase flash at 0x%.8x\n",
188*53c21aaaSAndrew Jeffery 						erase_info.start);
189*53c21aaaSAndrew Jeffery 				return -MBOX_R_SYSTEM_ERROR;
190*53c21aaaSAndrew Jeffery 			}
191*53c21aaaSAndrew Jeffery 			/* Mark ERASED where we just erased */
192*53c21aaaSAndrew Jeffery 			set_flash_bytemap(context, erase_info.start,
193*53c21aaaSAndrew Jeffery 					  erase_info.length, FLASH_ERASED);
194*53c21aaaSAndrew Jeffery 			erase_info.start = 0;
195*53c21aaaSAndrew Jeffery 			erase_info.length = 0;
196*53c21aaaSAndrew Jeffery 		}
197*53c21aaaSAndrew Jeffery 
198*53c21aaaSAndrew Jeffery 		offset += erase_size;
199*53c21aaaSAndrew Jeffery 		count -= erase_size;
200*53c21aaaSAndrew Jeffery 	}
201*53c21aaaSAndrew Jeffery 
202*53c21aaaSAndrew Jeffery 	if (erase_info.length) {
203*53c21aaaSAndrew Jeffery 		MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n",
204*53c21aaaSAndrew Jeffery 			erase_info.start, erase_info.length);
205*53c21aaaSAndrew Jeffery 		rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info);
206*53c21aaaSAndrew Jeffery 		if (rc < 0) {
207*53c21aaaSAndrew Jeffery 			MSG_ERR("Couldn't erase flash at 0x%.8x\n",
208*53c21aaaSAndrew Jeffery 					erase_info.start);
209*53c21aaaSAndrew Jeffery 			return -MBOX_R_SYSTEM_ERROR;
210*53c21aaaSAndrew Jeffery 		}
211*53c21aaaSAndrew Jeffery 		/* Mark ERASED where we just erased */
212*53c21aaaSAndrew Jeffery 		set_flash_bytemap(context, erase_info.start, erase_info.length,
213*53c21aaaSAndrew Jeffery 				  FLASH_ERASED);
214*53c21aaaSAndrew Jeffery 	}
215*53c21aaaSAndrew Jeffery 
216*53c21aaaSAndrew Jeffery 	return 0;
217*53c21aaaSAndrew Jeffery }
218*53c21aaaSAndrew Jeffery 
219*53c21aaaSAndrew Jeffery #define CHUNKSIZE (64 * 1024)
220*53c21aaaSAndrew Jeffery 
221*53c21aaaSAndrew Jeffery /*
222*53c21aaaSAndrew Jeffery  * copy_flash() - Copy data from the flash device into a provided buffer
223*53c21aaaSAndrew Jeffery  * @context:	The mbox context pointer
224*53c21aaaSAndrew Jeffery  * @offset:	The flash offset to copy from (bytes)
225*53c21aaaSAndrew Jeffery  * @mem:	The buffer to copy into (must be of atleast 'size' bytes)
226*53c21aaaSAndrew Jeffery  * @size:	The number of bytes to copy
227*53c21aaaSAndrew Jeffery  * Return:	Number of bytes copied on success, otherwise negative error
228*53c21aaaSAndrew Jeffery  *		code. copy_flash will copy at most 'size' bytes, but it may
229*53c21aaaSAndrew Jeffery  *		copy less.
230*53c21aaaSAndrew Jeffery  */
copy_flash(struct mbox_context * context,uint32_t offset,void * mem,uint32_t size)231*53c21aaaSAndrew Jeffery int64_t copy_flash(struct mbox_context *context, uint32_t offset, void *mem,
232*53c21aaaSAndrew Jeffery 		   uint32_t size)
233*53c21aaaSAndrew Jeffery {
234*53c21aaaSAndrew Jeffery 	int32_t size_read;
235*53c21aaaSAndrew Jeffery 	void *start = mem;
236*53c21aaaSAndrew Jeffery 
237*53c21aaaSAndrew Jeffery 	MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n",
238*53c21aaaSAndrew Jeffery 		mem, size, offset);
239*53c21aaaSAndrew Jeffery 	if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) {
240*53c21aaaSAndrew Jeffery 		MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
241*53c21aaaSAndrew Jeffery 			strerror(errno));
242*53c21aaaSAndrew Jeffery 		return -MBOX_R_SYSTEM_ERROR;
243*53c21aaaSAndrew Jeffery 	}
244*53c21aaaSAndrew Jeffery 
245*53c21aaaSAndrew Jeffery 	do {
246*53c21aaaSAndrew Jeffery 		size_read = read(context->fds[MTD_FD].fd, mem,
247*53c21aaaSAndrew Jeffery 					  min_u32(CHUNKSIZE, size));
248*53c21aaaSAndrew Jeffery 		if (size_read < 0) {
249*53c21aaaSAndrew Jeffery 			MSG_ERR("Couldn't copy mtd into ram: %s\n",
250*53c21aaaSAndrew Jeffery 				strerror(errno));
251*53c21aaaSAndrew Jeffery 			return -MBOX_R_SYSTEM_ERROR;
252*53c21aaaSAndrew Jeffery 		}
253*53c21aaaSAndrew Jeffery 
254*53c21aaaSAndrew Jeffery 		size -= size_read;
255*53c21aaaSAndrew Jeffery 		mem += size_read;
256*53c21aaaSAndrew Jeffery 	} while (size && size_read);
257*53c21aaaSAndrew Jeffery 
258*53c21aaaSAndrew Jeffery 	return size_read ? mem - start : -MBOX_R_SYSTEM_ERROR;
259*53c21aaaSAndrew Jeffery }
260*53c21aaaSAndrew Jeffery 
261*53c21aaaSAndrew Jeffery /*
262*53c21aaaSAndrew Jeffery  * write_flash() - Write the flash from a provided buffer
263*53c21aaaSAndrew Jeffery  * @context:	The mbox context pointer
264*53c21aaaSAndrew Jeffery  * @offset:	The flash offset to write to (bytes)
265*53c21aaaSAndrew Jeffery  * @buf:	The buffer to write from (must be of atleast size)
266*53c21aaaSAndrew Jeffery  * @size:	The number of bytes to write
267*53c21aaaSAndrew Jeffery  *
268*53c21aaaSAndrew Jeffery  * Return:	0 on success otherwise negative error code
269*53c21aaaSAndrew Jeffery  */
write_flash(struct mbox_context * context,uint32_t offset,void * buf,uint32_t count)270*53c21aaaSAndrew Jeffery int write_flash(struct mbox_context *context, uint32_t offset, void *buf,
271*53c21aaaSAndrew Jeffery 		uint32_t count)
272*53c21aaaSAndrew Jeffery {
273*53c21aaaSAndrew Jeffery 	uint32_t buf_offset = 0;
274*53c21aaaSAndrew Jeffery 	int rc;
275*53c21aaaSAndrew Jeffery 
276*53c21aaaSAndrew Jeffery 	MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, buf);
277*53c21aaaSAndrew Jeffery 
278*53c21aaaSAndrew Jeffery 	if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) {
279*53c21aaaSAndrew Jeffery 		MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
280*53c21aaaSAndrew Jeffery 			strerror(errno));
281*53c21aaaSAndrew Jeffery 		return -MBOX_R_SYSTEM_ERROR;
282*53c21aaaSAndrew Jeffery 	}
283*53c21aaaSAndrew Jeffery 
284*53c21aaaSAndrew Jeffery 	while (count) {
285*53c21aaaSAndrew Jeffery 		rc = write(context->fds[MTD_FD].fd, buf + buf_offset, count);
286*53c21aaaSAndrew Jeffery 		if (rc < 0) {
287*53c21aaaSAndrew Jeffery 			MSG_ERR("Couldn't write to flash, write lost: %s\n",
288*53c21aaaSAndrew Jeffery 				strerror(errno));
289*53c21aaaSAndrew Jeffery 			return -MBOX_R_WRITE_ERROR;
290*53c21aaaSAndrew Jeffery 		}
291*53c21aaaSAndrew Jeffery 		/* Mark *NOT* erased where we just wrote */
292*53c21aaaSAndrew Jeffery 		set_flash_bytemap(context, offset + buf_offset, rc,
293*53c21aaaSAndrew Jeffery 				  FLASH_DIRTY);
294*53c21aaaSAndrew Jeffery 		count -= rc;
295*53c21aaaSAndrew Jeffery 		buf_offset += rc;
296*53c21aaaSAndrew Jeffery 	}
297*53c21aaaSAndrew Jeffery 
298*53c21aaaSAndrew Jeffery 	return 0;
299*53c21aaaSAndrew Jeffery }
300