blob: 330015c98f4757e236aa860236688dfbdf8802de [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
David Hendrickscebee892015-05-23 20:30:30 -070031#include "flash.h"
32#include "programmer.h"
David Hendricks85f61c52015-06-04 19:52:59 -070033#include "writeprotect.h"
David Hendrickscebee892015-05-23 20:30:30 -070034
35#define LINUX_DEV_ROOT "/dev"
36#define LINUX_MTD_SYSFS_ROOT "/sys/class/mtd"
37
Nikolai Artemievb81761e2020-07-29 23:22:07 +100038static FILE *dev_fp = NULL;
David Hendrickscebee892015-05-23 20:30:30 -070039
David Hendrickscebee892015-05-23 20:30:30 -070040static int mtd_device_is_writeable;
41
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -070042static int mtd_no_erase;
43
David Hendrickscebee892015-05-23 20:30:30 -070044/* Size info is presented in bytes in sysfs. */
45static unsigned long int mtd_total_size;
46static unsigned long int mtd_numeraseregions;
47static unsigned long int mtd_erasesize; /* only valid if numeraseregions is 0 */
48
David Hendricks85f61c52015-06-04 19:52:59 -070049static struct wp wp_mtd; /* forward declaration */
50
David Hendrickscebee892015-05-23 20:30:30 -070051/* read a string from a sysfs file and sanitize it */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100052static int read_sysfs_string(const char *sysfs_path, const char *filename, char *buf, int len)
David Hendrickscebee892015-05-23 20:30:30 -070053{
Nikolai Artemievb81761e2020-07-29 23:22:07 +100054 int i;
55 size_t bytes_read;
56 FILE *fp;
David Hendrickscebee892015-05-23 20:30:30 -070057 char path[strlen(LINUX_MTD_SYSFS_ROOT) + 32];
58
59 snprintf(path, sizeof(path), "%s/%s", sysfs_path, filename);
60
Nikolai Artemievb81761e2020-07-29 23:22:07 +100061 if ((fp = fopen(path, "r")) == NULL) {
David Hendrickscebee892015-05-23 20:30:30 -070062 msg_perr("Cannot open %s\n", path);
63 return 1;
64 }
65
Nikolai Artemievb81761e2020-07-29 23:22:07 +100066 clearerr(fp);
67 bytes_read = fread(buf, 1, (size_t)len, fp);
68 if (!feof(fp) && ferror(fp)) {
69 msg_perr("Error occurred when reading %s\n", path);
70 fclose(fp);
David Hendrickscebee892015-05-23 20:30:30 -070071 return 1;
72 }
73
74 buf[bytes_read] = '\0';
75
76 /*
77 * Files from sysfs sometimes contain a newline or other garbage that
78 * can confuse functions like strtoul() and ruin formatting in print
79 * statements. Replace the first non-printable character (space is
80 * considered printable) with a proper string terminator.
81 */
82 for (i = 0; i < len; i++) {
83 if (!isprint(buf[i])) {
84 buf[i] = '\0';
85 break;
86 }
87 }
88
Nikolai Artemievb81761e2020-07-29 23:22:07 +100089 fclose(fp);
David Hendrickscebee892015-05-23 20:30:30 -070090 return 0;
91}
92
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100093static int read_sysfs_int(const char *sysfs_path, const char *filename, unsigned long int *val)
David Hendrickscebee892015-05-23 20:30:30 -070094{
David Hendrickscebee892015-05-23 20:30:30 -070095 char buf[32];
96 char *endptr;
97
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +100098 if (read_sysfs_string(sysfs_path, filename, buf, sizeof(buf)))
David Hendrickscebee892015-05-23 20:30:30 -070099 return 1;
100
101 errno = 0;
102 *val = strtoul(buf, &endptr, 0);
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000103 if (*endptr != '\0') {
David Hendrickscebee892015-05-23 20:30:30 -0700104 msg_perr("Error reading %s\n", filename);
105 return 1;
106 }
107
108 if (errno) {
109 msg_perr("Error reading %s: %s\n", filename, strerror(errno));
110 return 1;
111 }
112
113 return 0;
114}
115
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000116static int popcnt(unsigned int u)
117{
118 int count = 0;
119
120 while (u) {
121 u &= u - 1;
122 count++;
123 }
124
125 return count;
126}
127
David Hendrickscebee892015-05-23 20:30:30 -0700128/* returns 0 to indicate success, non-zero to indicate error */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000129static int get_mtd_info(const char *sysfs_path)
David Hendrickscebee892015-05-23 20:30:30 -0700130{
131 unsigned long int tmp;
132 char mtd_device_name[32];
133
134 /* Flags */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000135 if (read_sysfs_int(sysfs_path, "flags", &tmp))
David Hendrickscebee892015-05-23 20:30:30 -0700136 return 1;
137 if (tmp & MTD_WRITEABLE) {
138 /* cache for later use by write function */
139 mtd_device_is_writeable = 1;
140 }
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700141 if (tmp & MTD_NO_ERASE) {
142 mtd_no_erase = 1;
143 }
David Hendrickscebee892015-05-23 20:30:30 -0700144
145 /* Device name */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000146 if (read_sysfs_string(sysfs_path, "name", mtd_device_name, sizeof(mtd_device_name)))
David Hendrickscebee892015-05-23 20:30:30 -0700147 return 1;
148
149 /* Total size */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000150 if (read_sysfs_int(sysfs_path, "size", &mtd_total_size))
David Hendrickscebee892015-05-23 20:30:30 -0700151 return 1;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000152 if (popcnt(mtd_total_size) != 1) {
David Hendrickscebee892015-05-23 20:30:30 -0700153 msg_perr("MTD size is not a power of 2\n");
154 return 1;
155 }
156
157 /* Erase size */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000158 if (read_sysfs_int(sysfs_path, "erasesize", &mtd_erasesize))
David Hendrickscebee892015-05-23 20:30:30 -0700159 return 1;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000160 if (popcnt(mtd_erasesize) != 1) {
David Hendrickscebee892015-05-23 20:30:30 -0700161 msg_perr("MTD erase size is not a power of 2\n");
162 return 1;
163 }
164
165 /* Erase regions */
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000166 if (read_sysfs_int(sysfs_path, "numeraseregions", &mtd_numeraseregions))
David Hendrickscebee892015-05-23 20:30:30 -0700167 return 1;
168 if (mtd_numeraseregions != 0) {
169 msg_perr("Non-uniform eraseblock size is unsupported.\n");
170 return 1;
171 }
172
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000173 msg_pdbg("%s: device_name: \"%s\", is_writeable: %d, "
David Hendrickscebee892015-05-23 20:30:30 -0700174 "numeraseregions: %lu, total_size: %lu, erasesize: %lu\n",
175 __func__, mtd_device_name, mtd_device_is_writeable,
176 mtd_numeraseregions, mtd_total_size, mtd_erasesize);
177
178 return 0;
179}
180
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700181static int linux_mtd_probe(struct flashctx *flash)
David Hendrickscebee892015-05-23 20:30:30 -0700182{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100183 flash->chip->wp = &wp_mtd;
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700184 if (mtd_no_erase)
185 flash->chip->feature_bits |= FEATURE_NO_ERASE;
Patrick Georgif3fa2992017-02-02 16:24:44 +0100186 flash->chip->tested = TEST_OK_PREW;
187 flash->chip->total_size = mtd_total_size / 1024; /* bytes -> kB */
188 flash->chip->block_erasers[0].eraseblocks[0].size = mtd_erasesize;
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000189 flash->chip->block_erasers[0].eraseblocks[0].count = mtd_total_size / mtd_erasesize;
David Hendrickscebee892015-05-23 20:30:30 -0700190 return 1;
191}
192
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700193static int linux_mtd_read(struct flashctx *flash, uint8_t *buf,
David Hendrickscebee892015-05-23 20:30:30 -0700194 unsigned int start, unsigned int len)
195{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100196 unsigned int eb_size = flash->chip->block_erasers[0].eraseblocks[0].size;
Brian Norris99c8ad22016-05-10 15:36:02 -0700197 unsigned int i;
198
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000199 if (fseek(dev_fp, start, SEEK_SET) != 0) {
David Hendrickscebee892015-05-23 20:30:30 -0700200 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
201 return 1;
202 }
203
Brian Norris99c8ad22016-05-10 15:36:02 -0700204 for (i = 0; i < len; ) {
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700205 /*
206 * Try to align reads to eraseblock size.
207 * FIXME: Shouldn't actually be necessary, but not all MTD
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000208 * drivers handle arbitrary large reads well.
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700209 */
Brian Norris99c8ad22016-05-10 15:36:02 -0700210 unsigned int step = eb_size - ((start + i) % eb_size);
211 step = min(step, len - i);
212
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000213 if (fread(buf + i, step, 1, dev_fp) != 1) {
Brian Norris99c8ad22016-05-10 15:36:02 -0700214 msg_perr("Cannot read 0x%06x bytes at 0x%06x: %s\n",
215 step, start + i, strerror(errno));
216 return 1;
217 }
218
219 i += step;
David Hendrickscebee892015-05-23 20:30:30 -0700220 }
221
222 return 0;
223}
224
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700225/* this version assumes we must divide the write request into chunks ourselves */
Patrick Georgiab8353e2017-02-03 18:32:01 +0100226static int linux_mtd_write(struct flashctx *flash, const uint8_t *buf,
David Hendrickscebee892015-05-23 20:30:30 -0700227 unsigned int start, unsigned int len)
228{
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700229 unsigned int chunksize = flash->chip->block_erasers[0].eraseblocks[0].size;
230 unsigned int i;
David Hendrickscebee892015-05-23 20:30:30 -0700231
232 if (!mtd_device_is_writeable)
233 return 1;
234
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000235 if (fseek(dev_fp, start, SEEK_SET) != 0) {
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700236 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
237 return 1;
238 }
David Hendrickscebee892015-05-23 20:30:30 -0700239
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700240 /*
241 * Try to align writes to eraseblock size. We want these large enough
242 * to give MTD room for optimizing performance.
243 * FIXME: Shouldn't need to divide this up at all, but not all MTD
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000244 * drivers handle arbitrary large writes well.
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700245 */
246 for (i = 0; i < len; ) {
247 unsigned int step = chunksize - ((start + i) % chunksize);
248 step = min(step, len - i);
David Hendrickscebee892015-05-23 20:30:30 -0700249
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000250 if (fwrite(buf + i, step, 1, dev_fp) != 1) {
251 msg_perr("Cannot write 0x%06x bytes at 0x%06x\n", step, start + i);
252 return 1;
253 }
254
255 if (fflush(dev_fp) == EOF) {
256 msg_perr("Failed to flush buffer: %s\n", strerror(errno));
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700257 return 1;
David Hendrickscebee892015-05-23 20:30:30 -0700258 }
Brian Norris8bbb6dd2017-05-12 12:49:59 -0700259
260 i += step;
David Hendrickscebee892015-05-23 20:30:30 -0700261 }
262
263 return 0;
264}
265
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700266static int linux_mtd_erase(struct flashctx *flash,
David Hendrickscebee892015-05-23 20:30:30 -0700267 unsigned int start, unsigned int len)
268{
David Hendrickscebee892015-05-23 20:30:30 -0700269 uint32_t u;
270
William A. Kennington IIIf15c2fa2017-04-07 17:38:42 -0700271 if (mtd_no_erase) {
272 msg_perr("%s: device does not support erasing. Please file a "
273 "bug report at flashrom@flashrom.org\n", __func__);
274 return 1;
275 }
276
David Hendrickscebee892015-05-23 20:30:30 -0700277 if (mtd_numeraseregions != 0) {
278 /* TODO: Support non-uniform eraseblock size using
279 use MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls */
Nikolai Artemievd783fb92020-08-26 03:43:19 +1000280 msg_perr("%s: mtd_numeraseregions must be 0\n", __func__);
281 return 1;
David Hendrickscebee892015-05-23 20:30:30 -0700282 }
283
284 for (u = 0; u < len; u += mtd_erasesize) {
285 struct erase_info_user erase_info = {
286 .start = start + u,
287 .length = mtd_erasesize,
288 };
289
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000290 if (ioctl(fileno(dev_fp), MEMERASE, &erase_info) == -1) {
David Hendrickscebee892015-05-23 20:30:30 -0700291 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
292 return 1;
293 }
294 }
295
296 return 0;
297}
298
Edward O'Callaghanabd30192019-05-14 15:58:19 +1000299static struct opaque_master programmer_linux_mtd = {
Brian Norris99c8ad22016-05-10 15:36:02 -0700300 /* max_data_{read,write} don't have any effect for this programmer */
David Hendrickscebee892015-05-23 20:30:30 -0700301 .max_data_read = MAX_DATA_UNSPECIFIED,
302 .max_data_write = MAX_DATA_UNSPECIFIED,
303 .probe = linux_mtd_probe,
304 .read = linux_mtd_read,
305 .write = linux_mtd_write,
306 .erase = linux_mtd_erase,
307};
308
309/* Returns 0 if setup is successful, non-zero to indicate error */
310static int linux_mtd_setup(int dev_num)
311{
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000312 char sysfs_path[32];
David Hendrickscebee892015-05-23 20:30:30 -0700313 int ret = 1;
314
Nikolai Artemievd783fb92020-08-26 03:43:19 +1000315 /* Start by checking /sys/class/mtd/mtdN/type which should be "nor" for NOR flash */
316 if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d/", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
317 goto linux_mtd_setup_exit;
David Hendrickscebee892015-05-23 20:30:30 -0700318
Nikolai Artemievd783fb92020-08-26 03:43:19 +1000319 char buf[4];
320 memset(buf, 0, sizeof(buf));
321 if (read_sysfs_string(sysfs_path, "type", buf, sizeof(buf)))
322 return 1;
323
324 if (strcmp(buf, "nor")) {
325 msg_perr("MTD device %d type is not \"nor\"\n", dev_num);
326 goto linux_mtd_setup_exit;
327 }
328
329 /* sysfs shows the correct device type, see if corresponding device node exists */
Nikolai Artemieve739e092020-08-20 16:17:51 +1000330 char dev_path[32];
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000331 struct stat s;
Nikolai Artemieve739e092020-08-20 16:17:51 +1000332 snprintf(dev_path, sizeof(dev_path), "%s/mtd%d", LINUX_DEV_ROOT, dev_num);
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000333 errno = 0;
334 if (stat(dev_path, &s) < 0) {
335 msg_pdbg("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
336 goto linux_mtd_setup_exit;
337 }
338
Nikolai Artemievd783fb92020-08-26 03:43:19 +1000339 /* so far so good, get more info from other files in this dir */
340 if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d/", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000341 goto linux_mtd_setup_exit;
Nikolai Artemiev4a4d4d92020-07-17 15:45:48 +1000342 if (get_mtd_info(sysfs_path))
David Hendrickscebee892015-05-23 20:30:30 -0700343 goto linux_mtd_setup_exit;
344
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000345 /* open file stream and go! */
346 if ((dev_fp = fopen(dev_path, "r+")) == NULL) {
347 msg_perr("Cannot open file stream for %s\n", dev_path);
David Hendrickscebee892015-05-23 20:30:30 -0700348 goto linux_mtd_setup_exit;
349 }
Nikolai Artemievd783fb92020-08-26 03:43:19 +1000350 msg_pinfo("Opened %s successfully\n", dev_path);
David Hendrickscebee892015-05-23 20:30:30 -0700351
352 ret = 0;
353linux_mtd_setup_exit:
354 return ret;
355}
356
David Hendricks93784b42016-08-09 17:00:38 -0700357static int linux_mtd_shutdown(void *data)
David Hendrickscebee892015-05-23 20:30:30 -0700358{
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000359 if (dev_fp != NULL) {
360 fclose(dev_fp);
361 dev_fp = NULL;
David Hendrickscebee892015-05-23 20:30:30 -0700362 }
363
364 return 0;
365}
366
David Hendricksac1d25c2016-08-09 17:00:58 -0700367int linux_mtd_init(void)
David Hendrickscebee892015-05-23 20:30:30 -0700368{
369 char *param;
Nikolai Artemieve739e092020-08-20 16:17:51 +1000370 int dev_num = 0;
David Hendrickscebee892015-05-23 20:30:30 -0700371 int ret = 1;
372
373 if (alias && alias->type != ALIAS_HOST)
374 return 1;
375
376 param = extract_programmer_param("dev");
377 if (param) {
378 char *endptr;
379
380 dev_num = strtol(param, &endptr, 0);
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000381 if ((*endptr != '\0') || (dev_num < 0)) {
David Hendrickscebee892015-05-23 20:30:30 -0700382 msg_perr("Invalid device number %s. Use flashrom -p "
Nikolai Artemiev255f3352020-07-17 16:38:50 +1000383 "linux_mtd:dev=N where N is a valid MTD\n"
384 "device number.\n", param);
David Hendrickscebee892015-05-23 20:30:30 -0700385 goto linux_mtd_init_exit;
386 }
387 }
388
389 if (linux_mtd_setup(dev_num))
390 goto linux_mtd_init_exit;
391
392 if (register_shutdown(linux_mtd_shutdown, NULL))
393 goto linux_mtd_init_exit;
394
Edward O'Callaghanabd30192019-05-14 15:58:19 +1000395 register_opaque_master(&programmer_linux_mtd);
David Hendrickscebee892015-05-23 20:30:30 -0700396
397 ret = 0;
398linux_mtd_init_exit:
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000399 free(param);
David Hendrickscebee892015-05-23 20:30:30 -0700400 return ret;
401}
David Hendricks85f61c52015-06-04 19:52:59 -0700402
403/*
404 * Write-protect functions.
405 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700406static int mtd_wp_list_ranges(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700407{
408 /* TODO: implement this */
409 msg_perr("--wp-list is not currently implemented for MTD.\n");
410 return 1;
411}
412
413/*
414 * We only have MEMLOCK to enable write-protection for a particular block,
415 * so we need to do force the user to use --wp-range and --wp-enable
416 * command-line arguments simultaneously. (Fortunately, CrOS factory
417 * installer does this already).
418 *
419 * The --wp-range argument is processed first and will set these variables
420 * which --wp-enable will use afterward.
421 */
422static unsigned int wp_range_start;
423static unsigned int wp_range_len;
424static int wp_set_range_called = 0;
425
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700426static int mtd_wp_set_range(const struct flashctx *flash,
David Hendricks85f61c52015-06-04 19:52:59 -0700427 unsigned int start, unsigned int len)
428{
429 wp_range_start = start;
430 wp_range_len = len;
431
432 wp_set_range_called = 1;
433 return 0;
434}
435
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700436static int mtd_wp_enable_writeprotect(const struct flashctx *flash, enum wp_mode mode)
David Hendricks85f61c52015-06-04 19:52:59 -0700437{
438 struct erase_info_user entire_chip = {
439 .start = 0,
440 .length = mtd_total_size,
441 };
442 struct erase_info_user desired_range = {
443 .start = wp_range_start,
444 .length = wp_range_len,
445 };
446
447 if (!wp_set_range_called) {
448 msg_perr("For MTD, --wp-range and --wp-enable must be "
449 "used simultaneously.\n");
450 return 1;
451 }
452
453 /*
454 * MTD handles write-protection additively, so whatever new range is
455 * specified is added to the range which is currently protected. To be
456 * consistent with flashrom behavior with other programmer interfaces,
457 * we need to disable the current write protection and then enable
458 * it for the desired range.
459 */
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000460 if (ioctl(fileno(dev_fp), MEMUNLOCK, &entire_chip) == -1) {
David Hendricks85f61c52015-06-04 19:52:59 -0700461 msg_perr("%s: Failed to disable write-protection, ioctl: %s\n",
462 __func__, strerror(errno));
Brian Norris4df096b2016-07-27 18:36:38 -0700463 msg_perr("Did you disable WP#?\n");
David Hendricks85f61c52015-06-04 19:52:59 -0700464 return 1;
465 }
466
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000467 if (ioctl(fileno(dev_fp), MEMLOCK, &desired_range) == -1) {
David Hendricks85f61c52015-06-04 19:52:59 -0700468 msg_perr("%s: Failed to enable write-protection, ioctl: %s\n",
469 __func__, strerror(errno));
470 return 1;
471 }
472
473 return 0;
474}
475
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700476static int mtd_wp_disable_writeprotect(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700477{
478 struct erase_info_user erase_info;
479
480 if (wp_set_range_called) {
481 erase_info.start = wp_range_start;
482 erase_info.length = wp_range_len;
483 } else {
484 erase_info.start = 0;
485 erase_info.length = mtd_total_size;
486 }
487
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000488 if (ioctl(fileno(dev_fp), MEMUNLOCK, &erase_info) == -1) {
David Hendricks85f61c52015-06-04 19:52:59 -0700489 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
Brian Norris4df096b2016-07-27 18:36:38 -0700490 msg_perr("Did you disable WP#?\n");
David Hendricks85f61c52015-06-04 19:52:59 -0700491 return 1;
492 }
493
494 return 0;
495}
496
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700497static int mtd_wp_status(const struct flashctx *flash)
David Hendricks85f61c52015-06-04 19:52:59 -0700498{
Brian Norris3ce4c472016-06-02 16:40:14 -0700499 uint32_t start = 0, len = 0;
500 int start_found = 0;
David Hendricks85f61c52015-06-04 19:52:59 -0700501 unsigned int u;
502
503 /* For now, assume only one contiguous region can be locked (NOR) */
504 /* FIXME: use flash struct members instead of raw MTD values here */
Wei-Ning Huang699269b2016-03-02 22:08:05 +0800505 for (u = 0; u < mtd_total_size; u += mtd_erasesize) {
David Hendricks85f61c52015-06-04 19:52:59 -0700506 int rc;
507 struct erase_info_user erase_info = {
508 .start = u,
509 .length = mtd_erasesize,
510 };
511
Nikolai Artemievb81761e2020-07-29 23:22:07 +1000512 rc = ioctl(fileno(dev_fp), MEMISLOCKED, &erase_info);
David Hendricks85f61c52015-06-04 19:52:59 -0700513 if (rc < 0) {
514 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
515 return 1;
516 } else if (rc == 1) {
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800517 if (!start_found) {
David Hendricks85f61c52015-06-04 19:52:59 -0700518 start = erase_info.start;
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800519 start_found = 1;
520 }
Brian Norris3ce4c472016-06-02 16:40:14 -0700521 len += mtd_erasesize;
David Hendricks85f61c52015-06-04 19:52:59 -0700522 } else if (rc == 0) {
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800523 if (start_found) {
Brian Norris3ce4c472016-06-02 16:40:14 -0700524 /* TODO: changes required for supporting non-contiguous locked regions */
525 break;
Wei-Ning Huangbf285ee2016-03-04 11:36:14 +0800526 }
David Hendricks85f61c52015-06-04 19:52:59 -0700527 }
528
David Hendricks85f61c52015-06-04 19:52:59 -0700529 }
530
Wei-Ning Huangca907072016-04-27 11:30:17 +0800531 msg_cinfo("WP: write protect is %s.\n",
Brian Norris3ce4c472016-06-02 16:40:14 -0700532 start_found ? "enabled": "disabled");
Wei-Ning Huangca907072016-04-27 11:30:17 +0800533 msg_pinfo("WP: write protect range: start=0x%08x, "
Brian Norris3ce4c472016-06-02 16:40:14 -0700534 "len=0x%08x\n", start, len);
Wei-Ning Huangca907072016-04-27 11:30:17 +0800535
David Hendricks85f61c52015-06-04 19:52:59 -0700536 return 0;
537}
538
539static struct wp wp_mtd = {
540 .list_ranges = mtd_wp_list_ranges,
541 .set_range = mtd_wp_set_range,
542 .enable = mtd_wp_enable_writeprotect,
543 .disable = mtd_wp_disable_writeprotect,
544 .wp_status = mtd_wp_status,
545};