pflash: Require backend size to match device, improve errors

We reject undersized backends with a rather enigmatic "failed to read
the initial flash content" error.  For instance:

    $ qemu-system-ppc64 -S -display none -M sam460ex -drive if=pflash,format=raw,file=eins.img
    qemu-system-ppc64: Initialization of device cfi.pflash02 failed: failed to read the initial flash content

We happily accept oversized images, ignoring their tail.  Throwing
away parts of firmware that way is pretty much certain to end in an
even more enigmatic failure to boot.

Require the backend's size to match the device's size exactly.  Report
mismatch like this:

    qemu-system-ppc64: Initialization of device cfi.pflash01 failed: device requires 1048576 bytes, block backend provides 512 bytes

Improve the error for actual read failures to "can't read block
backend".

To avoid duplicating even more code between the two pflash device
models, do all that in new helper blk_check_size_and_read_all().

The error reporting can still be confusing.  For instance:

    qemu-system-ppc64 -S -display none -M taihu -drive if=pflash,format=raw,file=eins.img  -drive if=pflash,unit=1,format=raw,file=zwei.img
    qemu-system-ppc64: Initialization of device cfi.pflash02 failed: device requires 2097152 bytes, block backend provides 512 bytes

Leaves the user guessing which of the two -drive is wrong.  Mention
the issue in a TODO comment.

Suggested-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20190319163551.32499-2-armbru@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
This commit is contained in:
Markus Armbruster 2019-03-19 17:35:50 +01:00
parent d132baa05e
commit 06f1521795
4 changed files with 59 additions and 11 deletions

View File

@ -13,7 +13,53 @@
#include "hw/block/block.h" #include "hw/block/block.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qapi-types-block.h" #include "qapi/qapi-types-block.h"
#include "qemu/error-report.h"
/*
* Read the entire contents of @blk into @buf.
* @blk's contents must be @size bytes, and @size must be at most
* BDRV_REQUEST_MAX_BYTES.
* On success, return true.
* On failure, store an error through @errp and return false.
* Note that the error messages do not identify the block backend.
* TODO Since callers don't either, this can result in confusing
* errors.
* This function not intended for actual block devices, which read on
* demand. It's for things like memory devices that (ab)use a block
* backend to provide persistence.
*/
bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size,
Error **errp)
{
int64_t blk_len;
int ret;
blk_len = blk_getlength(blk);
if (blk_len < 0) {
error_setg_errno(errp, -blk_len,
"can't get size of block backend");
return false;
}
if (blk_len != size) {
error_setg(errp, "device requires %" HWADDR_PRIu " bytes, "
"block backend provides %" PRIu64 " bytes",
size, blk_len);
return false;
}
/*
* We could loop for @size > BDRV_REQUEST_MAX_BYTES, but if we
* ever get to the point we want to read *gigabytes* here, we
* should probably rework the device to be more like an actual
* block device and read only on demand.
*/
assert(size <= BDRV_REQUEST_MAX_BYTES);
ret = blk_pread(blk, 0, buf, size);
if (ret < 0) {
error_setg_errno(errp, -ret, "can't read block backend");
return false;
}
return true;
}
void blkconf_blocksizes(BlockConf *conf) void blkconf_blocksizes(BlockConf *conf)
{ {

View File

@ -38,6 +38,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/block/block.h"
#include "hw/block/flash.h" #include "hw/block/flash.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qapi/error.h" #include "qapi/error.h"
@ -763,12 +764,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
} }
if (pfl->blk) { if (pfl->blk) {
/* read the initial flash content */ if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, total_len,
ret = blk_pread(pfl->blk, 0, pfl->storage, total_len); errp)) {
if (ret < 0) {
vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); vmstate_unregister_ram(&pfl->mem, DEVICE(pfl));
error_setg(errp, "failed to read the initial flash content");
return; return;
} }
} }

View File

@ -37,6 +37,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/block/block.h"
#include "hw/block/flash.h" #include "hw/block/flash.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/timer.h" #include "qemu/timer.h"
@ -581,11 +582,9 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
} }
if (pfl->blk) { if (pfl->blk) {
/* read the initial flash content */ if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, chip_len,
ret = blk_pread(pfl->blk, 0, pfl->storage, chip_len); errp)) {
if (ret < 0) {
vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl)); vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
error_setg(errp, "failed to read the initial flash content");
return; return;
} }
} }

View File

@ -11,7 +11,7 @@
#ifndef HW_BLOCK_H #ifndef HW_BLOCK_H
#define HW_BLOCK_H #define HW_BLOCK_H
#include "qemu-common.h" #include "exec/hwaddr.h"
#include "qapi/qapi-types-block-core.h" #include "qapi/qapi-types-block-core.h"
/* Configuration */ /* Configuration */
@ -70,6 +70,11 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \ DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \
BLOCKDEV_ON_ERROR_AUTO) BLOCKDEV_ON_ERROR_AUTO)
/* Backend access helpers */
bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size,
Error **errp);
/* Configuration helpers */ /* Configuration helpers */
bool blkconf_geometry(BlockConf *conf, int *trans, bool blkconf_geometry(BlockConf *conf, int *trans,