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