blob: c9567d39b5bfe6f3257cef175d0bfb27a868a31e [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
Ot ten Thijec51182e2010-09-08 19:01:33 +010052/* Information on a single device/nand image used by the emulator
53 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080054typedef struct {
Ot ten Thijec51182e2010-09-08 19:01:33 +010055 char* devname; /* name for this device (not null-terminated, use len below) */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080056 size_t devname_len;
Ot ten Thijec51182e2010-09-08 19:01:33 +010057 uint8_t* data; /* buffer for read/write actions to underlying image */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080058 int fd;
59 uint32_t flags;
60 uint32_t page_size;
61 uint32_t extra_size;
Ot ten Thijec51182e2010-09-08 19:01:33 +010062 uint32_t erase_size; /* size of the data buffer mentioned above */
63 uint64_t max_size; /* Capacity limit for the image. The actual underlying
64 * file may be smaller. */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080065} nand_dev;
66
67nand_threshold android_nand_write_threshold;
68nand_threshold android_nand_read_threshold;
69
70#ifdef CONFIG_NAND_THRESHOLD
71
72/* update a threshold, return 1 if limit is hit, 0 otherwise */
73static void
74nand_threshold_update( nand_threshold* t, uint32_t len )
75{
76 if (t->counter < t->limit) {
77 uint64_t avail = t->limit - t->counter;
78 if (avail > len)
79 avail = len;
80
81 if (t->counter == 0) {
Vladimir Chtchetkinee1316862010-08-26 09:03:54 -070082 T("%s: starting threshold counting to %lld",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080083 __FUNCTION__, t->limit);
84 }
85 t->counter += avail;
86 if (t->counter >= t->limit) {
87 /* threshold reach, send a signal to an external process */
Vladimir Chtchetkinee1316862010-08-26 09:03:54 -070088 T( "%s: sending signal %d to pid %d !",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080089 __FUNCTION__, t->signal, t->pid );
90
91 kill( t->pid, t->signal );
92 }
93 }
94 return;
95}
96
97#define NAND_UPDATE_READ_THRESHOLD(len) \
98 nand_threshold_update( &android_nand_read_threshold, (uint32_t)(len) )
99
100#define NAND_UPDATE_WRITE_THRESHOLD(len) \
101 nand_threshold_update( &android_nand_write_threshold, (uint32_t)(len) )
102
103#else /* !NAND_THRESHOLD */
104
105#define NAND_UPDATE_READ_THRESHOLD(len) \
106 do {} while (0)
107
108#define NAND_UPDATE_WRITE_THRESHOLD(len) \
109 do {} while (0)
110
111#endif /* !NAND_THRESHOLD */
112
113static nand_dev *nand_devs = NULL;
114static uint32_t nand_dev_count = 0;
115
Ot ten Thijec51182e2010-09-08 19:01:33 +0100116/* The controller is the single access point for all NAND images currently
117 * attached to the system.
118 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800119typedef struct {
120 uint32_t base;
121
122 // register state
Ot ten Thijec51182e2010-09-08 19:01:33 +0100123 uint32_t dev; /* offset in nand_devs for the device that is
124 * currently being accessed */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800125 uint32_t addr_low;
126 uint32_t addr_high;
127 uint32_t transfer_size;
128 uint32_t data;
129 uint32_t result;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100130} nand_dev_controller_state;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800131
Ot ten Thijec51182e2010-09-08 19:01:33 +0100132/* update this everytime you change the nand_dev_controller_state structure
133 * 1: initial version, saving only nand_dev_controller_state fields
134 * 2: saving actual disk contents as well
Tim Baverstocka658bc82010-10-20 14:30:05 +0100135 * 3: use the correct data length and truncate to avoid padding.
Ot ten Thijec51182e2010-09-08 19:01:33 +0100136 */
Tim Baverstocka658bc82010-10-20 14:30:05 +0100137#define NAND_DEV_STATE_SAVE_VERSION 3
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800138
Ot ten Thijec51182e2010-09-08 19:01:33 +0100139#define QFIELD_STRUCT nand_dev_controller_state
140QFIELD_BEGIN(nand_dev_controller_state_fields)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800141 QFIELD_INT32(dev),
142 QFIELD_INT32(addr_low),
143 QFIELD_INT32(addr_high),
144 QFIELD_INT32(transfer_size),
145 QFIELD_INT32(data),
146 QFIELD_INT32(result),
147QFIELD_END
148
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800149
Tim Baverstocka658bc82010-10-20 14:30:05 +0100150/* EINTR-proof read - due to SIGALRM in use elsewhere */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800151static int do_read(int fd, void* buf, size_t size)
152{
153 int ret;
154 do {
155 ret = read(fd, buf, size);
156 } while (ret < 0 && errno == EINTR);
157
158 return ret;
159}
160
Tim Baverstocka658bc82010-10-20 14:30:05 +0100161/* EINTR-proof write - due to SIGALRM in use elsewhere */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800162static int do_write(int fd, const void* buf, size_t size)
163{
164 int ret;
165 do {
166 ret = write(fd, buf, size);
167 } while (ret < 0 && errno == EINTR);
168
169 return ret;
170}
171
Tim Baverstocka658bc82010-10-20 14:30:05 +0100172/* EINTR-proof lseek - due to SIGALRM in use elsewhere */
173static int do_lseek(int fd, off_t offset, int whence)
174{
175 int ret;
176 do {
177 ret = lseek(fd, offset, whence);
178 } while (ret < 0 && errno == EINTR);
179
180 return ret;
181}
182
183/* EINTR-proof ftruncate - due to SIGALRM in use elsewhere */
184static int do_ftruncate(int fd, size_t size)
185{
186 int ret;
187 do {
188 ret = ftruncate(fd, size);
189 } while (ret < 0 && errno == EINTR);
190
191 return ret;
192}
193
Ot ten Thijec51182e2010-09-08 19:01:33 +0100194#define NAND_DEV_SAVE_DISK_BUF_SIZE 2048
195
196
197/**
198 * Copies the current contents of a disk image into the snapshot file.
199 *
200 * TODO optimize this using some kind of copy-on-write mechanism for
201 * unchanged disk sections.
202 */
203static void nand_dev_save_disk_state(QEMUFile *f, nand_dev *dev)
204{
205 int buf_size = NAND_DEV_SAVE_DISK_BUF_SIZE;
206 uint8_t buffer[NAND_DEV_SAVE_DISK_BUF_SIZE] = {0};
207 int ret;
208 uint64_t total_copied = 0;
209
Tim Baverstocka658bc82010-10-20 14:30:05 +0100210 /* Size of file to restore, hence size of data block following.
211 * TODO Work out whether to use lseek64 here. */
212
213 ret = do_lseek(dev->fd, 0, SEEK_END);
214 if (ret < 0) {
215 XLOG("%s EOF seek failed: %s\n", __FUNCTION__, strerror(errno));
216 qemu_file_set_error(f);
217 return;
218 }
219 const uint64_t total_size = ret;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100220 qemu_put_be64(f, total_size);
221
222 /* copy all data from the stream to the stored image */
Tim Baverstocka658bc82010-10-20 14:30:05 +0100223 ret = do_lseek(dev->fd, 0, SEEK_SET);
Ot ten Thijec51182e2010-09-08 19:01:33 +0100224 if (ret < 0) {
225 XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno));
226 qemu_file_set_error(f);
227 return;
228 }
229 do {
230 ret = do_read(dev->fd, buffer, buf_size);
231 if (ret < 0) {
232 XLOG("%s read failed: %s\n", __FUNCTION__, strerror(errno));
233 qemu_file_set_error(f);
234 return;
235 }
236 qemu_put_buffer(f, buffer, ret);
237
238 total_copied += ret;
239 }
Tim Baverstocka658bc82010-10-20 14:30:05 +0100240 while (ret == buf_size && total_copied < dev->max_size);
Ot ten Thijec51182e2010-09-08 19:01:33 +0100241
Tim Baverstocka658bc82010-10-20 14:30:05 +0100242 /* TODO Maybe check that we've written total_size bytes */
Ot ten Thijec51182e2010-09-08 19:01:33 +0100243}
244
245
246/**
247 * Saves the state of all disks managed by this controller to a snapshot file.
248 */
249static void nand_dev_save_disks(QEMUFile *f)
250{
251 int i;
252 for (i = 0; i < nand_dev_count; i++) {
253 nand_dev_save_disk_state(f, nand_devs + i);
254 }
255}
256
257/**
258 * Overwrites the contents of the disk image managed by this device with the
259 * contents as they were at the point the snapshot was made.
260 */
261static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev)
262{
263 int buf_size = NAND_DEV_SAVE_DISK_BUF_SIZE;
264 uint8_t buffer[NAND_DEV_SAVE_DISK_BUF_SIZE] = {0};
265 int ret;
266
Tim Baverstocka658bc82010-10-20 14:30:05 +0100267 /* File size for restore and truncate */
Ot ten Thijec51182e2010-09-08 19:01:33 +0100268 uint64_t total_size = qemu_get_be64(f);
269 if (total_size > dev->max_size) {
270 XLOG("%s, restore failed: size required (%lld) exceeds device limit (%lld)\n",
271 __FUNCTION__, total_size, dev->max_size);
272 return -EIO;
273 }
274
275 /* overwrite disk contents with snapshot contents */
276 uint64_t next_offset = 0;
Tim Baverstocka658bc82010-10-20 14:30:05 +0100277 ret = do_lseek(dev->fd, 0, SEEK_SET);
Ot ten Thijec51182e2010-09-08 19:01:33 +0100278 if (ret < 0) {
279 XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno));
280 return -EIO;
281 }
282 while (next_offset < total_size) {
283 /* snapshot buffer may not be an exact multiple of buf_size
284 * if necessary, adjust buffer size for last copy operation */
285 if (total_size - next_offset < buf_size) {
286 buf_size = total_size - next_offset;
287 }
288
289 ret = qemu_get_buffer(f, buffer, buf_size);
290 if (ret != buf_size) {
291 XLOG("%s read failed: expected %d bytes but got %d\n",
292 __FUNCTION__, buf_size, ret);
293 return -EIO;
294 }
295 ret = do_write(dev->fd, buffer, buf_size);
296 if (ret != buf_size) {
297 XLOG("%s, write failed: %s\n", __FUNCTION__, strerror(errno));
298 return -EIO;
299 }
300
301 next_offset += buf_size;
302 }
303
Tim Baverstocka658bc82010-10-20 14:30:05 +0100304 ret = do_ftruncate(dev->fd, total_size);
305 if (ret < 0) {
306 XLOG("%s ftruncate failed: %s\n", __FUNCTION__, strerror(errno));
307 return -EIO;
308 }
309
Ot ten Thijec51182e2010-09-08 19:01:33 +0100310 return 0;
311}
312
313/**
314 * Restores the state of all disks managed by this driver from a snapshot file.
315 */
316static int nand_dev_load_disks(QEMUFile *f)
317{
318 int i, ret;
319 for (i = 0; i < nand_dev_count; i++) {
320 ret = nand_dev_load_disk_state(f, nand_devs + i);
321 if (ret)
322 return ret; // abort on error
323 }
324
325 return 0;
326}
327
328static void nand_dev_controller_state_save(QEMUFile *f, void *opaque)
329{
330 nand_dev_controller_state* s = opaque;
331
332 qemu_put_struct(f, nand_dev_controller_state_fields, s);
333
334 /* The guest will continue writing to the disk image after the state has
335 * been saved. To guarantee that the state is identical after resume, save
336 * a copy of the current disk state in the snapshot.
337 */
338 nand_dev_save_disks(f);
339}
340
341static int nand_dev_controller_state_load(QEMUFile *f, void *opaque, int version_id)
342{
343 nand_dev_controller_state* s = opaque;
344 int ret;
345
346 if (version_id != NAND_DEV_STATE_SAVE_VERSION)
347 return -1;
348
349 if ((ret = qemu_get_struct(f, nand_dev_controller_state_fields, s)))
350 return ret;
351 if ((ret = nand_dev_load_disks(f)))
352 return ret;
353
354 return 0;
355}
356
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800357static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
358{
359 uint32_t len = total_len;
360 size_t read_len = dev->erase_size;
361 int eof = 0;
362
363 NAND_UPDATE_READ_THRESHOLD(total_len);
364
Tim Baverstocka658bc82010-10-20 14:30:05 +0100365 do_lseek(dev->fd, addr, SEEK_SET);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800366 while(len > 0) {
367 if(read_len < dev->erase_size) {
368 memset(dev->data, 0xff, dev->erase_size);
369 read_len = dev->erase_size;
370 eof = 1;
371 }
372 if(len < read_len)
373 read_len = len;
374 if(!eof) {
375 read_len = do_read(dev->fd, dev->data, read_len);
376 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700377 cpu_memory_rw_debug(cpu_single_env, data, dev->data, read_len, 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800378 data += read_len;
379 len -= read_len;
380 }
381 return total_len;
382}
383
384static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
385{
386 uint32_t len = total_len;
387 size_t write_len = dev->erase_size;
388 int ret;
389
390 NAND_UPDATE_WRITE_THRESHOLD(total_len);
391
Tim Baverstocka658bc82010-10-20 14:30:05 +0100392 do_lseek(dev->fd, addr, SEEK_SET);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800393 while(len > 0) {
394 if(len < write_len)
395 write_len = len;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700396 cpu_memory_rw_debug(cpu_single_env, data, dev->data, write_len, 0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800397 ret = do_write(dev->fd, dev->data, write_len);
398 if(ret < write_len) {
399 XLOG("nand_dev_write_file, write failed: %s\n", strerror(errno));
400 break;
401 }
402 data += write_len;
403 len -= write_len;
404 }
405 return total_len - len;
406}
407
408static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total_len)
409{
410 uint32_t len = total_len;
411 size_t write_len = dev->erase_size;
412 int ret;
413
Tim Baverstocka658bc82010-10-20 14:30:05 +0100414 do_lseek(dev->fd, addr, SEEK_SET);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800415 memset(dev->data, 0xff, dev->erase_size);
416 while(len > 0) {
417 if(len < write_len)
418 write_len = len;
419 ret = do_write(dev->fd, dev->data, write_len);
420 if(ret < write_len) {
421 XLOG( "nand_dev_write_file, write failed: %s\n", strerror(errno));
422 break;
423 }
424 len -= write_len;
425 }
426 return total_len - len;
427}
428
429/* this is a huge hack required to make the PowerPC emulator binary usable
430 * on Mac OS X. If you define this function as 'static', the emulated kernel
431 * will panic when attempting to mount the /data partition.
432 *
433 * worse, if you do *not* define the function as static on Linux-x86, the
434 * emulated kernel will also panic !?
435 *
436 * I still wonder if this is a compiler bug, or due to some nasty thing the
437 * emulator does with CPU registers during execution of the translated code.
438 */
439#if !(defined __APPLE__ && defined __powerpc__)
440static
441#endif
Ot ten Thijec51182e2010-09-08 19:01:33 +0100442uint32_t nand_dev_do_cmd(nand_dev_controller_state *s, uint32_t cmd)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800443{
444 uint32_t size;
445 uint64_t addr;
446 nand_dev *dev;
447
448 addr = s->addr_low | ((uint64_t)s->addr_high << 32);
449 size = s->transfer_size;
450 if(s->dev >= nand_dev_count)
451 return 0;
452 dev = nand_devs + s->dev;
453
454 switch(cmd) {
455 case NAND_CMD_GET_DEV_NAME:
456 if(size > dev->devname_len)
457 size = dev->devname_len;
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200458 cpu_memory_rw_debug(cpu_single_env, s->data, (uint8_t*)dev->devname, size, 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800459 return size;
460 case NAND_CMD_READ:
Ot ten Thijec51182e2010-09-08 19:01:33 +0100461 if(addr >= dev->max_size)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800462 return 0;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100463 if(size > dev->max_size - addr)
464 size = dev->max_size - addr;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800465 if(dev->fd >= 0)
466 return nand_dev_read_file(dev, s->data, addr, size);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700467 cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800468 return size;
469 case NAND_CMD_WRITE:
470 if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
471 return 0;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100472 if(addr >= dev->max_size)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800473 return 0;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100474 if(size > dev->max_size - addr)
475 size = dev->max_size - addr;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800476 if(dev->fd >= 0)
477 return nand_dev_write_file(dev, s->data, addr, size);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700478 cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800479 return size;
480 case NAND_CMD_ERASE:
481 if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
482 return 0;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100483 if(addr >= dev->max_size)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800484 return 0;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100485 if(size > dev->max_size - addr)
486 size = dev->max_size - addr;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800487 if(dev->fd >= 0)
488 return nand_dev_erase_file(dev, addr, size);
489 memset(&dev->data[addr], 0xff, size);
490 return size;
491 case NAND_CMD_BLOCK_BAD_GET: // no bad block support
492 return 0;
493 case NAND_CMD_BLOCK_BAD_SET:
494 if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
495 return 0;
496 return 0;
497 default:
498 cpu_abort(cpu_single_env, "nand_dev_do_cmd: Bad command %x\n", cmd);
499 return 0;
500 }
501}
502
503/* I/O write */
504static void nand_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
505{
Ot ten Thijec51182e2010-09-08 19:01:33 +0100506 nand_dev_controller_state *s = (nand_dev_controller_state *)opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800507
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800508 switch (offset) {
509 case NAND_DEV:
510 s->dev = value;
511 if(s->dev >= nand_dev_count) {
512 cpu_abort(cpu_single_env, "nand_dev_write: Bad dev %x\n", value);
513 }
514 break;
515 case NAND_ADDR_HIGH:
516 s->addr_high = value;
517 break;
518 case NAND_ADDR_LOW:
519 s->addr_low = value;
520 break;
521 case NAND_TRANSFER_SIZE:
522 s->transfer_size = value;
523 break;
524 case NAND_DATA:
525 s->data = value;
526 break;
527 case NAND_COMMAND:
528 s->result = nand_dev_do_cmd(s, value);
529 break;
530 default:
531 cpu_abort(cpu_single_env, "nand_dev_write: Bad offset %x\n", offset);
532 break;
533 }
534}
535
536/* I/O read */
537static uint32_t nand_dev_read(void *opaque, target_phys_addr_t offset)
538{
Ot ten Thijec51182e2010-09-08 19:01:33 +0100539 nand_dev_controller_state *s = (nand_dev_controller_state *)opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800540 nand_dev *dev;
541
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800542 switch (offset) {
543 case NAND_VERSION:
544 return NAND_VERSION_CURRENT;
545 case NAND_NUM_DEV:
546 return nand_dev_count;
547 case NAND_RESULT:
548 return s->result;
549 }
550
551 if(s->dev >= nand_dev_count)
552 return 0;
553
554 dev = nand_devs + s->dev;
555
556 switch (offset) {
557 case NAND_DEV_FLAGS:
558 return dev->flags;
559
560 case NAND_DEV_NAME_LEN:
561 return dev->devname_len;
562
563 case NAND_DEV_PAGE_SIZE:
564 return dev->page_size;
565
566 case NAND_DEV_EXTRA_SIZE:
567 return dev->extra_size;
568
569 case NAND_DEV_ERASE_SIZE:
570 return dev->erase_size;
571
572 case NAND_DEV_SIZE_LOW:
Ot ten Thijec51182e2010-09-08 19:01:33 +0100573 return (uint32_t)dev->max_size;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800574
575 case NAND_DEV_SIZE_HIGH:
Ot ten Thijec51182e2010-09-08 19:01:33 +0100576 return (uint32_t)(dev->max_size >> 32);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800577
578 default:
579 cpu_abort(cpu_single_env, "nand_dev_read: Bad offset %x\n", offset);
580 return 0;
581 }
582}
583
584static CPUReadMemoryFunc *nand_dev_readfn[] = {
585 nand_dev_read,
586 nand_dev_read,
587 nand_dev_read
588};
589
590static CPUWriteMemoryFunc *nand_dev_writefn[] = {
591 nand_dev_write,
592 nand_dev_write,
593 nand_dev_write
594};
595
596/* initialize the QFB device */
597void nand_dev_init(uint32_t base)
598{
599 int iomemtype;
600 static int instance_id = 0;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100601 nand_dev_controller_state *s;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800602
Ot ten Thijec51182e2010-09-08 19:01:33 +0100603 s = (nand_dev_controller_state *)qemu_mallocz(sizeof(nand_dev_controller_state));
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700604 iomemtype = cpu_register_io_memory(nand_dev_readfn, nand_dev_writefn, s);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800605 cpu_register_physical_memory(base, 0x00000fff, iomemtype);
606 s->base = base;
607
608 register_savevm( "nand_dev", instance_id++, NAND_DEV_STATE_SAVE_VERSION,
Ot ten Thijec51182e2010-09-08 19:01:33 +0100609 nand_dev_controller_state_save, nand_dev_controller_state_load, s);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800610}
611
612static int arg_match(const char *a, const char *b, size_t b_len)
613{
614 while(*a && b_len--) {
615 if(*a++ != *b++)
616 return 0;
617 }
618 return b_len == 0;
619}
620
621void nand_add_dev(const char *arg)
622{
623 uint64_t dev_size = 0;
624 const char *next_arg;
625 const char *value;
626 size_t arg_len, value_len;
627 nand_dev *new_devs, *dev;
628 char *devname = NULL;
629 size_t devname_len = 0;
630 char *initfilename = NULL;
631 char *rwfilename = NULL;
632 int initfd = -1;
633 int rwfd = -1;
634 int read_only = 0;
635 int pad;
636 ssize_t read_size;
637 uint32_t page_size = 2048;
638 uint32_t extra_size = 64;
639 uint32_t erase_pages = 64;
640
David 'Digit' Turnerc480cca2011-02-25 16:43:34 +0100641 VERBOSE_PRINT(init, "%s: %s", __FUNCTION__, arg);
642
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800643 while(arg) {
644 next_arg = strchr(arg, ',');
645 value = strchr(arg, '=');
646 if(next_arg != NULL) {
647 arg_len = next_arg - arg;
648 next_arg++;
649 if(value >= next_arg)
650 value = NULL;
651 }
652 else
653 arg_len = strlen(arg);
654 if(value != NULL) {
655 size_t new_arg_len = value - arg;
656 value_len = arg_len - new_arg_len - 1;
657 arg_len = new_arg_len;
658 value++;
659 }
660 else
661 value_len = 0;
662
663 if(devname == NULL) {
664 if(value != NULL)
665 goto bad_arg_and_value;
666 devname_len = arg_len;
667 devname = malloc(arg_len);
668 if(devname == NULL)
669 goto out_of_memory;
670 memcpy(devname, arg, arg_len);
671 }
672 else if(value == NULL) {
673 if(arg_match("readonly", arg, arg_len)) {
674 read_only = 1;
675 }
676 else {
677 XLOG("bad arg: %.*s\n", arg_len, arg);
678 exit(1);
679 }
680 }
681 else {
682 if(arg_match("size", arg, arg_len)) {
683 char *ep;
684 dev_size = strtoull(value, &ep, 0);
685 if(ep != value + value_len)
686 goto bad_arg_and_value;
687 }
688 else if(arg_match("pagesize", arg, arg_len)) {
689 char *ep;
690 page_size = strtoul(value, &ep, 0);
691 if(ep != value + value_len)
692 goto bad_arg_and_value;
693 }
694 else if(arg_match("extrasize", arg, arg_len)) {
695 char *ep;
696 extra_size = strtoul(value, &ep, 0);
697 if(ep != value + value_len)
698 goto bad_arg_and_value;
699 }
700 else if(arg_match("erasepages", arg, arg_len)) {
701 char *ep;
702 erase_pages = strtoul(value, &ep, 0);
703 if(ep != value + value_len)
704 goto bad_arg_and_value;
705 }
706 else if(arg_match("initfile", arg, arg_len)) {
707 initfilename = malloc(value_len + 1);
708 if(initfilename == NULL)
709 goto out_of_memory;
710 memcpy(initfilename, value, value_len);
711 initfilename[value_len] = '\0';
712 }
713 else if(arg_match("file", arg, arg_len)) {
714 rwfilename = malloc(value_len + 1);
715 if(rwfilename == NULL)
716 goto out_of_memory;
717 memcpy(rwfilename, value, value_len);
718 rwfilename[value_len] = '\0';
719 }
720 else {
721 goto bad_arg_and_value;
722 }
723 }
724
725 arg = next_arg;
726 }
727
728 if (rwfilename == NULL) {
729 /* we create a temporary file to store everything */
730 TempFile* tmp = tempfile_create();
731
732 if (tmp == NULL) {
Raphael2779bee2010-11-01 15:29:34 -0700733 XLOG("could not create temp file for %.*s NAND disk image: %s\n",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800734 devname_len, devname, strerror(errno));
735 exit(1);
736 }
737 rwfilename = (char*) tempfile_path(tmp);
738 if (VERBOSE_CHECK(init))
739 dprint( "mapping '%.*s' NAND image to %s", devname_len, devname, rwfilename);
740 }
741
742 if(rwfilename) {
743 rwfd = open(rwfilename, O_BINARY | (read_only ? O_RDONLY : O_RDWR));
744 if(rwfd < 0 && read_only) {
745 XLOG("could not open file %s, %s\n", rwfilename, strerror(errno));
746 exit(1);
747 }
748 /* this could be a writable temporary file. use atexit_close_fd to ensure
749 * that it is properly cleaned up at exit on Win32
750 */
751 if (!read_only)
752 atexit_close_fd(rwfd);
753 }
754
755 if(initfilename) {
756 initfd = open(initfilename, O_BINARY | O_RDONLY);
757 if(initfd < 0) {
758 XLOG("could not open file %s, %s\n", initfilename, strerror(errno));
759 exit(1);
760 }
761 if(dev_size == 0) {
Tim Baverstocka658bc82010-10-20 14:30:05 +0100762 dev_size = do_lseek(initfd, 0, SEEK_END);
763 do_lseek(initfd, 0, SEEK_SET);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800764 }
765 }
766
767 new_devs = realloc(nand_devs, sizeof(nand_devs[0]) * (nand_dev_count + 1));
768 if(new_devs == NULL)
769 goto out_of_memory;
770 nand_devs = new_devs;
771 dev = &new_devs[nand_dev_count];
772
773 dev->page_size = page_size;
774 dev->extra_size = extra_size;
775 dev->erase_size = erase_pages * (page_size + extra_size);
776 pad = dev_size % dev->erase_size;
777 if (pad != 0) {
778 dev_size += (dev->erase_size - pad);
Xavier Ducrohetacbee352009-10-01 13:43:13 -0700779 D("rounding devsize up to a full eraseunit, now %llx\n", dev_size);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800780 }
781 dev->devname = devname;
782 dev->devname_len = devname_len;
Ot ten Thijec51182e2010-09-08 19:01:33 +0100783 dev->max_size = dev_size;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800784 dev->data = malloc(dev->erase_size);
785 if(dev->data == NULL)
786 goto out_of_memory;
787 dev->flags = read_only ? NAND_DEV_FLAG_READ_ONLY : 0;
788
789 if (initfd >= 0) {
790 do {
791 read_size = do_read(initfd, dev->data, dev->erase_size);
792 if(read_size < 0) {
793 XLOG("could not read file %s, %s\n", initfilename, strerror(errno));
794 exit(1);
795 }
796 if(do_write(rwfd, dev->data, read_size) != read_size) {
797 XLOG("could not write file %s, %s\n", initfilename, strerror(errno));
798 exit(1);
799 }
800 } while(read_size == dev->erase_size);
801 close(initfd);
802 }
803 dev->fd = rwfd;
804
805 nand_dev_count++;
806
807 return;
808
809out_of_memory:
810 XLOG("out of memory\n");
811 exit(1);
812
813bad_arg_and_value:
814 XLOG("bad arg: %.*s=%.*s\n", arg_len, arg, value_len, value);
815 exit(1);
816}
817
Vladimir Chtchetkinee1316862010-08-26 09:03:54 -0700818#ifdef CONFIG_NAND_LIMITS
819
820static uint64_t
821parse_nand_rw_limit( const char* value )
822{
823 char* end;
824 uint64_t val = strtoul( value, &end, 0 );
825
826 if (end == value) {
827 derror( "bad parameter value '%s': expecting unsigned integer", value );
828 exit(1);
829 }
830
831 switch (end[0]) {
832 case 'K': val <<= 10; break;
833 case 'M': val <<= 20; break;
834 case 'G': val <<= 30; break;
835 case 0: break;
836 default:
837 derror( "bad read/write limit suffix: use K, M or G" );
838 exit(1);
839 }
840 return val;
841}
842
843void
844parse_nand_limits(char* limits)
845{
846 int pid = -1, signal = -1;
847 int64_t reads = 0, writes = 0;
848 char* item = limits;
849
850 /* parse over comma-separated items */
851 while (item && *item) {
852 char* next = strchr(item, ',');
853 char* end;
854
855 if (next == NULL) {
856 next = item + strlen(item);
857 } else {
858 *next++ = 0;
859 }
860
861 if ( !memcmp(item, "pid=", 4) ) {
862 pid = strtol(item+4, &end, 10);
863 if (end == NULL || *end) {
864 derror( "bad parameter, expecting pid=<number>, got '%s'",
865 item );
866 exit(1);
867 }
868 if (pid <= 0) {
869 derror( "bad parameter: process identifier must be > 0" );
870 exit(1);
871 }
872 }
873 else if ( !memcmp(item, "signal=", 7) ) {
874 signal = strtol(item+7,&end, 10);
875 if (end == NULL || *end) {
876 derror( "bad parameter: expecting signal=<number>, got '%s'",
877 item );
878 exit(1);
879 }
880 if (signal <= 0) {
881 derror( "bad parameter: signal number must be > 0" );
882 exit(1);
883 }
884 }
885 else if ( !memcmp(item, "reads=", 6) ) {
886 reads = parse_nand_rw_limit(item+6);
887 }
888 else if ( !memcmp(item, "writes=", 7) ) {
889 writes = parse_nand_rw_limit(item+7);
890 }
891 else {
892 derror( "bad parameter '%s' (see -help-nand-limits)", item );
893 exit(1);
894 }
895 item = next;
896 }
897 if (pid < 0) {
898 derror( "bad paramater: missing pid=<number>" );
899 exit(1);
900 }
901 else if (signal < 0) {
902 derror( "bad parameter: missing signal=<number>" );
903 exit(1);
904 }
905 else if (reads == 0 && writes == 0) {
906 dwarning( "no read or write limit specified. ignoring -nand-limits" );
907 } else {
908 nand_threshold* t;
909
910 t = &android_nand_read_threshold;
911 t->pid = pid;
912 t->signal = signal;
913 t->counter = 0;
914 t->limit = reads;
915
916 t = &android_nand_write_threshold;
917 t->pid = pid;
918 t->signal = signal;
919 t->counter = 0;
920 t->limit = writes;
921 }
922}
923#endif /* CONFIG_NAND_LIMITS */