blob: ca3bb1ecc2a1be31062e75df9b12bceafe7864e5 [file] [log] [blame]
David Hendrickscebee892015-05-23 20:30:30 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright 2015 Google Inc.
Nikolai Artemiev255f3352020-07-17 16:38:50 +10005 * Copyright 2018-present Facebook, Inc.
David Hendrickscebee892015-05-23 20:30:30 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
David Hendrickscebee892015-05-23 20:30:30 -070015 */
16
17#include <ctype.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <inttypes.h>
21#include <libgen.h>
Gwendal Grignoudb133582018-03-23 12:28:20 -070022#include <limits.h>
David Hendrickscebee892015-05-23 20:30:30 -070023#include <stdio.h>
24#include <stdlib.h>
25#include <mtd/mtd-user.h>
26#include <string.h>
27#include <sys/ioctl.h>
28#include <sys/stat.h>
29#include <unistd.h>
30
31#include "file.h"
32#include "flash.h"
33#include "programmer.h"
David Hendricks85f61c52015-06-04 19:52:59 -070034#include "writeprotect.h"
David Hendrickscebee892015-05-23 20:30:30 -070035
36#define LINUX_DEV_ROOT "/dev"
37#define LINUX_MTD_SYSFS_ROOT "/sys/class/mtd"
38
David Hendrickscebee892015-05-23 20:30:30 -070039static int dev_fd = -1;
40
David Hendrickscebee892015-05-23 20:30:30 -070041static int mtd_device_is_writeable;
42
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -070043static int mtd_no_erase;
44
David Hendrickscebee892015-05-23 20:30:30 -070045/* Size info is presented in bytes in sysfs. */
46static unsigned long int mtd_total_size;
47static unsigned long int mtd_numeraseregions;
48static unsigned long int mtd_erasesize; /* only valid if numeraseregions is 0 */
49
David Hendricks85f61c52015-06-04 19:52:59 -070050static struct wp wp_mtd; /* forward declaration */
51
David Hendrickscebee892015-05-23 20:30:30 -070052/* read a string from a sysfs file and sanitize it */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100053static int read_sysfs_string(const char *sysfs_path, const char *filename, char *buf, int len)
David Hendrickscebee892015-05-23 20:30:30 -070054{
55 int fd, bytes_read, i;
56 char path[strlen(LINUX_MTD_SYSFS_ROOT) + 32];
57
58 snprintf(path, sizeof(path), "%s/%s", sysfs_path, filename);
59
60 if ((fd = open(path, O_RDONLY)) < 0) {
61 msg_perr("Cannot open %s\n", path);
62 return 1;
63 }
64
65 if ((bytes_read = read(fd, buf, len - 1)) < 0) {
66 msg_perr("Cannot read %s\n", path);
67 close(fd);
68 return 1;
69 }
70
71 buf[bytes_read] = '\0';
72
73 /*
74 * Files from sysfs sometimes contain a newline or other garbage that
75 * can confuse functions like strtoul() and ruin formatting in print
76 * statements. Replace the first non-printable character (space is
77 * considered printable) with a proper string terminator.
78 */
79 for (i = 0; i < len; i++) {
80 if (!isprint(buf[i])) {
81 buf[i] = '\0';
82 break;
83 }
84 }
85
86 close(fd);
87 return 0;
88}
89
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100090static int read_sysfs_int(const char *sysfs_path, const char *filename, unsigned long int *val)
David Hendrickscebee892015-05-23 20:30:30 -070091{
David Hendrickscebee892015-05-23 20:30:30 -070092 char buf[32];
93 char *endptr;
94
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100095 if (read_sysfs_string(sysfs_path, filename, buf, sizeof(buf)))
David Hendrickscebee892015-05-23 20:30:30 -070096 return 1;
97
98 errno = 0;
99 *val = strtoul(buf, &endptr, 0);
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000100 if (*endptr != '\0') {
David Hendrickscebee892015-05-23 20:30:30 -0700101 msg_perr("Error reading %s\n", filename);
102 return 1;
103 }
104
105 if (errno) {
106 msg_perr("Error reading %s: %s\n", filename, strerror(errno));
107 return 1;
108 }
109
110 return 0;
111}
112
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000113static int popcnt(unsigned int u)
114{
115 int count = 0;
116
117 while (u) {
118 u &= u - 1;
119 count++;
120 }
121
122 return count;
123}
124
David Hendrickscebee892015-05-23 20:30:30 -0700125/* returns 0 to indicate success, non-zero to indicate error */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000126static int get_mtd_info(const char *sysfs_path)
David Hendrickscebee892015-05-23 20:30:30 -0700127{
128 unsigned long int tmp;
129 char mtd_device_name[32];
130
131 /* Flags */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000132 if (read_sysfs_int(sysfs_path, "flags", &tmp))
David Hendrickscebee892015-05-23 20:30:30 -0700133 return 1;
134 if (tmp & MTD_WRITEABLE) {
135 /* cache for later use by write function */
136 mtd_device_is_writeable = 1;
137 }
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700138 if (tmp & MTD_NO_ERASE) {
139 mtd_no_erase = 1;
140 }
David Hendrickscebee892015-05-23 20:30:30 -0700141
142 /* Device name */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000143 if (read_sysfs_string(sysfs_path, "name", mtd_device_name, sizeof(mtd_device_name)))
David Hendrickscebee892015-05-23 20:30:30 -0700144 return 1;
145
146 /* Total size */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000147 if (read_sysfs_int(sysfs_path, "size", &mtd_total_size))
David Hendrickscebee892015-05-23 20:30:30 -0700148 return 1;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000149 if (popcnt(mtd_total_size) != 1) {
David Hendrickscebee892015-05-23 20:30:30 -0700150 msg_perr("MTD size is not a power of 2\n");
151 return 1;
152 }
153
154 /* Erase size */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000155 if (read_sysfs_int(sysfs_path, "erasesize", &mtd_erasesize))
David Hendrickscebee892015-05-23 20:30:30 -0700156 return 1;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000157 if (popcnt(mtd_erasesize) != 1) {
David Hendrickscebee892015-05-23 20:30:30 -0700158 msg_perr("MTD erase size is not a power of 2\n");
159 return 1;
160 }
161
162 /* Erase regions */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000163 if (read_sysfs_int(sysfs_path, "numeraseregions", &mtd_numeraseregions))
David Hendrickscebee892015-05-23 20:30:30 -0700164 return 1;
165 if (mtd_numeraseregions != 0) {
166 msg_perr("Non-uniform eraseblock size is unsupported.\n");
167 return 1;
168 }
169
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000170 msg_pdbg("%s: device_name: \"%s\", is_writeable: %d, "
David Hendrickscebee892015-05-23 20:30:30 -0700171 "numeraseregions: %lu, total_size: %lu, erasesize: %lu\n",
172 __func__, mtd_device_name, mtd_device_is_writeable,
173 mtd_numeraseregions, mtd_total_size, mtd_erasesize);
174
175 return 0;
176}
177
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700178static int linux_mtd_probe(struct flashctx *flash)
David Hendrickscebee892015-05-23 20:30:30 -0700179{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100180 flash->chip->wp = &wp_mtd;
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700181 if (mtd_no_erase)
182 flash->chip->feature_bits |= FEATURE_NO_ERASE;
Patrick Georgif3fa2992017-02-02 16:24:44 +0100183 flash->chip->tested = TEST_OK_PREW;
184 flash->chip->total_size = mtd_total_size / 1024; /* bytes -> kB */
185 flash->chip->block_erasers[0].eraseblocks[0].size = mtd_erasesize;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000186 flash->chip->block_erasers[0].eraseblocks[0].count = mtd_total_size / mtd_erasesize;
David Hendrickscebee892015-05-23 20:30:30 -0700187 return 1;
188}
189
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700190static int linux_mtd_read(struct flashctx *flash, uint8_t *buf,
David Hendrickscebee892015-05-23 20:30:30 -0700191 unsigned int start, unsigned int len)
192{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100193 unsigned int eb_size = flash->chip->block_erasers[0].eraseblocks[0].size;
Brian Norris99c8ad22016-05-10 15:36:02 -0700194 unsigned int i;
195
David Hendrickscebee892015-05-23 20:30:30 -0700196 if (lseek(dev_fd, start, SEEK_SET) != start) {
197 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
198 return 1;
199 }
200
Brian Norris99c8ad22016-05-10 15:36:02 -0700201 for (i = 0; i < len; ) {
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700202 /*
203 * Try to align reads to eraseblock size.
204 * FIXME: Shouldn't actually be necessary, but not all MTD
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000205 * drivers handle arbitrary large reads well.
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700206 */
Brian Norris99c8ad22016-05-10 15:36:02 -0700207 unsigned int step = eb_size - ((start + i) % eb_size);
208 step = min(step, len - i);
209
210 if (read(dev_fd, buf + i, step) != step) {
211 msg_perr("Cannot read 0x%06x bytes at 0x%06x: %s\n",
212 step, start + i, strerror(errno));
213 return 1;
214 }
215
216 i += step;
David Hendrickscebee892015-05-23 20:30:30 -0700217 }
218
219 return 0;
220}
221
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700222/* this version assumes we must divide the write request into chunks ourselves */
Patrick Georgiab8353e2017-02-03 18:32:01 +0100223static int linux_mtd_write(struct flashctx *flash, const uint8_t *buf,
David Hendrickscebee892015-05-23 20:30:30 -0700224 unsigned int start, unsigned int len)
225{
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700226 unsigned int chunksize = flash->chip->block_erasers[0].eraseblocks[0].size;
227 unsigned int i;
David Hendrickscebee892015-05-23 20:30:30 -0700228
229 if (!mtd_device_is_writeable)
230 return 1;
231
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700232 if (lseek(dev_fd, start, SEEK_SET) != start) {
233 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
234 return 1;
235 }
David Hendrickscebee892015-05-23 20:30:30 -0700236
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700237 /*
238 * Try to align writes to eraseblock size. We want these large enough
239 * to give MTD room for optimizing performance.
240 * FIXME: Shouldn't need to divide this up at all, but not all MTD
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000241 * drivers handle arbitrary large writes well.
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700242 */
243 for (i = 0; i < len; ) {
244 unsigned int step = chunksize - ((start + i) % chunksize);
245 step = min(step, len - i);
David Hendrickscebee892015-05-23 20:30:30 -0700246
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700247 if (write(dev_fd, buf + i, step) != step) {
248 msg_perr("Cannot write 0x%06x bytes at 0x%06x: %s\n",
249 step, start + i, strerror(errno));
250 return 1;
David Hendrickscebee892015-05-23 20:30:30 -0700251 }
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700252
253 i += step;
David Hendrickscebee892015-05-23 20:30:30 -0700254 }
255
256 return 0;
257}
258
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700259static int linux_mtd_erase(struct flashctx *flash,
David Hendrickscebee892015-05-23 20:30:30 -0700260 unsigned int start, unsigned int len)
261{
David Hendrickscebee892015-05-23 20:30:30 -0700262 uint32_t u;
263
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700264 if (mtd_no_erase) {
265 msg_perr("%s: device does not support erasing. Please file a "
266 "bug report at flashrom@flashrom.org\n", __func__);
267 return 1;
268 }
269
David Hendrickscebee892015-05-23 20:30:30 -0700270 if (mtd_numeraseregions != 0) {
271 /* TODO: Support non-uniform eraseblock size using
272 use MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls */
273 }
274
275 for (u = 0; u < len; u += mtd_erasesize) {
276 struct erase_info_user erase_info = {
277 .start = start + u,
278 .length = mtd_erasesize,
279 };
280
281 if (ioctl(dev_fd, MEMERASE, &erase_info) == -1) {
282 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
283 return 1;
284 }
285 }
286
287 return 0;
288}
289
Edward O'Callaghanabd30192019-05-14 15:58:19 +1000290static struct opaque_master programmer_linux_mtd = {
Brian Norris99c8ad22016-05-10 15:36:02 -0700291 /* max_data_{read,write} don't have any effect for this programmer */
David Hendrickscebee892015-05-23 20:30:30 -0700292 .max_data_read = MAX_DATA_UNSPECIFIED,
293 .max_data_write = MAX_DATA_UNSPECIFIED,
294 .probe = linux_mtd_probe,
295 .read = linux_mtd_read,
296 .write = linux_mtd_write,
297 .erase = linux_mtd_erase,
298};
299
300/* Returns 0 if setup is successful, non-zero to indicate error */
301static int linux_mtd_setup(int dev_num)
302{
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000303 char sysfs_path[32];
David Hendrickscebee892015-05-23 20:30:30 -0700304 char dev_path[16]; /* "/dev/mtdN" */
305 int ret = 1;
306
307 if (dev_num < 0) {
308 char *tmp, *p;
309
310 tmp = (char *)scanft(LINUX_MTD_SYSFS_ROOT, "type", "nor", 1);
311 if (!tmp) {
David Hendrickscf6fbe62015-09-24 14:22:39 -0700312 msg_pdbg("%s: NOR type device not found.\n", __func__);
David Hendrickscebee892015-05-23 20:30:30 -0700313 goto linux_mtd_setup_exit;
314 }
315
316 /* "tmp" should be something like "/sys/blah/mtdN/type" */
317 p = tmp + strlen(LINUX_MTD_SYSFS_ROOT);
318 while (p[0] == '/')
319 p++;
320
321 if (sscanf(p, "mtd%d", &dev_num) != 1) {
322 msg_perr("Can't obtain device number from \"%s\"\n", p);
323 free(tmp);
324 goto linux_mtd_setup_exit;
325 }
326 free(tmp);
327 }
328
329 snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d",
330 LINUX_MTD_SYSFS_ROOT, dev_num);
331 snprintf(dev_path, sizeof(dev_path), "%s/mtd%d",
332 LINUX_DEV_ROOT, dev_num);
333 msg_pdbg("%s: sysfs_path: \"%s\", dev_path: \"%s\"\n",
334 __func__, sysfs_path, dev_path);
335
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000336 struct stat s;
David Hendrickscebee892015-05-23 20:30:30 -0700337
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000338 errno = 0;
339 if (stat(dev_path, &s) < 0) {
340 msg_pdbg("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
341 goto linux_mtd_setup_exit;
342 }
343
344 if (lstat(sysfs_path, &s) < 0) {
345 msg_pdbg("Cannot stat \"%s\" : %s\n",
346 sysfs_path, strerror(errno));
347 goto linux_mtd_setup_exit;
348 }
349
350 if (get_mtd_info(sysfs_path))
David Hendrickscebee892015-05-23 20:30:30 -0700351 goto linux_mtd_setup_exit;
352
353 if ((dev_fd = open(dev_path, O_RDWR)) == -1) {
354 msg_pdbg("%s: failed to open %s: %s\n", __func__,
355 dev_path, strerror(errno));
356 goto linux_mtd_setup_exit;
357 }
358
359 ret = 0;
360linux_mtd_setup_exit:
361 return ret;
362}
363
David Hendricks93784b42016-08-09 17:00:38 -0700364static int linux_mtd_shutdown(void *data)
David Hendrickscebee892015-05-23 20:30:30 -0700365{
366 if (dev_fd != -1) {
367 close(dev_fd);
368 dev_fd = -1;
369 }
370
371 return 0;
372}
373
David Hendricksac1d25c2016-08-09 17:00:58 -0700374int linux_mtd_init(void)
David Hendrickscebee892015-05-23 20:30:30 -0700375{
376 char *param;
377 int dev_num = -1; /* linux_mtd_setup will search if dev_num < 0 */
378 int ret = 1;
379
380 if (alias && alias->type != ALIAS_HOST)
381 return 1;
382
383 param = extract_programmer_param("dev");
384 if (param) {
385 char *endptr;
386
387 dev_num = strtol(param, &endptr, 0);
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000388 if ((*endptr != '\0') || (dev_num < 0)) {
David Hendrickscebee892015-05-23 20:30:30 -0700389 msg_perr("Invalid device number %s. Use flashrom -p "
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000390 "linux_mtd:dev=N where N is a valid MTD\n"
391 "device number.\n", param);
David Hendrickscebee892015-05-23 20:30:30 -0700392 goto linux_mtd_init_exit;
393 }
394 }
395
396 if (linux_mtd_setup(dev_num))
397 goto linux_mtd_init_exit;
398
399 if (register_shutdown(linux_mtd_shutdown, NULL))
400 goto linux_mtd_init_exit;
401
Edward O'Callaghanabd30192019-05-14 15:58:19 +1000402 register_opaque_master(&programmer_linux_mtd);
David Hendrickscebee892015-05-23 20:30:30 -0700403
404 ret = 0;
405linux_mtd_init_exit:
David Hendrickscebee892015-05-23 20:30:30 -0700406 return ret;
407}
David Hendricks85f61c52015-06-04 19:52:59 -0700408
409/*
410 * Write-protect functions.
411 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700412static int mtd_wp_list_ranges(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700413{
414 /* TODO: implement this */
415 msg_perr("--wp-list is not currently implemented for MTD.\n");
416 return 1;
417}
418
419/*
420 * We only have MEMLOCK to enable write-protection for a particular block,
421 * so we need to do force the user to use --wp-range and --wp-enable
422 * command-line arguments simultaneously. (Fortunately, CrOS factory
423 * installer does this already).
424 *
425 * The --wp-range argument is processed first and will set these variables
426 * which --wp-enable will use afterward.
427 */
428static unsigned int wp_range_start;
429static unsigned int wp_range_len;
430static int wp_set_range_called = 0;
431
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700432static int mtd_wp_set_range(const struct flashctx *flash,
David Hendricks85f61c52015-06-04 19:52:59 -0700433 unsigned int start, unsigned int len)
434{
435 wp_range_start = start;
436 wp_range_len = len;
437
438 wp_set_range_called = 1;
439 return 0;
440}
441
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700442static int mtd_wp_enable_writeprotect(const struct flashctx *flash, enum wp_mode mode)
David Hendricks85f61c52015-06-04 19:52:59 -0700443{
444 struct erase_info_user entire_chip = {
445 .start = 0,
446 .length = mtd_total_size,
447 };
448 struct erase_info_user desired_range = {
449 .start = wp_range_start,
450 .length = wp_range_len,
451 };
452
453 if (!wp_set_range_called) {
454 msg_perr("For MTD, --wp-range and --wp-enable must be "
455 "used simultaneously.\n");
456 return 1;
457 }
458
459 /*
460 * MTD handles write-protection additively, so whatever new range is
461 * specified is added to the range which is currently protected. To be
462 * consistent with flashrom behavior with other programmer interfaces,
463 * we need to disable the current write protection and then enable
464 * it for the desired range.
465 */
466 if (ioctl(dev_fd, MEMUNLOCK, &entire_chip) == -1) {
467 msg_perr("%s: Failed to disable write-protection, ioctl: %s\n",
468 __func__, strerror(errno));
Brian Norris4df096b2016-07-27 18:36:38 -0700469 msg_perr("Did you disable WP#?\n");
David Hendricks85f61c52015-06-04 19:52:59 -0700470 return 1;
471 }
472
473 if (ioctl(dev_fd, MEMLOCK, &desired_range) == -1) {
474 msg_perr("%s: Failed to enable write-protection, ioctl: %s\n",
475 __func__, strerror(errno));
476 return 1;
477 }
478
479 return 0;
480}
481
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700482static int mtd_wp_disable_writeprotect(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700483{
484 struct erase_info_user erase_info;
485
486 if (wp_set_range_called) {
487 erase_info.start = wp_range_start;
488 erase_info.length = wp_range_len;
489 } else {
490 erase_info.start = 0;
491 erase_info.length = mtd_total_size;
492 }
493
494 if (ioctl(dev_fd, MEMUNLOCK, &erase_info) == -1) {
495 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
Brian Norris4df096b2016-07-27 18:36:38 -0700496 msg_perr("Did you disable WP#?\n");
David Hendricks85f61c52015-06-04 19:52:59 -0700497 return 1;
498 }
499
500 return 0;
501}
502
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700503static int mtd_wp_status(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700504{
Brian Norris3ce4c472016-06-02 16:40:14 -0700505 uint32_t start = 0, len = 0;
506 int start_found = 0;
David Hendricks85f61c52015-06-04 19:52:59 -0700507 unsigned int u;
508
509 /* For now, assume only one contiguous region can be locked (NOR) */
510 /* FIXME: use flash struct members instead of raw MTD values here */
Wei-Ning Huang699269b2016-03-02 22:08:05 +0800511 for (u = 0; u < mtd_total_size; u += mtd_erasesize) {
David Hendricks85f61c52015-06-04 19:52:59 -0700512 int rc;
513 struct erase_info_user erase_info = {
514 .start = u,
515 .length = mtd_erasesize,
516 };
517
518 rc = ioctl(dev_fd, MEMISLOCKED, &erase_info);
519 if (rc < 0) {
520 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
521 return 1;
522 } else if (rc == 1) {
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800523 if (!start_found) {
David Hendricks85f61c52015-06-04 19:52:59 -0700524 start = erase_info.start;
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800525 start_found = 1;
526 }
Brian Norris3ce4c472016-06-02 16:40:14 -0700527 len += mtd_erasesize;
David Hendricks85f61c52015-06-04 19:52:59 -0700528 } else if (rc == 0) {
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800529 if (start_found) {
Brian Norris3ce4c472016-06-02 16:40:14 -0700530 /* TODO: changes required for supporting non-contiguous locked regions */
531 break;
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800532 }
David Hendricks85f61c52015-06-04 19:52:59 -0700533 }
534
David Hendricks85f61c52015-06-04 19:52:59 -0700535 }
536
Wei-Ning Huangca907072016-04-27 11:30:17 +0800537 msg_cinfo("WP: write protect is %s.\n",
Brian Norris3ce4c472016-06-02 16:40:14 -0700538 start_found ? "enabled": "disabled");
Wei-Ning Huangca907072016-04-27 11:30:17 +0800539 msg_pinfo("WP: write protect range: start=0x%08x, "
Brian Norris3ce4c472016-06-02 16:40:14 -0700540 "len=0x%08x\n", start, len);
Wei-Ning Huangca907072016-04-27 11:30:17 +0800541
David Hendricks85f61c52015-06-04 19:52:59 -0700542 return 0;
543}
544
545static struct wp wp_mtd = {
546 .list_ranges = mtd_wp_list_ranges,
547 .set_range = mtd_wp_set_range,
548 .enable = mtd_wp_enable_writeprotect,
549 .disable = mtd_wp_disable_writeprotect,
550 .wp_status = mtd_wp_status,
551};