1*0d6140beSAndroid Build Coastguard Worker /*
2*0d6140beSAndroid Build Coastguard Worker * This file is part of the flashrom project.
3*0d6140beSAndroid Build Coastguard Worker *
4*0d6140beSAndroid Build Coastguard Worker * Copyright 2015 Google Inc.
5*0d6140beSAndroid Build Coastguard Worker * Copyright 2018-present Facebook, Inc.
6*0d6140beSAndroid Build Coastguard Worker *
7*0d6140beSAndroid Build Coastguard Worker * This program is free software; you can redistribute it and/or modify
8*0d6140beSAndroid Build Coastguard Worker * it under the terms of the GNU General Public License as published by
9*0d6140beSAndroid Build Coastguard Worker * the Free Software Foundation; version 2 of the License.
10*0d6140beSAndroid Build Coastguard Worker *
11*0d6140beSAndroid Build Coastguard Worker * This program is distributed in the hope that it will be useful,
12*0d6140beSAndroid Build Coastguard Worker * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*0d6140beSAndroid Build Coastguard Worker * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*0d6140beSAndroid Build Coastguard Worker * GNU General Public License for more details.
15*0d6140beSAndroid Build Coastguard Worker */
16*0d6140beSAndroid Build Coastguard Worker
17*0d6140beSAndroid Build Coastguard Worker #include <ctype.h>
18*0d6140beSAndroid Build Coastguard Worker #include <errno.h>
19*0d6140beSAndroid Build Coastguard Worker #include <fcntl.h>
20*0d6140beSAndroid Build Coastguard Worker #include <stdbool.h>
21*0d6140beSAndroid Build Coastguard Worker #include <stdio.h>
22*0d6140beSAndroid Build Coastguard Worker #include <stdlib.h>
23*0d6140beSAndroid Build Coastguard Worker #include <mtd/mtd-user.h>
24*0d6140beSAndroid Build Coastguard Worker #include <string.h>
25*0d6140beSAndroid Build Coastguard Worker #include <sys/ioctl.h>
26*0d6140beSAndroid Build Coastguard Worker #include <sys/stat.h>
27*0d6140beSAndroid Build Coastguard Worker #include <unistd.h>
28*0d6140beSAndroid Build Coastguard Worker
29*0d6140beSAndroid Build Coastguard Worker #include "flash.h"
30*0d6140beSAndroid Build Coastguard Worker #include "programmer.h"
31*0d6140beSAndroid Build Coastguard Worker
32*0d6140beSAndroid Build Coastguard Worker #define LINUX_DEV_ROOT "/dev"
33*0d6140beSAndroid Build Coastguard Worker #define LINUX_MTD_SYSFS_ROOT "/sys/class/mtd"
34*0d6140beSAndroid Build Coastguard Worker
35*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data {
36*0d6140beSAndroid Build Coastguard Worker FILE *dev_fp;
37*0d6140beSAndroid Build Coastguard Worker bool device_is_writeable;
38*0d6140beSAndroid Build Coastguard Worker bool no_erase;
39*0d6140beSAndroid Build Coastguard Worker /* Size info is presented in bytes in sysfs. */
40*0d6140beSAndroid Build Coastguard Worker unsigned long int total_size;
41*0d6140beSAndroid Build Coastguard Worker unsigned long int numeraseregions;
42*0d6140beSAndroid Build Coastguard Worker /* only valid if numeraseregions is 0 */
43*0d6140beSAndroid Build Coastguard Worker unsigned long int erasesize;
44*0d6140beSAndroid Build Coastguard Worker };
45*0d6140beSAndroid Build Coastguard Worker
46*0d6140beSAndroid Build Coastguard Worker /* read a string from a sysfs file and sanitize it */
read_sysfs_string(const char * sysfs_path,const char * filename,char * buf,int len)47*0d6140beSAndroid Build Coastguard Worker static int read_sysfs_string(const char *sysfs_path, const char *filename, char *buf, int len)
48*0d6140beSAndroid Build Coastguard Worker {
49*0d6140beSAndroid Build Coastguard Worker int i;
50*0d6140beSAndroid Build Coastguard Worker size_t bytes_read;
51*0d6140beSAndroid Build Coastguard Worker FILE *fp;
52*0d6140beSAndroid Build Coastguard Worker char path[sizeof(LINUX_MTD_SYSFS_ROOT) + 31];
53*0d6140beSAndroid Build Coastguard Worker
54*0d6140beSAndroid Build Coastguard Worker snprintf(path, sizeof(path), "%s/%s", sysfs_path, filename);
55*0d6140beSAndroid Build Coastguard Worker
56*0d6140beSAndroid Build Coastguard Worker if ((fp = fopen(path, "r")) == NULL) {
57*0d6140beSAndroid Build Coastguard Worker msg_perr("Cannot open %s\n", path);
58*0d6140beSAndroid Build Coastguard Worker return 1;
59*0d6140beSAndroid Build Coastguard Worker }
60*0d6140beSAndroid Build Coastguard Worker
61*0d6140beSAndroid Build Coastguard Worker clearerr(fp);
62*0d6140beSAndroid Build Coastguard Worker bytes_read = fread(buf, 1, (size_t)len, fp);
63*0d6140beSAndroid Build Coastguard Worker if (!feof(fp) && ferror(fp)) {
64*0d6140beSAndroid Build Coastguard Worker msg_perr("Error occurred when reading %s\n", path);
65*0d6140beSAndroid Build Coastguard Worker fclose(fp);
66*0d6140beSAndroid Build Coastguard Worker return 1;
67*0d6140beSAndroid Build Coastguard Worker }
68*0d6140beSAndroid Build Coastguard Worker
69*0d6140beSAndroid Build Coastguard Worker buf[bytes_read] = '\0';
70*0d6140beSAndroid Build Coastguard Worker
71*0d6140beSAndroid Build Coastguard Worker /*
72*0d6140beSAndroid Build Coastguard Worker * Files from sysfs sometimes contain a newline or other garbage that
73*0d6140beSAndroid Build Coastguard Worker * can confuse functions like strtoul() and ruin formatting in print
74*0d6140beSAndroid Build Coastguard Worker * statements. Replace the first non-printable character (space is
75*0d6140beSAndroid Build Coastguard Worker * considered printable) with a proper string terminator.
76*0d6140beSAndroid Build Coastguard Worker */
77*0d6140beSAndroid Build Coastguard Worker for (i = 0; i < len; i++) {
78*0d6140beSAndroid Build Coastguard Worker if (!isprint(buf[i])) {
79*0d6140beSAndroid Build Coastguard Worker buf[i] = '\0';
80*0d6140beSAndroid Build Coastguard Worker break;
81*0d6140beSAndroid Build Coastguard Worker }
82*0d6140beSAndroid Build Coastguard Worker }
83*0d6140beSAndroid Build Coastguard Worker
84*0d6140beSAndroid Build Coastguard Worker fclose(fp);
85*0d6140beSAndroid Build Coastguard Worker return 0;
86*0d6140beSAndroid Build Coastguard Worker }
87*0d6140beSAndroid Build Coastguard Worker
read_sysfs_int(const char * sysfs_path,const char * filename,unsigned long int * val)88*0d6140beSAndroid Build Coastguard Worker static int read_sysfs_int(const char *sysfs_path, const char *filename, unsigned long int *val)
89*0d6140beSAndroid Build Coastguard Worker {
90*0d6140beSAndroid Build Coastguard Worker char buf[32];
91*0d6140beSAndroid Build Coastguard Worker char *endptr;
92*0d6140beSAndroid Build Coastguard Worker
93*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_string(sysfs_path, filename, buf, sizeof(buf)))
94*0d6140beSAndroid Build Coastguard Worker return 1;
95*0d6140beSAndroid Build Coastguard Worker
96*0d6140beSAndroid Build Coastguard Worker errno = 0;
97*0d6140beSAndroid Build Coastguard Worker *val = strtoul(buf, &endptr, 0);
98*0d6140beSAndroid Build Coastguard Worker if (*endptr != '\0') {
99*0d6140beSAndroid Build Coastguard Worker msg_perr("Error reading %s\n", filename);
100*0d6140beSAndroid Build Coastguard Worker return 1;
101*0d6140beSAndroid Build Coastguard Worker }
102*0d6140beSAndroid Build Coastguard Worker
103*0d6140beSAndroid Build Coastguard Worker if (errno) {
104*0d6140beSAndroid Build Coastguard Worker msg_perr("Error reading %s: %s\n", filename, strerror(errno));
105*0d6140beSAndroid Build Coastguard Worker return 1;
106*0d6140beSAndroid Build Coastguard Worker }
107*0d6140beSAndroid Build Coastguard Worker
108*0d6140beSAndroid Build Coastguard Worker return 0;
109*0d6140beSAndroid Build Coastguard Worker }
110*0d6140beSAndroid Build Coastguard Worker
popcnt(unsigned int u)111*0d6140beSAndroid Build Coastguard Worker static int popcnt(unsigned int u)
112*0d6140beSAndroid Build Coastguard Worker {
113*0d6140beSAndroid Build Coastguard Worker int count = 0;
114*0d6140beSAndroid Build Coastguard Worker
115*0d6140beSAndroid Build Coastguard Worker while (u) {
116*0d6140beSAndroid Build Coastguard Worker u &= u - 1;
117*0d6140beSAndroid Build Coastguard Worker count++;
118*0d6140beSAndroid Build Coastguard Worker }
119*0d6140beSAndroid Build Coastguard Worker
120*0d6140beSAndroid Build Coastguard Worker return count;
121*0d6140beSAndroid Build Coastguard Worker }
122*0d6140beSAndroid Build Coastguard Worker
123*0d6140beSAndroid Build Coastguard Worker /* returns 0 to indicate success, non-zero to indicate error */
get_mtd_info(const char * sysfs_path,struct linux_mtd_data * data)124*0d6140beSAndroid Build Coastguard Worker static int get_mtd_info(const char *sysfs_path, struct linux_mtd_data *data)
125*0d6140beSAndroid Build Coastguard Worker {
126*0d6140beSAndroid Build Coastguard Worker unsigned long int tmp;
127*0d6140beSAndroid Build Coastguard Worker char device_name[32];
128*0d6140beSAndroid Build Coastguard Worker
129*0d6140beSAndroid Build Coastguard Worker /* Flags */
130*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_int(sysfs_path, "flags", &tmp))
131*0d6140beSAndroid Build Coastguard Worker return 1;
132*0d6140beSAndroid Build Coastguard Worker if (tmp & MTD_WRITEABLE) {
133*0d6140beSAndroid Build Coastguard Worker /* cache for later use by write function */
134*0d6140beSAndroid Build Coastguard Worker data->device_is_writeable = true;
135*0d6140beSAndroid Build Coastguard Worker }
136*0d6140beSAndroid Build Coastguard Worker if (tmp & MTD_NO_ERASE) {
137*0d6140beSAndroid Build Coastguard Worker data->no_erase = true;
138*0d6140beSAndroid Build Coastguard Worker }
139*0d6140beSAndroid Build Coastguard Worker
140*0d6140beSAndroid Build Coastguard Worker /* Device name */
141*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_string(sysfs_path, "name", device_name, sizeof(device_name)))
142*0d6140beSAndroid Build Coastguard Worker return 1;
143*0d6140beSAndroid Build Coastguard Worker
144*0d6140beSAndroid Build Coastguard Worker /* Total size */
145*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_int(sysfs_path, "size", &data->total_size))
146*0d6140beSAndroid Build Coastguard Worker return 1;
147*0d6140beSAndroid Build Coastguard Worker if (popcnt(data->total_size) != 1) {
148*0d6140beSAndroid Build Coastguard Worker msg_perr("MTD size is not a power of 2\n");
149*0d6140beSAndroid Build Coastguard Worker return 1;
150*0d6140beSAndroid Build Coastguard Worker }
151*0d6140beSAndroid Build Coastguard Worker
152*0d6140beSAndroid Build Coastguard Worker /* Erase size */
153*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_int(sysfs_path, "erasesize", &data->erasesize))
154*0d6140beSAndroid Build Coastguard Worker return 1;
155*0d6140beSAndroid Build Coastguard Worker if (popcnt(data->erasesize) != 1) {
156*0d6140beSAndroid Build Coastguard Worker msg_perr("MTD erase size is not a power of 2\n");
157*0d6140beSAndroid Build Coastguard Worker return 1;
158*0d6140beSAndroid Build Coastguard Worker }
159*0d6140beSAndroid Build Coastguard Worker
160*0d6140beSAndroid Build Coastguard Worker /* Erase regions */
161*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_int(sysfs_path, "numeraseregions", &data->numeraseregions))
162*0d6140beSAndroid Build Coastguard Worker return 1;
163*0d6140beSAndroid Build Coastguard Worker if (data->numeraseregions != 0) {
164*0d6140beSAndroid Build Coastguard Worker msg_perr("Non-uniform eraseblock size is unsupported.\n");
165*0d6140beSAndroid Build Coastguard Worker return 1;
166*0d6140beSAndroid Build Coastguard Worker }
167*0d6140beSAndroid Build Coastguard Worker
168*0d6140beSAndroid Build Coastguard Worker msg_pdbg("%s: device_name: \"%s\", is_writeable: %d, "
169*0d6140beSAndroid Build Coastguard Worker "numeraseregions: %lu, total_size: %lu, erasesize: %lu\n",
170*0d6140beSAndroid Build Coastguard Worker __func__, device_name, data->device_is_writeable,
171*0d6140beSAndroid Build Coastguard Worker data->numeraseregions, data->total_size, data->erasesize);
172*0d6140beSAndroid Build Coastguard Worker
173*0d6140beSAndroid Build Coastguard Worker return 0;
174*0d6140beSAndroid Build Coastguard Worker }
175*0d6140beSAndroid Build Coastguard Worker
linux_mtd_probe(struct flashctx * flash)176*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_probe(struct flashctx *flash)
177*0d6140beSAndroid Build Coastguard Worker {
178*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *data = flash->mst->opaque.data;
179*0d6140beSAndroid Build Coastguard Worker
180*0d6140beSAndroid Build Coastguard Worker if (data->no_erase)
181*0d6140beSAndroid Build Coastguard Worker flash->chip->feature_bits |= FEATURE_NO_ERASE;
182*0d6140beSAndroid Build Coastguard Worker flash->chip->tested = TEST_OK_PREWB;
183*0d6140beSAndroid Build Coastguard Worker flash->chip->total_size = data->total_size / 1024; /* bytes -> kB */
184*0d6140beSAndroid Build Coastguard Worker flash->chip->block_erasers[0].eraseblocks[0].size = data->erasesize;
185*0d6140beSAndroid Build Coastguard Worker flash->chip->block_erasers[0].eraseblocks[0].count =
186*0d6140beSAndroid Build Coastguard Worker data->total_size / data->erasesize;
187*0d6140beSAndroid Build Coastguard Worker return 1;
188*0d6140beSAndroid Build Coastguard Worker }
189*0d6140beSAndroid Build Coastguard Worker
linux_mtd_read(struct flashctx * flash,uint8_t * buf,unsigned int start,unsigned int len)190*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_read(struct flashctx *flash, uint8_t *buf,
191*0d6140beSAndroid Build Coastguard Worker unsigned int start, unsigned int len)
192*0d6140beSAndroid Build Coastguard Worker {
193*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *data = flash->mst->opaque.data;
194*0d6140beSAndroid Build Coastguard Worker unsigned int eb_size = flash->chip->block_erasers[0].eraseblocks[0].size;
195*0d6140beSAndroid Build Coastguard Worker unsigned int i;
196*0d6140beSAndroid Build Coastguard Worker
197*0d6140beSAndroid Build Coastguard Worker if (fseek(data->dev_fp, start, SEEK_SET) != 0) {
198*0d6140beSAndroid Build Coastguard Worker msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
199*0d6140beSAndroid Build Coastguard Worker return 1;
200*0d6140beSAndroid Build Coastguard Worker }
201*0d6140beSAndroid Build Coastguard Worker
202*0d6140beSAndroid Build Coastguard Worker for (i = 0; i < len; ) {
203*0d6140beSAndroid Build Coastguard Worker /*
204*0d6140beSAndroid Build Coastguard Worker * Try to align reads to eraseblock size.
205*0d6140beSAndroid Build Coastguard Worker * FIXME: Shouldn't actually be necessary, but not all MTD
206*0d6140beSAndroid Build Coastguard Worker * drivers handle arbitrary large reads well.
207*0d6140beSAndroid Build Coastguard Worker */
208*0d6140beSAndroid Build Coastguard Worker unsigned int step = eb_size - ((start + i) % eb_size);
209*0d6140beSAndroid Build Coastguard Worker step = min(step, len - i);
210*0d6140beSAndroid Build Coastguard Worker
211*0d6140beSAndroid Build Coastguard Worker if (fread(buf + i, step, 1, data->dev_fp) != 1) {
212*0d6140beSAndroid Build Coastguard Worker msg_perr("Cannot read 0x%06x bytes at 0x%06x: %s\n",
213*0d6140beSAndroid Build Coastguard Worker step, start + i, strerror(errno));
214*0d6140beSAndroid Build Coastguard Worker return 1;
215*0d6140beSAndroid Build Coastguard Worker }
216*0d6140beSAndroid Build Coastguard Worker
217*0d6140beSAndroid Build Coastguard Worker i += step;
218*0d6140beSAndroid Build Coastguard Worker update_progress(flash, FLASHROM_PROGRESS_READ, i, len);
219*0d6140beSAndroid Build Coastguard Worker }
220*0d6140beSAndroid Build Coastguard Worker
221*0d6140beSAndroid Build Coastguard Worker return 0;
222*0d6140beSAndroid Build Coastguard Worker }
223*0d6140beSAndroid Build Coastguard Worker
224*0d6140beSAndroid Build Coastguard Worker /* this version assumes we must divide the write request into chunks ourselves */
linux_mtd_write(struct flashctx * flash,const uint8_t * buf,unsigned int start,unsigned int len)225*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_write(struct flashctx *flash, const uint8_t *buf,
226*0d6140beSAndroid Build Coastguard Worker unsigned int start, unsigned int len)
227*0d6140beSAndroid Build Coastguard Worker {
228*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *data = flash->mst->opaque.data;
229*0d6140beSAndroid Build Coastguard Worker unsigned int chunksize = flash->chip->block_erasers[0].eraseblocks[0].size;
230*0d6140beSAndroid Build Coastguard Worker unsigned int i;
231*0d6140beSAndroid Build Coastguard Worker
232*0d6140beSAndroid Build Coastguard Worker if (!data->device_is_writeable)
233*0d6140beSAndroid Build Coastguard Worker return 1;
234*0d6140beSAndroid Build Coastguard Worker
235*0d6140beSAndroid Build Coastguard Worker if (fseek(data->dev_fp, start, SEEK_SET) != 0) {
236*0d6140beSAndroid Build Coastguard Worker msg_perr("Cannot seek to 0x%06x: %s\n", start, strerror(errno));
237*0d6140beSAndroid Build Coastguard Worker return 1;
238*0d6140beSAndroid Build Coastguard Worker }
239*0d6140beSAndroid Build Coastguard Worker
240*0d6140beSAndroid Build Coastguard Worker /*
241*0d6140beSAndroid Build Coastguard Worker * Try to align writes to eraseblock size. We want these large enough
242*0d6140beSAndroid Build Coastguard Worker * to give MTD room for optimizing performance.
243*0d6140beSAndroid Build Coastguard Worker * FIXME: Shouldn't need to divide this up at all, but not all MTD
244*0d6140beSAndroid Build Coastguard Worker * drivers handle arbitrary large writes well.
245*0d6140beSAndroid Build Coastguard Worker */
246*0d6140beSAndroid Build Coastguard Worker for (i = 0; i < len; ) {
247*0d6140beSAndroid Build Coastguard Worker unsigned int step = chunksize - ((start + i) % chunksize);
248*0d6140beSAndroid Build Coastguard Worker step = min(step, len - i);
249*0d6140beSAndroid Build Coastguard Worker
250*0d6140beSAndroid Build Coastguard Worker if (fwrite(buf + i, step, 1, data->dev_fp) != 1) {
251*0d6140beSAndroid Build Coastguard Worker msg_perr("Cannot write 0x%06x bytes at 0x%06x\n", step, start + i);
252*0d6140beSAndroid Build Coastguard Worker return 1;
253*0d6140beSAndroid Build Coastguard Worker }
254*0d6140beSAndroid Build Coastguard Worker
255*0d6140beSAndroid Build Coastguard Worker if (fflush(data->dev_fp) == EOF) {
256*0d6140beSAndroid Build Coastguard Worker msg_perr("Failed to flush buffer: %s\n", strerror(errno));
257*0d6140beSAndroid Build Coastguard Worker return 1;
258*0d6140beSAndroid Build Coastguard Worker }
259*0d6140beSAndroid Build Coastguard Worker
260*0d6140beSAndroid Build Coastguard Worker i += step;
261*0d6140beSAndroid Build Coastguard Worker update_progress(flash, FLASHROM_PROGRESS_WRITE, i, len);
262*0d6140beSAndroid Build Coastguard Worker }
263*0d6140beSAndroid Build Coastguard Worker
264*0d6140beSAndroid Build Coastguard Worker return 0;
265*0d6140beSAndroid Build Coastguard Worker }
266*0d6140beSAndroid Build Coastguard Worker
linux_mtd_erase(struct flashctx * flash,unsigned int start,unsigned int len)267*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_erase(struct flashctx *flash,
268*0d6140beSAndroid Build Coastguard Worker unsigned int start, unsigned int len)
269*0d6140beSAndroid Build Coastguard Worker {
270*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *data = flash->mst->opaque.data;
271*0d6140beSAndroid Build Coastguard Worker uint32_t u;
272*0d6140beSAndroid Build Coastguard Worker
273*0d6140beSAndroid Build Coastguard Worker if (data->no_erase) {
274*0d6140beSAndroid Build Coastguard Worker msg_perr("%s: device does not support erasing. Please file a "
275*0d6140beSAndroid Build Coastguard Worker "bug report at [email protected]\n", __func__);
276*0d6140beSAndroid Build Coastguard Worker return 1;
277*0d6140beSAndroid Build Coastguard Worker }
278*0d6140beSAndroid Build Coastguard Worker
279*0d6140beSAndroid Build Coastguard Worker if (data->numeraseregions != 0) {
280*0d6140beSAndroid Build Coastguard Worker /* TODO: Support non-uniform eraseblock size using
281*0d6140beSAndroid Build Coastguard Worker use MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls */
282*0d6140beSAndroid Build Coastguard Worker msg_perr("%s: numeraseregions must be 0\n", __func__);
283*0d6140beSAndroid Build Coastguard Worker return 1;
284*0d6140beSAndroid Build Coastguard Worker }
285*0d6140beSAndroid Build Coastguard Worker
286*0d6140beSAndroid Build Coastguard Worker for (u = 0; u < len; u += data->erasesize) {
287*0d6140beSAndroid Build Coastguard Worker struct erase_info_user erase_info = {
288*0d6140beSAndroid Build Coastguard Worker .start = start + u,
289*0d6140beSAndroid Build Coastguard Worker .length = data->erasesize,
290*0d6140beSAndroid Build Coastguard Worker };
291*0d6140beSAndroid Build Coastguard Worker
292*0d6140beSAndroid Build Coastguard Worker int ret = ioctl(fileno(data->dev_fp), MEMERASE, &erase_info);
293*0d6140beSAndroid Build Coastguard Worker if (ret < 0) {
294*0d6140beSAndroid Build Coastguard Worker msg_perr("%s: MEMERASE ioctl call returned %d, error: %s\n",
295*0d6140beSAndroid Build Coastguard Worker __func__, ret, strerror(errno));
296*0d6140beSAndroid Build Coastguard Worker return 1;
297*0d6140beSAndroid Build Coastguard Worker }
298*0d6140beSAndroid Build Coastguard Worker update_progress(flash, FLASHROM_PROGRESS_ERASE, u + data->erasesize, len);
299*0d6140beSAndroid Build Coastguard Worker }
300*0d6140beSAndroid Build Coastguard Worker
301*0d6140beSAndroid Build Coastguard Worker return 0;
302*0d6140beSAndroid Build Coastguard Worker }
303*0d6140beSAndroid Build Coastguard Worker
linux_mtd_shutdown(void * data)304*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_shutdown(void *data)
305*0d6140beSAndroid Build Coastguard Worker {
306*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *mtd_data = data;
307*0d6140beSAndroid Build Coastguard Worker if (mtd_data->dev_fp != NULL) {
308*0d6140beSAndroid Build Coastguard Worker fclose(mtd_data->dev_fp);
309*0d6140beSAndroid Build Coastguard Worker }
310*0d6140beSAndroid Build Coastguard Worker free(data);
311*0d6140beSAndroid Build Coastguard Worker
312*0d6140beSAndroid Build Coastguard Worker return 0;
313*0d6140beSAndroid Build Coastguard Worker }
314*0d6140beSAndroid Build Coastguard Worker
linux_mtd_wp_read_cfg(struct flashrom_wp_cfg * cfg,struct flashctx * flash)315*0d6140beSAndroid Build Coastguard Worker static enum flashrom_wp_result linux_mtd_wp_read_cfg(struct flashrom_wp_cfg *cfg, struct flashctx *flash)
316*0d6140beSAndroid Build Coastguard Worker {
317*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *data = flash->mst->opaque.data;
318*0d6140beSAndroid Build Coastguard Worker bool start_found = false;
319*0d6140beSAndroid Build Coastguard Worker bool end_found = false;
320*0d6140beSAndroid Build Coastguard Worker
321*0d6140beSAndroid Build Coastguard Worker cfg->mode = FLASHROM_WP_MODE_DISABLED;
322*0d6140beSAndroid Build Coastguard Worker cfg->range.start = 0;
323*0d6140beSAndroid Build Coastguard Worker cfg->range.len = 0;
324*0d6140beSAndroid Build Coastguard Worker
325*0d6140beSAndroid Build Coastguard Worker /* Check protection status of each block */
326*0d6140beSAndroid Build Coastguard Worker for (size_t u = 0; u < data->total_size; u += data->erasesize) {
327*0d6140beSAndroid Build Coastguard Worker struct erase_info_user erase_info = {
328*0d6140beSAndroid Build Coastguard Worker .start = u,
329*0d6140beSAndroid Build Coastguard Worker .length = data->erasesize,
330*0d6140beSAndroid Build Coastguard Worker };
331*0d6140beSAndroid Build Coastguard Worker
332*0d6140beSAndroid Build Coastguard Worker int ret = ioctl(fileno(data->dev_fp), MEMISLOCKED, &erase_info);
333*0d6140beSAndroid Build Coastguard Worker if (ret == 0) {
334*0d6140beSAndroid Build Coastguard Worker /* Block is unprotected. */
335*0d6140beSAndroid Build Coastguard Worker
336*0d6140beSAndroid Build Coastguard Worker if (start_found) {
337*0d6140beSAndroid Build Coastguard Worker end_found = true;
338*0d6140beSAndroid Build Coastguard Worker }
339*0d6140beSAndroid Build Coastguard Worker } else if (ret == 1) {
340*0d6140beSAndroid Build Coastguard Worker /* Block is protected. */
341*0d6140beSAndroid Build Coastguard Worker
342*0d6140beSAndroid Build Coastguard Worker if (end_found) {
343*0d6140beSAndroid Build Coastguard Worker /*
344*0d6140beSAndroid Build Coastguard Worker * We already found the end of another
345*0d6140beSAndroid Build Coastguard Worker * protection range, so this is the start of a
346*0d6140beSAndroid Build Coastguard Worker * new one.
347*0d6140beSAndroid Build Coastguard Worker */
348*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_OTHER;
349*0d6140beSAndroid Build Coastguard Worker }
350*0d6140beSAndroid Build Coastguard Worker if (!start_found) {
351*0d6140beSAndroid Build Coastguard Worker cfg->range.start = erase_info.start;
352*0d6140beSAndroid Build Coastguard Worker cfg->mode = FLASHROM_WP_MODE_HARDWARE;
353*0d6140beSAndroid Build Coastguard Worker start_found = true;
354*0d6140beSAndroid Build Coastguard Worker }
355*0d6140beSAndroid Build Coastguard Worker cfg->range.len += data->erasesize;
356*0d6140beSAndroid Build Coastguard Worker } else {
357*0d6140beSAndroid Build Coastguard Worker msg_perr("%s: ioctl: %s\n", __func__, strerror(errno));
358*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_READ_FAILED;
359*0d6140beSAndroid Build Coastguard Worker }
360*0d6140beSAndroid Build Coastguard Worker
361*0d6140beSAndroid Build Coastguard Worker }
362*0d6140beSAndroid Build Coastguard Worker
363*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_OK;
364*0d6140beSAndroid Build Coastguard Worker }
365*0d6140beSAndroid Build Coastguard Worker
linux_mtd_wp_write_cfg(struct flashctx * flash,const struct flashrom_wp_cfg * cfg)366*0d6140beSAndroid Build Coastguard Worker static enum flashrom_wp_result linux_mtd_wp_write_cfg(struct flashctx *flash, const struct flashrom_wp_cfg *cfg)
367*0d6140beSAndroid Build Coastguard Worker {
368*0d6140beSAndroid Build Coastguard Worker const struct linux_mtd_data *data = flash->mst->opaque.data;
369*0d6140beSAndroid Build Coastguard Worker
370*0d6140beSAndroid Build Coastguard Worker const struct erase_info_user entire_chip = {
371*0d6140beSAndroid Build Coastguard Worker .start = 0,
372*0d6140beSAndroid Build Coastguard Worker .length = data->total_size,
373*0d6140beSAndroid Build Coastguard Worker };
374*0d6140beSAndroid Build Coastguard Worker const struct erase_info_user desired_range = {
375*0d6140beSAndroid Build Coastguard Worker .start = cfg->range.start,
376*0d6140beSAndroid Build Coastguard Worker .length = cfg->range.len,
377*0d6140beSAndroid Build Coastguard Worker };
378*0d6140beSAndroid Build Coastguard Worker
379*0d6140beSAndroid Build Coastguard Worker /*
380*0d6140beSAndroid Build Coastguard Worker * MTD ioctls will enable hardware status register protection if and
381*0d6140beSAndroid Build Coastguard Worker * only if the protected region is non-empty. Return an error if the
382*0d6140beSAndroid Build Coastguard Worker * cfg cannot be activated using the MTD interface.
383*0d6140beSAndroid Build Coastguard Worker */
384*0d6140beSAndroid Build Coastguard Worker if ((cfg->range.len == 0) != (cfg->mode == FLASHROM_WP_MODE_DISABLED)) {
385*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_OTHER;
386*0d6140beSAndroid Build Coastguard Worker }
387*0d6140beSAndroid Build Coastguard Worker
388*0d6140beSAndroid Build Coastguard Worker /*
389*0d6140beSAndroid Build Coastguard Worker * MTD handles write-protection additively, so whatever new range is
390*0d6140beSAndroid Build Coastguard Worker * specified is added to the range which is currently protected. To
391*0d6140beSAndroid Build Coastguard Worker * just protect the requsted range, we need to disable the current
392*0d6140beSAndroid Build Coastguard Worker * write protection and then enable it for the desired range.
393*0d6140beSAndroid Build Coastguard Worker */
394*0d6140beSAndroid Build Coastguard Worker int ret = ioctl(fileno(data->dev_fp), MEMUNLOCK, &entire_chip);
395*0d6140beSAndroid Build Coastguard Worker if (ret < 0) {
396*0d6140beSAndroid Build Coastguard Worker msg_perr("%s: Failed to disable write-protection, MEMUNLOCK ioctl "
397*0d6140beSAndroid Build Coastguard Worker "retuned %d, error: %s\n", __func__, ret, strerror(errno));
398*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_WRITE_FAILED;
399*0d6140beSAndroid Build Coastguard Worker }
400*0d6140beSAndroid Build Coastguard Worker
401*0d6140beSAndroid Build Coastguard Worker if (cfg->range.len > 0) {
402*0d6140beSAndroid Build Coastguard Worker ret = ioctl(fileno(data->dev_fp), MEMLOCK, &desired_range);
403*0d6140beSAndroid Build Coastguard Worker if (ret < 0) {
404*0d6140beSAndroid Build Coastguard Worker msg_perr("%s: Failed to enable write-protection, "
405*0d6140beSAndroid Build Coastguard Worker "MEMLOCK ioctl retuned %d, error: %s\n",
406*0d6140beSAndroid Build Coastguard Worker __func__, ret, strerror(errno));
407*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_WRITE_FAILED;
408*0d6140beSAndroid Build Coastguard Worker }
409*0d6140beSAndroid Build Coastguard Worker }
410*0d6140beSAndroid Build Coastguard Worker
411*0d6140beSAndroid Build Coastguard Worker /* Verify */
412*0d6140beSAndroid Build Coastguard Worker struct flashrom_wp_cfg readback_cfg;
413*0d6140beSAndroid Build Coastguard Worker enum flashrom_wp_result read_ret = linux_mtd_wp_read_cfg(&readback_cfg, flash);
414*0d6140beSAndroid Build Coastguard Worker if (read_ret != FLASHROM_WP_OK)
415*0d6140beSAndroid Build Coastguard Worker return read_ret;
416*0d6140beSAndroid Build Coastguard Worker
417*0d6140beSAndroid Build Coastguard Worker if (readback_cfg.mode != cfg->mode ||
418*0d6140beSAndroid Build Coastguard Worker readback_cfg.range.start != cfg->range.start ||
419*0d6140beSAndroid Build Coastguard Worker readback_cfg.range.len != cfg->range.len) {
420*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_VERIFY_FAILED;
421*0d6140beSAndroid Build Coastguard Worker }
422*0d6140beSAndroid Build Coastguard Worker
423*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_OK;
424*0d6140beSAndroid Build Coastguard Worker }
425*0d6140beSAndroid Build Coastguard Worker
linux_mtd_wp_get_available_ranges(struct flashrom_wp_ranges ** list,struct flashctx * flash)426*0d6140beSAndroid Build Coastguard Worker static enum flashrom_wp_result linux_mtd_wp_get_available_ranges(struct flashrom_wp_ranges **list, struct flashctx *flash)
427*0d6140beSAndroid Build Coastguard Worker {
428*0d6140beSAndroid Build Coastguard Worker /* Not supported by MTD interface. */
429*0d6140beSAndroid Build Coastguard Worker return FLASHROM_WP_ERR_RANGE_LIST_UNAVAILABLE;
430*0d6140beSAndroid Build Coastguard Worker }
431*0d6140beSAndroid Build Coastguard Worker
linux_mtd_nop_delay(const struct flashctx * flash,unsigned int usecs)432*0d6140beSAndroid Build Coastguard Worker static void linux_mtd_nop_delay(const struct flashctx *flash, unsigned int usecs)
433*0d6140beSAndroid Build Coastguard Worker {
434*0d6140beSAndroid Build Coastguard Worker /*
435*0d6140beSAndroid Build Coastguard Worker * Ignore delay requests. The Linux MTD framework brokers all flash
436*0d6140beSAndroid Build Coastguard Worker * protocol, including timing, resets, etc.
437*0d6140beSAndroid Build Coastguard Worker */
438*0d6140beSAndroid Build Coastguard Worker }
439*0d6140beSAndroid Build Coastguard Worker
440*0d6140beSAndroid Build Coastguard Worker static const struct opaque_master linux_mtd_opaque_master = {
441*0d6140beSAndroid Build Coastguard Worker /* max_data_{read,write} don't have any effect for this programmer */
442*0d6140beSAndroid Build Coastguard Worker .max_data_read = MAX_DATA_UNSPECIFIED,
443*0d6140beSAndroid Build Coastguard Worker .max_data_write = MAX_DATA_UNSPECIFIED,
444*0d6140beSAndroid Build Coastguard Worker .probe = linux_mtd_probe,
445*0d6140beSAndroid Build Coastguard Worker .read = linux_mtd_read,
446*0d6140beSAndroid Build Coastguard Worker .write = linux_mtd_write,
447*0d6140beSAndroid Build Coastguard Worker .erase = linux_mtd_erase,
448*0d6140beSAndroid Build Coastguard Worker .shutdown = linux_mtd_shutdown,
449*0d6140beSAndroid Build Coastguard Worker .wp_read_cfg = linux_mtd_wp_read_cfg,
450*0d6140beSAndroid Build Coastguard Worker .wp_write_cfg = linux_mtd_wp_write_cfg,
451*0d6140beSAndroid Build Coastguard Worker .wp_get_ranges = linux_mtd_wp_get_available_ranges,
452*0d6140beSAndroid Build Coastguard Worker .delay = linux_mtd_nop_delay,
453*0d6140beSAndroid Build Coastguard Worker };
454*0d6140beSAndroid Build Coastguard Worker
455*0d6140beSAndroid Build Coastguard Worker /* Returns 0 if setup is successful, non-zero to indicate error */
linux_mtd_setup(int dev_num,struct linux_mtd_data * data)456*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_setup(int dev_num, struct linux_mtd_data *data)
457*0d6140beSAndroid Build Coastguard Worker {
458*0d6140beSAndroid Build Coastguard Worker char sysfs_path[32];
459*0d6140beSAndroid Build Coastguard Worker int ret = 1;
460*0d6140beSAndroid Build Coastguard Worker
461*0d6140beSAndroid Build Coastguard Worker /* Start by checking /sys/class/mtd/mtdN/type which should be "nor" for NOR flash */
462*0d6140beSAndroid Build Coastguard Worker if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d/", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
463*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_setup_exit;
464*0d6140beSAndroid Build Coastguard Worker
465*0d6140beSAndroid Build Coastguard Worker char buf[4] = { 0 };
466*0d6140beSAndroid Build Coastguard Worker if (read_sysfs_string(sysfs_path, "type", buf, sizeof(buf)))
467*0d6140beSAndroid Build Coastguard Worker return 1;
468*0d6140beSAndroid Build Coastguard Worker
469*0d6140beSAndroid Build Coastguard Worker if (strcmp(buf, "nor")) {
470*0d6140beSAndroid Build Coastguard Worker msg_perr("MTD device %d type is not \"nor\"\n", dev_num);
471*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_setup_exit;
472*0d6140beSAndroid Build Coastguard Worker }
473*0d6140beSAndroid Build Coastguard Worker
474*0d6140beSAndroid Build Coastguard Worker /* sysfs shows the correct device type, see if corresponding device node exists */
475*0d6140beSAndroid Build Coastguard Worker char dev_path[32];
476*0d6140beSAndroid Build Coastguard Worker struct stat s;
477*0d6140beSAndroid Build Coastguard Worker snprintf(dev_path, sizeof(dev_path), "%s/mtd%d", LINUX_DEV_ROOT, dev_num);
478*0d6140beSAndroid Build Coastguard Worker errno = 0;
479*0d6140beSAndroid Build Coastguard Worker if (stat(dev_path, &s) < 0) {
480*0d6140beSAndroid Build Coastguard Worker msg_pdbg("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
481*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_setup_exit;
482*0d6140beSAndroid Build Coastguard Worker }
483*0d6140beSAndroid Build Coastguard Worker
484*0d6140beSAndroid Build Coastguard Worker /* so far so good, get more info from other files in this dir */
485*0d6140beSAndroid Build Coastguard Worker if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d/", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
486*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_setup_exit;
487*0d6140beSAndroid Build Coastguard Worker if (get_mtd_info(sysfs_path, data))
488*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_setup_exit;
489*0d6140beSAndroid Build Coastguard Worker
490*0d6140beSAndroid Build Coastguard Worker /* open file stream and go! */
491*0d6140beSAndroid Build Coastguard Worker if ((data->dev_fp = fopen(dev_path, "r+")) == NULL) {
492*0d6140beSAndroid Build Coastguard Worker msg_perr("Cannot open file stream for %s\n", dev_path);
493*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_setup_exit;
494*0d6140beSAndroid Build Coastguard Worker }
495*0d6140beSAndroid Build Coastguard Worker ret = setvbuf(data->dev_fp, NULL, _IONBF, 0);
496*0d6140beSAndroid Build Coastguard Worker if (ret)
497*0d6140beSAndroid Build Coastguard Worker msg_pwarn("Failed to set MTD device to unbuffered: %d\n", ret);
498*0d6140beSAndroid Build Coastguard Worker
499*0d6140beSAndroid Build Coastguard Worker msg_pinfo("Opened %s successfully\n", dev_path);
500*0d6140beSAndroid Build Coastguard Worker
501*0d6140beSAndroid Build Coastguard Worker ret = 0;
502*0d6140beSAndroid Build Coastguard Worker linux_mtd_setup_exit:
503*0d6140beSAndroid Build Coastguard Worker return ret;
504*0d6140beSAndroid Build Coastguard Worker }
505*0d6140beSAndroid Build Coastguard Worker
linux_mtd_init(const struct programmer_cfg * cfg)506*0d6140beSAndroid Build Coastguard Worker static int linux_mtd_init(const struct programmer_cfg *cfg)
507*0d6140beSAndroid Build Coastguard Worker {
508*0d6140beSAndroid Build Coastguard Worker char *param_str;
509*0d6140beSAndroid Build Coastguard Worker int dev_num = 0;
510*0d6140beSAndroid Build Coastguard Worker int ret = 1;
511*0d6140beSAndroid Build Coastguard Worker struct linux_mtd_data *data = NULL;
512*0d6140beSAndroid Build Coastguard Worker
513*0d6140beSAndroid Build Coastguard Worker param_str = extract_programmer_param_str(cfg, "dev");
514*0d6140beSAndroid Build Coastguard Worker if (param_str) {
515*0d6140beSAndroid Build Coastguard Worker char *endptr;
516*0d6140beSAndroid Build Coastguard Worker
517*0d6140beSAndroid Build Coastguard Worker dev_num = strtol(param_str, &endptr, 0);
518*0d6140beSAndroid Build Coastguard Worker if ((*endptr != '\0') || (dev_num < 0)) {
519*0d6140beSAndroid Build Coastguard Worker msg_perr("Invalid device number %s. Use flashrom -p "
520*0d6140beSAndroid Build Coastguard Worker "linux_mtd:dev=N where N is a valid MTD\n"
521*0d6140beSAndroid Build Coastguard Worker "device number.\n", param_str);
522*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_init_exit;
523*0d6140beSAndroid Build Coastguard Worker }
524*0d6140beSAndroid Build Coastguard Worker }
525*0d6140beSAndroid Build Coastguard Worker
526*0d6140beSAndroid Build Coastguard Worker /*
527*0d6140beSAndroid Build Coastguard Worker * If user specified the MTD device number then error out if it doesn't
528*0d6140beSAndroid Build Coastguard Worker * appear to exist. Otherwise assume the error is benign and print a
529*0d6140beSAndroid Build Coastguard Worker * debug message. Bail out in either case.
530*0d6140beSAndroid Build Coastguard Worker */
531*0d6140beSAndroid Build Coastguard Worker char sysfs_path[32];
532*0d6140beSAndroid Build Coastguard Worker if (snprintf(sysfs_path, sizeof(sysfs_path), "%s/mtd%d", LINUX_MTD_SYSFS_ROOT, dev_num) < 0)
533*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_init_exit;
534*0d6140beSAndroid Build Coastguard Worker
535*0d6140beSAndroid Build Coastguard Worker struct stat s;
536*0d6140beSAndroid Build Coastguard Worker if (stat(sysfs_path, &s) < 0) {
537*0d6140beSAndroid Build Coastguard Worker if (param_str)
538*0d6140beSAndroid Build Coastguard Worker msg_perr("%s does not exist\n", sysfs_path);
539*0d6140beSAndroid Build Coastguard Worker else
540*0d6140beSAndroid Build Coastguard Worker msg_pdbg("%s does not exist\n", sysfs_path);
541*0d6140beSAndroid Build Coastguard Worker goto linux_mtd_init_exit;
542*0d6140beSAndroid Build Coastguard Worker }
543*0d6140beSAndroid Build Coastguard Worker free(param_str);
544*0d6140beSAndroid Build Coastguard Worker
545*0d6140beSAndroid Build Coastguard Worker data = calloc(1, sizeof(*data));
546*0d6140beSAndroid Build Coastguard Worker if (!data) {
547*0d6140beSAndroid Build Coastguard Worker msg_perr("Unable to allocate memory for linux_mtd_data\n");
548*0d6140beSAndroid Build Coastguard Worker return 1;
549*0d6140beSAndroid Build Coastguard Worker }
550*0d6140beSAndroid Build Coastguard Worker
551*0d6140beSAndroid Build Coastguard Worker /* Get MTD info and store it in `data` */
552*0d6140beSAndroid Build Coastguard Worker if (linux_mtd_setup(dev_num, data)) {
553*0d6140beSAndroid Build Coastguard Worker free(data);
554*0d6140beSAndroid Build Coastguard Worker return 1;
555*0d6140beSAndroid Build Coastguard Worker }
556*0d6140beSAndroid Build Coastguard Worker
557*0d6140beSAndroid Build Coastguard Worker return register_opaque_master(&linux_mtd_opaque_master, data);
558*0d6140beSAndroid Build Coastguard Worker
559*0d6140beSAndroid Build Coastguard Worker linux_mtd_init_exit:
560*0d6140beSAndroid Build Coastguard Worker free(param_str);
561*0d6140beSAndroid Build Coastguard Worker return ret;
562*0d6140beSAndroid Build Coastguard Worker }
563*0d6140beSAndroid Build Coastguard Worker
564*0d6140beSAndroid Build Coastguard Worker const struct programmer_entry programmer_linux_mtd = {
565*0d6140beSAndroid Build Coastguard Worker .name = "linux_mtd",
566*0d6140beSAndroid Build Coastguard Worker .type = OTHER,
567*0d6140beSAndroid Build Coastguard Worker .devs.note = "Device files /dev/mtd*\n",
568*0d6140beSAndroid Build Coastguard Worker .init = linux_mtd_init,
569*0d6140beSAndroid Build Coastguard Worker };
570