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 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 79 static void mtd_close(void *hnd) 80 { 81 struct mtd_data *dev = hnd; 82 83 close(dev->fd); 84 free(dev); 85 } 86 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 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 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 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 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 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 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