blob: 1e1b7a02a86043b0ab6e923f9dcaf7b872c0e571 [file] [log] [blame]
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +11001/*
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
48int 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +100058 MSG_DBG("Opening %s\n", filename);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +110059
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
Andrew Jeffery33640b32017-04-12 20:56:11 +093078 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
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100103 /* 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));
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000108 MSG_DBG("Flash erase size: 0x%.8x\n", context->mtd_info.erasesize);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100109
110out:
111 free(filename);
112 return rc;
113}
114
115void 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 */
134int copy_flash(struct mbox_context *context, uint32_t offset, void *mem,
135 uint32_t size)
136{
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930137 int32_t size_read;
138
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000139 MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n",
140 mem, size, offset);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100141 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
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930147 do {
148 size_read = read(context->fds[MTD_FD].fd, mem,
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100149 min_u32(CHUNKSIZE, size));
150 if (size_read < 0) {
Andrew Jeffery4f88bfb2017-04-24 12:34:15 +0930151 MSG_ERR("Couldn't copy mtd into ram: %s\n",
152 strerror(errno));
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100153 return -MBOX_R_SYSTEM_ERROR;
154 }
155
156 size -= size_read;
157 mem += size_read;
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930158 } while (size && size_read);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100159
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930160 return size ? -MBOX_R_SYSTEM_ERROR : 0;
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100161}
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 */
170static 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 */
189int 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000196 MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n",
197 offset, count, val ? "ERASED" : "DIRTY");
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100198 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 */
214int 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000220 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100221
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 */
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000237 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n",
238 erase_info.start, erase_info.length);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100239 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) {
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000258 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n",
259 erase_info.start, erase_info.length);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100260 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 */
283int 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000289 MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, buf);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100290
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}