On Sat, 22 Aug 2026 at 19:39, Om Barkare <[email protected]> wrote:
>
> From: OmBarkare <[email protected]>
>
> Device did not have migration support, which would result in its
> internal state being lost during migration or saves.
>
> Add VMStateDescription to serialize state and set memory regions
> romd_mode to rom_mode field in post load hook
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4157
>
> Signed-off-by: Om Barkare <[email protected]>
> ---
>  hw/block/pflash_cfi02.c | 73 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 72 insertions(+), 1 deletion(-)

Thanks for this patch; I have a few comments below, but mostly
this looks good.

We could mention in the commit message that the only boards
using pflash_cfi02 are the sh4 r2d and the arm canon-a1100,
musicpal and xilinx-zynq-a9.

> diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
> index 6f952fe7de..82965e6cdc 100644
> --- a/hw/block/pflash_cfi02.c
> +++ b/hw/block/pflash_cfi02.c
> @@ -46,6 +46,7 @@
>  #include "qemu/module.h"
>  #include "hw/core/sysbus.h"
>  #include "migration/vmstate.h"
> +#include "system/runstate.h"
>  #include "trace.h"
>
>  #define PFLASH_LAZY_ROMD_THRESHOLD 42
> @@ -71,7 +72,7 @@ struct PFlashCFI02 {
>      BlockBackend *blk;
>      uint32_t uniform_nb_blocs;
>      uint32_t uniform_sector_len;
> -    uint32_t total_sectors;
> +    int32_t total_sectors;

I think it's worth mentioning in the commit message that we
have to change total_sectors from uint32_t to int32_t to
satisfy the VMSTATE_BITMAP macro, but that this is OK because
it's a value we calculate based on the size of the flash,
and it's never going to be large enough to overflow an int32_t.

>      uint32_t nb_blocs[PFLASH_MAX_ERASE_REGIONS];
>      uint32_t sector_len[PFLASH_MAX_ERASE_REGIONS];
>      uint32_t chip_len;
> @@ -107,6 +108,50 @@ struct PFlashCFI02 {
>      unsigned long *sector_erase_map;
>      char *name;
>      void *storage;
> +    VMChangeStateEntry *vmstate;
> +};
> +
> +static int pflash_post_load(void *opaque, int version_id);
> +
> +static bool pflash_sector_erase_needed(void *opaque)
> +{
> +    PFlashCFI02 *pfl = opaque;
> +
> +    return (pfl->sectors_to_erase > 0);
> +}
> +
> +static const VMStateDescription vmstate_pflash_sector_erase = {
> +    .name = "pflash_cfi02_erase_sector",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .needed = pflash_sector_erase_needed,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_INT32(sectors_to_erase, PFlashCFI02),
> +        VMSTATE_UINT64(erase_time_remaining, PFlashCFI02),
> +        VMSTATE_BITMAP(sector_erase_map, PFlashCFI02, 1, total_sectors),
> +        VMSTATE_END_OF_LIST()
> +    }
> +
> +};

Why did you choose to put this in a subsection ?

> +
> +static const VMStateDescription vmstate_pflash = {
> +    .name = "pflash_cfi02",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .post_load = pflash_post_load,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_INT32(wcycle, PFlashCFI02),
> +        VMSTATE_UINT8(cmd, PFlashCFI02),
> +        VMSTATE_UINT8(status, PFlashCFI02),
> +        VMSTATE_INT32(read_counter, PFlashCFI02),
> +        VMSTATE_BOOL(rom_mode, PFlashCFI02),
> +        VMSTATE_TIMER(timer, PFlashCFI02),

You've missed out "bypass", which is also state that the guest
can cause the device to change at runtime.

> +        VMSTATE_END_OF_LIST()
> +    },
> +    .subsections = (const VMStateDescription * const []) {
> +        &vmstate_pflash_sector_erase,
> +        NULL
> +    }
>  };

thanks
-- PMM

Reply via email to