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