blob: 7425e0c98b35509320e2a2a840d1aa2ed369c31e [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
Nikolai Artemievb81761e2020-07-29 23:22:07 +100039static FILE *dev_fp = NULL;
David Hendrickscebee892015-05-23 20:30:30 -070040
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{
Nikolai Artemievb81761e2020-07-29 23:22:07 +100055 int i;
56 size_t bytes_read;
57 FILE *fp;
David Hendrickscebee892015-05-23 20:30:30 -070058 char path[strlen(LINUX_MTD_SYSFS_ROOT) + 32];
59
60 snprintf(path, sizeof(path), "%s/%s", sysfs_path, filename);
61
Nikolai Artemievb81761e2020-07-29 23:22:07 +100062 if ((fp = fopen(path, "r")) == NULL) {
David Hendrickscebee892015-05-23 20:30:30 -070063 msg_perr("Cannot open %s\n", path);
64 return 1;
65 }
66
Nikolai Artemievb81761e2020-07-29 23:22:07 +100067 clearerr(fp);
68 bytes_read = fread(buf, 1, (size_t)len, fp);
69 if (!feof(fp) && ferror(fp)) {
70 msg_perr("Error occurred when reading %s\n", path);
71 fclose(fp);
David Hendrickscebee892015-05-23 20:30:30 -070072 return 1;
73 }
74
75 buf[bytes_read] = '\0';
76
77 /*
78 * Files from sysfs sometimes contain a newline or other garbage that
79 * can confuse functions like strtoul() and ruin formatting in print
80 * statements. Replace the first non-printable character (space is
81 * considered printable) with a proper string terminator.
82 */
83 for (i = 0; i < len; i++) {
84 if (!isprint(buf[i])) {
85 buf[i] = '\0';
86 break;
87 }
88 }
89
Nikolai Artemievb81761e2020-07-29 23:22:07 +100090 fclose(fp);
David Hendrickscebee892015-05-23 20:30:30 -070091 return 0;
92}
93
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100094static int read_sysfs_int(const char *sysfs_path, const char *filename, unsigned long int *val)
David Hendrickscebee892015-05-23 20:30:30 -070095{
David Hendrickscebee892015-05-23 20:30:30 -070096 char buf[32];
97 char *endptr;
98
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100099 if (read_sysfs_string(sysfs_path, filename, buf, sizeof(buf)))
David Hendrickscebee892015-05-23 20:30:30 -0700100 return 1;
101
102 errno = 0;
103 *val = strtoul(buf, &endptr, 0);
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000104 if (*endptr != '\0') {
David Hendrickscebee892015-05-23 20:30:30 -0700105 msg_perr("Error reading %s\n", filename);
106 return 1;
107 }
108
109 if (errno) {
110 msg_perr("Error reading %s: %s\n", filename, strerror(errno));
111 return 1;
112 }
113
114 return 0;
115}
116
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000117static int popcnt(unsigned int u)
118{
119 int count = 0;
120
121 while (u) {
122 u &= u - 1;
123 count++;
124 }
125
126 return count;
127}
128
David Hendrickscebee892015-05-23 20:30:30 -0700129/* returns 0 to indicate success, non-zero to indicate error */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000130static int get_mtd_info(const char *sysfs_path)
David Hendrickscebee892015-05-23 20:30:30 -0700131{
132 unsigned long int tmp;
133 char mtd_device_name[32];
134
135 /* Flags */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000136 if (read_sysfs_int(sysfs_path, "flags", &tmp))
David Hendrickscebee892015-05-23 20:30:30 -0700137 return 1;
138 if (tmp & MTD_WRITEABLE) {
139 /* cache for later use by write function */
140 mtd_device_is_writeable = 1;
141 }
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700142 if (tmp & MTD_NO_ERASE) {
143 mtd_no_erase = 1;
144 }
David Hendrickscebee892015-05-23 20:30:30 -0700145
146 /* Device name */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000147 if (read_sysfs_string(sysfs_path, "name", mtd_device_name, sizeof(mtd_device_name)))
David Hendrickscebee892015-05-23 20:30:30 -0700148 return 1;
149
150 /* Total size */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000151 if (read_sysfs_int(sysfs_path, "size", &mtd_total_size))
David Hendrickscebee892015-05-23 20:30:30 -0700152 return 1;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000153 if (popcnt(mtd_total_size) != 1) {
David Hendrickscebee892015-05-23 20:30:30 -0700154 msg_perr("MTD size is not a power of 2\n");
155 return 1;
156 }
157
158 /* Erase size */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000159 if (read_sysfs_int(sysfs_path, "erasesize", &mtd_erasesize))
David Hendrickscebee892015-05-23 20:30:30 -0700160 return 1;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000161 if (popcnt(mtd_erasesize) != 1) {
David Hendrickscebee892015-05-23 20:30:30 -0700162 msg_perr("MTD erase size is not a power of 2\n");
163 return 1;
164 }
165
166 /* Erase regions */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000167 if (read_sysfs_int(sysfs_path, "numeraseregions", &mtd_numeraseregions))
David Hendrickscebee892015-05-23 20:30:30 -0700168 return 1;
169 if (mtd_numeraseregions != 0) {
170 msg_perr("Non-uniform eraseblock size is unsupported.\n");
171 return 1;
172 }
173
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000174 msg_pdbg("%s: device_name: \"%s\", is_writeable: %d, "
David Hendrickscebee892015-05-23 20:30:30 -0700175 "numeraseregions: %lu, total_size: %lu, erasesize: %lu\n",
176 __func__, mtd_device_name, mtd_device_is_writeable,
177 mtd_numeraseregions, mtd_total_size, mtd_erasesize);
178
179 return 0;
180}
181
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700182static int linux_mtd_probe(struct flashctx *flash)
David Hendrickscebee892015-05-23 20:30:30 -0700183{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100184 flash->chip->wp = &wp_mtd;
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700185 if (mtd_no_erase)
186 flash->chip->feature_bits |= FEATURE_NO_ERASE;
Patrick Georgif3fa2992017-02-02 16:24:44 +0100187 flash->chip->tested = TEST_OK_PREW;
188 flash->chip->total_size = mtd_total_size / 1024; /* bytes -> kB */
189 flash->chip->block_erasers[0].eraseblocks[0].size = mtd_erasesize;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000190 flash->chip->block_erasers[0].eraseblocks[0].count = mtd_total_size / mtd_erasesize;
David Hendrickscebee892015-05-23 20:30:30 -0700191 return 1;
192}
193
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700194static int linux_mtd_read(struct flashctx *flash, uint8_t *buf,
David Hendrickscebee892015-05-23 20:30:30 -0700195 unsigned int start, unsigned int len)
196{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100197 unsigned int eb_size = flash->chip->block_erasers[0].eraseblocks[0].size;
Brian Norris99c8ad22016-05-10 15:36:02 -0700198 unsigned int i;
199
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000200 if (fseek(dev_fp, start, SEEK_SET) != 0) {
David Hendrickscebee892015-05-23 20:30:30 -0700201 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
202 return 1;
203 }
204
Brian Norris99c8ad22016-05-10 15:36:02 -0700205 for (i = 0; i < len; ) {
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700206 /*
207 * Try to align reads to eraseblock size.
208 * FIXME: Shouldn't actually be necessary, but not all MTD
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000209 * drivers handle arbitrary large reads well.
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700210 */
Brian Norris99c8ad22016-05-10 15:36:02 -0700211 unsigned int step = eb_size - ((start + i) % eb_size);
212 step = min(step, len - i);
213
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000214 if (fread(buf + i, step, 1, dev_fp) != 1) {
Brian Norris99c8ad22016-05-10 15:36:02 -0700215 msg_perr("Cannot read 0x%06x bytes at 0x%06x: %s\n",
216 step, start + i, strerror(errno));
217 return 1;
218 }
219
220 i += step;
David Hendrickscebee892015-05-23 20:30:30 -0700221 }
222
223 return 0;
224}
225
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700226/* this version assumes we must divide the write request into chunks ourselves */
Patrick Georgiab8353e2017-02-03 18:32:01 +0100227static int linux_mtd_write(struct flashctx *flash, const uint8_t *buf,
David Hendrickscebee892015-05-23 20:30:30 -0700228 unsigned int start, unsigned int len)
229{
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700230 unsigned int chunksize = flash->chip->block_erasers[0].eraseblocks[0].size;
231 unsigned int i;
David Hendrickscebee892015-05-23 20:30:30 -0700232
233 if (!mtd_device_is_writeable)
234 return 1;
235
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000236 if (fseek(dev_fp, start, SEEK_SET) != 0) {
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700237 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
238 return 1;
239 }
David Hendrickscebee892015-05-23 20:30:30 -0700240
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700241 /*
242 * Try to align writes to eraseblock size. We want these large enough
243 * to give MTD room for optimizing performance.
244 * FIXME: Shouldn't need to divide this up at all, but not all MTD
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000245 * drivers handle arbitrary large writes well.
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700246 */
247 for (i = 0; i < len; ) {
248 unsigned int step = chunksize - ((start + i) % chunksize);
249 step = min(step, len - i);
David Hendrickscebee892015-05-23 20:30:30 -0700250
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000251 if (fwrite(buf + i, step, 1, dev_fp) != 1) {
252 msg_perr("Cannot write 0x%06x bytes at 0x%06x\n", step, start + i);
253 return 1;
254 }
255
256 if (fflush(dev_fp) == EOF) {
257 msg_perr("Failed to flush buffer: %s\n", strerror(errno));
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700258 return 1;
David Hendrickscebee892015-05-23 20:30:30 -0700259 }
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700260
261 i += step;
David Hendrickscebee892015-05-23 20:30:30 -0700262 }
263
264 return 0;
265}
266
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700267static int linux_mtd_erase(struct flashctx *flash,
David Hendrickscebee892015-05-23 20:30:30 -0700268 unsigned int start, unsigned int len)
269{
David Hendrickscebee892015-05-23 20:30:30 -0700270 uint32_t u;
271
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700272 if (mtd_no_erase) {
273 msg_perr("%s: device does not support erasing. Please file a "
274 "bug report at flashrom@flashrom.org\n", __func__);
275 return 1;
276 }
277
David Hendrickscebee892015-05-23 20:30:30 -0700278 if (mtd_numeraseregions != 0) {
279 /* TODO: Support non-uniform eraseblock size using
280 use MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls */
281 }
282
283 for (u = 0; u < len; u += mtd_erasesize) {
284 struct erase_info_user erase_info = {
285 .start = start + u,
286 .length = mtd_erasesize,
287 };
288
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000289 if (ioctl(fileno(dev_fp), MEMERASE, &erase_info) == -1) {
David Hendrickscebee892015-05-23 20:30:30 -0700290 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
291 return 1;
292 }
293 }
294
295 return 0;
296}
297
Edward O'Callaghanabd30192019-05-14 15:58:19 +1000298static struct opaque_master programmer_linux_mtd = {
Brian Norris99c8ad22016-05-10 15:36:02 -0700299 /* max_data_{read,write} don't have any effect for this programmer */
David Hendrickscebee892015-05-23 20:30:30 -0700300 .max_data_read = MAX_DATA_UNSPECIFIED,
301 .max_data_write = MAX_DATA_UNSPECIFIED,
302 .probe = linux_mtd_probe,
303 .read = linux_mtd_read,
304 .write = linux_mtd_write,
305 .erase = linux_mtd_erase,
306};
307
308/* Returns 0 if setup is successful, non-zero to indicate error */
309static int linux_mtd_setup(int dev_num)
310{
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000311 char sysfs_path[32];
David Hendrickscebee892015-05-23 20:30:30 -0700312 char dev_path[16]; /* "/dev/mtdN" */
313 int ret = 1;
314
315 if (dev_num < 0) {
316 char *tmp, *p;
317
318 tmp = (char *)scanft(LINUX_MTD_SYSFS_ROOT, "type", "nor", 1);
319 if (!tmp) {
David Hendrickscf6fbe62015-09-24 14:22:39 -0700320 msg_pdbg("%s: NOR type device not found.\n", __func__);
David Hendrickscebee892015-05-23 20:30:30 -0700321 goto linux_mtd_setup_exit;
322 }
323
324 /* "tmp" should be something like "/sys/blah/mtdN/type" */
325 p = tmp + strlen(LINUX_MTD_SYSFS_ROOT);
326 while (p[0] == '/')
327 p++;
328
329 if (sscanf(p, "mtd%d", &dev_num) != 1) {
330 msg_perr("Can't obtain device number from \"%s\"\n", p);
331 free(tmp);
332 goto linux_mtd_setup_exit;
333 }
334 free(tmp);
335 }
336
337 snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d",
338 LINUX_MTD_SYSFS_ROOT, dev_num);
339 snprintf(dev_path, sizeof(dev_path), "%s/mtd%d",
340 LINUX_DEV_ROOT, dev_num);
341 msg_pdbg("%s: sysfs_path: \"%s\", dev_path: \"%s\"\n",
342 __func__, sysfs_path, dev_path);
343
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000344 struct stat s;
David Hendrickscebee892015-05-23 20:30:30 -0700345
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000346 errno = 0;
347 if (stat(dev_path, &s) < 0) {
348 msg_pdbg("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
349 goto linux_mtd_setup_exit;
350 }
351
352 if (lstat(sysfs_path, &s) < 0) {
353 msg_pdbg("Cannot stat \"%s\" : %s\n",
354 sysfs_path, strerror(errno));
355 goto linux_mtd_setup_exit;
356 }
357
358 if (get_mtd_info(sysfs_path))
David Hendrickscebee892015-05-23 20:30:30 -0700359 goto linux_mtd_setup_exit;
360
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000361 /* open file stream and go! */
362 if ((dev_fp = fopen(dev_path, "r+")) == NULL) {
363 msg_perr("Cannot open file stream for %s\n", dev_path);
David Hendrickscebee892015-05-23 20:30:30 -0700364 goto linux_mtd_setup_exit;
365 }
366
367 ret = 0;
368linux_mtd_setup_exit:
369 return ret;
370}
371
David Hendricks93784b42016-08-09 17:00:38 -0700372static int linux_mtd_shutdown(void *data)
David Hendrickscebee892015-05-23 20:30:30 -0700373{
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000374 if (dev_fp != NULL) {
375 fclose(dev_fp);
376 dev_fp = NULL;
David Hendrickscebee892015-05-23 20:30:30 -0700377 }
378
379 return 0;
380}
381
David Hendricksac1d25c2016-08-09 17:00:58 -0700382int linux_mtd_init(void)
David Hendrickscebee892015-05-23 20:30:30 -0700383{
384 char *param;
385 int dev_num = -1; /* linux_mtd_setup will search if dev_num < 0 */
386 int ret = 1;
387
388 if (alias && alias->type != ALIAS_HOST)
389 return 1;
390
391 param = extract_programmer_param("dev");
392 if (param) {
393 char *endptr;
394
395 dev_num = strtol(param, &endptr, 0);
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000396 if ((*endptr != '\0') || (dev_num < 0)) {
David Hendrickscebee892015-05-23 20:30:30 -0700397 msg_perr("Invalid device number %s. Use flashrom -p "
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000398 "linux_mtd:dev=N where N is a valid MTD\n"
399 "device number.\n", param);
David Hendrickscebee892015-05-23 20:30:30 -0700400 goto linux_mtd_init_exit;
401 }
402 }
403
404 if (linux_mtd_setup(dev_num))
405 goto linux_mtd_init_exit;
406
407 if (register_shutdown(linux_mtd_shutdown, NULL))
408 goto linux_mtd_init_exit;
409
Edward O'Callaghanabd30192019-05-14 15:58:19 +1000410 register_opaque_master(&programmer_linux_mtd);
David Hendrickscebee892015-05-23 20:30:30 -0700411
412 ret = 0;
413linux_mtd_init_exit:
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000414 free(param);
David Hendrickscebee892015-05-23 20:30:30 -0700415 return ret;
416}
David Hendricks85f61c52015-06-04 19:52:59 -0700417
418/*
419 * Write-protect functions.
420 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700421static int mtd_wp_list_ranges(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700422{
423 /* TODO: implement this */
424 msg_perr("--wp-list is not currently implemented for MTD.\n");
425 return 1;
426}
427
428/*
429 * We only have MEMLOCK to enable write-protection for a particular block,
430 * so we need to do force the user to use --wp-range and --wp-enable
431 * command-line arguments simultaneously. (Fortunately, CrOS factory
432 * installer does this already).
433 *
434 * The --wp-range argument is processed first and will set these variables
435 * which --wp-enable will use afterward.
436 */
437static unsigned int wp_range_start;
438static unsigned int wp_range_len;
439static int wp_set_range_called = 0;
440
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700441static int mtd_wp_set_range(const struct flashctx *flash,
David Hendricks85f61c52015-06-04 19:52:59 -0700442 unsigned int start, unsigned int len)
443{
444 wp_range_start = start;
445 wp_range_len = len;
446
447 wp_set_range_called = 1;
448 return 0;
449}
450
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700451static int mtd_wp_enable_writeprotect(const struct flashctx *flash, enum wp_mode mode)
David Hendricks85f61c52015-06-04 19:52:59 -0700452{
453 struct erase_info_user entire_chip = {
454 .start = 0,
455 .length = mtd_total_size,
456 };
457 struct erase_info_user desired_range = {
458 .start = wp_range_start,
459 .length = wp_range_len,
460 };
461
462 if (!wp_set_range_called) {
463 msg_perr("For MTD, --wp-range and --wp-enable must be "
464 "used simultaneously.\n");
465 return 1;
466 }
467
468 /*
469 * MTD handles write-protection additively, so whatever new range is
470 * specified is added to the range which is currently protected. To be
471 * consistent with flashrom behavior with other programmer interfaces,
472 * we need to disable the current write protection and then enable
473 * it for the desired range.
474 */
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000475 if (ioctl(fileno(dev_fp), MEMUNLOCK, &entire_chip) == -1) {
David Hendricks85f61c52015-06-04 19:52:59 -0700476 msg_perr("%s: Failed to disable write-protection, ioctl: %s\n",
477 __func__, strerror(errno));
Brian Norris4df096b2016-07-27 18:36:38 -0700478 msg_perr("Did you disable WP#?\n");
David Hendricks85f61c52015-06-04 19:52:59 -0700479 return 1;
480 }
481
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000482 if (ioctl(fileno(dev_fp), MEMLOCK, &desired_range) == -1) {
David Hendricks85f61c52015-06-04 19:52:59 -0700483 msg_perr("%s: Failed to enable write-protection, ioctl: %s\n",
484 __func__, strerror(errno));
485 return 1;
486 }
487
488 return 0;
489}
490
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700491static int mtd_wp_disable_writeprotect(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700492{
493 struct erase_info_user erase_info;
494
495 if (wp_set_range_called) {
496 erase_info.start = wp_range_start;
497 erase_info.length = wp_range_len;
498 } else {
499 erase_info.start = 0;
500 erase_info.length = mtd_total_size;
501 }
502
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000503 if (ioctl(fileno(dev_fp), MEMUNLOCK, &erase_info) == -1) {
David Hendricks85f61c52015-06-04 19:52:59 -0700504 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
Brian Norris4df096b2016-07-27 18:36:38 -0700505 msg_perr("Did you disable WP#?\n");
David Hendricks85f61c52015-06-04 19:52:59 -0700506 return 1;
507 }
508
509 return 0;
510}
511
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700512static int mtd_wp_status(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700513{
Brian Norris3ce4c472016-06-02 16:40:14 -0700514 uint32_t start = 0, len = 0;
515 int start_found = 0;
David Hendricks85f61c52015-06-04 19:52:59 -0700516 unsigned int u;
517
518 /* For now, assume only one contiguous region can be locked (NOR) */
519 /* FIXME: use flash struct members instead of raw MTD values here */
Wei-Ning Huang699269b2016-03-02 22:08:05 +0800520 for (u = 0; u < mtd_total_size; u += mtd_erasesize) {
David Hendricks85f61c52015-06-04 19:52:59 -0700521 int rc;
522 struct erase_info_user erase_info = {
523 .start = u,
524 .length = mtd_erasesize,
525 };
526
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000527 rc = ioctl(fileno(dev_fp), MEMISLOCKED, &erase_info);
David Hendricks85f61c52015-06-04 19:52:59 -0700528 if (rc < 0) {
529 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
530 return 1;
531 } else if (rc == 1) {
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800532 if (!start_found) {
David Hendricks85f61c52015-06-04 19:52:59 -0700533 start = erase_info.start;
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800534 start_found = 1;
535 }
Brian Norris3ce4c472016-06-02 16:40:14 -0700536 len += mtd_erasesize;
David Hendricks85f61c52015-06-04 19:52:59 -0700537 } else if (rc == 0) {
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800538 if (start_found) {
Brian Norris3ce4c472016-06-02 16:40:14 -0700539 /* TODO: changes required for supporting non-contiguous locked regions */
540 break;
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800541 }
David Hendricks85f61c52015-06-04 19:52:59 -0700542 }
543
David Hendricks85f61c52015-06-04 19:52:59 -0700544 }
545
Wei-Ning Huangca907072016-04-27 11:30:17 +0800546 msg_cinfo("WP: write protect is %s.\n",
Brian Norris3ce4c472016-06-02 16:40:14 -0700547 start_found ? "enabled": "disabled");
Wei-Ning Huangca907072016-04-27 11:30:17 +0800548 msg_pinfo("WP: write protect range: start=0x%08x, "
Brian Norris3ce4c472016-06-02 16:40:14 -0700549 "len=0x%08x\n", start, len);
Wei-Ning Huangca907072016-04-27 11:30:17 +0800550
David Hendricks85f61c52015-06-04 19:52:59 -0700551 return 0;
552}
553
554static struct wp wp_mtd = {
555 .list_ranges = mtd_wp_list_ranges,
556 .set_range = mtd_wp_set_range,
557 .enable = mtd_wp_enable_writeprotect,
558 .disable = mtd_wp_disable_writeprotect,
559 .wp_status = mtd_wp_status,
560};