blob: 2d23baac56543283bdde1db9329fe1f7d1602c39 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* 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
Xavier Ducrohetacbee352009-10-01 13:43:13 -070021# define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
22# define D_ACTIVE VERBOSE_CHECK(init)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080023# 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
42static void
43xlog( 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
52typedef 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
64nand_threshold android_nand_write_threshold;
65nand_threshold android_nand_read_threshold;
66
67#ifdef CONFIG_NAND_THRESHOLD
68
69/* update a threshold, return 1 if limit is hit, 0 otherwise */
70static void
71nand_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
110static nand_dev *nand_devs = NULL;
111static uint32_t nand_dev_count = 0;
112
113typedef 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
129QFIELD_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),
136QFIELD_END
137
138static 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
145static 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
156static 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
166static 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
176static 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' Turner5d8f37a2009-09-14 14:32:27 -0700196 cpu_memory_rw_debug(cpu_single_env, data, dev->data, read_len, 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800197 data += read_len;
198 len -= read_len;
199 }
200 return total_len;
201}
202
203static 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' Turner5d8f37a2009-09-14 14:32:27 -0700215 cpu_memory_rw_debug(cpu_single_env, data, dev->data, write_len, 0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800216 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
227static 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__)
259static
260#endif
261uint32_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' Turner5d8f37a2009-09-14 14:32:27 -0700277 cpu_memory_rw_debug(cpu_single_env, s->data, dev->devname, size, 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800278 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' Turner5d8f37a2009-09-14 14:32:27 -0700286 cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800287 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' Turner5d8f37a2009-09-14 14:32:27 -0700297 cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800298 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 */
323static 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 Project8b23a6c2009-03-03 19:30:32 -0800327 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 */
356static 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 Project8b23a6c2009-03-03 19:30:32 -0800361 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
403static CPUReadMemoryFunc *nand_dev_readfn[] = {
404 nand_dev_read,
405 nand_dev_read,
406 nand_dev_read
407};
408
409static CPUWriteMemoryFunc *nand_dev_writefn[] = {
410 nand_dev_write,
411 nand_dev_write,
412 nand_dev_write
413};
414
415/* initialize the QFB device */
416void 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' Turner5d8f37a2009-09-14 14:32:27 -0700423 iomemtype = cpu_register_io_memory(nand_dev_readfn, nand_dev_writefn, s);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800424 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
431static 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
440void 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);
Xavier Ducrohetacbee352009-10-01 13:43:13 -0700596 D("rounding devsize up to a full eraseunit, now %llx\n", dev_size);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800597 }
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
626out_of_memory:
627 XLOG("out of memory\n");
628 exit(1);
629
630bad_arg_and_value:
631 XLOG("bad arg: %.*s=%.*s\n", arg_len, arg, value_len, value);
632 exit(1);
633}
634