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