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