1 /* 2 * Mailbox Daemon Flash Helpers 3 * 4 * Copyright 2016 IBM 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 20 #define _GNU_SOURCE 21 #include <assert.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <getopt.h> 25 #include <limits.h> 26 #include <poll.h> 27 #include <stdbool.h> 28 #include <stdint.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <signal.h> 34 #include <sys/ioctl.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <sys/timerfd.h> 38 #include <sys/types.h> 39 #include <time.h> 40 #include <unistd.h> 41 #include <inttypes.h> 42 #include <mtd/mtd-abi.h> 43 44 #include "mbox.h" 45 #include "common.h" 46 #include "mboxd_flash.h" 47 48 int init_flash_dev(struct mbox_context *context) 49 { 50 char *filename = get_dev_mtd(); 51 int fd, rc = 0; 52 53 if (!filename) { 54 MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n"); 55 return -1; 56 } 57 58 MSG_DBG("Opening %s\n", filename); 59 60 /* Open Flash Device */ 61 fd = open(filename, O_RDWR); 62 if (fd < 0) { 63 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", 64 filename, strerror(errno)); 65 rc = -errno; 66 goto out; 67 } 68 context->fds[MTD_FD].fd = fd; 69 70 /* Read the Flash Info */ 71 if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) { 72 MSG_ERR("Couldn't get information about MTD: %s\n", 73 strerror(errno)); 74 rc = -1; 75 goto out; 76 } 77 78 if (context->flash_size == 0) { 79 /* 80 * PNOR images for current OpenPOWER systems are at most 64MB 81 * despite the PNOR itself sometimes being as big as 128MB. To 82 * ensure the image read from the PNOR is exposed in the LPC 83 * address space at the location expected by the host firmware, 84 * it is required that the image size be used for 85 * context->flash_size, and not the size of the flash device. 86 * 87 * However, the test cases specify the flash size via special 88 * test APIs (controlling flash behaviour) which don't have 89 * access to the mbox context. Rather than requiring 90 * error-prone assignments in every test case, we instead rely 91 * on context->flash_size being set to the size reported by the 92 * MEMINFO ioctl(). 93 * 94 * As this case should never be hit in production (i.e. outside 95 * the test environment), log an error. As a consequence, this 96 * error is expected in the test case output. 97 */ 98 MSG_ERR("Flash size MUST be supplied on the commandline. However, continuing by assuming flash is %u bytes\n", 99 context->mtd_info.size); 100 context->flash_size = context->mtd_info.size; 101 } 102 103 /* We know the erase size so we can allocate the flash_erased bytemap */ 104 context->erase_size_shift = log_2(context->mtd_info.erasesize); 105 context->flash_bmap = calloc(context->flash_size >> 106 context->erase_size_shift, 107 sizeof(*context->flash_bmap)); 108 MSG_DBG("Flash erase size: 0x%.8x\n", context->mtd_info.erasesize); 109 110 out: 111 free(filename); 112 return rc; 113 } 114 115 void free_flash_dev(struct mbox_context *context) 116 { 117 free(context->flash_bmap); 118 close(context->fds[MTD_FD].fd); 119 } 120 121 /* Flash Functions */ 122 123 #define CHUNKSIZE (64 * 1024) 124 125 /* 126 * copy_flash() - Copy data from the flash device into a provided buffer 127 * @context: The mbox context pointer 128 * @offset: The flash offset to copy from (bytes) 129 * @mem: The buffer to copy into (must be of atleast size) 130 * @size: The number of bytes to copy 131 * 132 * Return: 0 on success otherwise negative error code 133 */ 134 int copy_flash(struct mbox_context *context, uint32_t offset, void *mem, 135 uint32_t size) 136 { 137 int32_t size_read; 138 139 MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n", 140 mem, size, offset); 141 if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) { 142 MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset, 143 strerror(errno)); 144 return -MBOX_R_SYSTEM_ERROR; 145 } 146 147 do { 148 size_read = read(context->fds[MTD_FD].fd, mem, 149 min_u32(CHUNKSIZE, size)); 150 if (size_read < 0) { 151 MSG_ERR("Couldn't copy mtd into ram: %s\n", 152 strerror(errno)); 153 return -MBOX_R_SYSTEM_ERROR; 154 } 155 156 size -= size_read; 157 mem += size_read; 158 } while (size && size_read); 159 160 return size ? -MBOX_R_SYSTEM_ERROR : 0; 161 } 162 163 /* 164 * flash_is_erased() - Check if an offset into flash is erased 165 * @context: The mbox context pointer 166 * @offset: The flash offset to check (bytes) 167 * 168 * Return: true if erased otherwise false 169 */ 170 static inline bool flash_is_erased(struct mbox_context *context, 171 uint32_t offset) 172 { 173 return context->flash_bmap[offset >> context->erase_size_shift] 174 == FLASH_ERASED; 175 } 176 177 /* 178 * set_flash_bytemap() - Set the flash erased bytemap 179 * @context: The mbox context pointer 180 * @offset: The flash offset to set (bytes) 181 * @count: Number of bytes to set 182 * @val: Value to set the bytemap to 183 * 184 * The flash bytemap only tracks the erased status at the erase block level so 185 * this will update the erased state for an (or many) erase blocks 186 * 187 * Return: 0 if success otherwise negative error code 188 */ 189 int set_flash_bytemap(struct mbox_context *context, uint32_t offset, 190 uint32_t count, uint8_t val) 191 { 192 if ((offset + count) > context->flash_size) { 193 return -MBOX_R_PARAM_ERROR; 194 } 195 196 MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n", 197 offset, count, val ? "ERASED" : "DIRTY"); 198 memset(context->flash_bmap + (offset >> context->erase_size_shift), 199 val, 200 align_up(count, 1 << context->erase_size_shift) >> 201 context->erase_size_shift); 202 203 return 0; 204 } 205 206 /* 207 * erase_flash() - Erase the flash 208 * @context: The mbox context pointer 209 * @offset: The flash offset to erase (bytes) 210 * @size: The number of bytes to erase 211 * 212 * Return: 0 on success otherwise negative error code 213 */ 214 int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count) 215 { 216 const uint32_t erase_size = 1 << context->erase_size_shift; 217 struct erase_info_user erase_info = { 0 }; 218 int rc; 219 220 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count); 221 222 /* 223 * We have an erased_bytemap for the flash so we want to avoid erasing 224 * blocks which we already know to be erased. Look for runs of blocks 225 * which aren't erased and erase the entire run at once to avoid how 226 * often we have to call the erase ioctl. If the block is already 227 * erased then there's nothing we need to do. 228 */ 229 while (count) { 230 if (!flash_is_erased(context, offset)) { /* Need to erase */ 231 if (!erase_info.length) { /* Start of not-erased run */ 232 erase_info.start = offset; 233 } 234 erase_info.length += erase_size; 235 } else if (erase_info.length) { /* Already erased|end of run? */ 236 /* Erase the previous run which just ended */ 237 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", 238 erase_info.start, erase_info.length); 239 rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, 240 &erase_info); 241 if (rc < 0) { 242 MSG_ERR("Couldn't erase flash at 0x%.8x\n", 243 erase_info.start); 244 return -MBOX_R_SYSTEM_ERROR; 245 } 246 /* Mark ERASED where we just erased */ 247 set_flash_bytemap(context, erase_info.start, 248 erase_info.length, FLASH_ERASED); 249 erase_info.start = 0; 250 erase_info.length = 0; 251 } 252 253 offset += erase_size; 254 count -= erase_size; 255 } 256 257 if (erase_info.length) { 258 MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", 259 erase_info.start, erase_info.length); 260 rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info); 261 if (rc < 0) { 262 MSG_ERR("Couldn't erase flash at 0x%.8x\n", 263 erase_info.start); 264 return -MBOX_R_SYSTEM_ERROR; 265 } 266 /* Mark ERASED where we just erased */ 267 set_flash_bytemap(context, erase_info.start, erase_info.length, 268 FLASH_ERASED); 269 } 270 271 return 0; 272 } 273 274 /* 275 * write_flash() - Write the flash from a provided buffer 276 * @context: The mbox context pointer 277 * @offset: The flash offset to write to (bytes) 278 * @buf: The buffer to write from (must be of atleast size) 279 * @size: The number of bytes to write 280 * 281 * Return: 0 on success otherwise negative error code 282 */ 283 int write_flash(struct mbox_context *context, uint32_t offset, void *buf, 284 uint32_t count) 285 { 286 uint32_t buf_offset = 0; 287 int rc; 288 289 MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, buf); 290 291 if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) { 292 MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset, 293 strerror(errno)); 294 return -MBOX_R_SYSTEM_ERROR; 295 } 296 297 while (count) { 298 rc = write(context->fds[MTD_FD].fd, buf + buf_offset, count); 299 if (rc < 0) { 300 MSG_ERR("Couldn't write to flash, write lost: %s\n", 301 strerror(errno)); 302 return -MBOX_R_WRITE_ERROR; 303 } 304 /* Mark *NOT* erased where we just wrote */ 305 set_flash_bytemap(context, offset + buf_offset, rc, 306 FLASH_DIRTY); 307 count -= rc; 308 buf_offset += rc; 309 } 310 311 return 0; 312 } 313