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