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 <errno.h>
21 #include <fcntl.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 #include "edify/expr.h"
31 #include "flash_device.h"
32 #include "update_log.h"
33 
34 struct file_data {
35 	int fd;
36 	uint8_t *data;
37 	struct stat info;
38 };
39 
file_blob_open(struct file_data * dev,const Value * param)40 static void *file_blob_open(struct file_data *dev, const Value *param)
41 {
42 	dev->fd = -1; /* No backing file */
43 	dev->data = reinterpret_cast<uint8_t*>(param->data);
44 
45 	dev->info.st_size = param->size;
46 
47 	return dev;
48 }
49 
file_open(const void * params)50 static void *file_open(const void *params)
51 {
52 	const Value *value = reinterpret_cast<const Value*>(params);
53 	struct file_data *dev = reinterpret_cast<struct file_data*>(calloc(1, sizeof(struct file_data)));
54 	if (!dev)
55 		return NULL;
56 
57 	if (value->type == VAL_BLOB)
58 		return file_blob_open(dev, value);
59 
60 	if (value->type != VAL_STRING)
61 		return NULL;
62 
63 	dev->fd = open(value->data, O_RDWR);
64 	if (dev->fd == -1) {
65 		ALOGE("Cannot open file %s : %d\n", value->data, errno);
66 		goto out_free;
67 	}
68 
69 	if (fstat(dev->fd, &dev->info)) {
70 		ALOGE("Cannot get file info for %s : %d\n", value->data, errno);
71 		goto out_close;
72 	}
73 
74 	dev->data = reinterpret_cast<uint8_t*>(mmap(NULL, dev->info.st_size, PROT_READ | PROT_WRITE,
75 			 MAP_SHARED, dev->fd, 0));
76 	if (dev->data == (void *)-1) {
77 		ALOGE("Cannot mmap %s : %d\n", value->data, errno);
78 		goto out_close;
79 	}
80 
81 	ALOGD("File %s: size %lld blksize %ld\n", value->data,
82 		(long long)dev->info.st_size, (long)dev->info.st_blksize);
83 
84 	return dev;
85 
86 out_close:
87 	close(dev->fd);
88 out_free:
89 	free(dev);
90 
91 	return NULL;
92 }
93 
file_close(void * hnd)94 static void file_close(void *hnd)
95 {
96 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
97 
98 	if (dev->fd > 0) {
99 		munmap(dev->data, dev->info.st_size);
100 		close(dev->fd);
101 	}
102 	free(dev);
103 }
104 
file_read(void * hnd,off_t offset,void * buffer,size_t count)105 static int file_read(void *hnd, off_t offset, void *buffer, size_t count)
106 {
107 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
108 
109 	if (offset + (off_t)count > dev->info.st_size) {
110 		ALOGW("Invalid offset/size %ld + %zd > %lld\n",
111 			offset, count, (long long)dev->info.st_size);
112 		return -EINVAL;
113 	}
114 
115 	memcpy(buffer, dev->data + offset, count);
116 
117 	return 0;
118 }
119 
file_write(void * hnd,off_t offset,void * buffer,size_t count)120 static int file_write(void *hnd, off_t offset, void *buffer, size_t count)
121 {
122 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
123 
124 	if (offset + (off_t)count > dev->info.st_size) {
125 		ALOGW("Invalid offset/size %ld + %zd > %lld\n",
126 			offset, count, (long long)dev->info.st_size);
127 		return -EINVAL;
128 	}
129 
130 	memcpy(dev->data + offset, buffer, count);
131 
132 	return 0;
133 }
134 
file_erase(void * hnd,off_t offset,size_t count)135 static int file_erase(void *hnd, off_t offset, size_t count)
136 {
137 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
138 
139 	if (offset + (off_t)count > dev->info.st_size) {
140 		ALOGW("Invalid offset/size %ld + %zd > %lld\n",
141 			offset, count, (long long)dev->info.st_size);
142 		return -EINVAL;
143 	}
144 
145 	memset(dev->data + offset, '\xff', count);
146 
147 	return 0;
148 }
149 
file_get_size(void * hnd)150 static size_t file_get_size(void *hnd)
151 {
152 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
153 
154 	return dev ? dev->info.st_size : 0;
155 }
156 
file_get_write_size(void * hnd)157 static size_t file_get_write_size(void *hnd)
158 {
159 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
160 
161 	return dev && dev->fd > 0 ? dev->info.st_blksize : 0;
162 }
163 
file_get_erase_size(void * hnd)164 static size_t file_get_erase_size(void *hnd)
165 {
166 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
167 
168 	return dev && dev->fd > 0 ? dev->info.st_blksize : 0;
169 }
170 
file_get_fmap_offset(void * hnd)171 static off_t file_get_fmap_offset(void *hnd)
172 {
173 	struct file_data *dev = reinterpret_cast<struct file_data*>(hnd);
174 
175 	return dev->info.st_size;
176 }
177 
178 const struct flash_device_ops flash_file_ops = {
179 	.name = "file",
180 	.open = file_open,
181 	.close = file_close,
182 	.read = file_read,
183 	.write = file_write,
184 	.erase = file_erase,
185 	.get_size = file_get_size,
186 	.get_write_size = file_get_write_size,
187 	.get_erase_size = file_get_erase_size,
188 	.get_fmap_offset = file_get_fmap_offset,
189 };
190