blob: e1b11cd5fe79459082246e7a6519de47bd793622 [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.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
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 the
13 * 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
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <ctype.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <inttypes.h>
24#include <libgen.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <mtd/mtd-user.h>
28#include <string.h>
29#include <sys/ioctl.h>
30#include <sys/stat.h>
31#include <unistd.h>
32
33#include "file.h"
34#include "flash.h"
35#include "programmer.h"
36
37#define LINUX_DEV_ROOT "/dev"
38#define LINUX_MTD_SYSFS_ROOT "/sys/class/mtd"
39
40/* enough space for LINUX_MTD_SYSFS_ROOT + directory name + filename */
41static char sysfs_path[PATH_MAX];
42
43static int dev_fd = -1;
44
45static char *mtd_device_name;
46static int mtd_device_is_writeable;
47
48/* Size info is presented in bytes in sysfs. */
49static unsigned long int mtd_total_size;
50static unsigned long int mtd_numeraseregions;
51static unsigned long int mtd_erasesize; /* only valid if numeraseregions is 0 */
52
53static int stat_mtd_files(char *dev_path, char *sysfs_path)
54{
55 struct stat s;
56
57 errno = 0;
58 if (stat(dev_path, &s) < 0) {
59 msg_pdbg("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
60 return 1;
61 }
62
63 if (lstat(sysfs_path, &s) < 0) {
64 msg_pdbg("Cannot stat \"%s\" : %s\n",
65 sysfs_path, strerror(errno));
66 return 1;
67 }
68
69 return 0;
70}
71
72/* read a string from a sysfs file and sanitize it */
73static int read_sysfs_string(const char *filename, char *buf, int len)
74{
75 int fd, bytes_read, i;
76 char path[strlen(LINUX_MTD_SYSFS_ROOT) + 32];
77
78 snprintf(path, sizeof(path), "%s/%s", sysfs_path, filename);
79
80 if ((fd = open(path, O_RDONLY)) < 0) {
81 msg_perr("Cannot open %s\n", path);
82 return 1;
83 }
84
85 if ((bytes_read = read(fd, buf, len - 1)) < 0) {
86 msg_perr("Cannot read %s\n", path);
87 close(fd);
88 return 1;
89 }
90
91 buf[bytes_read] = '\0';
92
93 /*
94 * Files from sysfs sometimes contain a newline or other garbage that
95 * can confuse functions like strtoul() and ruin formatting in print
96 * statements. Replace the first non-printable character (space is
97 * considered printable) with a proper string terminator.
98 */
99 for (i = 0; i < len; i++) {
100 if (!isprint(buf[i])) {
101 buf[i] = '\0';
102 break;
103 }
104 }
105
106 close(fd);
107 return 0;
108}
109
110static int read_sysfs_int(const char *filename, unsigned long int *val)
111{
112 uint32_t tmp;
113 char buf[32];
114 char *endptr;
115
116 if (read_sysfs_string(filename, buf, sizeof(buf)))
117 return 1;
118
119 errno = 0;
120 *val = strtoul(buf, &endptr, 0);
121 if (endptr != &buf[strlen(buf)]) {
122 msg_perr("Error reading %s\n", filename);
123 return 1;
124 }
125
126 if (errno) {
127 msg_perr("Error reading %s: %s\n", filename, strerror(errno));
128 return 1;
129 }
130
131 return 0;
132}
133
134/* returns 0 to indicate success, non-zero to indicate error */
135static int get_mtd_info(void)
136{
137 unsigned long int tmp;
138 char mtd_device_name[32];
139
140 /* Flags */
141 if (read_sysfs_int("flags", &tmp))
142 return 1;
143 if (tmp & MTD_WRITEABLE) {
144 /* cache for later use by write function */
145 mtd_device_is_writeable = 1;
146 }
147
148 /* Device name */
149 if (read_sysfs_string("name", mtd_device_name, sizeof(mtd_device_name)))
150 return 1;
151
152 /* Total size */
153 if (read_sysfs_int("size", &mtd_total_size))
154 return 1;
155 if (__builtin_popcount(mtd_total_size) != 1) {
156 msg_perr("MTD size is not a power of 2\n");
157 return 1;
158 }
159
160 /* Erase size */
161 if (read_sysfs_int("erasesize", &mtd_erasesize))
162 return 1;
163 if (__builtin_popcount(mtd_erasesize) != 1) {
164 msg_perr("MTD erase size is not a power of 2\n");
165 return 1;
166 }
167
168 /* Erase regions */
169 if (read_sysfs_int("numeraseregions", &mtd_numeraseregions))
170 return 1;
171 if (mtd_numeraseregions != 0) {
172 msg_perr("Non-uniform eraseblock size is unsupported.\n");
173 return 1;
174 }
175
176 msg_pspew("%s: device_name: \"%s\", is_writeable: %d, "
177 "numeraseregions: %lu, total_size: %lu, erasesize: %lu\n",
178 __func__, mtd_device_name, mtd_device_is_writeable,
179 mtd_numeraseregions, mtd_total_size, mtd_erasesize);
180
181 return 0;
182}
183
184static int linux_mtd_probe(struct flashchip *flash)
185{
186 flash->tested = TEST_OK_PREW;
187 flash->total_size = mtd_total_size / 1024; /* bytes -> kB */
188 flash->block_erasers[0].eraseblocks[0].size = mtd_erasesize;
189 flash->block_erasers[0].eraseblocks[0].count =
190 mtd_total_size / mtd_erasesize;
191 return 1;
192}
193
194static int linux_mtd_read(struct flashchip *flash, uint8_t *buf,
195 unsigned int start, unsigned int len)
196{
197 if (lseek(dev_fd, start, SEEK_SET) != start) {
198 msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
199 return 1;
200 }
201
202 if (read(dev_fd, buf, len) != len) {
203 msg_perr("Cannot read 0x%06x bytes at 0x%06x: %s\n",
204 start, len, strerror(errno));
205 return 1;
206 }
207
208 return 0;
209}
210
211/* this version assumes we must divide the write request into pages ourselves */
212static int linux_mtd_write(struct flashchip *flash, uint8_t *buf,
213 unsigned int start, unsigned int len)
214{
215 unsigned int page;
216 unsigned int chunksize, page_size;
217
218 chunksize = page_size = flash->page_size;
219
220 if (!mtd_device_is_writeable)
221 return 1;
222
223 for (page = start / page_size;
224 page <= (start + len - 1) / page_size; page++) {
225 unsigned int i, starthere, lenhere;
226
227 starthere = max(start, page * page_size);
228 lenhere = min(start + len, (page + 1) * page_size) - starthere;
229 for (i = 0; i < lenhere; i += chunksize) {
230 unsigned int towrite = min(chunksize, lenhere - i);
231
232 if (lseek(dev_fd, starthere, SEEK_SET) != starthere) {
233 msg_perr("Cannot seek to 0x%06x: %s\n",
234 start, strerror(errno));
235 return 1;
236 }
237
238 if (write(dev_fd, &buf[starthere - start], towrite) != towrite) {
239 msg_perr("Cannot read 0x%06x bytes at 0x%06x: "
240 "%s\n", start, len, strerror(errno));
241 return 1;
242 }
243 }
244 }
245
246 return 0;
247}
248
249static int linux_mtd_erase(struct flashchip *flash,
250 unsigned int start, unsigned int len)
251{
252 struct region_info_user region_info;
253 uint32_t u;
254
255 if (mtd_numeraseregions != 0) {
256 /* TODO: Support non-uniform eraseblock size using
257 use MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls */
258 }
259
260 for (u = 0; u < len; u += mtd_erasesize) {
261 struct erase_info_user erase_info = {
262 .start = start + u,
263 .length = mtd_erasesize,
264 };
265
266 if (ioctl(dev_fd, MEMERASE, &erase_info) == -1) {
267 msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
268 return 1;
269 }
270 }
271
272 return 0;
273}
274
275static struct opaque_programmer programmer_linux_mtd = {
276 /* FIXME: Do we need to change these (especially write) as per
277 * page size requirements? */
278 .max_data_read = MAX_DATA_UNSPECIFIED,
279 .max_data_write = MAX_DATA_UNSPECIFIED,
280 .probe = linux_mtd_probe,
281 .read = linux_mtd_read,
282 .write = linux_mtd_write,
283 .erase = linux_mtd_erase,
284};
285
286/* Returns 0 if setup is successful, non-zero to indicate error */
287static int linux_mtd_setup(int dev_num)
288{
289 char dev_path[16]; /* "/dev/mtdN" */
290 int ret = 1;
291
292 if (dev_num < 0) {
293 char *tmp, *p;
294
295 tmp = (char *)scanft(LINUX_MTD_SYSFS_ROOT, "type", "nor", 1);
296 if (!tmp) {
297 msg_perr("%s: NOR type device not found.\n", __func__);
298 goto linux_mtd_setup_exit;
299 }
300
301 /* "tmp" should be something like "/sys/blah/mtdN/type" */
302 p = tmp + strlen(LINUX_MTD_SYSFS_ROOT);
303 while (p[0] == '/')
304 p++;
305
306 if (sscanf(p, "mtd%d", &dev_num) != 1) {
307 msg_perr("Can't obtain device number from \"%s\"\n", p);
308 free(tmp);
309 goto linux_mtd_setup_exit;
310 }
311 free(tmp);
312 }
313
314 snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d",
315 LINUX_MTD_SYSFS_ROOT, dev_num);
316 snprintf(dev_path, sizeof(dev_path), "%s/mtd%d",
317 LINUX_DEV_ROOT, dev_num);
318 msg_pdbg("%s: sysfs_path: \"%s\", dev_path: \"%s\"\n",
319 __func__, sysfs_path, dev_path);
320
321 if (stat_mtd_files(dev_path, sysfs_path))
322 goto linux_mtd_setup_exit;
323
324 if (get_mtd_info())
325 goto linux_mtd_setup_exit;
326
327 if ((dev_fd = open(dev_path, O_RDWR)) == -1) {
328 msg_pdbg("%s: failed to open %s: %s\n", __func__,
329 dev_path, strerror(errno));
330 goto linux_mtd_setup_exit;
331 }
332
333 ret = 0;
334linux_mtd_setup_exit:
335 return ret;
336}
337
338static int linux_mtd_shutdown(void *data)
339{
340 if (dev_fd != -1) {
341 close(dev_fd);
342 dev_fd = -1;
343 }
344
345 return 0;
346}
347
348int linux_mtd_init(void)
349{
350 char *param;
351 int dev_num = -1; /* linux_mtd_setup will search if dev_num < 0 */
352 int ret = 1;
353
354 if (alias && alias->type != ALIAS_HOST)
355 return 1;
356
357 param = extract_programmer_param("dev");
358 if (param) {
359 char *endptr;
360
361 dev_num = strtol(param, &endptr, 0);
362 if ((param == endptr) || (dev_num < 0)) {
363 msg_perr("Invalid device number %s. Use flashrom -p "
364 "linux_mtd:dev=N where N is a valid MTD "
365 "device number\n", param);
366 goto linux_mtd_init_exit;
367 }
368 }
369
370 if (linux_mtd_setup(dev_num))
371 goto linux_mtd_init_exit;
372
373 if (register_shutdown(linux_mtd_shutdown, NULL))
374 goto linux_mtd_init_exit;
375
376 register_opaque_programmer(&programmer_linux_mtd);
377
378 ret = 0;
379linux_mtd_init_exit:
380 msg_pdbg("%s: %s\n", __func__, ret == 0 ? "success." : "failed.");
381 return ret;
382}