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