1 /*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <sys/mount.h> // for _IOW, _IOR, mount()
24 #include <sys/stat.h>
25 #include <mtd/mtd-user.h>
26 #undef NDEBUG
27 #include <assert.h>
28
29 #include "mtdutils.h"
30
31 struct MtdReadContext {
32 const MtdPartition *partition;
33 char *buffer;
34 size_t consumed;
35 int fd;
36 };
37
38 struct MtdWriteContext {
39 const MtdPartition *partition;
40 char *buffer;
41 size_t stored;
42 int fd;
43
44 off_t* bad_block_offsets;
45 int bad_block_alloc;
46 int bad_block_count;
47 };
48
49 typedef struct {
50 MtdPartition *partitions;
51 int partitions_allocd;
52 int partition_count;
53 } MtdState;
54
55 static MtdState g_mtd_state = {
56 NULL, // partitions
57 0, // partitions_allocd
58 -1 // partition_count
59 };
60
61 #define MTD_PROC_FILENAME "/proc/mtd"
62
63 int
mtd_scan_partitions()64 mtd_scan_partitions()
65 {
66 char buf[2048];
67 const char *bufp;
68 int fd;
69 int i;
70 ssize_t nbytes;
71
72 if (g_mtd_state.partitions == NULL) {
73 const int nump = 32;
74 MtdPartition *partitions = malloc(nump * sizeof(*partitions));
75 if (partitions == NULL) {
76 errno = ENOMEM;
77 return -1;
78 }
79 g_mtd_state.partitions = partitions;
80 g_mtd_state.partitions_allocd = nump;
81 memset(partitions, 0, nump * sizeof(*partitions));
82 }
83 g_mtd_state.partition_count = 0;
84
85 /* Initialize all of the entries to make things easier later.
86 * (Lets us handle sparsely-numbered partitions, which
87 * may not even be possible.)
88 */
89 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
90 MtdPartition *p = &g_mtd_state.partitions[i];
91 if (p->name != NULL) {
92 free(p->name);
93 p->name = NULL;
94 }
95 p->device_index = -1;
96 }
97
98 /* Open and read the file contents.
99 */
100 fd = open(MTD_PROC_FILENAME, O_RDONLY);
101 if (fd < 0) {
102 goto bail;
103 }
104 nbytes = read(fd, buf, sizeof(buf) - 1);
105 close(fd);
106 if (nbytes < 0) {
107 goto bail;
108 }
109 buf[nbytes] = '\0';
110
111 /* Parse the contents of the file, which looks like:
112 *
113 * # cat /proc/mtd
114 * dev: size erasesize name
115 * mtd0: 00080000 00020000 "bootloader"
116 * mtd1: 00400000 00020000 "mfg_and_gsm"
117 * mtd2: 00400000 00020000 "0000000c"
118 * mtd3: 00200000 00020000 "0000000d"
119 * mtd4: 04000000 00020000 "system"
120 * mtd5: 03280000 00020000 "userdata"
121 */
122 bufp = buf;
123 while (nbytes > 0) {
124 int mtdnum, mtdsize, mtderasesize;
125 int matches;
126 char mtdname[64];
127 mtdname[0] = '\0';
128 mtdnum = -1;
129
130 matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]",
131 &mtdnum, &mtdsize, &mtderasesize, mtdname);
132 /* This will fail on the first line, which just contains
133 * column headers.
134 */
135 if (matches == 4) {
136 MtdPartition *p = &g_mtd_state.partitions[mtdnum];
137 p->device_index = mtdnum;
138 p->size = mtdsize;
139 p->erase_size = mtderasesize;
140 p->name = strdup(mtdname);
141 if (p->name == NULL) {
142 errno = ENOMEM;
143 goto bail;
144 }
145 g_mtd_state.partition_count++;
146 }
147
148 /* Eat the line.
149 */
150 while (nbytes > 0 && *bufp != '\n') {
151 bufp++;
152 nbytes--;
153 }
154 if (nbytes > 0) {
155 bufp++;
156 nbytes--;
157 }
158 }
159
160 return g_mtd_state.partition_count;
161
162 bail:
163 // keep "partitions" around so we can free the names on a rescan.
164 g_mtd_state.partition_count = -1;
165 return -1;
166 }
167
168 const MtdPartition *
mtd_find_partition_by_name(const char * name)169 mtd_find_partition_by_name(const char *name)
170 {
171 if (g_mtd_state.partitions != NULL) {
172 int i;
173 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
174 MtdPartition *p = &g_mtd_state.partitions[i];
175 if (p->device_index >= 0 && p->name != NULL) {
176 if (strcmp(p->name, name) == 0) {
177 return p;
178 }
179 }
180 }
181 }
182 return NULL;
183 }
184
185 int
mtd_mount_partition(const MtdPartition * partition,const char * mount_point,const char * filesystem,int read_only)186 mtd_mount_partition(const MtdPartition *partition, const char *mount_point,
187 const char *filesystem, int read_only)
188 {
189 const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
190 char devname[64];
191 int rv = -1;
192
193 sprintf(devname, "/dev/block/mtdblock%d", partition->device_index);
194 if (!read_only) {
195 rv = mount(devname, mount_point, filesystem, flags, NULL);
196 }
197 if (read_only || rv < 0) {
198 rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0);
199 if (rv < 0) {
200 printf("Failed to mount %s on %s: %s\n",
201 devname, mount_point, strerror(errno));
202 } else {
203 printf("Mount %s on %s read-only\n", devname, mount_point);
204 }
205 }
206 #if 1 //TODO: figure out why this is happening; remove include of stat.h
207 if (rv >= 0) {
208 /* For some reason, the x bits sometimes aren't set on the root
209 * of mounted volumes.
210 */
211 struct stat st;
212 rv = stat(mount_point, &st);
213 if (rv < 0) {
214 return rv;
215 }
216 mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
217 if (new_mode != st.st_mode) {
218 printf("Fixing execute permissions for %s\n", mount_point);
219 rv = chmod(mount_point, new_mode);
220 if (rv < 0) {
221 printf("Couldn't fix permissions for %s: %s\n",
222 mount_point, strerror(errno));
223 }
224 }
225 }
226 #endif
227 return rv;
228 }
229
mtd_get_flash_info(size_t * total_size,size_t * block_size,size_t * page_size)230 int mtd_get_flash_info(size_t *total_size, size_t *block_size, size_t *page_size)
231 {
232 int fd = open("/dev/mtd0", O_RDONLY);
233 if (fd < 0) {
234 fprintf(stderr, "mtd: open /dev/mtd0 (%s)\n", strerror(errno));
235 return -1;
236 }
237
238 struct mtd_info_user mtd_info;
239 int ret = ioctl(fd, MEMGETINFO, &mtd_info);
240 close(fd);
241 if (ret < 0) {
242 fprintf(stderr, "mtd: ioctl MEMGETINFO (%s)\n", strerror(errno));
243 return -1;
244 }
245
246 printf("mtd_info.size = [%d]\n", mtd_info.size);
247 printf("mtd_info.erasesize = [%d]\n", mtd_info.erasesize);
248 printf("mtd_info.writesize = [%d]\n", mtd_info.writesize);
249 if (total_size) *total_size = mtd_info.size;
250 if (block_size) *block_size = mtd_info.erasesize;
251 if (page_size) *page_size = mtd_info.writesize;
252 return 0;
253 }
254
255 int
mtd_partition_info(const MtdPartition * partition,size_t * total_size,size_t * erase_size,size_t * write_size)256 mtd_partition_info(const MtdPartition *partition,
257 size_t *total_size, size_t *erase_size, size_t *write_size)
258 {
259 char mtddevname[32];
260 //sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
261 sprintf(mtddevname, "/dev/mtd%d", partition->device_index);
262 int fd = open(mtddevname, O_RDONLY);
263 if (fd < 0) return -1;
264
265 struct mtd_info_user mtd_info;
266 int ret = ioctl(fd, MEMGETINFO, &mtd_info);
267 close(fd);
268 if (ret < 0) return -1;
269
270 if (total_size != NULL) *total_size = mtd_info.size;
271 if (erase_size != NULL) *erase_size = mtd_info.erasesize;
272 if (write_size != NULL) *write_size = mtd_info.writesize;
273 return 0;
274 }
275
mtd_read_partition(const MtdPartition * partition)276 MtdReadContext *mtd_read_partition(const MtdPartition *partition)
277 {
278 MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext));
279 if (ctx == NULL) return NULL;
280
281 ctx->buffer = malloc(partition->erase_size);
282 if (ctx->buffer == NULL) {
283 free(ctx);
284 return NULL;
285 }
286
287 char mtddevname[32];
288 sprintf(mtddevname, "/dev/mtd%d", partition->device_index);
289 //sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
290 ctx->fd = open(mtddevname, O_RDONLY);
291 if (ctx->fd < 0) {
292 free(ctx);
293 free(ctx->buffer);
294 return NULL;
295 }
296
297 ctx->partition = partition;
298 ctx->consumed = partition->erase_size;
299 return ctx;
300 }
301
302 // Seeks to a location in the partition. Don't mix with reads of
303 // anything other than whole blocks; unpredictable things will result.
mtd_read_skip_to(const MtdReadContext * ctx,size_t offset)304 void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset)
305 {
306 lseek64(ctx->fd, offset, SEEK_SET);
307 }
308
read_block(const MtdPartition * partition,int fd,char * data)309 static int read_block(const MtdPartition *partition, int fd, char *data)
310 {
311 struct mtd_ecc_stats before, after;
312 if (ioctl(fd, ECCGETSTATS, &before)) {
313 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
314 return -1;
315 }
316
317 loff_t pos = lseek64(fd, 0, SEEK_CUR);
318
319 ssize_t size = partition->erase_size;
320 int mgbb;
321
322 while (pos + size <= (int) partition->size) {
323 if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
324 fprintf(stderr, "mtd: read error at 0x%08llx (%s)\n",
325 pos, strerror(errno));
326 } else if (ioctl(fd, ECCGETSTATS, &after)) {
327 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
328 return -1;
329 } else if (after.failed != before.failed) {
330 fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
331 after.corrected - before.corrected,
332 after.failed - before.failed, pos);
333 // copy the comparison baseline for the next read.
334 memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
335 } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
336 fprintf(stderr,
337 "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
338 mgbb, pos, errno);
339 } else {
340 return 0; // Success!
341 }
342
343 pos += partition->erase_size;
344 }
345
346 errno = ENOSPC;
347 return -1;
348 }
349
mtd_read_data(MtdReadContext * ctx,char * data,size_t len)350 ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
351 {
352 ssize_t read = 0;
353 while (read < (int) len) {
354 if (ctx->consumed < ctx->partition->erase_size) {
355 size_t avail = ctx->partition->erase_size - ctx->consumed;
356 size_t copy = len - read < avail ? len - read : avail;
357 memcpy(data + read, ctx->buffer + ctx->consumed, copy);
358 ctx->consumed += copy;
359 read += copy;
360 }
361
362 // Read complete blocks directly into the user's buffer
363 while (ctx->consumed == ctx->partition->erase_size &&
364 len - read >= ctx->partition->erase_size) {
365 if (read_block(ctx->partition, ctx->fd, data + read)) return -1;
366 read += ctx->partition->erase_size;
367 }
368
369 if (read >= len) {
370 return read;
371 }
372
373 // Read the next block into the buffer
374 if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
375 if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
376 ctx->consumed = 0;
377 }
378 }
379
380 return read;
381 }
382
mtd_read_close(MtdReadContext * ctx)383 void mtd_read_close(MtdReadContext *ctx)
384 {
385 close(ctx->fd);
386 free(ctx->buffer);
387 free(ctx);
388 }
389
mtd_write_partition(const MtdPartition * partition)390 MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
391 {
392 MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
393 if (ctx == NULL) return NULL;
394
395 ctx->bad_block_offsets = NULL;
396 ctx->bad_block_alloc = 0;
397 ctx->bad_block_count = 0;
398
399 ctx->buffer = malloc(partition->erase_size);
400 if (ctx->buffer == NULL) {
401 free(ctx);
402 return NULL;
403 }
404
405 char mtddevname[32];
406 sprintf(mtddevname, "/dev/mtd%d", partition->device_index);
407 //sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
408 ctx->fd = open(mtddevname, O_RDWR);
409 if (ctx->fd < 0) {
410 free(ctx->buffer);
411 free(ctx);
412 return NULL;
413 }
414
415 ctx->partition = partition;
416 ctx->stored = 0;
417 return ctx;
418 }
419
add_bad_block_offset(MtdWriteContext * ctx,off_t pos)420 static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos)
421 {
422 if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
423 ctx->bad_block_alloc = (ctx->bad_block_alloc * 2) + 1;
424 ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
425 ctx->bad_block_alloc * sizeof(off_t));
426 }
427 ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
428 }
429
write_block(MtdWriteContext * ctx,const char * data)430 static int write_block(MtdWriteContext *ctx, const char *data)
431 {
432 const MtdPartition *partition = ctx->partition;
433 int fd = ctx->fd;
434
435 off_t pos = lseek(fd, 0, SEEK_CUR);
436 if (pos == (off_t) -1) return 1;
437
438 ssize_t size = partition->erase_size;
439 while (pos + size <= (int) partition->size) {
440 loff_t bpos = pos;
441 int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
442 if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
443 add_bad_block_offset(ctx, pos);
444 fprintf(stderr,
445 "mtd: not writing bad block at 0x%08lx (ret %d errno %d)\n",
446 pos, ret, errno);
447 pos += partition->erase_size;
448 continue; // Don't try to erase known factory-bad blocks.
449 }
450
451 struct erase_info_user erase_info;
452 erase_info.start = pos;
453 erase_info.length = size;
454 int retry;
455 for (retry = 0; retry < 2; ++retry) {
456 if (ioctl(fd, MEMERASE, &erase_info) < 0) {
457 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
458 pos, strerror(errno));
459 continue;
460 }
461 if (lseek(fd, pos, SEEK_SET) != pos ||
462 write(fd, data, size) != size) {
463 fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
464 pos, strerror(errno));
465 }
466
467 char verify[size];
468 if (lseek(fd, pos, SEEK_SET) != pos ||
469 read(fd, verify, size) != size) {
470 fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n",
471 pos, strerror(errno));
472 continue;
473 }
474 if (memcmp(data, verify, size) != 0) {
475 fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n",
476 pos, strerror(errno));
477 continue;
478 }
479
480 if (retry > 0) {
481 fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
482 }
483 fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos);
484 return 0; // Success!
485 }
486
487 // Try to erase it once more as we give up on this block
488 add_bad_block_offset(ctx, pos);
489 fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
490 ioctl(fd, MEMERASE, &erase_info);
491 pos += partition->erase_size;
492 }
493
494 // Ran out of space on the device
495 errno = ENOSPC;
496 return -1;
497 }
498
mtd_write_data(MtdWriteContext * ctx,const char * data,size_t len)499 ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
500 {
501 size_t wrote = 0;
502 while (wrote < len) {
503 // Coalesce partial writes into complete blocks
504 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
505 size_t avail = ctx->partition->erase_size - ctx->stored;
506 size_t copy = len - wrote < avail ? len - wrote : avail;
507 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
508 ctx->stored += copy;
509 wrote += copy;
510 }
511
512 // If a complete block was accumulated, write it
513 if (ctx->stored == ctx->partition->erase_size) {
514 if (write_block(ctx, ctx->buffer)) return -1;
515 ctx->stored = 0;
516 }
517
518 // Write complete blocks directly from the user's buffer
519 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
520 if (write_block(ctx, data + wrote)) return -1;
521 wrote += ctx->partition->erase_size;
522 }
523 }
524
525 return wrote;
526 }
527
mtd_erase_blocks(MtdWriteContext * ctx,int blocks)528 off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
529 {
530 // Zero-pad and write any pending data to get us to a block boundary
531 if (ctx->stored > 0) {
532 size_t zero = ctx->partition->erase_size - ctx->stored;
533 memset(ctx->buffer + ctx->stored, 0, zero);
534 if (write_block(ctx, ctx->buffer)) return -1;
535 ctx->stored = 0;
536 }
537
538 off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
539 if ((off_t) pos == (off_t) -1) return pos;
540
541 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
542 if (blocks < 0) blocks = total;
543 if (blocks > total) {
544 errno = ENOSPC;
545 return -1;
546 }
547
548 // Erase the specified number of blocks
549 while (blocks-- > 0) {
550 loff_t bpos = pos;
551 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
552 fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos);
553 pos += ctx->partition->erase_size;
554 continue; // Don't try to erase known factory-bad blocks.
555 }
556
557 struct erase_info_user erase_info;
558 erase_info.start = pos;
559 erase_info.length = ctx->partition->erase_size;
560 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
561 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
562 }
563 pos += ctx->partition->erase_size;
564 }
565
566 return pos;
567 }
568
mtd_write_close(MtdWriteContext * ctx)569 int mtd_write_close(MtdWriteContext *ctx)
570 {
571 int r = 0;
572 // Make sure any pending data gets written
573 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
574 if (close(ctx->fd)) r = -1;
575 free(ctx->bad_block_offsets);
576 free(ctx->buffer);
577 free(ctx);
578 return r;
579 }
580
581 /* Return the offset of the first good block at or after pos (which
582 * might be pos itself).
583 */
mtd_find_write_start(MtdWriteContext * ctx,off_t pos)584 off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos)
585 {
586 int i;
587 for (i = 0; i < ctx->bad_block_count; ++i) {
588 if (ctx->bad_block_offsets[i] == pos) {
589 pos += ctx->partition->erase_size;
590 } else if (ctx->bad_block_offsets[i] > pos) {
591 return pos;
592 }
593 }
594 return pos;
595 }
596