Commit | Line | Data |
---|---|---|
94e9e396 DE |
1 | /* |
2 | * Copyright (c) International Business Machines Corp., 2006 | |
3 | * Copyright (C) 2009 Nokia Corporation | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
13 | * the GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
fa07eaa6 | 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
94e9e396 DE |
18 | * |
19 | * Author: Artem Bityutskiy | |
20 | * | |
21 | * MTD library. | |
22 | */ | |
23 | ||
24 | /* Imported from mtd-utils by dehrenberg */ | |
25 | ||
26 | #include <limits.h> | |
27 | #include <stdlib.h> | |
28 | #include <stdio.h> | |
29 | #include <errno.h> | |
30 | #include <unistd.h> | |
31 | #include <fcntl.h> | |
32 | #include <dirent.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/stat.h> | |
35 | #include <sys/ioctl.h> | |
36 | #include <inttypes.h> | |
37 | ||
59f94d26 JA |
38 | #include "../compiler/compiler.h" |
39 | ||
94e9e396 DE |
40 | #include <mtd/mtd-user.h> |
41 | #include "libmtd.h" | |
42 | ||
43 | #include "libmtd_int.h" | |
44 | #include "libmtd_common.h" | |
45 | ||
46 | /** | |
47 | * mkpath - compose full path from 2 given components. | |
48 | * @path: the first component | |
49 | * @name: the second component | |
50 | * | |
51 | * This function returns the resulting path in case of success and %NULL in | |
52 | * case of failure. | |
53 | */ | |
54 | static char *mkpath(const char *path, const char *name) | |
55 | { | |
56 | char *n; | |
57 | size_t len1 = strlen(path); | |
58 | size_t len2 = strlen(name); | |
59 | ||
51e962a7 | 60 | n = xmalloc(len1 + len2 + 6); |
94e9e396 DE |
61 | |
62 | memcpy(n, path, len1); | |
63 | if (n[len1 - 1] != '/') | |
64 | n[len1++] = '/'; | |
65 | ||
66 | memcpy(n + len1, name, len2 + 1); | |
67 | return n; | |
68 | } | |
69 | ||
70 | /** | |
71 | * read_data - read data from a file. | |
72 | * @file: the file to read from | |
73 | * @buf: the buffer to read to | |
74 | * @buf_len: buffer length | |
75 | * | |
76 | * This function returns number of read bytes in case of success and %-1 in | |
77 | * case of failure. Note, if the file contains more then @buf_len bytes of | |
78 | * date, this function fails with %EINVAL error code. | |
79 | */ | |
80 | static int read_data(const char *file, void *buf, int buf_len) | |
81 | { | |
82 | int fd, rd, tmp, tmp1; | |
83 | ||
84 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
85 | if (fd == -1) | |
86 | return -1; | |
87 | ||
88 | rd = read(fd, buf, buf_len); | |
89 | if (rd == -1) { | |
90 | sys_errmsg("cannot read \"%s\"", file); | |
91 | goto out_error; | |
92 | } | |
93 | ||
94 | if (rd == buf_len) { | |
95 | errmsg("contents of \"%s\" is too long", file); | |
96 | errno = EINVAL; | |
97 | goto out_error; | |
98 | } | |
99 | ||
100 | ((char *)buf)[rd] = '\0'; | |
101 | ||
102 | /* Make sure all data is read */ | |
103 | tmp1 = read(fd, &tmp, 1); | |
104 | if (tmp1 == 1) { | |
105 | sys_errmsg("cannot read \"%s\"", file); | |
106 | goto out_error; | |
107 | } | |
108 | if (tmp1) { | |
109 | errmsg("file \"%s\" contains too much data (> %d bytes)", | |
110 | file, buf_len); | |
111 | errno = EINVAL; | |
112 | goto out_error; | |
113 | } | |
114 | ||
115 | if (close(fd)) { | |
116 | sys_errmsg("close failed on \"%s\"", file); | |
117 | return -1; | |
118 | } | |
119 | ||
120 | return rd; | |
121 | ||
122 | out_error: | |
123 | close(fd); | |
124 | return -1; | |
125 | } | |
126 | ||
127 | /** | |
128 | * read_major - read major and minor numbers from a file. | |
129 | * @file: name of the file to read from | |
130 | * @major: major number is returned here | |
131 | * @minor: minor number is returned here | |
132 | * | |
133 | * This function returns % in case of success, and %-1 in case of failure. | |
134 | */ | |
135 | static int read_major(const char *file, int *major, int *minor) | |
136 | { | |
137 | int ret; | |
138 | char buf[50]; | |
139 | ||
140 | ret = read_data(file, buf, 50); | |
141 | if (ret < 0) | |
142 | return ret; | |
143 | ||
144 | ret = sscanf(buf, "%d:%d\n", major, minor); | |
145 | if (ret != 2) { | |
146 | errno = EINVAL; | |
147 | return errmsg("\"%s\" does not have major:minor format", file); | |
148 | } | |
149 | ||
150 | if (*major < 0 || *minor < 0) { | |
151 | errno = EINVAL; | |
152 | return errmsg("bad major:minor %d:%d in \"%s\"", | |
153 | *major, *minor, file); | |
154 | } | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | /** | |
160 | * dev_get_major - get major and minor numbers of an MTD device. | |
161 | * @lib: libmtd descriptor | |
162 | * @mtd_num: MTD device number | |
163 | * @major: major number is returned here | |
164 | * @minor: minor number is returned here | |
165 | * | |
166 | * This function returns zero in case of success and %-1 in case of failure. | |
167 | */ | |
168 | static int dev_get_major(struct libmtd *lib, int mtd_num, int *major, int *minor) | |
169 | { | |
170 | char file[strlen(lib->mtd_dev) + 50]; | |
171 | ||
172 | sprintf(file, lib->mtd_dev, mtd_num); | |
173 | return read_major(file, major, minor); | |
174 | } | |
175 | ||
176 | /** | |
177 | * dev_read_data - read data from an MTD device's sysfs file. | |
178 | * @patt: file pattern to read from | |
179 | * @mtd_num: MTD device number | |
180 | * @buf: buffer to read to | |
181 | * @buf_len: buffer length | |
182 | * | |
183 | * This function returns number of read bytes in case of success and %-1 in | |
184 | * case of failure. | |
185 | */ | |
186 | static int dev_read_data(const char *patt, int mtd_num, void *buf, int buf_len) | |
187 | { | |
188 | char file[strlen(patt) + 100]; | |
189 | ||
190 | sprintf(file, patt, mtd_num); | |
191 | return read_data(file, buf, buf_len); | |
192 | } | |
193 | ||
194 | /** | |
195 | * read_hex_ll - read a hex 'long long' value from a file. | |
196 | * @file: the file to read from | |
197 | * @value: the result is stored here | |
198 | * | |
199 | * This function reads file @file and interprets its contents as hexadecimal | |
200 | * 'long long' integer. If this is not true, it fails with %EINVAL error code. | |
201 | * Returns %0 in case of success and %-1 in case of failure. | |
202 | */ | |
203 | static int read_hex_ll(const char *file, long long *value) | |
204 | { | |
205 | int fd, rd; | |
206 | char buf[50]; | |
207 | ||
208 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
209 | if (fd == -1) | |
210 | return -1; | |
211 | ||
212 | rd = read(fd, buf, sizeof(buf)); | |
213 | if (rd == -1) { | |
214 | sys_errmsg("cannot read \"%s\"", file); | |
215 | goto out_error; | |
216 | } | |
217 | if (rd == sizeof(buf)) { | |
218 | errmsg("contents of \"%s\" is too long", file); | |
219 | errno = EINVAL; | |
220 | goto out_error; | |
221 | } | |
222 | buf[rd] = '\0'; | |
223 | ||
224 | if (sscanf(buf, "%llx\n", value) != 1) { | |
225 | errmsg("cannot read integer from \"%s\"\n", file); | |
226 | errno = EINVAL; | |
227 | goto out_error; | |
228 | } | |
229 | ||
230 | if (*value < 0) { | |
231 | errmsg("negative value %lld in \"%s\"", *value, file); | |
232 | errno = EINVAL; | |
233 | goto out_error; | |
234 | } | |
235 | ||
236 | if (close(fd)) | |
237 | return sys_errmsg("close failed on \"%s\"", file); | |
238 | ||
239 | return 0; | |
240 | ||
241 | out_error: | |
242 | close(fd); | |
243 | return -1; | |
244 | } | |
245 | ||
246 | /** | |
247 | * read_pos_ll - read a positive 'long long' value from a file. | |
248 | * @file: the file to read from | |
249 | * @value: the result is stored here | |
250 | * | |
251 | * This function reads file @file and interprets its contents as a positive | |
252 | * 'long long' integer. If this is not true, it fails with %EINVAL error code. | |
253 | * Returns %0 in case of success and %-1 in case of failure. | |
254 | */ | |
255 | static int read_pos_ll(const char *file, long long *value) | |
256 | { | |
257 | int fd, rd; | |
258 | char buf[50]; | |
259 | ||
260 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
261 | if (fd == -1) | |
262 | return -1; | |
263 | ||
264 | rd = read(fd, buf, 50); | |
265 | if (rd == -1) { | |
266 | sys_errmsg("cannot read \"%s\"", file); | |
267 | goto out_error; | |
268 | } | |
269 | if (rd == 50) { | |
270 | errmsg("contents of \"%s\" is too long", file); | |
271 | errno = EINVAL; | |
272 | goto out_error; | |
273 | } | |
274 | ||
275 | if (sscanf(buf, "%lld\n", value) != 1) { | |
276 | errmsg("cannot read integer from \"%s\"\n", file); | |
277 | errno = EINVAL; | |
278 | goto out_error; | |
279 | } | |
280 | ||
281 | if (*value < 0) { | |
282 | errmsg("negative value %lld in \"%s\"", *value, file); | |
283 | errno = EINVAL; | |
284 | goto out_error; | |
285 | } | |
286 | ||
287 | if (close(fd)) | |
288 | return sys_errmsg("close failed on \"%s\"", file); | |
289 | ||
290 | return 0; | |
291 | ||
292 | out_error: | |
293 | close(fd); | |
294 | return -1; | |
295 | } | |
296 | ||
297 | /** | |
298 | * read_hex_int - read an 'int' value from a file. | |
299 | * @file: the file to read from | |
300 | * @value: the result is stored here | |
301 | * | |
302 | * This function is the same as 'read_pos_ll()', but it reads an 'int' | |
303 | * value, not 'long long'. | |
304 | */ | |
305 | static int read_hex_int(const char *file, int *value) | |
306 | { | |
307 | long long res; | |
308 | ||
309 | if (read_hex_ll(file, &res)) | |
310 | return -1; | |
311 | ||
312 | /* Make sure the value has correct range */ | |
313 | if (res > INT_MAX || res < INT_MIN) { | |
314 | errmsg("value %lld read from file \"%s\" is out of range", | |
315 | res, file); | |
316 | errno = EINVAL; | |
317 | return -1; | |
318 | } | |
319 | ||
320 | *value = res; | |
321 | return 0; | |
322 | } | |
323 | ||
324 | /** | |
325 | * read_pos_int - read a positive 'int' value from a file. | |
326 | * @file: the file to read from | |
327 | * @value: the result is stored here | |
328 | * | |
329 | * This function is the same as 'read_pos_ll()', but it reads an 'int' | |
330 | * value, not 'long long'. | |
331 | */ | |
332 | static int read_pos_int(const char *file, int *value) | |
333 | { | |
334 | long long res; | |
335 | ||
336 | if (read_pos_ll(file, &res)) | |
337 | return -1; | |
338 | ||
339 | /* Make sure the value is not too big */ | |
340 | if (res > INT_MAX) { | |
341 | errmsg("value %lld read from file \"%s\" is out of range", | |
342 | res, file); | |
343 | errno = EINVAL; | |
344 | return -1; | |
345 | } | |
346 | ||
347 | *value = res; | |
348 | return 0; | |
349 | } | |
350 | ||
351 | /** | |
352 | * dev_read_hex_int - read an hex 'int' value from an MTD device sysfs file. | |
353 | * @patt: file pattern to read from | |
354 | * @mtd_num: MTD device number | |
355 | * @value: the result is stored here | |
356 | * | |
357 | * This function returns %0 in case of success and %-1 in case of failure. | |
358 | */ | |
359 | static int dev_read_hex_int(const char *patt, int mtd_num, int *value) | |
360 | { | |
361 | char file[strlen(patt) + 50]; | |
362 | ||
363 | sprintf(file, patt, mtd_num); | |
364 | return read_hex_int(file, value); | |
365 | } | |
366 | ||
367 | /** | |
368 | * dev_read_pos_int - read a positive 'int' value from an MTD device sysfs file. | |
369 | * @patt: file pattern to read from | |
370 | * @mtd_num: MTD device number | |
371 | * @value: the result is stored here | |
372 | * | |
373 | * This function returns %0 in case of success and %-1 in case of failure. | |
374 | */ | |
375 | static int dev_read_pos_int(const char *patt, int mtd_num, int *value) | |
376 | { | |
377 | char file[strlen(patt) + 50]; | |
378 | ||
379 | sprintf(file, patt, mtd_num); | |
380 | return read_pos_int(file, value); | |
381 | } | |
382 | ||
383 | /** | |
384 | * dev_read_pos_ll - read a positive 'long long' value from an MTD device sysfs file. | |
385 | * @patt: file pattern to read from | |
386 | * @mtd_num: MTD device number | |
387 | * @value: the result is stored here | |
388 | * | |
389 | * This function returns %0 in case of success and %-1 in case of failure. | |
390 | */ | |
391 | static int dev_read_pos_ll(const char *patt, int mtd_num, long long *value) | |
392 | { | |
393 | char file[strlen(patt) + 50]; | |
394 | ||
395 | sprintf(file, patt, mtd_num); | |
396 | return read_pos_ll(file, value); | |
397 | } | |
398 | ||
399 | /** | |
400 | * type_str2int - convert MTD device type to integer. | |
401 | * @str: MTD device type string to convert | |
402 | * | |
403 | * This function converts MTD device type string @str, read from sysfs, into an | |
404 | * integer. | |
405 | */ | |
406 | static int type_str2int(const char *str) | |
407 | { | |
408 | if (!strcmp(str, "nand")) | |
409 | return MTD_NANDFLASH; | |
410 | if (!strcmp(str, "mlc-nand")) | |
411 | return MTD_MLCNANDFLASH; | |
412 | if (!strcmp(str, "nor")) | |
413 | return MTD_NORFLASH; | |
414 | if (!strcmp(str, "rom")) | |
415 | return MTD_ROM; | |
416 | if (!strcmp(str, "absent")) | |
417 | return MTD_ABSENT; | |
418 | if (!strcmp(str, "dataflash")) | |
419 | return MTD_DATAFLASH; | |
420 | if (!strcmp(str, "ram")) | |
421 | return MTD_RAM; | |
422 | if (!strcmp(str, "ubi")) | |
423 | return MTD_UBIVOLUME; | |
424 | return -1; | |
425 | } | |
426 | ||
427 | /** | |
428 | * dev_node2num - find UBI device number by its character device node. | |
429 | * @lib: MTD library descriptor | |
430 | * @node: name of the MTD device node | |
431 | * @mtd_num: MTD device number is returned here | |
432 | * | |
433 | * This function returns %0 in case of success and %-1 in case of failure. | |
434 | */ | |
435 | static int dev_node2num(struct libmtd *lib, const char *node, int *mtd_num) | |
436 | { | |
437 | struct stat st; | |
438 | int i, mjr, mnr; | |
439 | struct mtd_info info; | |
440 | ||
441 | if (stat(node, &st)) | |
442 | return sys_errmsg("cannot get information about \"%s\"", node); | |
443 | ||
444 | if (!S_ISCHR(st.st_mode)) { | |
445 | errmsg("\"%s\" is not a character device", node); | |
446 | errno = EINVAL; | |
447 | return -1; | |
448 | } | |
449 | ||
450 | mjr = major(st.st_rdev); | |
451 | mnr = minor(st.st_rdev); | |
452 | ||
453 | if (mtd_get_info((libmtd_t *)lib, &info)) | |
454 | return -1; | |
455 | ||
456 | for (i = info.lowest_mtd_num; i <= info.highest_mtd_num; i++) { | |
457 | int mjr1, mnr1, ret; | |
458 | ||
459 | ret = dev_get_major(lib, i, &mjr1, &mnr1); | |
460 | if (ret) { | |
461 | if (errno == ENOENT) | |
462 | continue; | |
463 | if (!errno) | |
464 | break; | |
465 | return -1; | |
466 | } | |
467 | ||
468 | if (mjr1 == mjr && mnr1 == mnr) { | |
469 | errno = 0; | |
470 | *mtd_num = i; | |
471 | return 0; | |
472 | } | |
473 | } | |
474 | ||
475 | errno = ENODEV; | |
476 | return -1; | |
477 | } | |
478 | ||
479 | /** | |
480 | * sysfs_is_supported - check whether the MTD sub-system supports MTD. | |
481 | * @lib: MTD library descriptor | |
482 | * | |
483 | * The Linux kernel MTD subsystem gained MTD support starting from kernel | |
484 | * 2.6.30 and libmtd tries to use sysfs interface if possible, because the NAND | |
485 | * sub-page size is available there (and not available at all in pre-sysfs | |
486 | * kernels). | |
487 | * | |
488 | * Very old kernels did not have "/sys/class/mtd" directory. Not very old | |
489 | * kernels (e.g., 2.6.29) did have "/sys/class/mtd/mtdX" directories, by there | |
490 | * were no files there, e.g., the "name" file was not present. So all we can do | |
491 | * is to check for a "/sys/class/mtd/mtdX/name" file. But this is not a | |
492 | * reliable check, because if this is a new system with no MTD devices - we'll | |
493 | * treat it as a pre-sysfs system. | |
494 | */ | |
495 | static int sysfs_is_supported(struct libmtd *lib) | |
496 | { | |
497 | int fd, num = -1; | |
498 | DIR *sysfs_mtd; | |
499 | char file[strlen(lib->mtd_name) + 10]; | |
500 | ||
501 | sysfs_mtd = opendir(lib->sysfs_mtd); | |
502 | if (!sysfs_mtd) { | |
503 | if (errno == ENOENT) { | |
504 | errno = 0; | |
505 | return 0; | |
506 | } | |
507 | return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); | |
508 | } | |
509 | ||
510 | /* | |
511 | * First of all find an "mtdX" directory. This is needed because there | |
512 | * may be, for example, mtd1 but no mtd0. | |
513 | */ | |
514 | while (1) { | |
515 | int ret, mtd_num; | |
516 | char tmp_buf[256]; | |
517 | struct dirent *dirent; | |
518 | ||
519 | dirent = readdir(sysfs_mtd); | |
520 | if (!dirent) | |
521 | break; | |
522 | ||
523 | if (strlen(dirent->d_name) >= 255) { | |
524 | errmsg("invalid entry in %s: \"%s\"", | |
525 | lib->sysfs_mtd, dirent->d_name); | |
526 | errno = EINVAL; | |
527 | closedir(sysfs_mtd); | |
528 | return -1; | |
529 | } | |
530 | ||
531 | ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", | |
532 | &mtd_num, tmp_buf); | |
533 | if (ret == 1) { | |
534 | num = mtd_num; | |
535 | break; | |
536 | } | |
537 | } | |
538 | ||
539 | if (closedir(sysfs_mtd)) | |
540 | return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); | |
541 | ||
542 | if (num == -1) | |
543 | /* No mtd device, treat this as pre-sysfs system */ | |
544 | return 0; | |
545 | ||
546 | sprintf(file, lib->mtd_name, num); | |
547 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
548 | if (fd == -1) | |
549 | return 0; | |
550 | ||
551 | if (close(fd)) { | |
552 | sys_errmsg("close failed on \"%s\"", file); | |
553 | return -1; | |
554 | } | |
555 | ||
556 | return 1; | |
557 | } | |
558 | ||
559 | libmtd_t libmtd_open(void) | |
560 | { | |
561 | struct libmtd *lib; | |
562 | ||
563 | lib = xzalloc(sizeof(*lib)); | |
564 | ||
565 | lib->offs64_ioctls = OFFS64_IOCTLS_UNKNOWN; | |
566 | ||
567 | lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD); | |
568 | if (!lib->sysfs_mtd) | |
569 | goto out_error; | |
570 | ||
571 | lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT); | |
572 | if (!lib->mtd) | |
573 | goto out_error; | |
574 | ||
575 | lib->mtd_name = mkpath(lib->mtd, MTD_NAME); | |
576 | if (!lib->mtd_name) | |
577 | goto out_error; | |
578 | ||
579 | if (!sysfs_is_supported(lib)) { | |
580 | free(lib->mtd); | |
581 | free(lib->sysfs_mtd); | |
582 | free(lib->mtd_name); | |
583 | lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL; | |
584 | return lib; | |
585 | } | |
586 | ||
587 | lib->mtd_dev = mkpath(lib->mtd, MTD_DEV); | |
588 | if (!lib->mtd_dev) | |
589 | goto out_error; | |
590 | ||
591 | lib->mtd_type = mkpath(lib->mtd, MTD_TYPE); | |
592 | if (!lib->mtd_type) | |
593 | goto out_error; | |
594 | ||
595 | lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE); | |
596 | if (!lib->mtd_eb_size) | |
597 | goto out_error; | |
598 | ||
599 | lib->mtd_size = mkpath(lib->mtd, MTD_SIZE); | |
600 | if (!lib->mtd_size) | |
601 | goto out_error; | |
602 | ||
603 | lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE); | |
604 | if (!lib->mtd_min_io_size) | |
605 | goto out_error; | |
606 | ||
607 | lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE); | |
608 | if (!lib->mtd_subpage_size) | |
609 | goto out_error; | |
610 | ||
611 | lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE); | |
612 | if (!lib->mtd_oob_size) | |
613 | goto out_error; | |
614 | ||
615 | lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT); | |
616 | if (!lib->mtd_region_cnt) | |
617 | goto out_error; | |
618 | ||
619 | lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS); | |
620 | if (!lib->mtd_flags) | |
621 | goto out_error; | |
622 | ||
623 | lib->sysfs_supported = 1; | |
624 | return lib; | |
625 | ||
626 | out_error: | |
627 | libmtd_close((libmtd_t)lib); | |
628 | return NULL; | |
629 | } | |
630 | ||
631 | void libmtd_close(libmtd_t desc) | |
632 | { | |
633 | struct libmtd *lib = (struct libmtd *)desc; | |
634 | ||
635 | free(lib->mtd_flags); | |
636 | free(lib->mtd_region_cnt); | |
637 | free(lib->mtd_oob_size); | |
638 | free(lib->mtd_subpage_size); | |
639 | free(lib->mtd_min_io_size); | |
640 | free(lib->mtd_size); | |
641 | free(lib->mtd_eb_size); | |
642 | free(lib->mtd_type); | |
643 | free(lib->mtd_dev); | |
644 | free(lib->mtd_name); | |
645 | free(lib->mtd); | |
646 | free(lib->sysfs_mtd); | |
647 | free(lib); | |
648 | } | |
649 | ||
650 | int mtd_dev_present(libmtd_t desc, int mtd_num) { | |
651 | struct stat st; | |
652 | struct libmtd *lib = (struct libmtd *)desc; | |
653 | ||
654 | if (!lib->sysfs_supported) { | |
655 | return legacy_dev_present(mtd_num) == 1; | |
656 | } else { | |
657 | char file[strlen(lib->mtd) + 10]; | |
658 | ||
659 | sprintf(file, lib->mtd, mtd_num); | |
660 | return !stat(file, &st); | |
661 | } | |
662 | } | |
663 | ||
664 | int mtd_get_info(libmtd_t desc, struct mtd_info *info) | |
665 | { | |
666 | DIR *sysfs_mtd; | |
667 | struct dirent *dirent; | |
668 | struct libmtd *lib = (struct libmtd *)desc; | |
669 | ||
670 | memset(info, 0, sizeof(struct mtd_info)); | |
671 | ||
672 | if (!lib->sysfs_supported) | |
673 | return legacy_mtd_get_info(info); | |
674 | ||
675 | info->sysfs_supported = 1; | |
676 | ||
677 | /* | |
678 | * We have to scan the MTD sysfs directory to identify how many MTD | |
679 | * devices are present. | |
680 | */ | |
681 | sysfs_mtd = opendir(lib->sysfs_mtd); | |
682 | if (!sysfs_mtd) { | |
683 | if (errno == ENOENT) { | |
684 | errno = ENODEV; | |
685 | return -1; | |
686 | } | |
687 | return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); | |
688 | } | |
689 | ||
690 | info->lowest_mtd_num = INT_MAX; | |
691 | while (1) { | |
692 | int mtd_num, ret; | |
693 | char tmp_buf[256]; | |
694 | ||
695 | errno = 0; | |
696 | dirent = readdir(sysfs_mtd); | |
697 | if (!dirent) | |
698 | break; | |
699 | ||
700 | if (strlen(dirent->d_name) >= 255) { | |
701 | errmsg("invalid entry in %s: \"%s\"", | |
702 | lib->sysfs_mtd, dirent->d_name); | |
703 | errno = EINVAL; | |
704 | goto out_close; | |
705 | } | |
706 | ||
707 | ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", | |
708 | &mtd_num, tmp_buf); | |
709 | if (ret == 1) { | |
710 | info->mtd_dev_cnt += 1; | |
711 | if (mtd_num > info->highest_mtd_num) | |
712 | info->highest_mtd_num = mtd_num; | |
713 | if (mtd_num < info->lowest_mtd_num) | |
714 | info->lowest_mtd_num = mtd_num; | |
715 | } | |
716 | } | |
717 | ||
718 | if (!dirent && errno) { | |
719 | sys_errmsg("readdir failed on \"%s\"", lib->sysfs_mtd); | |
720 | goto out_close; | |
721 | } | |
722 | ||
723 | if (closedir(sysfs_mtd)) | |
724 | return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); | |
725 | ||
726 | if (info->lowest_mtd_num == INT_MAX) | |
727 | info->lowest_mtd_num = 0; | |
728 | ||
729 | return 0; | |
730 | ||
731 | out_close: | |
732 | closedir(sysfs_mtd); | |
733 | return -1; | |
734 | } | |
735 | ||
736 | int mtd_get_dev_info1(libmtd_t desc, int mtd_num, struct mtd_dev_info *mtd) | |
737 | { | |
738 | int ret; | |
739 | struct libmtd *lib = (struct libmtd *)desc; | |
740 | ||
741 | memset(mtd, 0, sizeof(struct mtd_dev_info)); | |
742 | mtd->mtd_num = mtd_num; | |
743 | ||
744 | if (!mtd_dev_present(desc, mtd_num)) { | |
745 | errno = ENODEV; | |
746 | return -1; | |
747 | } else if (!lib->sysfs_supported) | |
748 | return legacy_get_dev_info1(mtd_num, mtd); | |
749 | ||
750 | if (dev_get_major(lib, mtd_num, &mtd->major, &mtd->minor)) | |
751 | return -1; | |
752 | ||
753 | ret = dev_read_data(lib->mtd_name, mtd_num, &mtd->name, | |
754 | MTD_NAME_MAX + 1); | |
755 | if (ret < 0) | |
756 | return -1; | |
757 | ((char *)mtd->name)[ret - 1] = '\0'; | |
758 | ||
759 | ret = dev_read_data(lib->mtd_type, mtd_num, &mtd->type_str, | |
760 | MTD_TYPE_MAX + 1); | |
761 | if (ret < 0) | |
762 | return -1; | |
763 | ((char *)mtd->type_str)[ret - 1] = '\0'; | |
764 | ||
765 | if (dev_read_pos_int(lib->mtd_eb_size, mtd_num, &mtd->eb_size)) | |
766 | return -1; | |
767 | if (dev_read_pos_ll(lib->mtd_size, mtd_num, &mtd->size)) | |
768 | return -1; | |
769 | if (dev_read_pos_int(lib->mtd_min_io_size, mtd_num, &mtd->min_io_size)) | |
770 | return -1; | |
771 | if (dev_read_pos_int(lib->mtd_subpage_size, mtd_num, &mtd->subpage_size)) | |
772 | return -1; | |
773 | if (dev_read_pos_int(lib->mtd_oob_size, mtd_num, &mtd->oob_size)) | |
774 | return -1; | |
775 | if (dev_read_pos_int(lib->mtd_region_cnt, mtd_num, &mtd->region_cnt)) | |
776 | return -1; | |
777 | if (dev_read_hex_int(lib->mtd_flags, mtd_num, &ret)) | |
778 | return -1; | |
779 | mtd->writable = !!(ret & MTD_WRITEABLE); | |
780 | ||
781 | mtd->eb_cnt = mtd->size / mtd->eb_size; | |
782 | mtd->type = type_str2int(mtd->type_str); | |
783 | mtd->bb_allowed = !!(mtd->type == MTD_NANDFLASH || | |
784 | mtd->type == MTD_MLCNANDFLASH); | |
785 | ||
786 | return 0; | |
787 | } | |
788 | ||
789 | int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd) | |
790 | { | |
791 | int mtd_num; | |
792 | struct libmtd *lib = (struct libmtd *)desc; | |
793 | ||
794 | if (!lib->sysfs_supported) | |
795 | return legacy_get_dev_info(node, mtd); | |
796 | ||
797 | if (dev_node2num(lib, node, &mtd_num)) | |
798 | return -1; | |
799 | ||
800 | return mtd_get_dev_info1(desc, mtd_num, mtd); | |
801 | } | |
802 | ||
803 | static inline int mtd_ioctl_error(const struct mtd_dev_info *mtd, int eb, | |
804 | const char *sreq) | |
805 | { | |
806 | return sys_errmsg("%s ioctl failed for eraseblock %d (mtd%d)", | |
807 | sreq, eb, mtd->mtd_num); | |
808 | } | |
809 | ||
810 | static int mtd_valid_erase_block(const struct mtd_dev_info *mtd, int eb) | |
811 | { | |
812 | if (eb < 0 || eb >= mtd->eb_cnt) { | |
813 | errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", | |
814 | eb, mtd->mtd_num, mtd->eb_cnt); | |
815 | errno = EINVAL; | |
816 | return -1; | |
817 | } | |
818 | return 0; | |
819 | } | |
820 | ||
821 | static int mtd_xlock(const struct mtd_dev_info *mtd, int fd, int eb, int req, | |
822 | const char *sreq) | |
823 | { | |
824 | int ret; | |
825 | struct erase_info_user ei; | |
826 | ||
827 | ret = mtd_valid_erase_block(mtd, eb); | |
828 | if (ret) | |
829 | return ret; | |
830 | ||
831 | ei.start = eb * mtd->eb_size; | |
832 | ei.length = mtd->eb_size; | |
833 | ||
834 | ret = ioctl(fd, req, &ei); | |
835 | if (ret < 0) | |
836 | return mtd_ioctl_error(mtd, eb, sreq); | |
837 | ||
838 | return 0; | |
839 | } | |
840 | #define mtd_xlock(mtd, fd, eb, req) mtd_xlock(mtd, fd, eb, req, #req) | |
841 | ||
842 | int mtd_lock(const struct mtd_dev_info *mtd, int fd, int eb) | |
843 | { | |
844 | return mtd_xlock(mtd, fd, eb, MEMLOCK); | |
845 | } | |
846 | ||
847 | int mtd_unlock(const struct mtd_dev_info *mtd, int fd, int eb) | |
848 | { | |
849 | return mtd_xlock(mtd, fd, eb, MEMUNLOCK); | |
850 | } | |
851 | ||
852 | int mtd_erase(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb) | |
853 | { | |
854 | int ret; | |
855 | struct libmtd *lib = (struct libmtd *)desc; | |
856 | struct erase_info_user64 ei64; | |
857 | struct erase_info_user ei; | |
858 | ||
859 | ret = mtd_valid_erase_block(mtd, eb); | |
860 | if (ret) | |
861 | return ret; | |
862 | ||
863 | ei64.start = (__u64)eb * mtd->eb_size; | |
864 | ei64.length = mtd->eb_size; | |
865 | ||
866 | if (lib->offs64_ioctls == OFFS64_IOCTLS_SUPPORTED || | |
867 | lib->offs64_ioctls == OFFS64_IOCTLS_UNKNOWN) { | |
868 | ret = ioctl(fd, MEMERASE64, &ei64); | |
869 | if (ret == 0) | |
870 | return ret; | |
871 | ||
872 | if (errno != ENOTTY || | |
873 | lib->offs64_ioctls != OFFS64_IOCTLS_UNKNOWN) | |
874 | return mtd_ioctl_error(mtd, eb, "MEMERASE64"); | |
875 | ||
876 | /* | |
877 | * MEMERASE64 support was added in kernel version 2.6.31, so | |
878 | * probably we are working with older kernel and this ioctl is | |
879 | * not supported. | |
880 | */ | |
881 | lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; | |
882 | } | |
883 | ||
884 | if (ei64.start + ei64.length > 0xFFFFFFFF) { | |
885 | errmsg("this system can address only %u eraseblocks", | |
886 | 0xFFFFFFFFU / mtd->eb_size); | |
887 | errno = EINVAL; | |
888 | return -1; | |
889 | } | |
890 | ||
891 | ei.start = ei64.start; | |
892 | ei.length = ei64.length; | |
893 | ret = ioctl(fd, MEMERASE, &ei); | |
894 | if (ret < 0) | |
895 | return mtd_ioctl_error(mtd, eb, "MEMERASE"); | |
896 | return 0; | |
897 | } | |
898 | ||
899 | int mtd_regioninfo(int fd, int regidx, struct region_info_user *reginfo) | |
900 | { | |
901 | int ret; | |
902 | ||
903 | if (regidx < 0) { | |
904 | errno = ENODEV; | |
905 | return -1; | |
906 | } | |
907 | ||
908 | reginfo->regionindex = regidx; | |
909 | ||
910 | ret = ioctl(fd, MEMGETREGIONINFO, reginfo); | |
911 | if (ret < 0) | |
912 | return sys_errmsg("%s ioctl failed for erase region %d", | |
913 | "MEMGETREGIONINFO", regidx); | |
914 | ||
915 | return 0; | |
916 | } | |
917 | ||
918 | int mtd_is_locked(const struct mtd_dev_info *mtd, int fd, int eb) | |
919 | { | |
920 | int ret; | |
921 | erase_info_t ei; | |
922 | ||
923 | ei.start = eb * mtd->eb_size; | |
924 | ei.length = mtd->eb_size; | |
925 | ||
926 | ret = ioctl(fd, MEMISLOCKED, &ei); | |
927 | if (ret < 0) { | |
928 | if (errno != ENOTTY && errno != EOPNOTSUPP) | |
929 | return mtd_ioctl_error(mtd, eb, "MEMISLOCKED"); | |
930 | else | |
931 | errno = EOPNOTSUPP; | |
932 | } | |
933 | ||
934 | return ret; | |
935 | } | |
936 | ||
937 | /* Patterns to write to a physical eraseblock when torturing it */ | |
938 | static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; | |
939 | ||
940 | /** | |
941 | * check_pattern - check if buffer contains only a certain byte pattern. | |
942 | * @buf: buffer to check | |
943 | * @patt: the pattern to check | |
944 | * @size: buffer size in bytes | |
945 | * | |
946 | * This function returns %1 in there are only @patt bytes in @buf, and %0 if | |
947 | * something else was also found. | |
948 | */ | |
949 | static int check_pattern(const void *buf, uint8_t patt, int size) | |
950 | { | |
951 | int i; | |
952 | ||
953 | for (i = 0; i < size; i++) | |
954 | if (((const uint8_t *)buf)[i] != patt) | |
955 | return 0; | |
956 | return 1; | |
957 | } | |
958 | ||
959 | int mtd_torture(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb) | |
960 | { | |
961 | int err, i, patt_count; | |
962 | void *buf; | |
963 | ||
964 | normsg("run torture test for PEB %d", eb); | |
59f94d26 | 965 | patt_count = FIO_ARRAY_SIZE(patterns); |
94e9e396 DE |
966 | |
967 | buf = xmalloc(mtd->eb_size); | |
968 | ||
969 | for (i = 0; i < patt_count; i++) { | |
970 | err = mtd_erase(desc, mtd, fd, eb); | |
971 | if (err) | |
972 | goto out; | |
973 | ||
974 | /* Make sure the PEB contains only 0xFF bytes */ | |
975 | err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size); | |
976 | if (err) | |
977 | goto out; | |
978 | ||
979 | err = check_pattern(buf, 0xFF, mtd->eb_size); | |
980 | if (err == 0) { | |
981 | errmsg("erased PEB %d, but a non-0xFF byte found", eb); | |
982 | errno = EIO; | |
983 | goto out; | |
984 | } | |
985 | ||
986 | /* Write a pattern and check it */ | |
987 | memset(buf, patterns[i], mtd->eb_size); | |
988 | err = mtd_write(desc, mtd, fd, eb, 0, buf, mtd->eb_size, NULL, | |
989 | 0, 0); | |
990 | if (err) | |
991 | goto out; | |
992 | ||
993 | memset(buf, ~patterns[i], mtd->eb_size); | |
994 | err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size); | |
995 | if (err) | |
996 | goto out; | |
997 | ||
998 | err = check_pattern(buf, patterns[i], mtd->eb_size); | |
999 | if (err == 0) { | |
1000 | errmsg("pattern %x checking failed for PEB %d", | |
1001 | patterns[i], eb); | |
1002 | errno = EIO; | |
1003 | goto out; | |
1004 | } | |
1005 | } | |
1006 | ||
94e9e396 DE |
1007 | normsg("PEB %d passed torture test, do not mark it a bad", eb); |
1008 | ||
1009 | out: | |
1010 | free(buf); | |
1011 | return -1; | |
1012 | } | |
1013 | ||
1014 | int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb) | |
1015 | { | |
1016 | int ret; | |
1017 | loff_t seek; | |
1018 | ||
1019 | ret = mtd_valid_erase_block(mtd, eb); | |
1020 | if (ret) | |
1021 | return ret; | |
1022 | ||
1023 | if (!mtd->bb_allowed) | |
1024 | return 0; | |
1025 | ||
1026 | seek = (loff_t)eb * mtd->eb_size; | |
1027 | ret = ioctl(fd, MEMGETBADBLOCK, &seek); | |
1028 | if (ret == -1) | |
1029 | return mtd_ioctl_error(mtd, eb, "MEMGETBADBLOCK"); | |
1030 | return ret; | |
1031 | } | |
1032 | ||
1033 | int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb) | |
1034 | { | |
1035 | int ret; | |
1036 | loff_t seek; | |
1037 | ||
1038 | if (!mtd->bb_allowed) { | |
1039 | errno = EINVAL; | |
1040 | return -1; | |
1041 | } | |
1042 | ||
1043 | ret = mtd_valid_erase_block(mtd, eb); | |
1044 | if (ret) | |
1045 | return ret; | |
1046 | ||
1047 | seek = (loff_t)eb * mtd->eb_size; | |
1048 | ret = ioctl(fd, MEMSETBADBLOCK, &seek); | |
1049 | if (ret == -1) | |
1050 | return mtd_ioctl_error(mtd, eb, "MEMSETBADBLOCK"); | |
1051 | return 0; | |
1052 | } | |
1053 | ||
1054 | int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs, | |
1055 | void *buf, int len) | |
1056 | { | |
1057 | int ret, rd = 0; | |
1058 | off_t seek; | |
1059 | ||
1060 | ret = mtd_valid_erase_block(mtd, eb); | |
1061 | if (ret) | |
1062 | return ret; | |
1063 | ||
1064 | if (offs < 0 || offs + len > mtd->eb_size) { | |
1065 | errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", | |
1066 | offs, len, mtd->mtd_num, mtd->eb_size); | |
1067 | errno = EINVAL; | |
1068 | return -1; | |
1069 | } | |
1070 | ||
1071 | /* Seek to the beginning of the eraseblock */ | |
1072 | seek = (off_t)eb * mtd->eb_size + offs; | |
1073 | if (lseek(fd, seek, SEEK_SET) != seek) | |
1074 | return sys_errmsg("cannot seek mtd%d to offset %"PRIdoff_t, | |
1075 | mtd->mtd_num, seek); | |
1076 | ||
1077 | while (rd < len) { | |
1078 | ret = read(fd, buf, len); | |
1079 | if (ret < 0) | |
1080 | return sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)", | |
1081 | len, mtd->mtd_num, eb, offs); | |
1082 | rd += ret; | |
1083 | } | |
1084 | ||
1085 | return 0; | |
1086 | } | |
1087 | ||
1088 | static int legacy_auto_oob_layout(const struct mtd_dev_info *mtd, int fd, | |
1089 | int ooblen, void *oob) { | |
1090 | struct nand_oobinfo old_oobinfo; | |
1091 | int start, len; | |
1092 | uint8_t *tmp_buf; | |
1093 | ||
1094 | /* Read the current oob info */ | |
1095 | if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo)) | |
1096 | return sys_errmsg("MEMGETOOBSEL failed"); | |
1097 | ||
1098 | tmp_buf = malloc(ooblen); | |
1099 | memcpy(tmp_buf, oob, ooblen); | |
1100 | ||
1101 | /* | |
1102 | * We use autoplacement and have the oobinfo with the autoplacement | |
1103 | * information from the kernel available | |
1104 | */ | |
1105 | if (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { | |
1106 | int i, tags_pos = 0; | |
1107 | for (i = 0; old_oobinfo.oobfree[i][1]; i++) { | |
1108 | /* Set the reserved bytes to 0xff */ | |
1109 | start = old_oobinfo.oobfree[i][0]; | |
1110 | len = old_oobinfo.oobfree[i][1]; | |
1111 | memcpy(oob + start, tmp_buf + tags_pos, len); | |
1112 | tags_pos += len; | |
1113 | } | |
1114 | } else { | |
1115 | /* Set at least the ecc byte positions to 0xff */ | |
1116 | start = old_oobinfo.eccbytes; | |
1117 | len = mtd->oob_size - start; | |
1118 | memcpy(oob + start, tmp_buf + start, len); | |
1119 | } | |
471bb52b | 1120 | free(tmp_buf); |
94e9e396 DE |
1121 | |
1122 | return 0; | |
1123 | } | |
1124 | ||
1125 | int mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb, | |
1126 | int offs, void *data, int len, void *oob, int ooblen, | |
1127 | uint8_t mode) | |
1128 | { | |
1129 | int ret; | |
1130 | off_t seek; | |
1131 | struct mtd_write_req ops; | |
1132 | ||
1133 | ret = mtd_valid_erase_block(mtd, eb); | |
1134 | if (ret) | |
1135 | return ret; | |
1136 | ||
1137 | if (offs < 0 || offs + len > mtd->eb_size) { | |
1138 | errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", | |
1139 | offs, len, mtd->mtd_num, mtd->eb_size); | |
1140 | errno = EINVAL; | |
1141 | return -1; | |
1142 | } | |
1143 | if (offs % mtd->subpage_size) { | |
1144 | errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", | |
1145 | offs, mtd->mtd_num, mtd->subpage_size); | |
1146 | errno = EINVAL; | |
1147 | return -1; | |
1148 | } | |
1149 | if (len % mtd->subpage_size) { | |
1150 | errmsg("write length %d is not aligned to mtd%d min. I/O size %d", | |
1151 | len, mtd->mtd_num, mtd->subpage_size); | |
1152 | errno = EINVAL; | |
1153 | return -1; | |
1154 | } | |
1155 | ||
1156 | /* Calculate seek address */ | |
1157 | seek = (off_t)eb * mtd->eb_size + offs; | |
1158 | ||
1159 | if (oob) { | |
1160 | ops.start = seek; | |
1161 | ops.len = len; | |
1162 | ops.ooblen = ooblen; | |
1163 | ops.usr_data = (uint64_t)(unsigned long)data; | |
1164 | ops.usr_oob = (uint64_t)(unsigned long)oob; | |
1165 | ops.mode = mode; | |
1166 | ||
1167 | ret = ioctl(fd, MEMWRITE, &ops); | |
1168 | if (ret == 0) | |
1169 | return 0; | |
1170 | else if (errno != ENOTTY && errno != EOPNOTSUPP) | |
1171 | return mtd_ioctl_error(mtd, eb, "MEMWRITE"); | |
1172 | ||
1173 | /* Fall back to old OOB ioctl() if necessary */ | |
1174 | if (mode == MTD_OPS_AUTO_OOB) | |
1175 | if (legacy_auto_oob_layout(mtd, fd, ooblen, oob)) | |
1176 | return -1; | |
1177 | if (mtd_write_oob(desc, mtd, fd, seek, ooblen, oob) < 0) | |
1178 | return sys_errmsg("cannot write to OOB"); | |
1179 | } | |
1180 | if (data) { | |
1181 | /* Seek to the beginning of the eraseblock */ | |
1182 | if (lseek(fd, seek, SEEK_SET) != seek) | |
1183 | return sys_errmsg("cannot seek mtd%d to offset %"PRIdoff_t, | |
1184 | mtd->mtd_num, seek); | |
1185 | ret = write(fd, data, len); | |
1186 | if (ret != len) | |
1187 | return sys_errmsg("cannot write %d bytes to mtd%d " | |
1188 | "(eraseblock %d, offset %d)", | |
1189 | len, mtd->mtd_num, eb, offs); | |
1190 | } | |
1191 | ||
1192 | return 0; | |
1193 | } | |
1194 | ||
a89ba4b1 | 1195 | static int do_oob_op(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, |
94e9e396 DE |
1196 | uint64_t start, uint64_t length, void *data, unsigned int cmd64, |
1197 | unsigned int cmd) | |
1198 | { | |
1199 | int ret, oob_offs; | |
1200 | struct mtd_oob_buf64 oob64; | |
1201 | struct mtd_oob_buf oob; | |
1202 | unsigned long long max_offs; | |
1203 | const char *cmd64_str, *cmd_str; | |
1204 | struct libmtd *lib = (struct libmtd *)desc; | |
1205 | ||
1206 | if (cmd64 == MEMREADOOB64) { | |
1207 | cmd64_str = "MEMREADOOB64"; | |
1208 | cmd_str = "MEMREADOOB"; | |
1209 | } else { | |
1210 | cmd64_str = "MEMWRITEOOB64"; | |
1211 | cmd_str = "MEMWRITEOOB"; | |
1212 | } | |
1213 | ||
1214 | max_offs = (unsigned long long)mtd->eb_cnt * mtd->eb_size; | |
1215 | if (start >= max_offs) { | |
1216 | errmsg("bad page address %" PRIu64 ", mtd%d has %d eraseblocks (%llu bytes)", | |
1217 | start, mtd->mtd_num, mtd->eb_cnt, max_offs); | |
1218 | errno = EINVAL; | |
1219 | return -1; | |
1220 | } | |
1221 | ||
1222 | oob_offs = start & (mtd->min_io_size - 1); | |
1223 | if (oob_offs + length > mtd->oob_size || length == 0) { | |
1224 | errmsg("Cannot write %" PRIu64 " OOB bytes to address %" PRIu64 " (OOB offset %u) - mtd%d OOB size is only %d bytes", | |
1225 | length, start, oob_offs, mtd->mtd_num, mtd->oob_size); | |
1226 | errno = EINVAL; | |
1227 | return -1; | |
1228 | } | |
1229 | ||
1230 | oob64.start = start; | |
1231 | oob64.length = length; | |
1232 | oob64.usr_ptr = (uint64_t)(unsigned long)data; | |
1233 | ||
1234 | if (lib->offs64_ioctls == OFFS64_IOCTLS_SUPPORTED || | |
1235 | lib->offs64_ioctls == OFFS64_IOCTLS_UNKNOWN) { | |
1236 | ret = ioctl(fd, cmd64, &oob64); | |
1237 | if (ret == 0) | |
1238 | return ret; | |
1239 | ||
1240 | if (errno != ENOTTY || | |
1241 | lib->offs64_ioctls != OFFS64_IOCTLS_UNKNOWN) { | |
1242 | sys_errmsg("%s ioctl failed for mtd%d, offset %" PRIu64 " (eraseblock %" PRIu64 ")", | |
1243 | cmd64_str, mtd->mtd_num, start, start / mtd->eb_size); | |
1244 | } | |
1245 | ||
1246 | /* | |
1247 | * MEMREADOOB64/MEMWRITEOOB64 support was added in kernel | |
1248 | * version 2.6.31, so probably we are working with older kernel | |
1249 | * and these ioctls are not supported. | |
1250 | */ | |
1251 | lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; | |
1252 | } | |
1253 | ||
1254 | if (oob64.start > 0xFFFFFFFFULL) { | |
1255 | errmsg("this system can address only up to address %lu", | |
1256 | 0xFFFFFFFFUL); | |
1257 | errno = EINVAL; | |
1258 | return -1; | |
1259 | } | |
1260 | ||
1261 | oob.start = oob64.start; | |
1262 | oob.length = oob64.length; | |
1263 | oob.ptr = data; | |
1264 | ||
1265 | ret = ioctl(fd, cmd, &oob); | |
1266 | if (ret < 0) | |
1267 | sys_errmsg("%s ioctl failed for mtd%d, offset %" PRIu64 " (eraseblock %" PRIu64 ")", | |
1268 | cmd_str, mtd->mtd_num, start, start / mtd->eb_size); | |
1269 | return ret; | |
1270 | } | |
1271 | ||
1272 | int mtd_read_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, | |
1273 | uint64_t start, uint64_t length, void *data) | |
1274 | { | |
1275 | return do_oob_op(desc, mtd, fd, start, length, data, | |
1276 | MEMREADOOB64, MEMREADOOB); | |
1277 | } | |
1278 | ||
1279 | int mtd_write_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, | |
1280 | uint64_t start, uint64_t length, void *data) | |
1281 | { | |
1282 | return do_oob_op(desc, mtd, fd, start, length, data, | |
1283 | MEMWRITEOOB64, MEMWRITEOOB); | |
1284 | } | |
1285 | ||
1286 | int mtd_write_img(const struct mtd_dev_info *mtd, int fd, int eb, int offs, | |
1287 | const char *img_name) | |
1288 | { | |
1289 | int tmp, ret, in_fd, len, written = 0; | |
1290 | off_t seek; | |
1291 | struct stat st; | |
1292 | char *buf; | |
1293 | ||
1294 | ret = mtd_valid_erase_block(mtd, eb); | |
1295 | if (ret) | |
1296 | return ret; | |
1297 | ||
1298 | if (offs < 0 || offs >= mtd->eb_size) { | |
1299 | errmsg("bad offset %d, mtd%d eraseblock size is %d", | |
1300 | offs, mtd->mtd_num, mtd->eb_size); | |
1301 | errno = EINVAL; | |
1302 | return -1; | |
1303 | } | |
1304 | if (offs % mtd->subpage_size) { | |
1305 | errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", | |
1306 | offs, mtd->mtd_num, mtd->subpage_size); | |
1307 | errno = EINVAL; | |
1308 | return -1; | |
1309 | } | |
1310 | ||
1311 | in_fd = open(img_name, O_RDONLY | O_CLOEXEC); | |
1312 | if (in_fd == -1) | |
1313 | return sys_errmsg("cannot open \"%s\"", img_name); | |
1314 | ||
1315 | if (fstat(in_fd, &st)) { | |
1316 | sys_errmsg("cannot stat %s", img_name); | |
1317 | goto out_close; | |
1318 | } | |
1319 | ||
1320 | len = st.st_size; | |
1321 | if (len % mtd->subpage_size) { | |
1322 | errmsg("size of \"%s\" is %d byte, which is not aligned to " | |
1323 | "mtd%d min. I/O size %d", img_name, len, mtd->mtd_num, | |
1324 | mtd->subpage_size); | |
1325 | errno = EINVAL; | |
1326 | goto out_close; | |
1327 | } | |
1328 | tmp = (offs + len + mtd->eb_size - 1) / mtd->eb_size; | |
1329 | if (eb + tmp > mtd->eb_cnt) { | |
1330 | errmsg("\"%s\" image size is %d bytes, mtd%d size is %d " | |
1331 | "eraseblocks, the image does not fit if we write it " | |
1332 | "starting from eraseblock %d, offset %d", | |
1333 | img_name, len, mtd->mtd_num, mtd->eb_cnt, eb, offs); | |
1334 | errno = EINVAL; | |
1335 | goto out_close; | |
1336 | } | |
1337 | ||
1338 | /* Seek to the beginning of the eraseblock */ | |
1339 | seek = (off_t)eb * mtd->eb_size + offs; | |
1340 | if (lseek(fd, seek, SEEK_SET) != seek) { | |
1341 | sys_errmsg("cannot seek mtd%d to offset %"PRIdoff_t, | |
1342 | mtd->mtd_num, seek); | |
1343 | goto out_close; | |
1344 | } | |
1345 | ||
1346 | buf = xmalloc(mtd->eb_size); | |
1347 | ||
1348 | while (written < len) { | |
1349 | int rd = 0; | |
1350 | ||
1351 | do { | |
1352 | ret = read(in_fd, buf, mtd->eb_size - offs - rd); | |
1353 | if (ret == -1) { | |
1354 | sys_errmsg("cannot read \"%s\"", img_name); | |
1355 | goto out_free; | |
1356 | } | |
1357 | rd += ret; | |
1358 | } while (ret && rd < mtd->eb_size - offs); | |
1359 | ||
1360 | ret = write(fd, buf, rd); | |
1361 | if (ret != rd) { | |
1362 | sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)", | |
1363 | len, mtd->mtd_num, eb, offs); | |
1364 | goto out_free; | |
1365 | } | |
1366 | ||
1367 | offs = 0; | |
1368 | eb += 1; | |
1369 | written += rd; | |
1370 | } | |
1371 | ||
1372 | free(buf); | |
1373 | close(in_fd); | |
1374 | return 0; | |
1375 | ||
1376 | out_free: | |
1377 | free(buf); | |
1378 | out_close: | |
1379 | close(in_fd); | |
1380 | return -1; | |
1381 | } | |
1382 | ||
1383 | int mtd_probe_node(libmtd_t desc, const char *node) | |
1384 | { | |
1385 | struct stat st; | |
1386 | struct mtd_info info; | |
1387 | int i, mjr, mnr; | |
1388 | struct libmtd *lib = (struct libmtd *)desc; | |
1389 | ||
1390 | if (stat(node, &st)) | |
1391 | return sys_errmsg("cannot get information about \"%s\"", node); | |
1392 | ||
1393 | if (!S_ISCHR(st.st_mode)) { | |
1394 | errmsg("\"%s\" is not a character device", node); | |
1395 | errno = EINVAL; | |
1396 | return -1; | |
1397 | } | |
1398 | ||
1399 | mjr = major(st.st_rdev); | |
1400 | mnr = minor(st.st_rdev); | |
1401 | ||
1402 | if (mtd_get_info((libmtd_t *)lib, &info)) | |
1403 | return -1; | |
1404 | ||
1405 | if (!lib->sysfs_supported) | |
1406 | return 0; | |
1407 | ||
1408 | for (i = info.lowest_mtd_num; i <= info.highest_mtd_num; i++) { | |
1409 | int mjr1, mnr1, ret; | |
1410 | ||
1411 | ret = dev_get_major(lib, i, &mjr1, &mnr1); | |
1412 | if (ret) { | |
1413 | if (errno == ENOENT) | |
1414 | continue; | |
1415 | if (!errno) | |
1416 | break; | |
1417 | return -1; | |
1418 | } | |
1419 | ||
1420 | if (mjr1 == mjr && mnr1 == mnr) | |
1421 | return 1; | |
1422 | } | |
1423 | ||
1424 | errno = 0; | |
1425 | return -1; | |
1426 | } |