blob: ae8bef2fe515080a51820d1d93065e2476f350f6 [file] [log] [blame]
David Hendricksf9a30552015-05-23 20:30:30 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright 2015 Google Inc.
5 * Copyright 2018-present Facebook, Inc.
6 *
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.
15 */
16
17#include <ctype.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <mtd/mtd-user.h>
23#include <string.h>
24#include <sys/ioctl.h>
25#include <sys/stat.h>
26#include <unistd.h>
27
28#include "flash.h"
29#include "programmer.h"
30
31#define LINUX_DEV_ROOT "/dev"
32#define LINUX_MTD_SYSFS_ROOT "/sys/class/mtd"
33
34static FILE *dev_fp = NULL;
35
36static int mtd_device_is_writeable;
37
38static int mtd_no_erase;
39
40/* Size info is presented in bytes in sysfs. */
41static unsigned long int mtd_total_size;
42static unsigned long int mtd_numeraseregions;
43static unsigned long int mtd_erasesize; /* only valid if numeraseregions is 0 */
44
45/* read a string from a sysfs file and sanitize it */
46static int read_sysfs_string(const char *sysfs_path, const char *filename, char *buf, int len)
47{
48 int i;
49 size_t bytes_read;
50 FILE *fp;
51 char path[strlen(LINUX_MTD_SYSFS_ROOT) + 32];
52
53 snprintf(path, sizeof(path), "%s/%s", sysfs_path, filename);
54
55 if ((fp = fopen(path, "r")) == NULL) {
56 msg_perr("Cannot open %s\n", path);
57 return 1;
58 }
59
60 clearerr(fp);
61 bytes_read = fread(buf, 1, (size_t)len, fp);
62 if (!feof(fp) && ferror(fp)) {
63 msg_perr("Error occurred when reading %s\n", path);
64 fclose(fp);
65 return 1;
66 }
67
68 buf[bytes_read] = '\0';
69
70 /*
71 * Files from sysfs sometimes contain a newline or other garbage that
72 * can confuse functions like strtoul() and ruin formatting in print
73 * statements. Replace the first non-printable character (space is
74 * considered printable) with a proper string terminator.
75 */
76 for (i = 0; i < len; i++) {
77 if (!isprint(buf[i])) {
78 buf[i] = '\0';
79 break;
80 }
81 }
82
83 fclose(fp);
84 return 0;
85}
86
87static int read_sysfs_int(const char *sysfs_path, const char *filename, unsigned long int *val)
88{
89 char buf[32];
90 char *endptr;
91
92 if (read_sysfs_string(sysfs_path, filename, buf, sizeof(buf)))
93 return 1;
94
95 errno = 0;
96 *val = strtoul(buf, &endptr, 0);
97 if (*endptr != '\0') {
98 msg_perr("Error reading %s\n", filename);
99 return 1;
100 }
101
102 if (errno) {
103 msg_perr("Error reading %s: %s\n", filename, strerror(errno));
104 return 1;
105 }
106
107 return 0;
108}
109
110static int popcnt(unsigned int u)
111{
112 int count = 0;
113
114 while (u) {
115 u &= u - 1;
116 count++;
117 }
118
119 return count;
120}
121
122/* returns 0 to indicate success, non-zero to indicate error */
123static int get_mtd_info(const char *sysfs_path)
124{
125 unsigned long int tmp;
126 char mtd_device_name[32];
127
128 /* Flags */
129 if (read_sysfs_int(sysfs_path, "flags", &tmp))
130 return 1;
131 if (tmp & MTD_WRITEABLE) {
132 /* cache for later use by write function */
133 mtd_device_is_writeable = 1;
134 }
135 if (tmp & MTD_NO_ERASE) {
136 mtd_no_erase = 1;
137 }
138
139 /* Device name */
140 if (read_sysfs_string(sysfs_path, "name", mtd_device_name, sizeof(mtd_device_name)))
141 return 1;
142
143 /* Total size */
144 if (read_sysfs_int(sysfs_path, "size", &mtd_total_size))
145 return 1;
146 if (popcnt(mtd_total_size) != 1) {
147 msg_perr("MTD size is not a power of 2\n");
148 return 1;
149 }
150
151 /* Erase size */
152 if (read_sysfs_int(sysfs_path, "erasesize", &mtd_erasesize))
153 return 1;
154 if (popcnt(mtd_erasesize) != 1) {
155 msg_perr("MTD erase size is not a power of 2\n");
156 return 1;
157 }
158
159 /* Erase regions */
160 if (read_sysfs_int(sysfs_path, "numeraseregions", &mtd_numeraseregions))
161 return 1;
162 if (mtd_numeraseregions != 0) {
163 msg_perr("Non-uniform eraseblock size is unsupported.\n");
164 return 1;
165 }
166
167 msg_pdbg("%s: device_name: \"%s\", is_writeable: %d, "
168 "numeraseregions: %lu, total_size: %lu, erasesize: %lu\n",
169 __func__, mtd_device_name, mtd_device_is_writeable,
170 mtd_numeraseregions, mtd_total_size, mtd_erasesize);
171
172 return 0;
173}
174
175static int linux_mtd_probe(struct flashctx *flash)
176{
177 if (mtd_no_erase)
178 flash->chip->feature_bits |= FEATURE_NO_ERASE;
179 flash->chip->tested = TEST_OK_PREW;
180 flash->chip->total_size = mtd_total_size / 1024; /* bytes -> kB */
181 flash->chip->block_erasers[0].eraseblocks[0].size = mtd_erasesize;
182 flash->chip->block_erasers[0].eraseblocks[0].count = mtd_total_size / mtd_erasesize;
183 return 1;
184}
185
186static int linux_mtd_read(struct flashctx *flash, uint8_t *buf,
187 unsigned int start, unsigned int len)
188{
189 unsigned int eb_size = flash->chip->block_erasers[0].eraseblocks[0].size;
190 unsigned int i;
191
192 if (fseek(dev_fp, start, SEEK_SET) != 0) {
193 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
194 return 1;
195 }
196
197 for (i = 0; i < len; ) {
198 /*
199 * Try to align reads to eraseblock size.
200 * FIXME: Shouldn't actually be necessary, but not all MTD
201 * drivers handle arbitrary large reads well.
202 */
203 unsigned int step = eb_size - ((start + i) % eb_size);
204 step = min(step, len - i);
205
206 if (fread(buf + i, step, 1, dev_fp) != 1) {
207 msg_perr("Cannot read 0x%06x bytes at 0x%06x: %s\n",
208 step, start + i, strerror(errno));
209 return 1;
210 }
211
212 i += step;
213 }
214
215 return 0;
216}
217
218/* this version assumes we must divide the write request into chunks ourselves */
219static int linux_mtd_write(struct flashctx *flash, const uint8_t *buf,
220 unsigned int start, unsigned int len)
221{
222 unsigned int chunksize = flash->chip->block_erasers[0].eraseblocks[0].size;
223 unsigned int i;
224
225 if (!mtd_device_is_writeable)
226 return 1;
227
228 if (fseek(dev_fp, start, SEEK_SET) != 0) {
229 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
230 return 1;
231 }
232
233 /*
234 * Try to align writes to eraseblock size. We want these large enough
235 * to give MTD room for optimizing performance.
236 * FIXME: Shouldn't need to divide this up at all, but not all MTD
237 * drivers handle arbitrary large writes well.
238 */
239 for (i = 0; i < len; ) {
240 unsigned int step = chunksize - ((start + i) % chunksize);
241 step = min(step, len - i);
242
243 if (fwrite(buf + i, step, 1, dev_fp) != 1) {
244 msg_perr("Cannot write 0x%06x bytes at 0x%06x\n", step, start + i);
245 return 1;
246 }
247
248 if (fflush(dev_fp) == EOF) {
249 msg_perr("Failed to flush buffer: %s\n", strerror(errno));
250 return 1;
251 }
252
253 i += step;
254 }
255
256 return 0;
257}
258
259static int linux_mtd_erase(struct flashctx *flash,
260 unsigned int start, unsigned int len)
261{
262 uint32_t u;
263
264 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
270 if (mtd_numeraseregions != 0) {
271 /* TODO: Support non-uniform eraseblock size using
272 use MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls */
273 msg_perr("%s: mtd_numeraseregions must be 0\n", __func__);
274 return 1;
275 }
276
277 for (u = 0; u < len; u += mtd_erasesize) {
278 struct erase_info_user erase_info = {
279 .start = start + u,
280 .length = mtd_erasesize,
281 };
282
283 if (ioctl(fileno(dev_fp), MEMERASE, &erase_info) == -1) {
284 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
285 return 1;
286 }
287 }
288
289 return 0;
290}
291
292static struct opaque_master programmer_linux_mtd = {
293 /* max_data_{read,write} don't have any effect for this programmer */
294 .max_data_read = MAX_DATA_UNSPECIFIED,
295 .max_data_write = MAX_DATA_UNSPECIFIED,
296 .probe = linux_mtd_probe,
297 .read = linux_mtd_read,
298 .write = linux_mtd_write,
299 .erase = linux_mtd_erase,
300};
301
302/* Returns 0 if setup is successful, non-zero to indicate error */
303static int linux_mtd_setup(int dev_num)
304{
305 char sysfs_path[32];
306 int ret = 1;
307
308 /* Start by checking /sys/class/mtd/mtdN/type which should be "nor" for NOR flash */
309 if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d/", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
310 goto linux_mtd_setup_exit;
311
312 char buf[4];
313 memset(buf, 0, sizeof(buf));
314 if (read_sysfs_string(sysfs_path, "type", buf, sizeof(buf)))
315 return 1;
316
317 if (strcmp(buf, "nor")) {
318 msg_perr("MTD device %d type is not \"nor\"\n", dev_num);
319 goto linux_mtd_setup_exit;
320 }
321
322 /* sysfs shows the correct device type, see if corresponding device node exists */
323 char dev_path[32];
324 struct stat s;
325 snprintf(dev_path, sizeof(dev_path), "%s/mtd%d", LINUX_DEV_ROOT, dev_num);
326 errno = 0;
327 if (stat(dev_path, &s) < 0) {
328 msg_pdbg("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
329 goto linux_mtd_setup_exit;
330 }
331
332 /* so far so good, get more info from other files in this dir */
333 if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d/", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
334 goto linux_mtd_setup_exit;
335 if (get_mtd_info(sysfs_path))
336 goto linux_mtd_setup_exit;
337
338 /* open file stream and go! */
339 if ((dev_fp = fopen(dev_path, "r+")) == NULL) {
340 msg_perr("Cannot open file stream for %s\n", dev_path);
341 goto linux_mtd_setup_exit;
342 }
343 msg_pinfo("Opened %s successfully\n", dev_path);
344
345 ret = 0;
346linux_mtd_setup_exit:
347 return ret;
348}
349
350static int linux_mtd_shutdown(void *data)
351{
352 if (dev_fp != NULL) {
353 fclose(dev_fp);
354 dev_fp = NULL;
355 }
356
357 return 0;
358}
359
360int linux_mtd_init(void)
361{
362 char *param;
363 int dev_num = 0;
364 int ret = 1;
365
366 param = extract_programmer_param("dev");
367 if (param) {
368 char *endptr;
369
370 dev_num = strtol(param, &endptr, 0);
371 if ((*endptr != '\0') || (dev_num < 0)) {
372 msg_perr("Invalid device number %s. Use flashrom -p "
373 "linux_mtd:dev=N where N is a valid MTD\n"
374 "device number.\n", param);
375 goto linux_mtd_init_exit;
376 }
377 }
378
David Hendricksb0247b32018-05-23 21:50:18 -0700379 /*
380 * If user specified the MTD device number then error out if it doesn't
381 * appear to exist. Otherwise assume the error is benign and print a
382 * debug message. Bail out in either case.
383 */
384 char sysfs_path[32];
385 if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
386 goto linux_mtd_init_exit;
387
388 struct stat s;
389 if (stat(sysfs_path, &s) < 0) {
390 if (param)
391 msg_perr("%s does not exist\n", sysfs_path);
392 else
393 msg_pdbg("%s does not exist\n", sysfs_path);
394 goto linux_mtd_init_exit;
395 }
396
David Hendricksf9a30552015-05-23 20:30:30 -0700397 if (linux_mtd_setup(dev_num))
398 goto linux_mtd_init_exit;
399
400 if (register_shutdown(linux_mtd_shutdown, NULL))
401 goto linux_mtd_init_exit;
402
403 register_opaque_master(&programmer_linux_mtd);
404
405 ret = 0;
406linux_mtd_init_exit:
407 return ret;
408}