blob: e8b147d9e71ec4f74c9dfc8d2e62a5ba720ea282 [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
58 MSG_OUT("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 /* We know the erase size so we can allocate the flash_erased bytemap */
79 context->erase_size_shift = log_2(context->mtd_info.erasesize);
80 context->flash_bmap = calloc(context->flash_size >>
81 context->erase_size_shift,
82 sizeof(*context->flash_bmap));
83
84out:
85 free(filename);
86 return rc;
87}
88
89void free_flash_dev(struct mbox_context *context)
90{
91 free(context->flash_bmap);
92 close(context->fds[MTD_FD].fd);
93}
94
95/* Flash Functions */
96
97#define CHUNKSIZE (64 * 1024)
98
99/*
100 * copy_flash() - Copy data from the flash device into a provided buffer
101 * @context: The mbox context pointer
102 * @offset: The flash offset to copy from (bytes)
103 * @mem: The buffer to copy into (must be of atleast size)
104 * @size: The number of bytes to copy
105 *
106 * Return: 0 on success otherwise negative error code
107 */
108int copy_flash(struct mbox_context *context, uint32_t offset, void *mem,
109 uint32_t size)
110{
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930111 int32_t size_read;
112
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100113 MSG_OUT("Loading flash at %p for 0x%08x bytes from offset 0x%.8x\n",
114 mem, size, offset);
115 if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) {
116 MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
117 strerror(errno));
118 return -MBOX_R_SYSTEM_ERROR;
119 }
120
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930121 do {
122 size_read = read(context->fds[MTD_FD].fd, mem,
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100123 min_u32(CHUNKSIZE, size));
124 if (size_read < 0) {
125 MSG_ERR("Couldn't copy mtd into ram: %d. %s\n",
126 size_read, strerror(size_read));
127 return -MBOX_R_SYSTEM_ERROR;
128 }
129
130 size -= size_read;
131 mem += size_read;
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930132 } while (size && size_read);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100133
Andrew Jeffery9aa5ff72017-04-12 00:16:56 +0930134 return size ? -MBOX_R_SYSTEM_ERROR : 0;
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100135}
136
137/*
138 * flash_is_erased() - Check if an offset into flash is erased
139 * @context: The mbox context pointer
140 * @offset: The flash offset to check (bytes)
141 *
142 * Return: true if erased otherwise false
143 */
144static inline bool flash_is_erased(struct mbox_context *context,
145 uint32_t offset)
146{
147 return context->flash_bmap[offset >> context->erase_size_shift]
148 == FLASH_ERASED;
149}
150
151/*
152 * set_flash_bytemap() - Set the flash erased bytemap
153 * @context: The mbox context pointer
154 * @offset: The flash offset to set (bytes)
155 * @count: Number of bytes to set
156 * @val: Value to set the bytemap to
157 *
158 * The flash bytemap only tracks the erased status at the erase block level so
159 * this will update the erased state for an (or many) erase blocks
160 *
161 * Return: 0 if success otherwise negative error code
162 */
163int set_flash_bytemap(struct mbox_context *context, uint32_t offset,
164 uint32_t count, uint8_t val)
165{
166 if ((offset + count) > context->flash_size) {
167 return -MBOX_R_PARAM_ERROR;
168 }
169
170 memset(context->flash_bmap + (offset >> context->erase_size_shift),
171 val,
172 align_up(count, 1 << context->erase_size_shift) >>
173 context->erase_size_shift);
174
175 return 0;
176}
177
178/*
179 * erase_flash() - Erase the flash
180 * @context: The mbox context pointer
181 * @offset: The flash offset to erase (bytes)
182 * @size: The number of bytes to erase
183 *
184 * Return: 0 on success otherwise negative error code
185 */
186int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count)
187{
188 const uint32_t erase_size = 1 << context->erase_size_shift;
189 struct erase_info_user erase_info = { 0 };
190 int rc;
191
192 MSG_OUT("Erasing 0x%.8x for 0x%.8x\n", offset, count);
193
194 /*
195 * We have an erased_bytemap for the flash so we want to avoid erasing
196 * blocks which we already know to be erased. Look for runs of blocks
197 * which aren't erased and erase the entire run at once to avoid how
198 * often we have to call the erase ioctl. If the block is already
199 * erased then there's nothing we need to do.
200 */
201 while (count) {
202 if (!flash_is_erased(context, offset)) { /* Need to erase */
203 if (!erase_info.length) { /* Start of not-erased run */
204 erase_info.start = offset;
205 }
206 erase_info.length += erase_size;
207 } else if (erase_info.length) { /* Already erased|end of run? */
208 /* Erase the previous run which just ended */
209 rc = ioctl(context->fds[MTD_FD].fd, MEMERASE,
210 &erase_info);
211 if (rc < 0) {
212 MSG_ERR("Couldn't erase flash at 0x%.8x\n",
213 erase_info.start);
214 return -MBOX_R_SYSTEM_ERROR;
215 }
216 /* Mark ERASED where we just erased */
217 set_flash_bytemap(context, erase_info.start,
218 erase_info.length, FLASH_ERASED);
219 erase_info.start = 0;
220 erase_info.length = 0;
221 }
222
223 offset += erase_size;
224 count -= erase_size;
225 }
226
227 if (erase_info.length) {
228 rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info);
229 if (rc < 0) {
230 MSG_ERR("Couldn't erase flash at 0x%.8x\n",
231 erase_info.start);
232 return -MBOX_R_SYSTEM_ERROR;
233 }
234 /* Mark ERASED where we just erased */
235 set_flash_bytemap(context, erase_info.start, erase_info.length,
236 FLASH_ERASED);
237 }
238
239 return 0;
240}
241
242/*
243 * write_flash() - Write the flash from a provided buffer
244 * @context: The mbox context pointer
245 * @offset: The flash offset to write to (bytes)
246 * @buf: The buffer to write from (must be of atleast size)
247 * @size: The number of bytes to write
248 *
249 * Return: 0 on success otherwise negative error code
250 */
251int write_flash(struct mbox_context *context, uint32_t offset, void *buf,
252 uint32_t count)
253{
254 uint32_t buf_offset = 0;
255 int rc;
256
257 MSG_OUT("Writing 0x%.8x for 0x%.8x from %p\n", offset, count, buf);
258
259 if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) {
260 MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
261 strerror(errno));
262 return -MBOX_R_SYSTEM_ERROR;
263 }
264
265 while (count) {
266 rc = write(context->fds[MTD_FD].fd, buf + buf_offset, count);
267 if (rc < 0) {
268 MSG_ERR("Couldn't write to flash, write lost: %s\n",
269 strerror(errno));
270 return -MBOX_R_WRITE_ERROR;
271 }
272 /* Mark *NOT* erased where we just wrote */
273 set_flash_bytemap(context, offset + buf_offset, rc,
274 FLASH_DIRTY);
275 count -= rc;
276 buf_offset += rc;
277 }
278
279 return 0;
280}