diff options
-rw-r--r-- | hw/goldfish_nand.c | 86 |
1 files changed, 53 insertions, 33 deletions
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c index af921b7..2aff835 100644 --- a/hw/goldfish_nand.c +++ b/hw/goldfish_nand.c @@ -132,8 +132,9 @@ typedef struct { /* update this everytime you change the nand_dev_controller_state structure * 1: initial version, saving only nand_dev_controller_state fields * 2: saving actual disk contents as well + * 3: use the correct data length and truncate to avoid padding. */ -#define NAND_DEV_STATE_SAVE_VERSION 2 +#define NAND_DEV_STATE_SAVE_VERSION 3 #define QFIELD_STRUCT nand_dev_controller_state QFIELD_BEGIN(nand_dev_controller_state_fields) @@ -146,6 +147,7 @@ QFIELD_BEGIN(nand_dev_controller_state_fields) QFIELD_END +/* EINTR-proof read - due to SIGALRM in use elsewhere */ static int do_read(int fd, void* buf, size_t size) { int ret; @@ -156,6 +158,7 @@ static int do_read(int fd, void* buf, size_t size) return ret; } +/* EINTR-proof write - due to SIGALRM in use elsewhere */ static int do_write(int fd, const void* buf, size_t size) { int ret; @@ -166,6 +169,28 @@ static int do_write(int fd, const void* buf, size_t size) return ret; } +/* EINTR-proof lseek - due to SIGALRM in use elsewhere */ +static int do_lseek(int fd, off_t offset, int whence) +{ + int ret; + do { + ret = lseek(fd, offset, whence); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +/* EINTR-proof ftruncate - due to SIGALRM in use elsewhere */ +static int do_ftruncate(int fd, size_t size) +{ + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + + return ret; +} + #define NAND_DEV_SAVE_DISK_BUF_SIZE 2048 @@ -182,14 +207,20 @@ static void nand_dev_save_disk_state(QEMUFile *f, nand_dev *dev) int ret; uint64_t total_copied = 0; - /* Put the size up front, since otherwise we don't know how much to read - * when restoring. - */ - const uint64_t total_size = dev->max_size; + /* Size of file to restore, hence size of data block following. + * TODO Work out whether to use lseek64 here. */ + + ret = do_lseek(dev->fd, 0, SEEK_END); + if (ret < 0) { + XLOG("%s EOF seek failed: %s\n", __FUNCTION__, strerror(errno)); + qemu_file_set_error(f); + return; + } + const uint64_t total_size = ret; qemu_put_be64(f, total_size); /* copy all data from the stream to the stored image */ - ret = lseek(dev->fd, 0, SEEK_SET); + ret = do_lseek(dev->fd, 0, SEEK_SET); if (ret < 0) { XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno)); qemu_file_set_error(f); @@ -206,24 +237,9 @@ static void nand_dev_save_disk_state(QEMUFile *f, nand_dev *dev) total_copied += ret; } - while (ret == buf_size && total_copied < total_size); - - /* The file may be smaller than the device size. Pad with 0xff (pattern for - * erased data) until we have filled the snapshot buffer we declared earlier. - * TODO avoid padding. Unfortunately, attempts to use the actual size of the - * underlying image instead result in broken restores. This also happens - * when limited padding is inserted so the image size is a multiple of - * page_size or erase_size. - */ - memset(buffer, 0xff, NAND_DEV_SAVE_DISK_BUF_SIZE); - while (total_copied < total_size) { - /* adjust buffer size for last part of the image */ - if (total_size - total_copied < buf_size) { - buf_size = total_size - total_copied; - } - qemu_put_buffer(f, buffer, buf_size); - total_copied += buf_size; - } + while (ret == buf_size && total_copied < dev->max_size); + + /* TODO Maybe check that we've written total_size bytes */ } @@ -248,7 +264,7 @@ static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev) uint8_t buffer[NAND_DEV_SAVE_DISK_BUF_SIZE] = {0}; int ret; - /* number of bytes to restore */ + /* File size for restore and truncate */ uint64_t total_size = qemu_get_be64(f); if (total_size > dev->max_size) { XLOG("%s, restore failed: size required (%lld) exceeds device limit (%lld)\n", @@ -258,9 +274,7 @@ static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev) /* overwrite disk contents with snapshot contents */ uint64_t next_offset = 0; - do { - ret = lseek(dev->fd, 0, SEEK_SET); - } while (ret < 0 && errno == EINTR); + ret = do_lseek(dev->fd, 0, SEEK_SET); if (ret < 0) { XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno)); return -EIO; @@ -287,6 +301,12 @@ static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev) next_offset += buf_size; } + ret = do_ftruncate(dev->fd, total_size); + if (ret < 0) { + XLOG("%s ftruncate failed: %s\n", __FUNCTION__, strerror(errno)); + return -EIO; + } + return 0; } @@ -342,7 +362,7 @@ static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr, NAND_UPDATE_READ_THRESHOLD(total_len); - lseek(dev->fd, addr, SEEK_SET); + do_lseek(dev->fd, addr, SEEK_SET); while(len > 0) { if(read_len < dev->erase_size) { memset(dev->data, 0xff, dev->erase_size); @@ -369,7 +389,7 @@ static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr, NAND_UPDATE_WRITE_THRESHOLD(total_len); - lseek(dev->fd, addr, SEEK_SET); + do_lseek(dev->fd, addr, SEEK_SET); while(len > 0) { if(len < write_len) write_len = len; @@ -391,7 +411,7 @@ static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total size_t write_len = dev->erase_size; int ret; - lseek(dev->fd, addr, SEEK_SET); + do_lseek(dev->fd, addr, SEEK_SET); memset(dev->data, 0xff, dev->erase_size); while(len > 0) { if(len < write_len) @@ -737,8 +757,8 @@ void nand_add_dev(const char *arg) exit(1); } if(dev_size == 0) { - dev_size = lseek(initfd, 0, SEEK_END); - lseek(initfd, 0, SEEK_SET); + dev_size = do_lseek(initfd, 0, SEEK_END); + do_lseek(initfd, 0, SEEK_SET); } } |