| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 1 | /* Copyright (C) 2007-2008 The Android Open Source Project |
| 2 | ** |
| 3 | ** This software is licensed under the terms of the GNU General Public |
| 4 | ** License version 2, as published by the Free Software Foundation, and |
| 5 | ** may be copied, distributed, and modified under those terms. |
| 6 | ** |
| 7 | ** This program is distributed in the hope that it will be useful, |
| 8 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | ** GNU General Public License for more details. |
| 11 | */ |
| 12 | #include "qemu_file.h" |
| 13 | #include "goldfish_nand_reg.h" |
| 14 | #include "goldfish_nand.h" |
| 15 | #include "android/utils/tempfile.h" |
| 16 | #include "qemu_debug.h" |
| 17 | #include "android/android.h" |
| 18 | |
| 19 | #define DEBUG 1 |
| 20 | #if DEBUG |
| 21 | # define D(...) VERBOSE_PRINT(nand,__VA_ARGS__) |
| 22 | # define D_ACTIVE VERBOSE_CHECK(nand) |
| 23 | # define T(...) VERBOSE_PRINT(nand_limits,__VA_ARGS__) |
| 24 | # define T_ACTIVE VERBOSE_CHECK(nand_limits) |
| 25 | #else |
| 26 | # define D(...) ((void)0) |
| 27 | # define D_ACTIVE 0 |
| 28 | # define T(...) ((void)0) |
| 29 | # define T_ACTIVE 0 |
| 30 | #endif |
| 31 | |
| 32 | /* lseek uses 64-bit offsets on Darwin. */ |
| 33 | /* prefer lseek64 on Linux */ |
| 34 | #ifdef __APPLE__ |
| 35 | # define llseek lseek |
| 36 | #elif defined(__linux__) |
| 37 | # define llseek lseek64 |
| 38 | #endif |
| 39 | |
| 40 | #define XLOG xlog |
| 41 | |
| 42 | static void |
| 43 | xlog( const char* format, ... ) |
| 44 | { |
| 45 | va_list args; |
| 46 | va_start(args, format); |
| 47 | fprintf(stderr, "NAND: "); |
| 48 | vfprintf(stderr, format, args); |
| 49 | va_end(args); |
| 50 | } |
| 51 | |
| 52 | typedef struct { |
| 53 | char* devname; |
| 54 | size_t devname_len; |
| 55 | char* data; |
| 56 | int fd; |
| 57 | uint32_t flags; |
| 58 | uint32_t page_size; |
| 59 | uint32_t extra_size; |
| 60 | uint32_t erase_size; |
| 61 | uint64_t size; |
| 62 | } nand_dev; |
| 63 | |
| 64 | nand_threshold android_nand_write_threshold; |
| 65 | nand_threshold android_nand_read_threshold; |
| 66 | |
| 67 | #ifdef CONFIG_NAND_THRESHOLD |
| 68 | |
| 69 | /* update a threshold, return 1 if limit is hit, 0 otherwise */ |
| 70 | static void |
| 71 | nand_threshold_update( nand_threshold* t, uint32_t len ) |
| 72 | { |
| 73 | if (t->counter < t->limit) { |
| 74 | uint64_t avail = t->limit - t->counter; |
| 75 | if (avail > len) |
| 76 | avail = len; |
| 77 | |
| 78 | if (t->counter == 0) { |
| 79 | T("%s: starting threshold counting to %lld", |
| 80 | __FUNCTION__, t->limit); |
| 81 | } |
| 82 | t->counter += avail; |
| 83 | if (t->counter >= t->limit) { |
| 84 | /* threshold reach, send a signal to an external process */ |
| 85 | T( "%s: sending signal %d to pid %d !", |
| 86 | __FUNCTION__, t->signal, t->pid ); |
| 87 | |
| 88 | kill( t->pid, t->signal ); |
| 89 | } |
| 90 | } |
| 91 | return; |
| 92 | } |
| 93 | |
| 94 | #define NAND_UPDATE_READ_THRESHOLD(len) \ |
| 95 | nand_threshold_update( &android_nand_read_threshold, (uint32_t)(len) ) |
| 96 | |
| 97 | #define NAND_UPDATE_WRITE_THRESHOLD(len) \ |
| 98 | nand_threshold_update( &android_nand_write_threshold, (uint32_t)(len) ) |
| 99 | |
| 100 | #else /* !NAND_THRESHOLD */ |
| 101 | |
| 102 | #define NAND_UPDATE_READ_THRESHOLD(len) \ |
| 103 | do {} while (0) |
| 104 | |
| 105 | #define NAND_UPDATE_WRITE_THRESHOLD(len) \ |
| 106 | do {} while (0) |
| 107 | |
| 108 | #endif /* !NAND_THRESHOLD */ |
| 109 | |
| 110 | static nand_dev *nand_devs = NULL; |
| 111 | static uint32_t nand_dev_count = 0; |
| 112 | |
| 113 | typedef struct { |
| 114 | uint32_t base; |
| 115 | |
| 116 | // register state |
| 117 | uint32_t dev; |
| 118 | uint32_t addr_low; |
| 119 | uint32_t addr_high; |
| 120 | uint32_t transfer_size; |
| 121 | uint32_t data; |
| 122 | uint32_t result; |
| 123 | } nand_dev_state; |
| 124 | |
| 125 | /* update this everytime you change the nand_dev_state structure */ |
| 126 | #define NAND_DEV_STATE_SAVE_VERSION 1 |
| 127 | |
| 128 | #define QFIELD_STRUCT nand_dev_state |
| 129 | QFIELD_BEGIN(nand_dev_state_fields) |
| 130 | QFIELD_INT32(dev), |
| 131 | QFIELD_INT32(addr_low), |
| 132 | QFIELD_INT32(addr_high), |
| 133 | QFIELD_INT32(transfer_size), |
| 134 | QFIELD_INT32(data), |
| 135 | QFIELD_INT32(result), |
| 136 | QFIELD_END |
| 137 | |
| 138 | static void nand_dev_state_save(QEMUFile* f, void* opaque) |
| 139 | { |
| 140 | nand_dev_state* s = opaque; |
| 141 | |
| 142 | qemu_put_struct(f, nand_dev_state_fields, s); |
| 143 | } |
| 144 | |
| 145 | static int nand_dev_state_load(QEMUFile* f, void* opaque, int version_id) |
| 146 | { |
| 147 | nand_dev_state* s = opaque; |
| 148 | |
| 149 | if (version_id != NAND_DEV_STATE_SAVE_VERSION) |
| 150 | return -1; |
| 151 | |
| 152 | return qemu_get_struct(f, nand_dev_state_fields, s); |
| 153 | } |
| 154 | |
| 155 | |
| 156 | static int do_read(int fd, void* buf, size_t size) |
| 157 | { |
| 158 | int ret; |
| 159 | do { |
| 160 | ret = read(fd, buf, size); |
| 161 | } while (ret < 0 && errno == EINTR); |
| 162 | |
| 163 | return ret; |
| 164 | } |
| 165 | |
| 166 | static int do_write(int fd, const void* buf, size_t size) |
| 167 | { |
| 168 | int ret; |
| 169 | do { |
| 170 | ret = write(fd, buf, size); |
| 171 | } while (ret < 0 && errno == EINTR); |
| 172 | |
| 173 | return ret; |
| 174 | } |
| 175 | |
| 176 | static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len) |
| 177 | { |
| 178 | uint32_t len = total_len; |
| 179 | size_t read_len = dev->erase_size; |
| 180 | int eof = 0; |
| 181 | |
| 182 | NAND_UPDATE_READ_THRESHOLD(total_len); |
| 183 | |
| 184 | lseek(dev->fd, addr, SEEK_SET); |
| 185 | while(len > 0) { |
| 186 | if(read_len < dev->erase_size) { |
| 187 | memset(dev->data, 0xff, dev->erase_size); |
| 188 | read_len = dev->erase_size; |
| 189 | eof = 1; |
| 190 | } |
| 191 | if(len < read_len) |
| 192 | read_len = len; |
| 193 | if(!eof) { |
| 194 | read_len = do_read(dev->fd, dev->data, read_len); |
| 195 | } |
| David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 196 | cpu_memory_rw_debug(cpu_single_env, data, dev->data, read_len, 1); |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 197 | data += read_len; |
| 198 | len -= read_len; |
| 199 | } |
| 200 | return total_len; |
| 201 | } |
| 202 | |
| 203 | static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len) |
| 204 | { |
| 205 | uint32_t len = total_len; |
| 206 | size_t write_len = dev->erase_size; |
| 207 | int ret; |
| 208 | |
| 209 | NAND_UPDATE_WRITE_THRESHOLD(total_len); |
| 210 | |
| 211 | lseek(dev->fd, addr, SEEK_SET); |
| 212 | while(len > 0) { |
| 213 | if(len < write_len) |
| 214 | write_len = len; |
| David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 215 | cpu_memory_rw_debug(cpu_single_env, data, dev->data, write_len, 0); |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 216 | ret = do_write(dev->fd, dev->data, write_len); |
| 217 | if(ret < write_len) { |
| 218 | XLOG("nand_dev_write_file, write failed: %s\n", strerror(errno)); |
| 219 | break; |
| 220 | } |
| 221 | data += write_len; |
| 222 | len -= write_len; |
| 223 | } |
| 224 | return total_len - len; |
| 225 | } |
| 226 | |
| 227 | static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total_len) |
| 228 | { |
| 229 | uint32_t len = total_len; |
| 230 | size_t write_len = dev->erase_size; |
| 231 | int ret; |
| 232 | |
| 233 | lseek(dev->fd, addr, SEEK_SET); |
| 234 | memset(dev->data, 0xff, dev->erase_size); |
| 235 | while(len > 0) { |
| 236 | if(len < write_len) |
| 237 | write_len = len; |
| 238 | ret = do_write(dev->fd, dev->data, write_len); |
| 239 | if(ret < write_len) { |
| 240 | XLOG( "nand_dev_write_file, write failed: %s\n", strerror(errno)); |
| 241 | break; |
| 242 | } |
| 243 | len -= write_len; |
| 244 | } |
| 245 | return total_len - len; |
| 246 | } |
| 247 | |
| 248 | /* this is a huge hack required to make the PowerPC emulator binary usable |
| 249 | * on Mac OS X. If you define this function as 'static', the emulated kernel |
| 250 | * will panic when attempting to mount the /data partition. |
| 251 | * |
| 252 | * worse, if you do *not* define the function as static on Linux-x86, the |
| 253 | * emulated kernel will also panic !? |
| 254 | * |
| 255 | * I still wonder if this is a compiler bug, or due to some nasty thing the |
| 256 | * emulator does with CPU registers during execution of the translated code. |
| 257 | */ |
| 258 | #if !(defined __APPLE__ && defined __powerpc__) |
| 259 | static |
| 260 | #endif |
| 261 | uint32_t nand_dev_do_cmd(nand_dev_state *s, uint32_t cmd) |
| 262 | { |
| 263 | uint32_t size; |
| 264 | uint64_t addr; |
| 265 | nand_dev *dev; |
| 266 | |
| 267 | addr = s->addr_low | ((uint64_t)s->addr_high << 32); |
| 268 | size = s->transfer_size; |
| 269 | if(s->dev >= nand_dev_count) |
| 270 | return 0; |
| 271 | dev = nand_devs + s->dev; |
| 272 | |
| 273 | switch(cmd) { |
| 274 | case NAND_CMD_GET_DEV_NAME: |
| 275 | if(size > dev->devname_len) |
| 276 | size = dev->devname_len; |
| David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 277 | cpu_memory_rw_debug(cpu_single_env, s->data, dev->devname, size, 1); |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 278 | return size; |
| 279 | case NAND_CMD_READ: |
| 280 | if(addr >= dev->size) |
| 281 | return 0; |
| 282 | if(size + addr > dev->size) |
| 283 | size = dev->size - addr; |
| 284 | if(dev->fd >= 0) |
| 285 | return nand_dev_read_file(dev, s->data, addr, size); |
| David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 286 | cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 1); |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 287 | return size; |
| 288 | case NAND_CMD_WRITE: |
| 289 | if(dev->flags & NAND_DEV_FLAG_READ_ONLY) |
| 290 | return 0; |
| 291 | if(addr >= dev->size) |
| 292 | return 0; |
| 293 | if(size + addr > dev->size) |
| 294 | size = dev->size - addr; |
| 295 | if(dev->fd >= 0) |
| 296 | return nand_dev_write_file(dev, s->data, addr, size); |
| David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 297 | cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 0); |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 298 | return size; |
| 299 | case NAND_CMD_ERASE: |
| 300 | if(dev->flags & NAND_DEV_FLAG_READ_ONLY) |
| 301 | return 0; |
| 302 | if(addr >= dev->size) |
| 303 | return 0; |
| 304 | if(size + addr > dev->size) |
| 305 | size = dev->size - addr; |
| 306 | if(dev->fd >= 0) |
| 307 | return nand_dev_erase_file(dev, addr, size); |
| 308 | memset(&dev->data[addr], 0xff, size); |
| 309 | return size; |
| 310 | case NAND_CMD_BLOCK_BAD_GET: // no bad block support |
| 311 | return 0; |
| 312 | case NAND_CMD_BLOCK_BAD_SET: |
| 313 | if(dev->flags & NAND_DEV_FLAG_READ_ONLY) |
| 314 | return 0; |
| 315 | return 0; |
| 316 | default: |
| 317 | cpu_abort(cpu_single_env, "nand_dev_do_cmd: Bad command %x\n", cmd); |
| 318 | return 0; |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | /* I/O write */ |
| 323 | static void nand_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value) |
| 324 | { |
| 325 | nand_dev_state *s = (nand_dev_state *)opaque; |
| 326 | |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 327 | switch (offset) { |
| 328 | case NAND_DEV: |
| 329 | s->dev = value; |
| 330 | if(s->dev >= nand_dev_count) { |
| 331 | cpu_abort(cpu_single_env, "nand_dev_write: Bad dev %x\n", value); |
| 332 | } |
| 333 | break; |
| 334 | case NAND_ADDR_HIGH: |
| 335 | s->addr_high = value; |
| 336 | break; |
| 337 | case NAND_ADDR_LOW: |
| 338 | s->addr_low = value; |
| 339 | break; |
| 340 | case NAND_TRANSFER_SIZE: |
| 341 | s->transfer_size = value; |
| 342 | break; |
| 343 | case NAND_DATA: |
| 344 | s->data = value; |
| 345 | break; |
| 346 | case NAND_COMMAND: |
| 347 | s->result = nand_dev_do_cmd(s, value); |
| 348 | break; |
| 349 | default: |
| 350 | cpu_abort(cpu_single_env, "nand_dev_write: Bad offset %x\n", offset); |
| 351 | break; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | /* I/O read */ |
| 356 | static uint32_t nand_dev_read(void *opaque, target_phys_addr_t offset) |
| 357 | { |
| 358 | nand_dev_state *s = (nand_dev_state *)opaque; |
| 359 | nand_dev *dev; |
| 360 | |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 361 | switch (offset) { |
| 362 | case NAND_VERSION: |
| 363 | return NAND_VERSION_CURRENT; |
| 364 | case NAND_NUM_DEV: |
| 365 | return nand_dev_count; |
| 366 | case NAND_RESULT: |
| 367 | return s->result; |
| 368 | } |
| 369 | |
| 370 | if(s->dev >= nand_dev_count) |
| 371 | return 0; |
| 372 | |
| 373 | dev = nand_devs + s->dev; |
| 374 | |
| 375 | switch (offset) { |
| 376 | case NAND_DEV_FLAGS: |
| 377 | return dev->flags; |
| 378 | |
| 379 | case NAND_DEV_NAME_LEN: |
| 380 | return dev->devname_len; |
| 381 | |
| 382 | case NAND_DEV_PAGE_SIZE: |
| 383 | return dev->page_size; |
| 384 | |
| 385 | case NAND_DEV_EXTRA_SIZE: |
| 386 | return dev->extra_size; |
| 387 | |
| 388 | case NAND_DEV_ERASE_SIZE: |
| 389 | return dev->erase_size; |
| 390 | |
| 391 | case NAND_DEV_SIZE_LOW: |
| 392 | return (uint32_t)dev->size; |
| 393 | |
| 394 | case NAND_DEV_SIZE_HIGH: |
| 395 | return (uint32_t)(dev->size >> 32); |
| 396 | |
| 397 | default: |
| 398 | cpu_abort(cpu_single_env, "nand_dev_read: Bad offset %x\n", offset); |
| 399 | return 0; |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | static CPUReadMemoryFunc *nand_dev_readfn[] = { |
| 404 | nand_dev_read, |
| 405 | nand_dev_read, |
| 406 | nand_dev_read |
| 407 | }; |
| 408 | |
| 409 | static CPUWriteMemoryFunc *nand_dev_writefn[] = { |
| 410 | nand_dev_write, |
| 411 | nand_dev_write, |
| 412 | nand_dev_write |
| 413 | }; |
| 414 | |
| 415 | /* initialize the QFB device */ |
| 416 | void nand_dev_init(uint32_t base) |
| 417 | { |
| 418 | int iomemtype; |
| 419 | static int instance_id = 0; |
| 420 | nand_dev_state *s; |
| 421 | |
| 422 | s = (nand_dev_state *)qemu_mallocz(sizeof(nand_dev_state)); |
| David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 423 | iomemtype = cpu_register_io_memory(nand_dev_readfn, nand_dev_writefn, s); |
| The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 424 | cpu_register_physical_memory(base, 0x00000fff, iomemtype); |
| 425 | s->base = base; |
| 426 | |
| 427 | register_savevm( "nand_dev", instance_id++, NAND_DEV_STATE_SAVE_VERSION, |
| 428 | nand_dev_state_save, nand_dev_state_load, s); |
| 429 | } |
| 430 | |
| 431 | static int arg_match(const char *a, const char *b, size_t b_len) |
| 432 | { |
| 433 | while(*a && b_len--) { |
| 434 | if(*a++ != *b++) |
| 435 | return 0; |
| 436 | } |
| 437 | return b_len == 0; |
| 438 | } |
| 439 | |
| 440 | void nand_add_dev(const char *arg) |
| 441 | { |
| 442 | uint64_t dev_size = 0; |
| 443 | const char *next_arg; |
| 444 | const char *value; |
| 445 | size_t arg_len, value_len; |
| 446 | nand_dev *new_devs, *dev; |
| 447 | char *devname = NULL; |
| 448 | size_t devname_len = 0; |
| 449 | char *initfilename = NULL; |
| 450 | char *rwfilename = NULL; |
| 451 | int initfd = -1; |
| 452 | int rwfd = -1; |
| 453 | int read_only = 0; |
| 454 | int pad; |
| 455 | ssize_t read_size; |
| 456 | uint32_t page_size = 2048; |
| 457 | uint32_t extra_size = 64; |
| 458 | uint32_t erase_pages = 64; |
| 459 | |
| 460 | while(arg) { |
| 461 | next_arg = strchr(arg, ','); |
| 462 | value = strchr(arg, '='); |
| 463 | if(next_arg != NULL) { |
| 464 | arg_len = next_arg - arg; |
| 465 | next_arg++; |
| 466 | if(value >= next_arg) |
| 467 | value = NULL; |
| 468 | } |
| 469 | else |
| 470 | arg_len = strlen(arg); |
| 471 | if(value != NULL) { |
| 472 | size_t new_arg_len = value - arg; |
| 473 | value_len = arg_len - new_arg_len - 1; |
| 474 | arg_len = new_arg_len; |
| 475 | value++; |
| 476 | } |
| 477 | else |
| 478 | value_len = 0; |
| 479 | |
| 480 | if(devname == NULL) { |
| 481 | if(value != NULL) |
| 482 | goto bad_arg_and_value; |
| 483 | devname_len = arg_len; |
| 484 | devname = malloc(arg_len); |
| 485 | if(devname == NULL) |
| 486 | goto out_of_memory; |
| 487 | memcpy(devname, arg, arg_len); |
| 488 | } |
| 489 | else if(value == NULL) { |
| 490 | if(arg_match("readonly", arg, arg_len)) { |
| 491 | read_only = 1; |
| 492 | } |
| 493 | else { |
| 494 | XLOG("bad arg: %.*s\n", arg_len, arg); |
| 495 | exit(1); |
| 496 | } |
| 497 | } |
| 498 | else { |
| 499 | if(arg_match("size", arg, arg_len)) { |
| 500 | char *ep; |
| 501 | dev_size = strtoull(value, &ep, 0); |
| 502 | if(ep != value + value_len) |
| 503 | goto bad_arg_and_value; |
| 504 | } |
| 505 | else if(arg_match("pagesize", arg, arg_len)) { |
| 506 | char *ep; |
| 507 | page_size = strtoul(value, &ep, 0); |
| 508 | if(ep != value + value_len) |
| 509 | goto bad_arg_and_value; |
| 510 | } |
| 511 | else if(arg_match("extrasize", arg, arg_len)) { |
| 512 | char *ep; |
| 513 | extra_size = strtoul(value, &ep, 0); |
| 514 | if(ep != value + value_len) |
| 515 | goto bad_arg_and_value; |
| 516 | } |
| 517 | else if(arg_match("erasepages", arg, arg_len)) { |
| 518 | char *ep; |
| 519 | erase_pages = strtoul(value, &ep, 0); |
| 520 | if(ep != value + value_len) |
| 521 | goto bad_arg_and_value; |
| 522 | } |
| 523 | else if(arg_match("initfile", arg, arg_len)) { |
| 524 | initfilename = malloc(value_len + 1); |
| 525 | if(initfilename == NULL) |
| 526 | goto out_of_memory; |
| 527 | memcpy(initfilename, value, value_len); |
| 528 | initfilename[value_len] = '\0'; |
| 529 | } |
| 530 | else if(arg_match("file", arg, arg_len)) { |
| 531 | rwfilename = malloc(value_len + 1); |
| 532 | if(rwfilename == NULL) |
| 533 | goto out_of_memory; |
| 534 | memcpy(rwfilename, value, value_len); |
| 535 | rwfilename[value_len] = '\0'; |
| 536 | } |
| 537 | else { |
| 538 | goto bad_arg_and_value; |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | arg = next_arg; |
| 543 | } |
| 544 | |
| 545 | if (rwfilename == NULL) { |
| 546 | /* we create a temporary file to store everything */ |
| 547 | TempFile* tmp = tempfile_create(); |
| 548 | |
| 549 | if (tmp == NULL) { |
| 550 | XLOG("could not create temp file for %.*s NAND disk image: %s", |
| 551 | devname_len, devname, strerror(errno)); |
| 552 | exit(1); |
| 553 | } |
| 554 | rwfilename = (char*) tempfile_path(tmp); |
| 555 | if (VERBOSE_CHECK(init)) |
| 556 | dprint( "mapping '%.*s' NAND image to %s", devname_len, devname, rwfilename); |
| 557 | } |
| 558 | |
| 559 | if(rwfilename) { |
| 560 | rwfd = open(rwfilename, O_BINARY | (read_only ? O_RDONLY : O_RDWR)); |
| 561 | if(rwfd < 0 && read_only) { |
| 562 | XLOG("could not open file %s, %s\n", rwfilename, strerror(errno)); |
| 563 | exit(1); |
| 564 | } |
| 565 | /* this could be a writable temporary file. use atexit_close_fd to ensure |
| 566 | * that it is properly cleaned up at exit on Win32 |
| 567 | */ |
| 568 | if (!read_only) |
| 569 | atexit_close_fd(rwfd); |
| 570 | } |
| 571 | |
| 572 | if(initfilename) { |
| 573 | initfd = open(initfilename, O_BINARY | O_RDONLY); |
| 574 | if(initfd < 0) { |
| 575 | XLOG("could not open file %s, %s\n", initfilename, strerror(errno)); |
| 576 | exit(1); |
| 577 | } |
| 578 | if(dev_size == 0) { |
| 579 | dev_size = lseek(initfd, 0, SEEK_END); |
| 580 | lseek(initfd, 0, SEEK_SET); |
| 581 | } |
| 582 | } |
| 583 | |
| 584 | new_devs = realloc(nand_devs, sizeof(nand_devs[0]) * (nand_dev_count + 1)); |
| 585 | if(new_devs == NULL) |
| 586 | goto out_of_memory; |
| 587 | nand_devs = new_devs; |
| 588 | dev = &new_devs[nand_dev_count]; |
| 589 | |
| 590 | dev->page_size = page_size; |
| 591 | dev->extra_size = extra_size; |
| 592 | dev->erase_size = erase_pages * (page_size + extra_size); |
| 593 | pad = dev_size % dev->erase_size; |
| 594 | if (pad != 0) { |
| 595 | dev_size += (dev->erase_size - pad); |
| 596 | XLOG("rounding devsize up to a full eraseunit, now %llx\n", dev_size); |
| 597 | } |
| 598 | dev->devname = devname; |
| 599 | dev->devname_len = devname_len; |
| 600 | dev->size = dev_size; |
| 601 | dev->data = malloc(dev->erase_size); |
| 602 | if(dev->data == NULL) |
| 603 | goto out_of_memory; |
| 604 | dev->flags = read_only ? NAND_DEV_FLAG_READ_ONLY : 0; |
| 605 | |
| 606 | if (initfd >= 0) { |
| 607 | do { |
| 608 | read_size = do_read(initfd, dev->data, dev->erase_size); |
| 609 | if(read_size < 0) { |
| 610 | XLOG("could not read file %s, %s\n", initfilename, strerror(errno)); |
| 611 | exit(1); |
| 612 | } |
| 613 | if(do_write(rwfd, dev->data, read_size) != read_size) { |
| 614 | XLOG("could not write file %s, %s\n", initfilename, strerror(errno)); |
| 615 | exit(1); |
| 616 | } |
| 617 | } while(read_size == dev->erase_size); |
| 618 | close(initfd); |
| 619 | } |
| 620 | dev->fd = rwfd; |
| 621 | |
| 622 | nand_dev_count++; |
| 623 | |
| 624 | return; |
| 625 | |
| 626 | out_of_memory: |
| 627 | XLOG("out of memory\n"); |
| 628 | exit(1); |
| 629 | |
| 630 | bad_arg_and_value: |
| 631 | XLOG("bad arg: %.*s=%.*s\n", arg_len, arg, value_len, value); |
| 632 | exit(1); |
| 633 | } |
| 634 | |