Message ID | 20250206151214.2947842-5-peter.maydell@linaro.org |
---|---|
State | New |
Headers | show |
Series | hw: Centralize handling, improve error messages for -machine dumpdtb | expand |
Hi Peter, On 6/2/25 16:12, Peter Maydell wrote: > The boston machine doesn't set MachineState::fdt to the DTB blob that > it has loaded or created, which means that the QMP/HMP dumpdtb > monitor commands don't work. > > Setting MachineState::fdt is easy in the non-FIT codepath: we can > simply do so immediately before loading the DTB into guest memory. > The FIT codepath is a bit more awkward as currently the FIT loader > throws away the memory that the FDT was in after it loads it into > guest memory. So we add a void *pfdt argument to load_fit() for it > to store the FDT pointer into. > > There is some readjustment required of the pointer handling in > loader-fit.c, so that it applies 'const' only where it should (e.g. > the data pointer we get back from fdt_getprop() is const, because > it's into the middle of the input FDT data, but the pointer that > fit_load_image_alloc() should not be const, because it's freshly > allocated memory that the caller can change if it likes). > > Signed-off-by: Peter Maydell <peter.maydell@linaro.org> > --- > include/hw/loader-fit.h | 21 ++++++++++++++++++--- > hw/core/loader-fit.c | 38 +++++++++++++++++++++----------------- > hw/mips/boston.c | 11 +++++++---- > 3 files changed, 46 insertions(+), 24 deletions(-) > > diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h > index 0832e379dc9..9a43490ed63 100644 > --- a/include/hw/loader-fit.h > +++ b/include/hw/loader-fit.h > @@ -30,12 +30,27 @@ struct fit_loader_match { > struct fit_loader { > const struct fit_loader_match *matches; > hwaddr (*addr_to_phys)(void *opaque, uint64_t addr); > - const void *(*fdt_filter)(void *opaque, const void *fdt, > - const void *match_data, hwaddr *load_addr); > + void *(*fdt_filter)(void *opaque, const void *fdt, > + const void *match_data, hwaddr *load_addr); > const void *(*kernel_filter)(void *opaque, const void *kernel, > hwaddr *load_addr, hwaddr *entry_addr); > }; > > -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); > +/** > + * load_fit: load a FIT format image > + * @ldr: structure defining board specific properties and hooks > + * @filename: image to load > + * @pfdt: pointer to update with address of FDT blob It is not clear this field is optional or mandatory. Looking at other docstrings, optional is not always precised, and code often consider optional if not precised. Mandatory is mentioned explicitly. > + * @opaque: opaque value passed back to the hook functions in @ldr > + * Returns: 0 on success, or a negative errno on failure > + * > + * @pfdt is used to tell the caller about the FDT blob. On return, it > + * has been set to point to the FDT blob, and it is now the caller's > + * responsibility to free that memory with g_free(). Usually the caller > + * will want to pass in &machine->fdt here, to record the FDT blob for > + * the dumpdtb option and QMP/HMP commands. > + */ > +int load_fit(const struct fit_loader *ldr, const char *filename, void **pfdt, > + void *opaque); > static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, > int cfg, void *opaque, const void *match_data, > - hwaddr kernel_end, Error **errp) > + hwaddr kernel_end, void **pfdt, Error **errp) > { > ERRP_GUARD(); > Error *err = NULL; > const char *name; > - const void *data; > - const void *load_data; > + void *data; > hwaddr load_addr; > int img_off; > size_t sz; > @@ -194,7 +193,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, > return 0; > } > > - load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); > + data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); > if (!data) { > error_prepend(errp, "unable to load FDT image from FIT: "); > return -EINVAL; > @@ -211,19 +210,23 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, > } > > if (ldr->fdt_filter) { > - load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); > + void *filtered_data; > + > + filtered_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); > + if (filtered_data != data) { > + g_free(data); > + data = filtered_data; > + } > } > > load_addr = ldr->addr_to_phys(opaque, load_addr); > - sz = fdt_totalsize(load_data); > - rom_add_blob_fixed(name, load_data, sz, load_addr); > + sz = fdt_totalsize(data); > + rom_add_blob_fixed(name, data, sz, load_addr); > > - ret = 0; > + *pfdt = data; Here pfdt is assumed to be non-NULL, so a mandatory field. Could you update the documentation? Otherwise consider adding a non-NULL check. Either ways: Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> > + return 0; > out: > g_free((void *) data); > - if (data != load_data) { > - g_free((void *) load_data); > - } > return ret; > } > > @@ -259,7 +262,8 @@ out: > return ret; > }
On Mon, 10 Feb 2025 at 10:56, Philippe Mathieu-Daudé <philmd@linaro.org> wrote: > > Hi Peter, > > On 6/2/25 16:12, Peter Maydell wrote: > > -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); > > +/** > > + * load_fit: load a FIT format image > > + * @ldr: structure defining board specific properties and hooks > > + * @filename: image to load > > + * @pfdt: pointer to update with address of FDT blob > > It is not clear this field is optional or mandatory. > > Looking at other docstrings, optional is not always precised, > and code often consider optional if not precised. Mandatory is > mentioned explicitly. I did go back and forth while I was writing this on whether to make it optional or not (and the versions where it is optional, I had a note in the documentation about that). But there is exactly one caller of this function, and that callsite wants to pass a non-NULL pointer, so I ended up deciding that it was pointless to make the argument optional and include the code to handle "pfdt is NULL". If you get it wrong you'll find out very quickly because your code will segfault :-) Generally we (should) say arguments are optional when they're optional; in those cases we also should document what the behaviour when they're omitted is. So I think "mandatory" is the default. In this function, ldr and filename also are mandatory. If we mark every mandatory argument as "mandatory" then we will end up with a lot of boilerplate markup for most arguments, I think. > Could you update the documentation? Otherwise consider adding > a non-NULL check. > > Either ways: > Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> thanks -- PMM
On 10/2/25 12:09, Peter Maydell wrote: > On Mon, 10 Feb 2025 at 10:56, Philippe Mathieu-Daudé <philmd@linaro.org> wrote: >> >> Hi Peter, >> >> On 6/2/25 16:12, Peter Maydell wrote: >>> -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); >>> +/** >>> + * load_fit: load a FIT format image >>> + * @ldr: structure defining board specific properties and hooks >>> + * @filename: image to load >>> + * @pfdt: pointer to update with address of FDT blob >> >> It is not clear this field is optional or mandatory. >> >> Looking at other docstrings, optional is not always precised, >> and code often consider optional if not precised. Mandatory is >> mentioned explicitly. > > I did go back and forth while I was writing this on whether to > make it optional or not (and the versions where it is optional, > I had a note in the documentation about that). But there is exactly > one caller of this function, and that callsite wants to pass a > non-NULL pointer, so I ended up deciding that it was pointless > to make the argument optional and include the code to handle > "pfdt is NULL". > > If you get it wrong you'll find out very quickly because your > code will segfault :-) > > Generally we (should) say arguments are optional when they're > optional; in those cases we also should document what the behaviour > when they're omitted is. So I think "mandatory" is the default. > In this function, ldr and filename also are mandatory. If > we mark every mandatory argument as "mandatory" then we will > end up with a lot of boilerplate markup for most arguments, > I think. OK, fine as is then! > >> Could you update the documentation? Otherwise consider adding >> a non-NULL check. >> >> Either ways: >> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> > > thanks > -- PMM
diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h index 0832e379dc9..9a43490ed63 100644 --- a/include/hw/loader-fit.h +++ b/include/hw/loader-fit.h @@ -30,12 +30,27 @@ struct fit_loader_match { struct fit_loader { const struct fit_loader_match *matches; hwaddr (*addr_to_phys)(void *opaque, uint64_t addr); - const void *(*fdt_filter)(void *opaque, const void *fdt, - const void *match_data, hwaddr *load_addr); + void *(*fdt_filter)(void *opaque, const void *fdt, + const void *match_data, hwaddr *load_addr); const void *(*kernel_filter)(void *opaque, const void *kernel, hwaddr *load_addr, hwaddr *entry_addr); }; -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); +/** + * load_fit: load a FIT format image + * @ldr: structure defining board specific properties and hooks + * @filename: image to load + * @pfdt: pointer to update with address of FDT blob + * @opaque: opaque value passed back to the hook functions in @ldr + * Returns: 0 on success, or a negative errno on failure + * + * @pfdt is used to tell the caller about the FDT blob. On return, it + * has been set to point to the FDT blob, and it is now the caller's + * responsibility to free that memory with g_free(). Usually the caller + * will want to pass in &machine->fdt here, to record the FDT blob for + * the dumpdtb option and QMP/HMP commands. + */ +int load_fit(const struct fit_loader *ldr, const char *filename, void **pfdt, + void *opaque); #endif /* HW_LOADER_FIT_H */ diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 9bdd4fa17c6..6eb66406b07 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -32,8 +32,8 @@ #define FIT_LOADER_MAX_PATH (128) -static const void *fit_load_image_alloc(const void *itb, const char *name, - int *poff, size_t *psz, Error **errp) +static void *fit_load_image_alloc(const void *itb, const char *name, + int *poff, size_t *psz, Error **errp) { const void *data; const char *comp; @@ -80,11 +80,11 @@ static const void *fit_load_image_alloc(const void *itb, const char *name, return NULL; } - data = g_realloc(uncomp_data, uncomp_len); + uncomp_data = g_realloc(uncomp_data, uncomp_len); if (psz) { *psz = uncomp_len; } - return data; + return uncomp_data; } error_setg(errp, "unknown compression '%s'", comp); @@ -177,13 +177,12 @@ out: static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, int cfg, void *opaque, const void *match_data, - hwaddr kernel_end, Error **errp) + hwaddr kernel_end, void **pfdt, Error **errp) { ERRP_GUARD(); Error *err = NULL; const char *name; - const void *data; - const void *load_data; + void *data; hwaddr load_addr; int img_off; size_t sz; @@ -194,7 +193,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, return 0; } - load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); + data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); if (!data) { error_prepend(errp, "unable to load FDT image from FIT: "); return -EINVAL; @@ -211,19 +210,23 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, } if (ldr->fdt_filter) { - load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + void *filtered_data; + + filtered_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + if (filtered_data != data) { + g_free(data); + data = filtered_data; + } } load_addr = ldr->addr_to_phys(opaque, load_addr); - sz = fdt_totalsize(load_data); - rom_add_blob_fixed(name, load_data, sz, load_addr); + sz = fdt_totalsize(data); + rom_add_blob_fixed(name, data, sz, load_addr); - ret = 0; + *pfdt = data; + return 0; out: g_free((void *) data); - if (data != load_data) { - g_free((void *) load_data); - } return ret; } @@ -259,7 +262,8 @@ out: return ret; } -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) +int load_fit(const struct fit_loader *ldr, const char *filename, + void **pfdt, void *opaque) { Error *err = NULL; const struct fit_loader_match *match; @@ -323,7 +327,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) goto out; } - ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, + ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, pfdt, &err); if (ret) { error_report_err(err); diff --git a/hw/mips/boston.c b/hw/mips/boston.c index f0e9a2461a0..99e65f9fafb 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -358,8 +358,8 @@ static void gen_firmware(void *p, hwaddr kernel_entry, hwaddr fdt_addr) kernel_entry); } -static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, - const void *match_data, hwaddr *load_addr) +static void *boston_fdt_filter(void *opaque, const void *fdt_orig, + const void *match_data, hwaddr *load_addr) { BostonState *s = BOSTON(opaque); MachineState *machine = s->mach; @@ -797,7 +797,7 @@ static void boston_mach_init(MachineState *machine) if (kernel_size > 0) { int dt_size; g_autofree const void *dtb_file_data = NULL; - g_autofree const void *dtb_load_data = NULL; + void *dtb_load_data = NULL; hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); @@ -815,6 +815,8 @@ static void boston_mach_init(MachineState *machine) exit(1); } + machine->fdt = dtb_load_data; + /* Calculate real fdt size after filter */ dt_size = fdt_totalsize(dtb_load_data); rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); @@ -822,7 +824,8 @@ static void boston_mach_init(MachineState *machine) rom_ptr(dtb_paddr, dt_size)); } else { /* Try to load file as FIT */ - fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); + fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, + &machine->fdt, s); if (fit_err) { error_report("unable to load kernel image"); exit(1);
The boston machine doesn't set MachineState::fdt to the DTB blob that it has loaded or created, which means that the QMP/HMP dumpdtb monitor commands don't work. Setting MachineState::fdt is easy in the non-FIT codepath: we can simply do so immediately before loading the DTB into guest memory. The FIT codepath is a bit more awkward as currently the FIT loader throws away the memory that the FDT was in after it loads it into guest memory. So we add a void *pfdt argument to load_fit() for it to store the FDT pointer into. There is some readjustment required of the pointer handling in loader-fit.c, so that it applies 'const' only where it should (e.g. the data pointer we get back from fdt_getprop() is const, because it's into the middle of the input FDT data, but the pointer that fit_load_image_alloc() should not be const, because it's freshly allocated memory that the caller can change if it likes). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- include/hw/loader-fit.h | 21 ++++++++++++++++++--- hw/core/loader-fit.c | 38 +++++++++++++++++++++----------------- hw/mips/boston.c | 11 +++++++---- 3 files changed, 46 insertions(+), 24 deletions(-)