1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /* Read/write/erase SPI flash through Linux kernel MTD interface */
17 
18 #define LOG_TAG "fwtool"
19 
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 
29 #include <mtd/mtd-user.h>
30 
31 #include "flash_device.h"
32 #include "update_log.h"
33 #include "vboot_interface.h"
34 
35 static const char * const DEFAULT_MTD_FILE = "/dev/mtd/mtd0";
36 
37 struct mtd_data {
38 	int fd;
39 	struct mtd_info_user info;
40 };
41 
mtd_open(const void * params)42 static void *mtd_open(const void *params)
43 {
44 	const char *path = params ? params : DEFAULT_MTD_FILE;
45 	struct mtd_data *dev = calloc(1, sizeof(struct mtd_data));
46 	if (!dev)
47 		return NULL;
48 
49 	dev->fd = open(path, O_RDWR);
50 	if (dev->fd == -1) {
51 		ALOGE("No MTD device %s : %d\n", path, errno);
52 		goto out_free;
53 	}
54 
55 	if (ioctl(dev->fd, MEMGETINFO, &dev->info)) {
56 		ALOGE("Cannot get MTD info for %s : %d\n", path, errno);
57 		goto out_close;
58 	}
59 
60 	if (dev->info.type != MTD_NORFLASH) {
61 		ALOGE("Unsupported MTD device type: %d\n", dev->info.type);
62 		goto out_close;
63 	}
64 
65 	ALOGD("MTD %s: size %d erasesize %d min_io_size %d\n",
66 		path, dev->info.size, dev->info.erasesize, dev->info.writesize);
67 
68 	return dev;
69 
70 out_close:
71 	close(dev->fd);
72 	dev->fd = -1;
73 out_free:
74 	free(dev);
75 
76 	return NULL;
77 }
78 
mtd_close(void * hnd)79 static void mtd_close(void *hnd)
80 {
81 	struct mtd_data *dev = hnd;
82 
83 	close(dev->fd);
84 	free(dev);
85 }
86 
mtd_read(void * hnd,off_t offset,void * buffer,size_t count)87 static int mtd_read(void *hnd, off_t offset, void *buffer, size_t count)
88 {
89 	struct mtd_data *dev = hnd;
90 	ssize_t res;
91 	uint8_t *ptr = buffer;
92 
93 	if (lseek(dev->fd, offset, SEEK_SET) != offset) {
94 		ALOGW("Cannot seek to %ld\n", offset);
95 		return errno;
96 
97 	}
98 
99 	while (count) {
100 		res = read(dev->fd, ptr, count);
101 		if (res < 0) {
102 			ALOGW("Cannot read at %ld : %zd\n", offset, res);
103 			return errno;
104 		}
105 		count -= res;
106 		ptr += res;
107 	}
108 	return 0;
109 }
110 
mtd_write(void * hnd,off_t offset,void * buffer,size_t count)111 static int mtd_write(void *hnd, off_t offset, void *buffer, size_t count)
112 {
113 	struct mtd_data *dev = hnd;
114 	ssize_t res;
115 	uint8_t *ptr = buffer;
116 
117 	if (lseek(dev->fd, offset, SEEK_SET) != offset) {
118 		ALOGW("Cannot seek to %ld\n", offset);
119 		return errno;
120 	}
121 
122 	while (count) {
123 		res = write(dev->fd, ptr, count);
124 		if (res < 0) {
125 			ALOGW("Cannot write at %ld : %zd\n", offset, res);
126 			return errno;
127 		}
128 		count -= res;
129 		ptr += res;
130 	}
131 	return 0;
132 }
133 
mtd_erase(void * hnd,off_t offset,size_t count)134 static int mtd_erase(void *hnd, off_t offset, size_t count)
135 {
136 	struct mtd_data *dev = hnd;
137 	int res;
138 	struct erase_info_user ei;
139 
140 	ei.start = offset;
141 	ei.length = count;
142 	res = ioctl(dev->fd, MEMERASE, &ei);
143 	if (res < 0) {
144 		ALOGW("Cannot erase at %ld : %d\n", offset, res);
145 		return errno;
146 	}
147 
148 	return 0;
149 }
150 
151 /*
152  * Write Protect handling :
153  * struct erase_info_user ei;
154  *
155  * ei.start = eb * info.erasesize;
156  * ei.length = info.erasesize;
157  * ret = ioctl(fd, MEMISLOCKED, &ei);
158  * ret = ioctl(fd, MEMLOCK, &ei);
159  * ret = ioctl(fd, MEMUNLOCK, &ei);
160  */
161 
mtd_get_size(void * hnd)162 static size_t mtd_get_size(void *hnd)
163 {
164 	struct mtd_data *dev = hnd;
165 
166 	return dev && dev->fd > 0 ? dev->info.size : 0;
167 }
168 
mtd_get_write_size(void * hnd)169 static size_t mtd_get_write_size(void *hnd)
170 {
171 	struct mtd_data *dev = hnd;
172 
173 	return dev && dev->fd > 0 ? dev->info.writesize : 0;
174 }
175 
mtd_get_erase_size(void * hnd)176 static size_t mtd_get_erase_size(void *hnd)
177 {
178 	struct mtd_data *dev = hnd;
179 
180 	return dev && dev->fd > 0 ? dev->info.erasesize : 0;
181 }
182 
mtd_get_fmap_offset(void * hnd)183 static off_t mtd_get_fmap_offset(void *hnd __attribute__((unused)))
184 {
185 	/* Get the SPI FMAP offset passed by the firmware in the device-tree */
186 	return fdt_read_u32("fmap-offset") + 64;
187 }
188 
189 const struct flash_device_ops flash_mtd_ops = {
190 	.name = "spi",
191 	.open = mtd_open,
192 	.close = mtd_close,
193 	.read = mtd_read,
194 	.write = mtd_write,
195 	.erase = mtd_erase,
196 	.get_size = mtd_get_size,
197 	.get_write_size = mtd_get_write_size,
198 	.get_erase_size = mtd_get_erase_size,
199 	.get_fmap_offset = mtd_get_fmap_offset,
200 };
201