blob: 14be57f1e16efe459a74a70420012ee580403f97 [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
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
Kra1o531679362014-06-20 19:06:06 +020031#ifdef RK3X
32 #include "rk3xhack.h"
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +010033#endif
34
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080035struct MtdReadContext {
36 const MtdPartition *partition;
37 char *buffer;
38 size_t consumed;
39 int fd;
40};
41
42struct MtdWriteContext {
43 const MtdPartition *partition;
44 char *buffer;
45 size_t stored;
46 int fd;
Doug Zongker4c5f9f32010-01-12 16:18:33 -080047
48 off_t* bad_block_offsets;
49 int bad_block_alloc;
50 int bad_block_count;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080051};
52
53typedef struct {
54 MtdPartition *partitions;
55 int partitions_allocd;
56 int partition_count;
57} MtdState;
58
59static MtdState g_mtd_state = {
60 NULL, // partitions
61 0, // partitions_allocd
62 -1 // partition_count
63};
64
65#define MTD_PROC_FILENAME "/proc/mtd"
66
67int
68mtd_scan_partitions()
69{
70 char buf[2048];
71 const char *bufp;
72 int fd;
73 int i;
74 ssize_t nbytes;
75
76 if (g_mtd_state.partitions == NULL) {
77 const int nump = 32;
78 MtdPartition *partitions = malloc(nump * sizeof(*partitions));
79 if (partitions == NULL) {
80 errno = ENOMEM;
81 return -1;
82 }
83 g_mtd_state.partitions = partitions;
84 g_mtd_state.partitions_allocd = nump;
85 memset(partitions, 0, nump * sizeof(*partitions));
86 }
87 g_mtd_state.partition_count = 0;
88
89 /* Initialize all of the entries to make things easier later.
90 * (Lets us handle sparsely-numbered partitions, which
91 * may not even be possible.)
92 */
93 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
94 MtdPartition *p = &g_mtd_state.partitions[i];
95 if (p->name != NULL) {
96 free(p->name);
97 p->name = NULL;
98 }
99 p->device_index = -1;
100 }
101
102 /* Open and read the file contents.
103 */
104 fd = open(MTD_PROC_FILENAME, O_RDONLY);
105 if (fd < 0) {
106 goto bail;
107 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700108 nbytes = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf) - 1));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800109 close(fd);
110 if (nbytes < 0) {
111 goto bail;
112 }
113 buf[nbytes] = '\0';
114
115 /* Parse the contents of the file, which looks like:
116 *
117 * # cat /proc/mtd
118 * dev: size erasesize name
119 * mtd0: 00080000 00020000 "bootloader"
120 * mtd1: 00400000 00020000 "mfg_and_gsm"
121 * mtd2: 00400000 00020000 "0000000c"
122 * mtd3: 00200000 00020000 "0000000d"
123 * mtd4: 04000000 00020000 "system"
124 * mtd5: 03280000 00020000 "userdata"
125 */
126 bufp = buf;
127 while (nbytes > 0) {
128 int mtdnum, mtdsize, mtderasesize;
129 int matches;
130 char mtdname[64];
131 mtdname[0] = '\0';
132 mtdnum = -1;
133
134 matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]",
135 &mtdnum, &mtdsize, &mtderasesize, mtdname);
136 /* This will fail on the first line, which just contains
137 * column headers.
138 */
139 if (matches == 4) {
140 MtdPartition *p = &g_mtd_state.partitions[mtdnum];
141 p->device_index = mtdnum;
142 p->size = mtdsize;
143 p->erase_size = mtderasesize;
144 p->name = strdup(mtdname);
145 if (p->name == NULL) {
146 errno = ENOMEM;
147 goto bail;
148 }
149 g_mtd_state.partition_count++;
150 }
151
152 /* Eat the line.
153 */
154 while (nbytes > 0 && *bufp != '\n') {
155 bufp++;
156 nbytes--;
157 }
158 if (nbytes > 0) {
159 bufp++;
160 nbytes--;
161 }
162 }
163
164 return g_mtd_state.partition_count;
165
166bail:
167 // keep "partitions" around so we can free the names on a rescan.
168 g_mtd_state.partition_count = -1;
169 return -1;
170}
171
172const MtdPartition *
173mtd_find_partition_by_name(const char *name)
174{
175 if (g_mtd_state.partitions != NULL) {
176 int i;
177 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
178 MtdPartition *p = &g_mtd_state.partitions[i];
179 if (p->device_index >= 0 && p->name != NULL) {
180 if (strcmp(p->name, name) == 0) {
181 return p;
182 }
183 }
184 }
185 }
186 return NULL;
187}
188
189int
190mtd_mount_partition(const MtdPartition *partition, const char *mount_point,
191 const char *filesystem, int read_only)
192{
193 const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
194 char devname[64];
195 int rv = -1;
196
197 sprintf(devname, "/dev/block/mtdblock%d", partition->device_index);
198 if (!read_only) {
199 rv = mount(devname, mount_point, filesystem, flags, NULL);
200 }
201 if (read_only || rv < 0) {
202 rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0);
203 if (rv < 0) {
204 printf("Failed to mount %s on %s: %s\n",
205 devname, mount_point, strerror(errno));
206 } else {
207 printf("Mount %s on %s read-only\n", devname, mount_point);
208 }
209 }
210#if 1 //TODO: figure out why this is happening; remove include of stat.h
211 if (rv >= 0) {
212 /* For some reason, the x bits sometimes aren't set on the root
213 * of mounted volumes.
214 */
215 struct stat st;
216 rv = stat(mount_point, &st);
217 if (rv < 0) {
218 return rv;
219 }
220 mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
221 if (new_mode != st.st_mode) {
222printf("Fixing execute permissions for %s\n", mount_point);
223 rv = chmod(mount_point, new_mode);
224 if (rv < 0) {
225 printf("Couldn't fix permissions for %s: %s\n",
226 mount_point, strerror(errno));
227 }
228 }
229 }
230#endif
231 return rv;
232}
233
234int
235mtd_partition_info(const MtdPartition *partition,
236 size_t *total_size, size_t *erase_size, size_t *write_size)
237{
238 char mtddevname[32];
239 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
240 int fd = open(mtddevname, O_RDONLY);
241 if (fd < 0) return -1;
242
243 struct mtd_info_user mtd_info;
244 int ret = ioctl(fd, MEMGETINFO, &mtd_info);
245 close(fd);
246 if (ret < 0) return -1;
247
248 if (total_size != NULL) *total_size = mtd_info.size;
249 if (erase_size != NULL) *erase_size = mtd_info.erasesize;
250 if (write_size != NULL) *write_size = mtd_info.writesize;
251 return 0;
252}
253
254MtdReadContext *mtd_read_partition(const MtdPartition *partition)
255{
256 MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext));
257 if (ctx == NULL) return NULL;
258
259 ctx->buffer = malloc(partition->erase_size);
260 if (ctx->buffer == NULL) {
261 free(ctx);
262 return NULL;
263 }
264
265 char mtddevname[32];
266 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
267 ctx->fd = open(mtddevname, O_RDONLY);
268 if (ctx->fd < 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800269 free(ctx->buffer);
Christian Lindeberg862c83b2011-01-19 12:22:41 +0100270 free(ctx);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800271 return NULL;
272 }
273
274 ctx->partition = partition;
275 ctx->consumed = partition->erase_size;
276 return ctx;
277}
278
279static int read_block(const MtdPartition *partition, int fd, char *data)
280{
281 struct mtd_ecc_stats before, after;
282 if (ioctl(fd, ECCGETSTATS, &before)) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700283 printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800284 return -1;
285 }
286
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700287 loff_t pos = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
288 if (pos == -1) {
289 printf("mtd: read_block: couldn't SEEK_CUR: %s\n", strerror(errno));
290 return -1;
291 }
Doug Zongker17a47092009-12-14 18:03:27 -0800292
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800293 ssize_t size = partition->erase_size;
Doug Zongker17a47092009-12-14 18:03:27 -0800294 int mgbb;
295
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800296 while (pos + size <= (int) partition->size) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700297 if (TEMP_FAILURE_RETRY(lseek64(fd, pos, SEEK_SET)) != pos ||
298 TEMP_FAILURE_RETRY(read(fd, data, size)) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700299 printf("mtd: read error at 0x%08llx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800300 pos, strerror(errno));
301 } else if (ioctl(fd, ECCGETSTATS, &after)) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700302 printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800303 return -1;
304 } else if (after.failed != before.failed) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700305 printf("mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800306 after.corrected - before.corrected,
307 after.failed - before.failed, pos);
Doug Zongker5d6309e2010-11-03 14:31:01 -0700308 // copy the comparison baseline for the next read.
309 memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
Doug Zongker17a47092009-12-14 18:03:27 -0800310 } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
311 fprintf(stderr,
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700312 "mtd: MEMGETBADBLOCK returned %d at 0x%08llx: %s\n",
313 mgbb, pos, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800314 } else {
Doug Zongker61ba7a82010-09-12 13:36:40 -0700315 return 0; // Success!
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800316 }
317
318 pos += partition->erase_size;
319 }
320
321 errno = ENOSPC;
322 return -1;
323}
324
325ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
326{
Dees_Troy51a0e822012-09-05 15:24:24 -0400327 ssize_t read = 0;
328 while (read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800329 if (ctx->consumed < ctx->partition->erase_size) {
330 size_t avail = ctx->partition->erase_size - ctx->consumed;
331 size_t copy = len - read < avail ? len - read : avail;
332 memcpy(data + read, ctx->buffer + ctx->consumed, copy);
333 ctx->consumed += copy;
334 read += copy;
335 }
336
337 // Read complete blocks directly into the user's buffer
338 while (ctx->consumed == ctx->partition->erase_size &&
339 len - read >= ctx->partition->erase_size) {
340 if (read_block(ctx->partition, ctx->fd, data + read)) return -1;
341 read += ctx->partition->erase_size;
342 }
343
Dees_Troy51a0e822012-09-05 15:24:24 -0400344 if (read >= (int)len) {
Doug Zongkerbec02d52009-07-01 12:09:29 -0700345 return read;
346 }
347
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800348 // Read the next block into the buffer
Dees_Troy51a0e822012-09-05 15:24:24 -0400349 if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800350 if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
351 ctx->consumed = 0;
352 }
353 }
354
355 return read;
356}
357
358void mtd_read_close(MtdReadContext *ctx)
359{
360 close(ctx->fd);
361 free(ctx->buffer);
362 free(ctx);
363}
364
365MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
366{
367 MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
368 if (ctx == NULL) return NULL;
369
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800370 ctx->bad_block_offsets = NULL;
371 ctx->bad_block_alloc = 0;
372 ctx->bad_block_count = 0;
373
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800374 ctx->buffer = malloc(partition->erase_size);
375 if (ctx->buffer == NULL) {
376 free(ctx);
377 return NULL;
378 }
379
380 char mtddevname[32];
381 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
382 ctx->fd = open(mtddevname, O_RDWR);
383 if (ctx->fd < 0) {
384 free(ctx->buffer);
385 free(ctx);
386 return NULL;
387 }
388
389 ctx->partition = partition;
390 ctx->stored = 0;
391 return ctx;
392}
393
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800394static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
395 if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
396 ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
397 ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
398 ctx->bad_block_alloc * sizeof(off_t));
399 }
400 ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
401}
402
403static int write_block(MtdWriteContext *ctx, const char *data)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800404{
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800405 const MtdPartition *partition = ctx->partition;
406 int fd = ctx->fd;
407
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700408 off_t pos = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_CUR));
409 if (pos == (off_t) -1) {
410 printf("mtd: write_block: couldn't SEEK_CUR: %s\n", strerror(errno));
411 return -1;
412 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800413
414 ssize_t size = partition->erase_size;
415 while (pos + size <= (int) partition->size) {
416 loff_t bpos = pos;
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700417 int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
418 if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800419 add_bad_block_offset(ctx, pos);
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700420 fprintf(stderr,
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700421 "mtd: not writing bad block at 0x%08lx (ret %d): %s\n",
422 pos, ret, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800423 pos += partition->erase_size;
424 continue; // Don't try to erase known factory-bad blocks.
425 }
426
427 struct erase_info_user erase_info;
428 erase_info.start = pos;
429 erase_info.length = size;
430 int retry;
431 for (retry = 0; retry < 2; ++retry) {
Kra1o531679362014-06-20 19:06:06 +0200432#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100433 if (rk30_zero_out(fd, pos, size) < 0) {
434 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
435 pos, strerror(errno));
436 continue;
437 }
438#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800439 if (ioctl(fd, MEMERASE, &erase_info) < 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700440 printf("mtd: erase failure at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800441 pos, strerror(errno));
442 continue;
443 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100444#endif
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700445 if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
446 TEMP_FAILURE_RETRY(write(fd, data, size)) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700447 printf("mtd: write error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800448 pos, strerror(errno));
449 }
450
451 char verify[size];
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700452 if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
453 TEMP_FAILURE_RETRY(read(fd, verify, size)) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700454 printf("mtd: re-read error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800455 pos, strerror(errno));
456 continue;
457 }
458 if (memcmp(data, verify, size) != 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700459 printf("mtd: verification error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800460 pos, strerror(errno));
461 continue;
462 }
463
464 if (retry > 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700465 printf("mtd: wrote block after %d retries\n", retry);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800466 }
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700467 printf("mtd: successfully wrote block at %lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800468 return 0; // Success!
469 }
470
471 // Try to erase it once more as we give up on this block
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800472 add_bad_block_offset(ctx, pos);
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700473 printf("mtd: skipping write block at 0x%08lx\n", pos);
Kra1o531679362014-06-20 19:06:06 +0200474#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100475 rk30_zero_out(fd, pos, size);
476#else
Dees Troybb4c0cb2013-11-02 20:25:14 +0000477
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800478 ioctl(fd, MEMERASE, &erase_info);
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100479#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800480 pos += partition->erase_size;
481 }
482
483 // Ran out of space on the device
484 errno = ENOSPC;
485 return -1;
486}
487
488ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
489{
490 size_t wrote = 0;
491 while (wrote < len) {
492 // Coalesce partial writes into complete blocks
493 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
494 size_t avail = ctx->partition->erase_size - ctx->stored;
495 size_t copy = len - wrote < avail ? len - wrote : avail;
496 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
497 ctx->stored += copy;
498 wrote += copy;
499 }
500
501 // If a complete block was accumulated, write it
502 if (ctx->stored == ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800503 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800504 ctx->stored = 0;
505 }
506
507 // Write complete blocks directly from the user's buffer
508 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800509 if (write_block(ctx, data + wrote)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800510 wrote += ctx->partition->erase_size;
511 }
512 }
513
514 return wrote;
515}
516
517off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
518{
519 // Zero-pad and write any pending data to get us to a block boundary
520 if (ctx->stored > 0) {
521 size_t zero = ctx->partition->erase_size - ctx->stored;
522 memset(ctx->buffer + ctx->stored, 0, zero);
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800523 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800524 ctx->stored = 0;
525 }
526
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700527 off_t pos = TEMP_FAILURE_RETRY(lseek(ctx->fd, 0, SEEK_CUR));
528 if ((off_t) pos == (off_t) -1) {
529 printf("mtd_erase_blocks: couldn't SEEK_CUR: %s\n", strerror(errno));
530 return -1;
531 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800532
533 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
534 if (blocks < 0) blocks = total;
535 if (blocks > total) {
536 errno = ENOSPC;
537 return -1;
538 }
539
540 // Erase the specified number of blocks
541 while (blocks-- > 0) {
542 loff_t bpos = pos;
543 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700544 printf("mtd: not erasing bad block at 0x%08lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800545 pos += ctx->partition->erase_size;
546 continue; // Don't try to erase known factory-bad blocks.
547 }
548
549 struct erase_info_user erase_info;
550 erase_info.start = pos;
551 erase_info.length = ctx->partition->erase_size;
Kra1o531679362014-06-20 19:06:06 +0200552#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100553 if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) {
554 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
555 }
556#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800557 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700558 printf("mtd: erase failure at 0x%08lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800559 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100560#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800561 pos += ctx->partition->erase_size;
562 }
563
564 return pos;
565}
566
567int mtd_write_close(MtdWriteContext *ctx)
568{
569 int r = 0;
570 // Make sure any pending data gets written
571 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
572 if (close(ctx->fd)) r = -1;
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800573 free(ctx->bad_block_offsets);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800574 free(ctx->buffer);
575 free(ctx);
576 return r;
577}
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800578
579/* Return the offset of the first good block at or after pos (which
580 * might be pos itself).
581 */
582off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
583 int i;
584 for (i = 0; i < ctx->bad_block_count; ++i) {
585 if (ctx->bad_block_offsets[i] == pos) {
586 pos += ctx->partition->erase_size;
587 } else if (ctx->bad_block_offsets[i] > pos) {
588 return pos;
589 }
590 }
591 return pos;
592}
Dees_Troy51a0e822012-09-05 15:24:24 -0400593
594#define BLOCK_SIZE 2048
595#define SPARE_SIZE (BLOCK_SIZE >> 5)
596#define HEADER_SIZE 2048
597
598int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
599{
600 const MtdPartition *ptn;
601 MtdWriteContext *write;
602 void *data;
603
604 FILE* f = fopen(filename, "rb");
605 if (f == NULL) {
606 fprintf(stderr, "error opening %s", filename);
607 return -1;
608 }
609
610 if (mtd_scan_partitions() <= 0)
611 {
612 fprintf(stderr, "error scanning partitions");
613 return -1;
614 }
615 const MtdPartition *mtd = mtd_find_partition_by_name(partition_name);
616 if (mtd == NULL)
617 {
618 fprintf(stderr, "can't find %s partition", partition_name);
619 return -1;
620 }
621
622 int fd = open(filename, O_RDONLY);
623 if (fd < 0)
624 {
625 printf("error opening %s", filename);
626 return -1;
627 }
628
629 MtdWriteContext* ctx = mtd_write_partition(mtd);
630 if (ctx == NULL) {
631 printf("error writing %s", partition_name);
632 return -1;
633 }
634
635 int success = 1;
636 char* buffer = malloc(BUFSIZ);
637 int read;
638 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
639 int wrote = mtd_write_data(ctx, buffer, read);
640 success = success && (wrote == read);
641 }
642 free(buffer);
643 fclose(f);
644
645 if (!success) {
646 fprintf(stderr, "error writing %s", partition_name);
647 return -1;
648 }
649
650 if (mtd_erase_blocks(ctx, -1) == -1) {
651 fprintf(stderr, "error erasing blocks of %s\n", partition_name);
652 }
653 if (mtd_write_close(ctx) != 0) {
654 fprintf(stderr, "error closing write of %s\n", partition_name);
655 }
656 printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name);
657 return 0;
658}
659
660
661int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
662{
663 MtdReadContext *in;
664 const MtdPartition *partition;
665 char buf[BLOCK_SIZE + SPARE_SIZE];
666 size_t partition_size;
667 size_t read_size;
668 size_t total;
669 int fd;
670 int wrote;
671 int len;
672
673 if (mtd_scan_partitions() <= 0)
674 {
675 printf("error scanning partitions");
676 return -1;
677 }
678
679 partition = mtd_find_partition_by_name(partition_name);
680 if (partition == NULL)
681 {
682 printf("can't find %s partition", partition_name);
683 return -1;
684 }
685
686 if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
687 printf("can't get info of partition %s", partition_name);
688 return -1;
689 }
690
691 if (!strcmp(filename, "-")) {
692 fd = fileno(stdout);
693 }
694 else {
695 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
696 }
697
698 if (fd < 0)
699 {
700 printf("error opening %s", filename);
701 return -1;
702 }
703
704 in = mtd_read_partition(partition);
705 if (in == NULL) {
706 close(fd);
707 unlink(filename);
708 printf("error opening %s: %s\n", partition_name, strerror(errno));
709 return -1;
710 }
711
712 total = 0;
713 while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
714 wrote = write(fd, buf, len);
715 if (wrote != len) {
716 close(fd);
717 unlink(filename);
718 printf("error writing %s", filename);
719 return -1;
720 }
721 total += BLOCK_SIZE;
722 }
723
724 mtd_read_close(in);
725
726 if (close(fd)) {
727 unlink(filename);
728 printf("error closing %s", filename);
729 return -1;
730 }
731 return 0;
732}
733
734int cmd_mtd_erase_raw_partition(const char *partition_name)
735{
736 MtdWriteContext *out;
737 size_t erased;
738 size_t total_size;
739 size_t erase_size;
740
741 if (mtd_scan_partitions() <= 0)
742 {
743 printf("error scanning partitions");
744 return -1;
745 }
746 const MtdPartition *p = mtd_find_partition_by_name(partition_name);
747 if (p == NULL)
748 {
749 printf("can't find %s partition", partition_name);
750 return -1;
751 }
752
753 out = mtd_write_partition(p);
754 if (out == NULL)
755 {
756 printf("could not estabilish write context for %s", partition_name);
757 return -1;
758 }
759
760 // do the actual erase, -1 = full partition erase
761 erased = mtd_erase_blocks(out, -1);
762
763 // erased = bytes erased, if zero, something borked
764 if (!erased)
765 {
766 printf("error erasing %s", partition_name);
767 return -1;
768 }
769
770 return 0;
771}
772
773int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
774{
775 return cmd_mtd_erase_raw_partition(partition);
776}
777
778
779int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
780{
781 mtd_scan_partitions();
782 const MtdPartition *p;
783 p = mtd_find_partition_by_name(partition);
784 if (p == NULL) {
785 return -1;
786 }
787 return mtd_mount_partition(p, mount_point, filesystem, read_only);
788}
789
790int cmd_mtd_get_partition_device(const char *partition, char *device)
791{
792 mtd_scan_partitions();
793 MtdPartition *p = mtd_find_partition_by_name(partition);
794 if (p == NULL)
795 return -1;
796 sprintf(device, "/dev/block/mtdblock%d", p->device_index);
797 return 0;
798}