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