Message ID | 20231129172200.430674-3-sjg@chromium.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | arm64: Add a build target for Flat Image Tree | expand |
Hello Simon, On 29.11.23 18:21, Simon Glass wrote: > Add a script which produces a Flat Image Tree (FIT), a single file > containing the built kernel and associated devicetree files. > Compression defaults to gzip which gives a good balance of size and > performance. Thanks for working on this. I think it's useful to have the kernel generate a FIT image out of the box. More complex use cases are always free to call mkimage with a custom ITS. > The files compress from about 86MB to 24MB using this approach. > > The FIT can be used by bootloaders which support it, such as U-Boot > and Linuxboot. It permits automatic selection of the correct > devicetree, matching the compatible string of the running board with > the closest compatible string in the FIT. There is no need for > filenames or other workarounds. > > Add a 'make image.fit' build target for arm64, as well. not that it matters much, but should this maybe called Image.fit as the other Image types are capitalized too? > EFI_ZBOOT_PAYLOAD := Image > EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 > EFI_ZBOOT_MACH_TYPE := ARM64 > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib > index 1a965fe68e01..e1c06ca3c847 100644 > --- a/scripts/Makefile.lib > +++ b/scripts/Makefile.lib > @@ -496,6 +496,19 @@ quiet_cmd_uimage = UIMAGE $@ > -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ > -n '$(UIMAGE_NAME)' -d $< $@ Doesn't hardcoding a load address and entry address here defeat the point of having FIT as generic portable image format? At least barebox will try to place the kernel image at physical address 0 and will exit with an error message if no SDRAM is located at that address. The recommendation in that case is to omit load and entry address altogether to have barebox find a suitable location, but I see now that the FIT specification requires a load and entry address. What would happen if U-Boot tries to load this FIT image on a board that has no DRAM at address 0? Please Cc me on subsequent revisions. I am interested in testing that this works for barebox too. Thanks, Ahmad
Hi, a few more comments after decompiling the FIT image: On 29.11.23 18:21, Simon Glass wrote: > + with fsw.add_node('kernel'): > + fsw.property_string('description', args.name) > + fsw.property_string('type', 'kernel_noload') The specification only says no loading done, but doesn't explain what it means for a bootloader to _not_ load an image. Looking into the U-Boot commit b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, apparently no loading means ignoring load and entry address? I presume missing load and entry is something older U-Boot versions were unhappy about? Please let me know if the semantics are as I understood, so I can prepare a barebox patch supporting it. > + fsw.property_string('arch', args.arch) > + fsw.property_string('os', args.os) > + fsw.property_string('compression', args.compress) > + fsw.property('data', data) > + fsw.property_u32('load', 0) > + fsw.property_u32('entry', 0) > + > + > +def finish_fit(fsw, entries): > + """Finish the FIT ready for use > + > + Writes the /configurations node and subnodes > + > + Args: > + fsw (libfdt.FdtSw): Object to use for writing > + entries (list of tuple): List of configurations: > + str: Description of model > + str: Compatible stringlist > + """ > + fsw.end_node() > + seq = 0 > + with fsw.add_node('configurations'): > + for model, compat in entries: > + seq += 1 > + with fsw.add_node(f'conf-{seq}'): > + fsw.property('compatible', bytes(compat)) The specification says that this is the root U-Boot compatible, which I presume to mean the top-level compatible, which makes sense to me. The code here though adds all compatible strings from the device tree though, is this intended? > + fsw.property_string('description', model) > + fsw.property_string('type', 'flat_dt') > + fsw.property_string('arch', arch) > + fsw.property_string('compression', compress) > + fsw.property('compatible', bytes(compat)) I think I've never seen a compatible for a fdt node before. What use does this serve? Cheers, Ahmad
Hi Ahmad, On Wed, 29 Nov 2023 at 11:35, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hello Simon, > > On 29.11.23 18:21, Simon Glass wrote: > > Add a script which produces a Flat Image Tree (FIT), a single file > > containing the built kernel and associated devicetree files. > > Compression defaults to gzip which gives a good balance of size and > > performance. > > Thanks for working on this. I think it's useful to have the kernel > generate a FIT image out of the box. More complex use cases are always > free to call mkimage with a custom ITS. > > > > The files compress from about 86MB to 24MB using this approach. > > > > The FIT can be used by bootloaders which support it, such as U-Boot > > and Linuxboot. It permits automatic selection of the correct > > devicetree, matching the compatible string of the running board with > > the closest compatible string in the FIT. There is no need for > > filenames or other workarounds. > > > > Add a 'make image.fit' build target for arm64, as well. > > not that it matters much, but should this maybe called Image.fit > as the other Image types are capitalized too? > > > EFI_ZBOOT_PAYLOAD := Image > > EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 > > EFI_ZBOOT_MACH_TYPE := ARM64 > > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib > > index 1a965fe68e01..e1c06ca3c847 100644 > > --- a/scripts/Makefile.lib > > +++ b/scripts/Makefile.lib > > @@ -496,6 +496,19 @@ quiet_cmd_uimage = UIMAGE $@ > > -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ > > -n '$(UIMAGE_NAME)' -d $< $@ > > Doesn't hardcoding a load address and entry address here defeat the point > of having FIT as generic portable image format? > > At least barebox will try to place the kernel image at physical address 0 and > will exit with an error message if no SDRAM is located at that address. > The recommendation in that case is to omit load and entry address altogether > to have barebox find a suitable location, but I see now that the FIT specification > requires a load and entry address. What would happen if U-Boot tries to load this > FIT image on a board that has no DRAM at address 0? The 'kernel_noload' type indicates that the load/exec address are ignored. > > Please Cc me on subsequent revisions. I am interested in testing that this works for barebox > too. There has been some discussion about this recently in U-Boot too, along with a series [1] which you could try if you like. The FIT spec[2] does not provide enough detail on exactly what kernel_noload means and we should improve this at some point. Regards, Simon [1] https://patchwork.ozlabs.org/project/uboot/list/?series=382849 [2] https://github.com/open-source-firmware/flat-image-tree > > Thanks, > Ahmad > > -- > Pengutronix e.K. | | > Steuerwalder Str. 21 | http://www.pengutronix.de/ | > 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | > Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | >
On Wed, Nov 29, 2023 at 07:59:00PM +0100, Ahmad Fatoum wrote: > Hi, > > a few more comments after decompiling the FIT image: > > On 29.11.23 18:21, Simon Glass wrote: > > + with fsw.add_node('kernel'): > > + fsw.property_string('description', args.name) > > + fsw.property_string('type', 'kernel_noload') > > The specification only says no loading done, but doesn't explain what it > means for a bootloader to _not_ load an image. Looking into the U-Boot commit > b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, > apparently no loading means ignoring load and entry address? > > I presume missing load and entry is something older U-Boot versions > were unhappy about? Please let me know if the semantics are as I understood, > so I can prepare a barebox patch supporting it. So the matching side for this series in U-Boot is: https://patchwork.ozlabs.org/project/uboot/list/?series=382849&state=* And in short, for IH_TYPE_KERNEL_NOLOAD we do our best to use it in-place. For decompression we allocate some space to decompress to.
Hi Ahmad, On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hi, > > a few more comments after decompiling the FIT image: > > On 29.11.23 18:21, Simon Glass wrote: > > + with fsw.add_node('kernel'): > > + fsw.property_string('description', args.name) > > + fsw.property_string('type', 'kernel_noload') > > The specification only says no loading done, but doesn't explain what it > means for a bootloader to _not_ load an image. Looking into the U-Boot commit > b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, > apparently no loading means ignoring load and entry address? > > I presume missing load and entry is something older U-Boot versions > were unhappy about? Please let me know if the semantics are as I understood, > so I can prepare a barebox patch supporting it. Oh, see my previous email. > > > + fsw.property_string('arch', args.arch) > > + fsw.property_string('os', args.os) > > + fsw.property_string('compression', args.compress) > > + fsw.property('data', data) > > + fsw.property_u32('load', 0) > > + fsw.property_u32('entry', 0) > > + > > + > > +def finish_fit(fsw, entries): > > + """Finish the FIT ready for use > > + > > + Writes the /configurations node and subnodes > > + > > + Args: > > + fsw (libfdt.FdtSw): Object to use for writing > > + entries (list of tuple): List of configurations: > > + str: Description of model > > + str: Compatible stringlist > > + """ > > + fsw.end_node() > > + seq = 0 > > + with fsw.add_node('configurations'): > > + for model, compat in entries: > > + seq += 1 > > + with fsw.add_node(f'conf-{seq}'): > > + fsw.property('compatible', bytes(compat)) > > The specification says that this is the root U-Boot compatible, > which I presume to mean the top-level compatible, which makes sense to me. > > The code here though adds all compatible strings from the device tree though, > is this intended? Yes, since it saves needing to read in each DT just to get the compatible stringlist. > > > + fsw.property_string('description', model) > > + fsw.property_string('type', 'flat_dt') > > + fsw.property_string('arch', arch) > > + fsw.property_string('compression', compress) > > + fsw.property('compatible', bytes(compat)) > > I think I've never seen a compatible for a fdt node before. > What use does this serve? It indicates the machine that the DT is for. Regards, Simon
Hello Simon, On 29.11.23 20:02, Simon Glass wrote: > Hi Ahmad, > > On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >> >> Hi, >> >> a few more comments after decompiling the FIT image: >> >> On 29.11.23 18:21, Simon Glass wrote: >>> + with fsw.add_node('kernel'): >>> + fsw.property_string('description', args.name) >>> + fsw.property_string('type', 'kernel_noload') >> >> The specification only says no loading done, but doesn't explain what it >> means for a bootloader to _not_ load an image. Looking into the U-Boot commit >> b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, >> apparently no loading means ignoring load and entry address? >> >> I presume missing load and entry is something older U-Boot versions >> were unhappy about? Please let me know if the semantics are as I understood, >> so I can prepare a barebox patch supporting it. > > Oh, see my previous email. Thanks. > >> >>> + fsw.property_string('arch', args.arch) >>> + fsw.property_string('os', args.os) >>> + fsw.property_string('compression', args.compress) >>> + fsw.property('data', data) >>> + fsw.property_u32('load', 0) >>> + fsw.property_u32('entry', 0) >>> + >>> + >>> +def finish_fit(fsw, entries): >>> + """Finish the FIT ready for use >>> + >>> + Writes the /configurations node and subnodes >>> + >>> + Args: >>> + fsw (libfdt.FdtSw): Object to use for writing >>> + entries (list of tuple): List of configurations: >>> + str: Description of model >>> + str: Compatible stringlist >>> + """ >>> + fsw.end_node() >>> + seq = 0 >>> + with fsw.add_node('configurations'): >>> + for model, compat in entries: >>> + seq += 1 >>> + with fsw.add_node(f'conf-{seq}'): >>> + fsw.property('compatible', bytes(compat)) >> >> The specification says that this is the root U-Boot compatible, >> which I presume to mean the top-level compatible, which makes sense to me. >> >> The code here though adds all compatible strings from the device tree though, >> is this intended? > > Yes, since it saves needing to read in each DT just to get the > compatible stringlist. The spec reads as if only one string (root) is supposed to be in the list. The script adds all compatibles though. This is not really useful as a bootloader that's compatible with e.g. fsl,imx8mm would just take the first device tree with that SoC, which is most likely to be wrong. It would be better to just specify the top-level compatible, so the bootloader fails instead of taking the first DT it finds. >>> + fsw.property_string('description', model) >>> + fsw.property_string('type', 'flat_dt') >>> + fsw.property_string('arch', arch) >>> + fsw.property_string('compression', compress) >>> + fsw.property('compatible', bytes(compat)) >> >> I think I've never seen a compatible for a fdt node before. >> What use does this serve? > > It indicates the machine that the DT is for. Who makes use of this information? Thanks, Ahmad > > Regards, > Simon >
Hello Tom, On 29.11.23 20:02, Tom Rini wrote: > On Wed, Nov 29, 2023 at 07:59:00PM +0100, Ahmad Fatoum wrote: >> Hi, >> >> a few more comments after decompiling the FIT image: >> >> On 29.11.23 18:21, Simon Glass wrote: >>> + with fsw.add_node('kernel'): >>> + fsw.property_string('description', args.name) >>> + fsw.property_string('type', 'kernel_noload') >> >> The specification only says no loading done, but doesn't explain what it >> means for a bootloader to _not_ load an image. Looking into the U-Boot commit >> b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, >> apparently no loading means ignoring load and entry address? >> >> I presume missing load and entry is something older U-Boot versions >> were unhappy about? Please let me know if the semantics are as I understood, >> so I can prepare a barebox patch supporting it. > > So the matching side for this series in U-Boot is: > https://patchwork.ozlabs.org/project/uboot/list/?series=382849&state=* > > And in short, for IH_TYPE_KERNEL_NOLOAD we do our best to use it > in-place. For decompression we allocate some space to decompress to. Thanks. I am still curious why "kernel" couldn't have been used back then with missing entry and load address to arrive at the same result? Thanks, Ahmad
Hi Ahmad, On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hello Simon, > > On 29.11.23 20:02, Simon Glass wrote: > > Hi Ahmad, > > > > On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >> > >> Hi, > >> > >> a few more comments after decompiling the FIT image: > >> > >> On 29.11.23 18:21, Simon Glass wrote: > >>> + with fsw.add_node('kernel'): > >>> + fsw.property_string('description', args.name) > >>> + fsw.property_string('type', 'kernel_noload') > >> > >> The specification only says no loading done, but doesn't explain what it > >> means for a bootloader to _not_ load an image. Looking into the U-Boot commit > >> b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, > >> apparently no loading means ignoring load and entry address? > >> > >> I presume missing load and entry is something older U-Boot versions > >> were unhappy about? Please let me know if the semantics are as I understood, > >> so I can prepare a barebox patch supporting it. > > > > Oh, see my previous email. > > Thanks. > > > > >> > >>> + fsw.property_string('arch', args.arch) > >>> + fsw.property_string('os', args.os) > >>> + fsw.property_string('compression', args.compress) > >>> + fsw.property('data', data) > >>> + fsw.property_u32('load', 0) > >>> + fsw.property_u32('entry', 0) > >>> + > >>> + > >>> +def finish_fit(fsw, entries): > >>> + """Finish the FIT ready for use > >>> + > >>> + Writes the /configurations node and subnodes > >>> + > >>> + Args: > >>> + fsw (libfdt.FdtSw): Object to use for writing > >>> + entries (list of tuple): List of configurations: > >>> + str: Description of model > >>> + str: Compatible stringlist > >>> + """ > >>> + fsw.end_node() > >>> + seq = 0 > >>> + with fsw.add_node('configurations'): > >>> + for model, compat in entries: > >>> + seq += 1 > >>> + with fsw.add_node(f'conf-{seq}'): > >>> + fsw.property('compatible', bytes(compat)) > >> > >> The specification says that this is the root U-Boot compatible, > >> which I presume to mean the top-level compatible, which makes sense to me. > >> > >> The code here though adds all compatible strings from the device tree though, > >> is this intended? > > > > Yes, since it saves needing to read in each DT just to get the > > compatible stringlist. > > The spec reads as if only one string (root) is supposed to be in the list. > The script adds all compatibles though. This is not really useful as a bootloader > that's compatible with e.g. fsl,imx8mm would just take the first device tree > with that SoC, which is most likely to be wrong. It would be better to just > specify the top-level compatible, so the bootloader fails instead of taking > the first DT it finds. We do need to have a list, since we have to support different board revs, etc. > > >>> + fsw.property_string('description', model) > >>> + fsw.property_string('type', 'flat_dt') > >>> + fsw.property_string('arch', arch) > >>> + fsw.property_string('compression', compress) > >>> + fsw.property('compatible', bytes(compat)) > >> > >> I think I've never seen a compatible for a fdt node before. > >> What use does this serve? > > > > It indicates the machine that the DT is for. > > Who makes use of this information? U-Boot uses it, I believe. There is an optimisation to use this instead of reading the DT itself. Regards, Simon
On 29.11.23 20:27, Simon Glass wrote: > On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >> On 29.11.23 20:02, Simon Glass wrote: >>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>> The specification says that this is the root U-Boot compatible, >>>> which I presume to mean the top-level compatible, which makes sense to me. >>>> >>>> The code here though adds all compatible strings from the device tree though, >>>> is this intended? >>> >>> Yes, since it saves needing to read in each DT just to get the >>> compatible stringlist. >> >> The spec reads as if only one string (root) is supposed to be in the list. >> The script adds all compatibles though. This is not really useful as a bootloader >> that's compatible with e.g. fsl,imx8mm would just take the first device tree >> with that SoC, which is most likely to be wrong. It would be better to just >> specify the top-level compatible, so the bootloader fails instead of taking >> the first DT it finds. > > We do need to have a list, since we have to support different board revs, etc. Can you give me an example? The way I see it, a bootloader with compatible "vendor,board" and a FIT with configuration with compatibles: "vendor,board-rev-a", "vendor,board" "vendor,board-rev-b", "vendor,board" would just result in the bootloader booting the first configuration, even if the device is actually rev-b. >>>>> + fsw.property_string('description', model) >>>>> + fsw.property_string('type', 'flat_dt') >>>>> + fsw.property_string('arch', arch) >>>>> + fsw.property_string('compression', compress) >>>>> + fsw.property('compatible', bytes(compat)) >>>> >>>> I think I've never seen a compatible for a fdt node before. >>>> What use does this serve? >>> >>> It indicates the machine that the DT is for. >> >> Who makes use of this information? > > U-Boot uses it, I believe. There is an optimisation to use this > instead of reading the DT itself. The configuration already has a compatible entry. What extra use is the compatible entry in the FDT node? Thanks, Ahmad > > Regards, > Simon >
Hi Ahmad, On Wed, 29 Nov 2023 at 12:33, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > On 29.11.23 20:27, Simon Glass wrote: > > On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >> On 29.11.23 20:02, Simon Glass wrote: > >>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>> The specification says that this is the root U-Boot compatible, > >>>> which I presume to mean the top-level compatible, which makes sense to me. > >>>> > >>>> The code here though adds all compatible strings from the device tree though, > >>>> is this intended? > >>> > >>> Yes, since it saves needing to read in each DT just to get the > >>> compatible stringlist. > >> > >> The spec reads as if only one string (root) is supposed to be in the list. > >> The script adds all compatibles though. This is not really useful as a bootloader > >> that's compatible with e.g. fsl,imx8mm would just take the first device tree > >> with that SoC, which is most likely to be wrong. It would be better to just > >> specify the top-level compatible, so the bootloader fails instead of taking > >> the first DT it finds. > > > > We do need to have a list, since we have to support different board revs, etc. > > Can you give me an example? The way I see it, a bootloader with > compatible "vendor,board" and a FIT with configuration with compatibles: > > "vendor,board-rev-a", "vendor,board" > "vendor,board-rev-b", "vendor,board" > > would just result in the bootloader booting the first configuration, even if > the device is actually rev-b. You need to find the best match, not just any match. This is documented in the function comment for fit_conf_find_compat(). > > > >>>>> + fsw.property_string('description', model) > >>>>> + fsw.property_string('type', 'flat_dt') > >>>>> + fsw.property_string('arch', arch) > >>>>> + fsw.property_string('compression', compress) > >>>>> + fsw.property('compatible', bytes(compat)) > >>>> > >>>> I think I've never seen a compatible for a fdt node before. > >>>> What use does this serve? > >>> > >>> It indicates the machine that the DT is for. > >> > >> Who makes use of this information? > > > > U-Boot uses it, I believe. There is an optimisation to use this > > instead of reading the DT itself. > > The configuration already has a compatible entry. What extra use is the compatible > entry in the FDT node? It allows seeing the compatible stringlist without having to read the FDT itself. I don't believe it is necessary though, so long as we are scanning the configurations and not the FDT nodes. Regards, Simon
Hello Simon, On 29.11.23 20:44, Simon Glass wrote: > Hi Ahmad, > > On Wed, 29 Nov 2023 at 12:33, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >> >> On 29.11.23 20:27, Simon Glass wrote: >>> On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>> On 29.11.23 20:02, Simon Glass wrote: >>>>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>>>> The specification says that this is the root U-Boot compatible, >>>>>> which I presume to mean the top-level compatible, which makes sense to me. >>>>>> >>>>>> The code here though adds all compatible strings from the device tree though, >>>>>> is this intended? >>>>> >>>>> Yes, since it saves needing to read in each DT just to get the >>>>> compatible stringlist. >>>> >>>> The spec reads as if only one string (root) is supposed to be in the list. >>>> The script adds all compatibles though. This is not really useful as a bootloader >>>> that's compatible with e.g. fsl,imx8mm would just take the first device tree >>>> with that SoC, which is most likely to be wrong. It would be better to just >>>> specify the top-level compatible, so the bootloader fails instead of taking >>>> the first DT it finds. >>> >>> We do need to have a list, since we have to support different board revs, etc. >> >> Can you give me an example? The way I see it, a bootloader with >> compatible "vendor,board" and a FIT with configuration with compatibles: >> >> "vendor,board-rev-a", "vendor,board" >> "vendor,board-rev-b", "vendor,board" >> >> would just result in the bootloader booting the first configuration, even if >> the device is actually rev-b. > > You need to find the best match, not just any match. This is > documented in the function comment for fit_conf_find_compat(). In my above example, both configuration are equally good. Can you give me an example where it makes sense to have multiple compatibles automatically extracted from the device tree compatible? The way I see it having more than one compatible here just has downsides. >> The configuration already has a compatible entry. What extra use is the compatible >> entry in the FDT node? > > It allows seeing the compatible stringlist without having to read the > FDT itself. I don't believe it is necessary though, so long as we are > scanning the configurations and not the FDT nodes. I think it's better to drop this if it has no use. Cheers, Ahmad > > Regards, > Simon >
On Wed, Nov 29, 2023 at 08:16:20PM +0100, Ahmad Fatoum wrote: > Hello Tom, > > On 29.11.23 20:02, Tom Rini wrote: > > On Wed, Nov 29, 2023 at 07:59:00PM +0100, Ahmad Fatoum wrote: > >> Hi, > >> > >> a few more comments after decompiling the FIT image: > >> > >> On 29.11.23 18:21, Simon Glass wrote: > >>> + with fsw.add_node('kernel'): > >>> + fsw.property_string('description', args.name) > >>> + fsw.property_string('type', 'kernel_noload') > >> > >> The specification only says no loading done, but doesn't explain what it > >> means for a bootloader to _not_ load an image. Looking into the U-Boot commit > >> b9b50e89d317 ("image: Implement IH_TYPE_KERNEL_NOLOAD") that introduces this, > >> apparently no loading means ignoring load and entry address? > >> > >> I presume missing load and entry is something older U-Boot versions > >> were unhappy about? Please let me know if the semantics are as I understood, > >> so I can prepare a barebox patch supporting it. > > > > So the matching side for this series in U-Boot is: > > https://patchwork.ozlabs.org/project/uboot/list/?series=382849&state=* > > > > And in short, for IH_TYPE_KERNEL_NOLOAD we do our best to use it > > in-place. For decompression we allocate some space to decompress to. > > Thanks. I am still curious why "kernel" couldn't have been used back then > with missing entry and load address to arrive at the same result? Some level or another of historical oversight, yeah.
Hi Simon, On 29.11.23 20:00, Simon Glass wrote: > On Wed, 29 Nov 2023 at 11:35, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >> Doesn't hardcoding a load address and entry address here defeat the point >> of having FIT as generic portable image format? >> >> At least barebox will try to place the kernel image at physical address 0 and >> will exit with an error message if no SDRAM is located at that address. >> The recommendation in that case is to omit load and entry address altogether >> to have barebox find a suitable location, but I see now that the FIT specification >> requires a load and entry address. What would happen if U-Boot tries to load this >> FIT image on a board that has no DRAM at address 0? > > The 'kernel_noload' type indicates that the load/exec address are ignored. Can the script not insert load/exec addresses with dummy values to avoid confusion? >> Please Cc me on subsequent revisions. I am interested in testing that this works for barebox >> too. > > There has been some discussion about this recently in U-Boot too, > along with a series [1] which you could try if you like. Thanks for the pointer. I have just sent out a first patch to add support for kernel_noload to the barebox mailing list[1]. With that change applied, barebox can boot the FIT images generated by this series. Once that's accepted, I'll reply with a Tested-by. [1]: https://lore.barebox.org/barebox/20231129203106.2417486-1-a.fatoum@pengutronix.de/T/#u > The FIT spec[2] does not provide enough detail on exactly what > kernel_noload means and we should improve this at some point. Yes, that would be nice. Also straight references to e.g. U-Boot configuration symbols could use some rewording. Thanks, Ahmad > > Regards, > Simon > > [1] https://patchwork.ozlabs.org/project/uboot/list/?series=382849 > [2] https://github.com/open-source-firmware/flat-image-tree > > > >> >> Thanks, >> Ahmad >> >> -- >> Pengutronix e.K. | | >> Steuerwalder Str. 21 | http://www.pengutronix.de/ | >> 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | >> Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | >> >
Simon, thanks for the patch! Below are some nitpicks and bike-shedding questions. On Wed 29 Nov 2023 10:21:53 GMT, Simon Glass wrote: > Add a script which produces a Flat Image Tree (FIT), a single file > containing the built kernel and associated devicetree files. > Compression defaults to gzip which gives a good balance of size and > performance. > > The files compress from about 86MB to 24MB using this approach. > > The FIT can be used by bootloaders which support it, such as U-Boot > and Linuxboot. It permits automatic selection of the correct > devicetree, matching the compatible string of the running board with > the closest compatible string in the FIT. There is no need for > filenames or other workarounds. Have you thought about updating the arch/mips ITB rules to also use the new scripts/make_fit.py? Or is the FIT/ITB format for mips different from the one for arm64? > Add a 'make image.fit' build target for arm64, as well. > > The FIT can be examined using 'dumpimage -l'. > > This features requires pylibfdt (use 'pip install libfdt'). It also > requires compression utilities for the algorithm being used. Supported > compression options are the same as the Image.xxx files. For now there > is no way to change the compression other than by editing the rule for > $(obj)/image.fit > > While FIT supports a ramdisk / initrd, no attempt is made to support > this here, since it must be built separately from the Linux build. > > Signed-off-by: Simon Glass <sjg@chromium.org> > --- > > Changes in v7: > - Add Image as a dependency of image.fit > - Drop kbuild tag > - Add dependency on dtbs > - Drop unnecessary path separator for dtbs > - Rebase to -next > > Changes in v5: > - Drop patch previously applied > - Correct compression rule which was broken in v4 > > Changes in v4: > - Use single quotes for UIMAGE_NAME > > Changes in v3: > - Drop temporary file image.itk > - Drop patch 'Use double quotes for image name' > - Drop double quotes in use of UIMAGE_NAME > - Drop unnecessary CONFIG_EFI_ZBOOT condition for help > - Avoid hard-coding "arm64" for the DT architecture > > Changes in v2: > - Drop patch previously applied > - Add .gitignore file > - Move fit rule to Makefile.lib using an intermediate file > - Drop dependency on CONFIG_EFI_ZBOOT > - Pick up .dtb files separately from the kernel > - Correct pylint too-many-args warning for write_kernel() > - Include the kernel image in the file count > - Add a pointer to the FIT spec and mention of its wide industry usage > - Mention the kernel version in the FIT description > > MAINTAINERS | 7 + > arch/arm64/Makefile | 9 +- > arch/arm64/boot/.gitignore | 1 + > arch/arm64/boot/Makefile | 6 +- > scripts/Makefile.lib | 13 ++ > scripts/make_fit.py | 289 +++++++++++++++++++++++++++++++++++++ > 6 files changed, 322 insertions(+), 3 deletions(-) > create mode 100755 scripts/make_fit.py > > diff --git a/MAINTAINERS b/MAINTAINERS > index 14587be87a33..d609f0e8deb3 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1585,6 +1585,13 @@ F: Documentation/process/maintainer-soc*.rst > F: arch/arm/boot/dts/Makefile > F: arch/arm64/boot/dts/Makefile > > +ARM64 FIT SUPPORT > +M: Simon Glass <sjg@chromium.org> > +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) > +S: Maintained > +F: arch/arm64/boot/Makefile > +F: scripts/make_fit.py > + I'm afraid that the location does not match the requested sorting, it should be right before "ARM64 PORT". > ARM ARCHITECTED TIMER DRIVER > M: Mark Rutland <mark.rutland@arm.com> > M: Marc Zyngier <maz@kernel.org> > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile > index 1bd4fae6e806..18e092de7cdb 100644 > --- a/arch/arm64/Makefile > +++ b/arch/arm64/Makefile > @@ -36,6 +36,8 @@ ifeq ($(CONFIG_BROKEN_GAS_INST),y) > $(warning Detected assembler with broken .inst; disassembly will be unreliable) > endif > > +KBUILD_DTBS := dtbs Might you want to use tabs here as in the lines below? > + > KBUILD_CFLAGS += -mgeneral-regs-only \ > $(compat_vdso) $(cc_has_k_constraint) > KBUILD_CFLAGS += $(call cc-disable-warning, psabi) > @@ -150,7 +152,7 @@ libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a > # Default target when executing plain make > boot := arch/arm64/boot > > -BOOT_TARGETS := Image vmlinuz.efi > +BOOT_TARGETS := Image vmlinuz.efi image.fit > > PHONY += $(BOOT_TARGETS) > > @@ -162,7 +164,9 @@ endif > > all: $(notdir $(KBUILD_IMAGE)) > > -vmlinuz.efi: Image > +image.fit: $(KBUILD_DTBS) > + > +vmlinuz.efi image.fit: Image > $(BOOT_TARGETS): vmlinux > $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ > > @@ -215,6 +219,7 @@ virtconfig: > define archhelp > echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)' > echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' > + echo ' image.fit - Flat Image Tree (arch/$(ARCH)/boot/image.fit)' > echo ' install - Install uncompressed kernel' > echo ' zinstall - Install compressed kernel' > echo ' Install using (your) ~/bin/installkernel or' > diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore > index af5dc61f8b43..abaae9de1bdd 100644 > --- a/arch/arm64/boot/.gitignore > +++ b/arch/arm64/boot/.gitignore > @@ -2,3 +2,4 @@ > Image > Image.gz > vmlinuz* > +image.fit > diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile > index 1761f5972443..8d591fda078f 100644 > --- a/arch/arm64/boot/Makefile > +++ b/arch/arm64/boot/Makefile > @@ -16,7 +16,8 @@ > > OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S > > -targets := Image Image.bz2 Image.gz Image.lz4 Image.lzma Image.lzo Image.zst > +targets := Image Image.bz2 Image.gz Image.lz4 Image.lzma Image.lzo \ > + Image.zst image.fit Do you introduce the first lower-case image.* target by intention? (All others have a capital 'I'.) > > $(obj)/Image: vmlinux FORCE > $(call if_changed,objcopy) > @@ -39,6 +40,9 @@ $(obj)/Image.lzo: $(obj)/Image FORCE > $(obj)/Image.zst: $(obj)/Image FORCE > $(call if_changed,zstd) > > +$(obj)/image.fit: $(obj)/Image FORCE > + $(call cmd,fit,gzip) > + > EFI_ZBOOT_PAYLOAD := Image > EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 > EFI_ZBOOT_MACH_TYPE := ARM64 > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib > index 1a965fe68e01..e1c06ca3c847 100644 > --- a/scripts/Makefile.lib > +++ b/scripts/Makefile.lib > @@ -496,6 +496,19 @@ quiet_cmd_uimage = UIMAGE $@ > -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ > -n '$(UIMAGE_NAME)' -d $< $@ > > +# Flat Image Tree (FIT) > +# This allows for packaging of a kernel and all devicetrees files, using > +# compression. > +# --------------------------------------------------------------------------- > + > +MAKE_FIT := $(srctree)/scripts/make_fit.py > + > +quiet_cmd_fit = FIT $@ > + cmd_fit = $(MAKE_FIT) -f $@ --arch $(UIMAGE_ARCH) --os linux \ > + --name '$(UIMAGE_NAME)' \ > + --compress $(UIMAGE_COMPRESSION) -k $< \ > + $(dir $<)dts Alternatively, you could use '$(<D)/dts' instead of '$(dir $<)dts' (bike-shedding). > + > # XZ > # --------------------------------------------------------------------------- > # Use xzkern to compress the kernel image and xzmisc to compress other things. > diff --git a/scripts/make_fit.py b/scripts/make_fit.py > new file mode 100755 > index 000000000000..e1059825de9c > --- /dev/null > +++ b/scripts/make_fit.py > @@ -0,0 +1,289 @@ > +#!/usr/bin/env python3 > +# SPDX-License-Identifier: GPL-2.0+ > +# > +# Copyright 2023 Google LLC > +# Written by Simon Glass <sjg@chromium.org> > +# > + > +"""Build a FIT containing a lot of devicetree files > + > +Usage: > + make_fit.py -A arm64 -n 'Linux-6.6' -O linux > + -f arch/arm64/boot/image.fit -k /tmp/kern/arch/arm64/boot/image.itk > + /tmp/kern/arch/arm64/boot/dts/ -E -c gzip > + > +Creates a FIT containing the supplied kernel and a directory containing the > +devicetree files. > + > +Use -E to generate an external FIT (where the data is placed after the > +FIT data structure). This allows parsing of the data without loading > +the entire FIT. > + > +Use -c to compress the data, using bzip2, gzip, lz4, lzma, lzo and > +zstd Lost a dot at EOL? > + > +The resulting FIT can be booted by bootloaders which support FIT, such > +as U-Boot, Linuxboot, Tianocore, etc. > + > +Note that this tool does not yet support adding a ramdisk / initrd. > +""" > + > +import argparse > +import collections > +import os > +import subprocess > +import sys > +import tempfile > +import time > + > +import libfdt > + > + > +# Tool extension and the name of the command-line tools > +CompTool = collections.namedtuple('CompTool', 'ext,tools') > + > +COMP_TOOLS = { > + 'bzip2': CompTool('.bz2', 'bzip2'), > + 'gzip': CompTool('.gz', 'pigz,gzip'), > + 'lz4': CompTool('.lz4', 'lz4'), > + 'lzma': CompTool('.lzma', 'lzma'), > + 'lzo': CompTool('.lzo', 'lzop'), > + 'zstd': CompTool('.zstd', 'zstd'), > +} > + > +def parse_args(): > + """Parse the program ArgumentParser > + > + Returns: > + Namespace object containing the arguments > + """ > + epilog = 'Build a FIT from a directory tree containing .dtb files' > + parser = argparse.ArgumentParser(epilog=epilog) > + parser.add_argument('-A', '--arch', type=str, required=True, > + help='Specifies the architecture') > + parser.add_argument('-c', '--compress', type=str, default='none', > + help='Specifies the compression') > + parser.add_argument('-E', '--external', action='store_true', > + help='Convert the FIT to use external data') > + parser.add_argument('-n', '--name', type=str, required=True, > + help='Specifies the name') > + parser.add_argument('-O', '--os', type=str, required=True, > + help='Specifies the operating system') > + parser.add_argument('-f', '--fit', type=str, required=True, > + help='Specifies the output file (.fit)') > + parser.add_argument('-k', '--kernel', type=str, required=True, > + help='Specifies the (uncompressed) kernel input file (.itk)') > + parser.add_argument('srcdir', type=str, nargs='*', > + help='Specifies the directory tree that contains .dtb files') > + > + return parser.parse_args() > + > +def setup_fit(fsw, name): > + """Make a start on writing the FIT > + > + Outputs the root properties and the 'images' node > + > + Args: > + fsw (libfdt.FdtSw): Object to use for writing > + name (str): Name of kernel image > + """ > + fsw.INC_SIZE = 65536 > + fsw.finish_reservemap() > + fsw.begin_node('') > + fsw.property_string('description', f'{name} with devicetree set') > + fsw.property_u32('#address-cells', 1) > + > + fsw.property_u32('timestamp', int(time.time())) > + fsw.begin_node('images') > + > + > +def write_kernel(fsw, data, args): > + """Write out the kernel image > + > + Writes a kernel node along with the required properties > + > + Args: > + fsw (libfdt.FdtSw): Object to use for writing > + data (bytes): Data to write (possibly compressed) > + args (Namespace): Contains necessary strings: > + arch: FIT architecture, e.g. 'arm64' > + fit_os: Operating Systems, e.g. 'linux' > + name: Name of OS, e.g. 'Linux-6.6.0-rc7' > + compress: Compression algorithm to use, e.g. 'gzip' > + """ > + with fsw.add_node('kernel'): > + fsw.property_string('description', args.name) > + fsw.property_string('type', 'kernel_noload') > + fsw.property_string('arch', args.arch) > + fsw.property_string('os', args.os) > + fsw.property_string('compression', args.compress) > + fsw.property('data', data) > + fsw.property_u32('load', 0) > + fsw.property_u32('entry', 0) > + > + > +def finish_fit(fsw, entries): > + """Finish the FIT ready for use > + > + Writes the /configurations node and subnodes > + > + Args: > + fsw (libfdt.FdtSw): Object to use for writing > + entries (list of tuple): List of configurations: > + str: Description of model > + str: Compatible stringlist > + """ > + fsw.end_node() > + seq = 0 > + with fsw.add_node('configurations'): > + for model, compat in entries: > + seq += 1 > + with fsw.add_node(f'conf-{seq}'): > + fsw.property('compatible', bytes(compat)) > + fsw.property_string('description', model) > + fsw.property_string('fdt', f'fdt-{seq}') > + fsw.property_string('kernel', 'kernel') > + fsw.end_node() > + > + > +def compress_data(inf, compress): > + """Compress data using a selected algorithm > + > + Args: > + inf (IOBase): Filename containing the data to compress > + compress (str): Compression algorithm, e.g. 'gzip' > + > + Return: > + bytes: Compressed data > + """ > + if compress == 'none': > + return inf.read() > + > + comp = COMP_TOOLS.get(compress) > + if not comp: > + raise ValueError(f"Unknown compression algorithm '{compress}'") > + > + with tempfile.NamedTemporaryFile() as comp_fname: > + with open(comp_fname.name, 'wb') as outf: > + done = False > + for tool in comp.tools.split(','): > + try: > + subprocess.call([tool, '-c'], stdin=inf, stdout=outf) > + done = True > + break > + except FileNotFoundError: > + pass > + if not done: > + raise ValueError(f'Missing tool(s): {comp.tools}\n') > + with open(comp_fname.name, 'rb') as compf: > + comp_data = compf.read() > + return comp_data > + > + > +def output_dtb(fsw, seq, fname, arch, compress): > + """Write out a single devicetree to the FIT > + > + Args: > + fsw (libfdt.FdtSw): Object to use for writing > + seq (int): Sequence number (1 for first) > + fmame (str): Filename containing the DTB > + arch: FIT architecture, e.g. 'arm64' > + compress (str): Compressed algorithm, e.g. 'gzip' > + > + Returns: > + tuple: > + str: Model name > + bytes: Compatible stringlist > + """ > + with fsw.add_node(f'fdt-{seq}'): > + # Get the compatible / model information > + with open(fname, 'rb') as inf: > + data = inf.read() > + fdt = libfdt.FdtRo(data) > + model = fdt.getprop(0, 'model').as_str() > + compat = fdt.getprop(0, 'compatible') > + > + fsw.property_string('description', model) > + fsw.property_string('type', 'flat_dt') > + fsw.property_string('arch', arch) > + fsw.property_string('compression', compress) > + fsw.property('compatible', bytes(compat)) > + > + with open(fname, 'rb') as inf: > + compressed = compress_data(inf, compress) > + fsw.property('data', compressed) > + return model, compat > + > + > +def build_fit(args): > + """Build the FIT from the provided files and arguments > + > + Args: > + args (Namespace): Program arguments > + > + Returns: > + tuple: > + bytes: FIT data > + int: Number of configurations generated > + size: Total uncompressed size of data > + """ > + fsw = libfdt.FdtSw() > + setup_fit(fsw, args.name) > + seq = 0 > + size = 0 > + entries = [] > + > + # Handle the kernel > + with open(args.kernel, 'rb') as inf: > + comp_data = compress_data(inf, args.compress) > + size += os.path.getsize(args.kernel) > + write_kernel(fsw, comp_data, args) > + > + for path in args.srcdir: > + # Handle devicetree files > + if os.path.isdir(path): > + for dirpath, _, fnames in os.walk(path): > + for fname in fnames: > + if os.path.splitext(fname)[1] != '.dtb': > + continue > + pathname = os.path.join(dirpath, fname) > + seq += 1 > + size += os.path.getsize(pathname) > + model, compat = output_dtb(fsw, seq, pathname, > + args.arch, args.compress) > + entries.append([model, compat]) > + > + finish_fit(fsw, entries) > + > + # Include the kernel itself in the returned file count > + return fsw.as_fdt().as_bytearray(), seq + 1, size > + > + > +def run_make_fit(): > + """Run the tool's main logic""" > + args = parse_args() > + > + out_data, count, size = build_fit(args) > + with open(args.fit, 'wb') as outf: > + outf.write(out_data) > + > + ext_fit_size = None > + if args.external: > + mkimage = os.environ.get('MKIMAGE', 'mkimage') Do we need to add a 'mkimage' line in Documentation/process/changes.rst? Kind regards, Nicolas > + subprocess.check_call([mkimage, '-E', '-F', args.fit], > + stdout=subprocess.DEVNULL) > + > + with open(args.fit, 'rb') as inf: > + data = inf.read() > + ext_fit = libfdt.FdtRo(data) > + ext_fit_size = ext_fit.totalsize() > + > + comp_size = len(out_data) > + print(f'FIT size {comp_size:#x}/{comp_size / 1024 / 1024:.1f} MB', end='') > + if ext_fit_size: > + print(f', header {ext_fit_size:#x}/{ext_fit_size / 1024:.1f} KB', end='') > + print(f', {count} files, uncompressed {size / 1024 / 1024:.1f} MB') > + > + > +if __name__ == "__main__": > + sys.exit(run_make_fit()) > -- > 2.43.0.rc2.451.g8631bc7472-goog >
On Thu, Nov 30, 2023 at 5:26 PM Nicolas Schier <nicolas@fjasle.eu> wrote: > > Simon, > > thanks for the patch! Below are some nitpicks and bike-shedding > questions. > > On Wed 29 Nov 2023 10:21:53 GMT, Simon Glass wrote: > > Add a script which produces a Flat Image Tree (FIT), a single file > > containing the built kernel and associated devicetree files. > > Compression defaults to gzip which gives a good balance of size and > > performance. > > > > The files compress from about 86MB to 24MB using this approach. > > > > The FIT can be used by bootloaders which support it, such as U-Boot > > and Linuxboot. It permits automatic selection of the correct > > devicetree, matching the compatible string of the running board with > > the closest compatible string in the FIT. There is no need for > > filenames or other workarounds. > > Have you thought about updating the arch/mips ITB rules to also use the > new scripts/make_fit.py? Or is the FIT/ITB format for mips different > from the one for arm64? I recommend not touching MIPS at this moment because this tool simply picks up *.dtb files that exist under arch/*/boot/dts/, some of which may be stale files. Think of this scenario: [1] Enable CONFIG_ARCH_FOO and build foo.dtb will be created. [2] Next, disable CONFIG_ARCH_FOO and enable CONFIG_ARCH_BAR, and build. bar.dtb will be created. This script will pick up both foo.dtb and bar.dtb although foo.dtb is a left-over from the previous build. Without cleaning, stale *.dtb will accumulate and unwanted files will be included in image.fit. Currently, MIPS hard-codes its files. It always works in a deterministic way. I do not request Simon to implement everything perfectly because I know that would require much more effort. We could do something like modules.order to list out the dtb files from the current build, but I am not asking for it in this patchset. But, you are right. This tool is not arm64-specific at all (and that is the reason why I think the MAINTAINERS entry is a little odd) Perhaps it can be applicable to MIPS after everything works correctly. > > ARM ARCHITECTED TIMER DRIVER > > M: Mark Rutland <mark.rutland@arm.com> > > M: Marc Zyngier <maz@kernel.org> > > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile > > index 1bd4fae6e806..18e092de7cdb 100644 > > --- a/arch/arm64/Makefile > > +++ b/arch/arm64/Makefile > > @@ -36,6 +36,8 @@ ifeq ($(CONFIG_BROKEN_GAS_INST),y) > > $(warning Detected assembler with broken .inst; disassembly will be unreliable) > > endif > > > > +KBUILD_DTBS := dtbs > > Might you want to use tabs here as in the lines below? This should not exist in the first place. image.fit: dtbs is better. -- Best Regards Masahiro Yamada
On Thu, Nov 30, 2023 at 2:22 AM Simon Glass <sjg@chromium.org> wrote: > > Add a script which produces a Flat Image Tree (FIT), a single file > containing the built kernel and associated devicetree files. > Compression defaults to gzip which gives a good balance of size and > performance. > > The files compress from about 86MB to 24MB using this approach. > > The FIT can be used by bootloaders which support it, such as U-Boot > and Linuxboot. It permits automatic selection of the correct > devicetree, matching the compatible string of the running board with > the closest compatible string in the FIT. There is no need for > filenames or other workarounds. > > Add a 'make image.fit' build target for arm64, as well. > > The FIT can be examined using 'dumpimage -l'. > > This features requires pylibfdt (use 'pip install libfdt'). It also > requires compression utilities for the algorithm being used. Supported > compression options are the same as the Image.xxx files. For now there > is no way to change the compression other than by editing the rule for > $(obj)/image.fit > > While FIT supports a ramdisk / initrd, no attempt is made to support > this here, since it must be built separately from the Linux build. > > Signed-off-by: Simon Glass <sjg@chromium.org> > --- > > Changes in v7: > - Add Image as a dependency of image.fit > - Drop kbuild tag > - Add dependency on dtbs > - Drop unnecessary path separator for dtbs > - Rebase to -next > > Changes in v5: > - Drop patch previously applied > - Correct compression rule which was broken in v4 > > Changes in v4: > - Use single quotes for UIMAGE_NAME > > Changes in v3: > - Drop temporary file image.itk > - Drop patch 'Use double quotes for image name' > - Drop double quotes in use of UIMAGE_NAME > - Drop unnecessary CONFIG_EFI_ZBOOT condition for help > - Avoid hard-coding "arm64" for the DT architecture > > Changes in v2: > - Drop patch previously applied > - Add .gitignore file > - Move fit rule to Makefile.lib using an intermediate file > - Drop dependency on CONFIG_EFI_ZBOOT > - Pick up .dtb files separately from the kernel > - Correct pylint too-many-args warning for write_kernel() > - Include the kernel image in the file count > - Add a pointer to the FIT spec and mention of its wide industry usage > - Mention the kernel version in the FIT description > > MAINTAINERS | 7 + > arch/arm64/Makefile | 9 +- > arch/arm64/boot/.gitignore | 1 + > arch/arm64/boot/Makefile | 6 +- > scripts/Makefile.lib | 13 ++ > scripts/make_fit.py | 289 +++++++++++++++++++++++++++++++++++++ > 6 files changed, 322 insertions(+), 3 deletions(-) > create mode 100755 scripts/make_fit.py > > diff --git a/MAINTAINERS b/MAINTAINERS > index 14587be87a33..d609f0e8deb3 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1585,6 +1585,13 @@ F: Documentation/process/maintainer-soc*.rst > F: arch/arm/boot/dts/Makefile > F: arch/arm64/boot/dts/Makefile > > +ARM64 FIT SUPPORT > +M: Simon Glass <sjg@chromium.org> > +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) > +S: Maintained > +F: arch/arm64/boot/Makefile > +F: scripts/make_fit.py > + > ARM ARCHITECTED TIMER DRIVER > M: Mark Rutland <mark.rutland@arm.com> > M: Marc Zyngier <maz@kernel.org> > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile > index 1bd4fae6e806..18e092de7cdb 100644 > --- a/arch/arm64/Makefile > +++ b/arch/arm64/Makefile > @@ -36,6 +36,8 @@ ifeq ($(CONFIG_BROKEN_GAS_INST),y) > $(warning Detected assembler with broken .inst; disassembly will be unreliable) > endif > > +KBUILD_DTBS := dtbs Please remove this, and hard-code image.fit: dtbs > > $(obj)/Image: vmlinux FORCE > $(call if_changed,objcopy) > @@ -39,6 +40,9 @@ $(obj)/Image.lzo: $(obj)/Image FORCE > $(obj)/Image.zst: $(obj)/Image FORCE > $(call if_changed,zstd) > > +$(obj)/image.fit: $(obj)/Image FORCE > + $(call cmd,fit,gzip) The gzip parameter is not used. Please do $(call cmd,fit) In the python script, functions are separated with two blank lines, but there is only one blank line between parse_args() and setup_fit(). I do not mind either way because it does not contain any class, but please keep consistency. -- Best Regards Masahiro Yamada
Hi Ahmad, On Wed, 29 Nov 2023 at 12:54, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hello Simon, > > On 29.11.23 20:44, Simon Glass wrote: > > Hi Ahmad, > > > > On Wed, 29 Nov 2023 at 12:33, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >> > >> On 29.11.23 20:27, Simon Glass wrote: > >>> On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>> On 29.11.23 20:02, Simon Glass wrote: > >>>>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>>>> The specification says that this is the root U-Boot compatible, > >>>>>> which I presume to mean the top-level compatible, which makes sense to me. > >>>>>> > >>>>>> The code here though adds all compatible strings from the device tree though, > >>>>>> is this intended? > >>>>> > >>>>> Yes, since it saves needing to read in each DT just to get the > >>>>> compatible stringlist. > >>>> > >>>> The spec reads as if only one string (root) is supposed to be in the list. > >>>> The script adds all compatibles though. This is not really useful as a bootloader > >>>> that's compatible with e.g. fsl,imx8mm would just take the first device tree > >>>> with that SoC, which is most likely to be wrong. It would be better to just > >>>> specify the top-level compatible, so the bootloader fails instead of taking > >>>> the first DT it finds. > >>> > >>> We do need to have a list, since we have to support different board revs, etc. > >> > >> Can you give me an example? The way I see it, a bootloader with > >> compatible "vendor,board" and a FIT with configuration with compatibles: > >> > >> "vendor,board-rev-a", "vendor,board" > >> "vendor,board-rev-b", "vendor,board" > >> > >> would just result in the bootloader booting the first configuration, even if > >> the device is actually rev-b. > > > > You need to find the best match, not just any match. This is > > documented in the function comment for fit_conf_find_compat(). > > In my above example, both configuration are equally good. > Can you give me an example where it makes sense to have multiple > compatibles automatically extracted from the device tree compatible? > > The way I see it having more than one compatible here just has > downsides. I don't have an example to hand, but this is the required mechanism of FIT. This feature has been in place for many years and is used by ChromeOS, at least. > > >> The configuration already has a compatible entry. What extra use is the compatible > >> entry in the FDT node? > > > > It allows seeing the compatible stringlist without having to read the > > FDT itself. I don't believe it is necessary though, so long as we are > > scanning the configurations and not the FDT nodes. > > I think it's better to drop this if it has no use. OK. I cannot think of a use for it. Regards, Simon
Hi Ahmad, On Wed, 29 Nov 2023 at 11:35, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hello Simon, > > On 29.11.23 18:21, Simon Glass wrote: > > Add a script which produces a Flat Image Tree (FIT), a single file > > containing the built kernel and associated devicetree files. > > Compression defaults to gzip which gives a good balance of size and > > performance. > > Thanks for working on this. I think it's useful to have the kernel > generate a FIT image out of the box. More complex use cases are always > free to call mkimage with a custom ITS. > > > > The files compress from about 86MB to 24MB using this approach. > > > > The FIT can be used by bootloaders which support it, such as U-Boot > > and Linuxboot. It permits automatic selection of the correct > > devicetree, matching the compatible string of the running board with > > the closest compatible string in the FIT. There is no need for > > filenames or other workarounds. > > > > Add a 'make image.fit' build target for arm64, as well. > > not that it matters much, but should this maybe called Image.fit > as the other Image types are capitalized too? I missed this comment earlier. I believe Image is intended to refer to a raw image, with the other extensions being compressed versions of these. So I believe it would be confusing for the FIT version to have a capital I. > > > EFI_ZBOOT_PAYLOAD := Image > > EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 > > EFI_ZBOOT_MACH_TYPE := ARM64 > > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib > > index 1a965fe68e01..e1c06ca3c847 100644 > > --- a/scripts/Makefile.lib > > +++ b/scripts/Makefile.lib > > @@ -496,6 +496,19 @@ quiet_cmd_uimage = UIMAGE $@ > > -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ > > -n '$(UIMAGE_NAME)' -d $< $@ > > Doesn't hardcoding a load address and entry address here defeat the point > of having FIT as generic portable image format? > > At least barebox will try to place the kernel image at physical address 0 and > will exit with an error message if no SDRAM is located at that address. > The recommendation in that case is to omit load and entry address altogether > to have barebox find a suitable location, but I see now that the FIT specification > requires a load and entry address. What would happen if U-Boot tries to load this > FIT image on a board that has no DRAM at address 0? > > Please Cc me on subsequent revisions. I am interested in testing that this works for barebox > too. I have added you. Regards, Simon
Hi Masahiro, On Thu, 30 Nov 2023 at 08:39, Masahiro Yamada <masahiroy@kernel.org> wrote: > > On Thu, Nov 30, 2023 at 2:22 AM Simon Glass <sjg@chromium.org> wrote: > > > > Add a script which produces a Flat Image Tree (FIT), a single file > > containing the built kernel and associated devicetree files. > > Compression defaults to gzip which gives a good balance of size and > > performance. > > > > The files compress from about 86MB to 24MB using this approach. > > > > The FIT can be used by bootloaders which support it, such as U-Boot > > and Linuxboot. It permits automatic selection of the correct > > devicetree, matching the compatible string of the running board with > > the closest compatible string in the FIT. There is no need for > > filenames or other workarounds. > > > > Add a 'make image.fit' build target for arm64, as well. > > > > The FIT can be examined using 'dumpimage -l'. > > > > This features requires pylibfdt (use 'pip install libfdt'). It also > > requires compression utilities for the algorithm being used. Supported > > compression options are the same as the Image.xxx files. For now there > > is no way to change the compression other than by editing the rule for > > $(obj)/image.fit > > > > While FIT supports a ramdisk / initrd, no attempt is made to support > > this here, since it must be built separately from the Linux build. > > > > Signed-off-by: Simon Glass <sjg@chromium.org> > > --- > > > > Changes in v7: > > - Add Image as a dependency of image.fit > > - Drop kbuild tag > > - Add dependency on dtbs > > - Drop unnecessary path separator for dtbs > > - Rebase to -next > > > > Changes in v5: > > - Drop patch previously applied > > - Correct compression rule which was broken in v4 > > > > Changes in v4: > > - Use single quotes for UIMAGE_NAME > > > > Changes in v3: > > - Drop temporary file image.itk > > - Drop patch 'Use double quotes for image name' > > - Drop double quotes in use of UIMAGE_NAME > > - Drop unnecessary CONFIG_EFI_ZBOOT condition for help > > - Avoid hard-coding "arm64" for the DT architecture > > > > Changes in v2: > > - Drop patch previously applied > > - Add .gitignore file > > - Move fit rule to Makefile.lib using an intermediate file > > - Drop dependency on CONFIG_EFI_ZBOOT > > - Pick up .dtb files separately from the kernel > > - Correct pylint too-many-args warning for write_kernel() > > - Include the kernel image in the file count > > - Add a pointer to the FIT spec and mention of its wide industry usage > > - Mention the kernel version in the FIT description > > > > MAINTAINERS | 7 + > > arch/arm64/Makefile | 9 +- > > arch/arm64/boot/.gitignore | 1 + > > arch/arm64/boot/Makefile | 6 +- > > scripts/Makefile.lib | 13 ++ > > scripts/make_fit.py | 289 +++++++++++++++++++++++++++++++++++++ > > 6 files changed, 322 insertions(+), 3 deletions(-) > > create mode 100755 scripts/make_fit.py > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 14587be87a33..d609f0e8deb3 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -1585,6 +1585,13 @@ F: Documentation/process/maintainer-soc*.rst > > F: arch/arm/boot/dts/Makefile > > F: arch/arm64/boot/dts/Makefile > > > > +ARM64 FIT SUPPORT > > +M: Simon Glass <sjg@chromium.org> > > +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) > > +S: Maintained > > +F: arch/arm64/boot/Makefile > > +F: scripts/make_fit.py > > + > > ARM ARCHITECTED TIMER DRIVER > > M: Mark Rutland <mark.rutland@arm.com> > > M: Marc Zyngier <maz@kernel.org> > > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile > > index 1bd4fae6e806..18e092de7cdb 100644 > > --- a/arch/arm64/Makefile > > +++ b/arch/arm64/Makefile > > @@ -36,6 +36,8 @@ ifeq ($(CONFIG_BROKEN_GAS_INST),y) > > $(warning Detected assembler with broken .inst; disassembly will be unreliable) > > endif > > > > +KBUILD_DTBS := dtbs > > > Please remove this, and hard-code > > image.fit: dtbs OK > > > > > > > $(obj)/Image: vmlinux FORCE > > $(call if_changed,objcopy) > > @@ -39,6 +40,9 @@ $(obj)/Image.lzo: $(obj)/Image FORCE > > $(obj)/Image.zst: $(obj)/Image FORCE > > $(call if_changed,zstd) > > > > +$(obj)/image.fit: $(obj)/Image FORCE > > + $(call cmd,fit,gzip) > > > The gzip parameter is not used. > Please do > > $(call cmd,fit) I do want to be able to control the compression algo. I added a FIT_COMPRESS for that, so that this arg is used. > > In the python script, functions are separated with two blank lines, > but there is only one blank line between parse_args() and setup_fit(). > > > I do not mind either way because it does not contain any class, > but please keep consistency. OK Regards, Simon
Hello Simon, On 30.11.23 21:30, Simon Glass wrote: > On Wed, 29 Nov 2023 at 12:54, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >> On 29.11.23 20:44, Simon Glass wrote: >>> On Wed, 29 Nov 2023 at 12:33, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>> >>>> On 29.11.23 20:27, Simon Glass wrote: >>>>> On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>>>> On 29.11.23 20:02, Simon Glass wrote: >>>>>>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>>>>>> The specification says that this is the root U-Boot compatible, >>>>>>>> which I presume to mean the top-level compatible, which makes sense to me. >>>>>>>> >>>>>>>> The code here though adds all compatible strings from the device tree though, >>>>>>>> is this intended? >>>>>>> >>>>>>> Yes, since it saves needing to read in each DT just to get the >>>>>>> compatible stringlist. >>>>>> >>>>>> The spec reads as if only one string (root) is supposed to be in the list. >>>>>> The script adds all compatibles though. This is not really useful as a bootloader >>>>>> that's compatible with e.g. fsl,imx8mm would just take the first device tree >>>>>> with that SoC, which is most likely to be wrong. It would be better to just >>>>>> specify the top-level compatible, so the bootloader fails instead of taking >>>>>> the first DT it finds. >>>>> >>>>> We do need to have a list, since we have to support different board revs, etc. >>>> >>>> Can you give me an example? The way I see it, a bootloader with >>>> compatible "vendor,board" and a FIT with configuration with compatibles: >>>> >>>> "vendor,board-rev-a", "vendor,board" >>>> "vendor,board-rev-b", "vendor,board" >>>> >>>> would just result in the bootloader booting the first configuration, even if >>>> the device is actually rev-b. >>> >>> You need to find the best match, not just any match. This is >>> documented in the function comment for fit_conf_find_compat(). >> >> In my above example, both configuration are equally good. >> Can you give me an example where it makes sense to have multiple >> compatibles automatically extracted from the device tree compatible? >> >> The way I see it having more than one compatible here just has >> downsides. > > I don't have an example to hand, but this is the required mechanism of > FIT. This feature has been in place for many years and is used by > ChromeOS, at least. I see the utility of a FIT configuration with compatible = "vendor,board-rev-a", "vendor,board-rev-b"; I fail to see a utility for a configuration with compatible = "vendor,board", "vendor,SoM", "vendor,SoC"; Any configuration that ends up being booted because "vendor,SoC" was matched is most likely doomed to fail. Therefore, I would suggest that only the top level configuration is written into the FIT configurations automatically. Cheers, Ahmad
Hi Ahmad, On Thu, 30 Nov 2023 at 19:04, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hello Simon, > > On 30.11.23 21:30, Simon Glass wrote: > > On Wed, 29 Nov 2023 at 12:54, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >> On 29.11.23 20:44, Simon Glass wrote: > >>> On Wed, 29 Nov 2023 at 12:33, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>> > >>>> On 29.11.23 20:27, Simon Glass wrote: > >>>>> On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>>>> On 29.11.23 20:02, Simon Glass wrote: > >>>>>>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>>>>>> The specification says that this is the root U-Boot compatible, > >>>>>>>> which I presume to mean the top-level compatible, which makes sense to me. > >>>>>>>> > >>>>>>>> The code here though adds all compatible strings from the device tree though, > >>>>>>>> is this intended? > >>>>>>> > >>>>>>> Yes, since it saves needing to read in each DT just to get the > >>>>>>> compatible stringlist. > >>>>>> > >>>>>> The spec reads as if only one string (root) is supposed to be in the list. > >>>>>> The script adds all compatibles though. This is not really useful as a bootloader > >>>>>> that's compatible with e.g. fsl,imx8mm would just take the first device tree > >>>>>> with that SoC, which is most likely to be wrong. It would be better to just > >>>>>> specify the top-level compatible, so the bootloader fails instead of taking > >>>>>> the first DT it finds. > >>>>> > >>>>> We do need to have a list, since we have to support different board revs, etc. > >>>> > >>>> Can you give me an example? The way I see it, a bootloader with > >>>> compatible "vendor,board" and a FIT with configuration with compatibles: > >>>> > >>>> "vendor,board-rev-a", "vendor,board" > >>>> "vendor,board-rev-b", "vendor,board" > >>>> > >>>> would just result in the bootloader booting the first configuration, even if > >>>> the device is actually rev-b. > >>> > >>> You need to find the best match, not just any match. This is > >>> documented in the function comment for fit_conf_find_compat(). > >> > >> In my above example, both configuration are equally good. > >> Can you give me an example where it makes sense to have multiple > >> compatibles automatically extracted from the device tree compatible? > >> > >> The way I see it having more than one compatible here just has > >> downsides. > > > > I don't have an example to hand, but this is the required mechanism of > > FIT. This feature has been in place for many years and is used by > > ChromeOS, at least. > > I see the utility of a FIT configuration with > > compatible = "vendor,board-rev-a", "vendor,board-rev-b"; > > I fail to see a utility for a configuration with > > compatible = "vendor,board", "vendor,SoM", "vendor,SoC"; > > Any configuration that ends up being booted because "vendor,SoC" was matched is > most likely doomed to fail. Therefore, I would suggest that only the top level > configuration is written into the FIT configurations automatically. Firstly, I am not an expert on this. Say you have a board with variants. The compatible string in U-Boot may be something like: "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", "rockchip,rk3288"; If you then have several FIT configurations, they may be something like: "google,veyron-brain-rev0", "google,veyron-brain", "google,veyron", "rockchip,rk3288"; "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", "rockchip,rk3288"; "google,veyron-brain-rev2", "google,veyron-brain", "google,veyron", "rockchip,rk3288"; You want to choose the second one, since it is a better match than the others. +Doug Anderson who knows a lot more about this than me. Regards, Simon
Hi, On Sat, Dec 2, 2023 at 8:37 AM Simon Glass <sjg@chromium.org> wrote: > > Hi Ahmad, > > On Thu, 30 Nov 2023 at 19:04, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > > > Hello Simon, > > > > On 30.11.23 21:30, Simon Glass wrote: > > > On Wed, 29 Nov 2023 at 12:54, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > >> On 29.11.23 20:44, Simon Glass wrote: > > >>> On Wed, 29 Nov 2023 at 12:33, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > >>>> > > >>>> On 29.11.23 20:27, Simon Glass wrote: > > >>>>> On Wed, 29 Nov 2023 at 12:15, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > >>>>>> On 29.11.23 20:02, Simon Glass wrote: > > >>>>>>> On Wed, 29 Nov 2023 at 11:59, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > >>>>>>>> The specification says that this is the root U-Boot compatible, > > >>>>>>>> which I presume to mean the top-level compatible, which makes sense to me. > > >>>>>>>> > > >>>>>>>> The code here though adds all compatible strings from the device tree though, > > >>>>>>>> is this intended? > > >>>>>>> > > >>>>>>> Yes, since it saves needing to read in each DT just to get the > > >>>>>>> compatible stringlist. > > >>>>>> > > >>>>>> The spec reads as if only one string (root) is supposed to be in the list. > > >>>>>> The script adds all compatibles though. This is not really useful as a bootloader > > >>>>>> that's compatible with e.g. fsl,imx8mm would just take the first device tree > > >>>>>> with that SoC, which is most likely to be wrong. It would be better to just > > >>>>>> specify the top-level compatible, so the bootloader fails instead of taking > > >>>>>> the first DT it finds. > > >>>>> > > >>>>> We do need to have a list, since we have to support different board revs, etc. > > >>>> > > >>>> Can you give me an example? The way I see it, a bootloader with > > >>>> compatible "vendor,board" and a FIT with configuration with compatibles: > > >>>> > > >>>> "vendor,board-rev-a", "vendor,board" > > >>>> "vendor,board-rev-b", "vendor,board" > > >>>> > > >>>> would just result in the bootloader booting the first configuration, even if > > >>>> the device is actually rev-b. > > >>> > > >>> You need to find the best match, not just any match. This is > > >>> documented in the function comment for fit_conf_find_compat(). > > >> > > >> In my above example, both configuration are equally good. > > >> Can you give me an example where it makes sense to have multiple > > >> compatibles automatically extracted from the device tree compatible? > > >> > > >> The way I see it having more than one compatible here just has > > >> downsides. > > > > > > I don't have an example to hand, but this is the required mechanism of > > > FIT. This feature has been in place for many years and is used by > > > ChromeOS, at least. > > > > I see the utility of a FIT configuration with > > > > compatible = "vendor,board-rev-a", "vendor,board-rev-b"; > > > > I fail to see a utility for a configuration with > > > > compatible = "vendor,board", "vendor,SoM", "vendor,SoC"; > > > > Any configuration that ends up being booted because "vendor,SoC" was matched is > > most likely doomed to fail. Therefore, I would suggest that only the top level > > configuration is written into the FIT configurations automatically. > > Firstly, I am not an expert on this. > > Say you have a board with variants. The compatible string in U-Boot > may be something like: > > "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", > "rockchip,rk3288"; > > If you then have several FIT configurations, they may be something like: > > "google,veyron-brain-rev0", "google,veyron-brain", "google,veyron", > "rockchip,rk3288"; > "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", > "rockchip,rk3288"; > "google,veyron-brain-rev2", "google,veyron-brain", "google,veyron", > "rockchip,rk3288"; > > You want to choose the second one, since it is a better match than the others. > > +Doug Anderson who knows a lot more about this than me. Hopefully this is all explained by: https://docs.kernel.org/arch/arm/google/chromebook-boot-flow.html
Hello, On 04.12.23 18:52, Doug Anderson wrote:> On Sat, Dec 2, 2023 at 8:37 AM Simon Glass <sjg@chromium.org> wrote: >> On Thu, 30 Nov 2023 at 19:04, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>> On 30.11.23 21:30, Simon Glass wrote: >>>> On Wed, 29 Nov 2023 at 12:54, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: >>>>> On 29.11.23 20:44, Simon Glass wrote: >>>> I don't have an example to hand, but this is the required mechanism of >>>> FIT. This feature has been in place for many years and is used by >>>> ChromeOS, at least. >>> >>> I see the utility of a FIT configuration with >>> >>> compatible = "vendor,board-rev-a", "vendor,board-rev-b"; >>> >>> I fail to see a utility for a configuration with >>> >>> compatible = "vendor,board", "vendor,SoM", "vendor,SoC"; >>> >>> Any configuration that ends up being booted because "vendor,SoC" was matched is >>> most likely doomed to fail. Therefore, I would suggest that only the top level >>> configuration is written into the FIT configurations automatically. >> >> Firstly, I am not an expert on this. >> >> Say you have a board with variants. The compatible string in U-Boot >> may be something like: >> >> "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", >> "rockchip,rk3288"; >> >> If you then have several FIT configurations, they may be something like: >> >> "google,veyron-brain-rev0", "google,veyron-brain", "google,veyron", >> "rockchip,rk3288"; >> "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", >> "rockchip,rk3288"; >> "google,veyron-brain-rev2", "google,veyron-brain", "google,veyron", >> "rockchip,rk3288"; >> >> You want to choose the second one, since it is a better match than the others. Now imagine, you are building a kernel that has no DT support for the Veyron, but instead has support for the Phytec RK3288 PCM-947: phytec,rk3288-pcm-947", "phytec,rk3288-phycore-som", "rockchip,rk3288 As far as I understand U-Boot code, A veyron U-Boot would boot the Phytec DT if CONFIG_FIT_BEST_MATCH is set, although it's a bad match and a boot failure should rather have occurred. >> +Doug Anderson who knows a lot more about this than me. > > Hopefully this is all explained by: > > https://docs.kernel.org/arch/arm/google/chromebook-boot-flow.html Thanks Doug, this was helpful. The missing information to me was that depthcharge only compares the top-level board compatible in addition to runtime generated board compatibles, unlike what U-Boot seems to do. barebox only compares its top-level compatible against the FIT configuration compatibles, so adding a full compatible list to the configuration nodes as done by this series should be ok there as well. I think U-Boot could run into issues though as described above. Out of curiosity: I only heard about Depthcharge before as exploitation toolkit for U-Boot. Can you point me at some documentation on what the Depthcharge bootloader does what U-Boot (which was presumably used before?) doesn't? Thanks, Ahmad >
Hi, On Tue, Dec 5, 2023 at 3:16 AM Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > > Hello, > > On 04.12.23 18:52, Doug Anderson wrote:> On Sat, Dec 2, 2023 at 8:37 AM Simon Glass <sjg@chromium.org> wrote: > >> On Thu, 30 Nov 2023 at 19:04, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>> On 30.11.23 21:30, Simon Glass wrote: > >>>> On Wed, 29 Nov 2023 at 12:54, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote: > >>>>> On 29.11.23 20:44, Simon Glass wrote: > >>>> I don't have an example to hand, but this is the required mechanism of > >>>> FIT. This feature has been in place for many years and is used by > >>>> ChromeOS, at least. > >>> > >>> I see the utility of a FIT configuration with > >>> > >>> compatible = "vendor,board-rev-a", "vendor,board-rev-b"; > >>> > >>> I fail to see a utility for a configuration with > >>> > >>> compatible = "vendor,board", "vendor,SoM", "vendor,SoC"; > >>> > >>> Any configuration that ends up being booted because "vendor,SoC" was matched is > >>> most likely doomed to fail. Therefore, I would suggest that only the top level > >>> configuration is written into the FIT configurations automatically. > >> > >> Firstly, I am not an expert on this. > >> > >> Say you have a board with variants. The compatible string in U-Boot > >> may be something like: > >> > >> "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", > >> "rockchip,rk3288"; > >> > >> If you then have several FIT configurations, they may be something like: > >> > >> "google,veyron-brain-rev0", "google,veyron-brain", "google,veyron", > >> "rockchip,rk3288"; > >> "google,veyron-brain-rev1", "google,veyron-brain", "google,veyron", > >> "rockchip,rk3288"; > >> "google,veyron-brain-rev2", "google,veyron-brain", "google,veyron", > >> "rockchip,rk3288"; > >> > >> You want to choose the second one, since it is a better match than the others. > > Now imagine, you are building a kernel that has no DT support for the Veyron, > but instead has support for the Phytec RK3288 PCM-947: > > phytec,rk3288-pcm-947", "phytec,rk3288-phycore-som", "rockchip,rk3288 > > As far as I understand U-Boot code, A veyron U-Boot would boot the Phytec DT > if CONFIG_FIT_BEST_MATCH is set, although it's a bad match and a boot failure > should rather have occurred. On depthcharge the bootloader never matches on just a SoC name. > >> +Doug Anderson who knows a lot more about this than me. > > > > Hopefully this is all explained by: > > > > https://docs.kernel.org/arch/arm/google/chromebook-boot-flow.html > > Thanks Doug, this was helpful. The missing information to me was that > depthcharge only compares the top-level board compatible in addition > to runtime generated board compatibles, unlike what U-Boot seems to do. > > barebox only compares its top-level compatible against the FIT configuration > compatibles, so adding a full compatible list to the configuration nodes as done > by this series should be ok there as well. I think U-Boot could run into > issues though as described above. > > Out of curiosity: I only heard about Depthcharge before as exploitation toolkit > for U-Boot. Can you point me at some documentation on what the Depthcharge bootloader > does what U-Boot (which was presumably used before?) doesn't? I can only assume that the depthcharge you're talking about is different. The one used by Chromebooks is basically: https://chromium.googlesource.com/chromiumos/platform/depthcharge/+/refs/heads/main I assume you're asking: why are we using depthcharge in ChromeOS instead of U-Boot? There was much discussion about this back in the day. From what I recall, part of the reason was that folks wanted the boot flow to be a bit more standard between x86 Chromebooks and ARM Chromebooks. x86 Chromebooks were using coreboot for the initial hardware booting and then needed a 2nd stage to actually load Linux and ended up creating depthcharge. ...and then we switched to the same model for ARM boards. I didn't personally make that decision and I'm also not on the firmware team, so that's about all I'll say on the topic. ;-) Oh, hmmm. Searching found me: https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices/custom-firmware/ ...which pointed at: https://www.chromium.org/chromium-os/2014-firmware-summit/ChromeOS%20firmware%20summit%20-%20Depthcharge.pdf -Doug
diff --git a/MAINTAINERS b/MAINTAINERS index 14587be87a33..d609f0e8deb3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1585,6 +1585,13 @@ F: Documentation/process/maintainer-soc*.rst F: arch/arm/boot/dts/Makefile F: arch/arm64/boot/dts/Makefile +ARM64 FIT SUPPORT +M: Simon Glass <sjg@chromium.org> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: arch/arm64/boot/Makefile +F: scripts/make_fit.py + ARM ARCHITECTED TIMER DRIVER M: Mark Rutland <mark.rutland@arm.com> M: Marc Zyngier <maz@kernel.org> diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 1bd4fae6e806..18e092de7cdb 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -36,6 +36,8 @@ ifeq ($(CONFIG_BROKEN_GAS_INST),y) $(warning Detected assembler with broken .inst; disassembly will be unreliable) endif +KBUILD_DTBS := dtbs + KBUILD_CFLAGS += -mgeneral-regs-only \ $(compat_vdso) $(cc_has_k_constraint) KBUILD_CFLAGS += $(call cc-disable-warning, psabi) @@ -150,7 +152,7 @@ libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make boot := arch/arm64/boot -BOOT_TARGETS := Image vmlinuz.efi +BOOT_TARGETS := Image vmlinuz.efi image.fit PHONY += $(BOOT_TARGETS) @@ -162,7 +164,9 @@ endif all: $(notdir $(KBUILD_IMAGE)) -vmlinuz.efi: Image +image.fit: $(KBUILD_DTBS) + +vmlinuz.efi image.fit: Image $(BOOT_TARGETS): vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ @@ -215,6 +219,7 @@ virtconfig: define archhelp echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)' echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' + echo ' image.fit - Flat Image Tree (arch/$(ARCH)/boot/image.fit)' echo ' install - Install uncompressed kernel' echo ' zinstall - Install compressed kernel' echo ' Install using (your) ~/bin/installkernel or' diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore index af5dc61f8b43..abaae9de1bdd 100644 --- a/arch/arm64/boot/.gitignore +++ b/arch/arm64/boot/.gitignore @@ -2,3 +2,4 @@ Image Image.gz vmlinuz* +image.fit diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index 1761f5972443..8d591fda078f 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -16,7 +16,8 @@ OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S -targets := Image Image.bz2 Image.gz Image.lz4 Image.lzma Image.lzo Image.zst +targets := Image Image.bz2 Image.gz Image.lz4 Image.lzma Image.lzo \ + Image.zst image.fit $(obj)/Image: vmlinux FORCE $(call if_changed,objcopy) @@ -39,6 +40,9 @@ $(obj)/Image.lzo: $(obj)/Image FORCE $(obj)/Image.zst: $(obj)/Image FORCE $(call if_changed,zstd) +$(obj)/image.fit: $(obj)/Image FORCE + $(call cmd,fit,gzip) + EFI_ZBOOT_PAYLOAD := Image EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 EFI_ZBOOT_MACH_TYPE := ARM64 diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 1a965fe68e01..e1c06ca3c847 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -496,6 +496,19 @@ quiet_cmd_uimage = UIMAGE $@ -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ -n '$(UIMAGE_NAME)' -d $< $@ +# Flat Image Tree (FIT) +# This allows for packaging of a kernel and all devicetrees files, using +# compression. +# --------------------------------------------------------------------------- + +MAKE_FIT := $(srctree)/scripts/make_fit.py + +quiet_cmd_fit = FIT $@ + cmd_fit = $(MAKE_FIT) -f $@ --arch $(UIMAGE_ARCH) --os linux \ + --name '$(UIMAGE_NAME)' \ + --compress $(UIMAGE_COMPRESSION) -k $< \ + $(dir $<)dts + # XZ # --------------------------------------------------------------------------- # Use xzkern to compress the kernel image and xzmisc to compress other things. diff --git a/scripts/make_fit.py b/scripts/make_fit.py new file mode 100755 index 000000000000..e1059825de9c --- /dev/null +++ b/scripts/make_fit.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2023 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# + +"""Build a FIT containing a lot of devicetree files + +Usage: + make_fit.py -A arm64 -n 'Linux-6.6' -O linux + -f arch/arm64/boot/image.fit -k /tmp/kern/arch/arm64/boot/image.itk + /tmp/kern/arch/arm64/boot/dts/ -E -c gzip + +Creates a FIT containing the supplied kernel and a directory containing the +devicetree files. + +Use -E to generate an external FIT (where the data is placed after the +FIT data structure). This allows parsing of the data without loading +the entire FIT. + +Use -c to compress the data, using bzip2, gzip, lz4, lzma, lzo and +zstd + +The resulting FIT can be booted by bootloaders which support FIT, such +as U-Boot, Linuxboot, Tianocore, etc. + +Note that this tool does not yet support adding a ramdisk / initrd. +""" + +import argparse +import collections +import os +import subprocess +import sys +import tempfile +import time + +import libfdt + + +# Tool extension and the name of the command-line tools +CompTool = collections.namedtuple('CompTool', 'ext,tools') + +COMP_TOOLS = { + 'bzip2': CompTool('.bz2', 'bzip2'), + 'gzip': CompTool('.gz', 'pigz,gzip'), + 'lz4': CompTool('.lz4', 'lz4'), + 'lzma': CompTool('.lzma', 'lzma'), + 'lzo': CompTool('.lzo', 'lzop'), + 'zstd': CompTool('.zstd', 'zstd'), +} + +def parse_args(): + """Parse the program ArgumentParser + + Returns: + Namespace object containing the arguments + """ + epilog = 'Build a FIT from a directory tree containing .dtb files' + parser = argparse.ArgumentParser(epilog=epilog) + parser.add_argument('-A', '--arch', type=str, required=True, + help='Specifies the architecture') + parser.add_argument('-c', '--compress', type=str, default='none', + help='Specifies the compression') + parser.add_argument('-E', '--external', action='store_true', + help='Convert the FIT to use external data') + parser.add_argument('-n', '--name', type=str, required=True, + help='Specifies the name') + parser.add_argument('-O', '--os', type=str, required=True, + help='Specifies the operating system') + parser.add_argument('-f', '--fit', type=str, required=True, + help='Specifies the output file (.fit)') + parser.add_argument('-k', '--kernel', type=str, required=True, + help='Specifies the (uncompressed) kernel input file (.itk)') + parser.add_argument('srcdir', type=str, nargs='*', + help='Specifies the directory tree that contains .dtb files') + + return parser.parse_args() + +def setup_fit(fsw, name): + """Make a start on writing the FIT + + Outputs the root properties and the 'images' node + + Args: + fsw (libfdt.FdtSw): Object to use for writing + name (str): Name of kernel image + """ + fsw.INC_SIZE = 65536 + fsw.finish_reservemap() + fsw.begin_node('') + fsw.property_string('description', f'{name} with devicetree set') + fsw.property_u32('#address-cells', 1) + + fsw.property_u32('timestamp', int(time.time())) + fsw.begin_node('images') + + +def write_kernel(fsw, data, args): + """Write out the kernel image + + Writes a kernel node along with the required properties + + Args: + fsw (libfdt.FdtSw): Object to use for writing + data (bytes): Data to write (possibly compressed) + args (Namespace): Contains necessary strings: + arch: FIT architecture, e.g. 'arm64' + fit_os: Operating Systems, e.g. 'linux' + name: Name of OS, e.g. 'Linux-6.6.0-rc7' + compress: Compression algorithm to use, e.g. 'gzip' + """ + with fsw.add_node('kernel'): + fsw.property_string('description', args.name) + fsw.property_string('type', 'kernel_noload') + fsw.property_string('arch', args.arch) + fsw.property_string('os', args.os) + fsw.property_string('compression', args.compress) + fsw.property('data', data) + fsw.property_u32('load', 0) + fsw.property_u32('entry', 0) + + +def finish_fit(fsw, entries): + """Finish the FIT ready for use + + Writes the /configurations node and subnodes + + Args: + fsw (libfdt.FdtSw): Object to use for writing + entries (list of tuple): List of configurations: + str: Description of model + str: Compatible stringlist + """ + fsw.end_node() + seq = 0 + with fsw.add_node('configurations'): + for model, compat in entries: + seq += 1 + with fsw.add_node(f'conf-{seq}'): + fsw.property('compatible', bytes(compat)) + fsw.property_string('description', model) + fsw.property_string('fdt', f'fdt-{seq}') + fsw.property_string('kernel', 'kernel') + fsw.end_node() + + +def compress_data(inf, compress): + """Compress data using a selected algorithm + + Args: + inf (IOBase): Filename containing the data to compress + compress (str): Compression algorithm, e.g. 'gzip' + + Return: + bytes: Compressed data + """ + if compress == 'none': + return inf.read() + + comp = COMP_TOOLS.get(compress) + if not comp: + raise ValueError(f"Unknown compression algorithm '{compress}'") + + with tempfile.NamedTemporaryFile() as comp_fname: + with open(comp_fname.name, 'wb') as outf: + done = False + for tool in comp.tools.split(','): + try: + subprocess.call([tool, '-c'], stdin=inf, stdout=outf) + done = True + break + except FileNotFoundError: + pass + if not done: + raise ValueError(f'Missing tool(s): {comp.tools}\n') + with open(comp_fname.name, 'rb') as compf: + comp_data = compf.read() + return comp_data + + +def output_dtb(fsw, seq, fname, arch, compress): + """Write out a single devicetree to the FIT + + Args: + fsw (libfdt.FdtSw): Object to use for writing + seq (int): Sequence number (1 for first) + fmame (str): Filename containing the DTB + arch: FIT architecture, e.g. 'arm64' + compress (str): Compressed algorithm, e.g. 'gzip' + + Returns: + tuple: + str: Model name + bytes: Compatible stringlist + """ + with fsw.add_node(f'fdt-{seq}'): + # Get the compatible / model information + with open(fname, 'rb') as inf: + data = inf.read() + fdt = libfdt.FdtRo(data) + model = fdt.getprop(0, 'model').as_str() + compat = fdt.getprop(0, 'compatible') + + fsw.property_string('description', model) + fsw.property_string('type', 'flat_dt') + fsw.property_string('arch', arch) + fsw.property_string('compression', compress) + fsw.property('compatible', bytes(compat)) + + with open(fname, 'rb') as inf: + compressed = compress_data(inf, compress) + fsw.property('data', compressed) + return model, compat + + +def build_fit(args): + """Build the FIT from the provided files and arguments + + Args: + args (Namespace): Program arguments + + Returns: + tuple: + bytes: FIT data + int: Number of configurations generated + size: Total uncompressed size of data + """ + fsw = libfdt.FdtSw() + setup_fit(fsw, args.name) + seq = 0 + size = 0 + entries = [] + + # Handle the kernel + with open(args.kernel, 'rb') as inf: + comp_data = compress_data(inf, args.compress) + size += os.path.getsize(args.kernel) + write_kernel(fsw, comp_data, args) + + for path in args.srcdir: + # Handle devicetree files + if os.path.isdir(path): + for dirpath, _, fnames in os.walk(path): + for fname in fnames: + if os.path.splitext(fname)[1] != '.dtb': + continue + pathname = os.path.join(dirpath, fname) + seq += 1 + size += os.path.getsize(pathname) + model, compat = output_dtb(fsw, seq, pathname, + args.arch, args.compress) + entries.append([model, compat]) + + finish_fit(fsw, entries) + + # Include the kernel itself in the returned file count + return fsw.as_fdt().as_bytearray(), seq + 1, size + + +def run_make_fit(): + """Run the tool's main logic""" + args = parse_args() + + out_data, count, size = build_fit(args) + with open(args.fit, 'wb') as outf: + outf.write(out_data) + + ext_fit_size = None + if args.external: + mkimage = os.environ.get('MKIMAGE', 'mkimage') + subprocess.check_call([mkimage, '-E', '-F', args.fit], + stdout=subprocess.DEVNULL) + + with open(args.fit, 'rb') as inf: + data = inf.read() + ext_fit = libfdt.FdtRo(data) + ext_fit_size = ext_fit.totalsize() + + comp_size = len(out_data) + print(f'FIT size {comp_size:#x}/{comp_size / 1024 / 1024:.1f} MB', end='') + if ext_fit_size: + print(f', header {ext_fit_size:#x}/{ext_fit_size / 1024:.1f} KB', end='') + print(f', {count} files, uncompressed {size / 1024 / 1024:.1f} MB') + + +if __name__ == "__main__": + sys.exit(run_make_fit())
Add a script which produces a Flat Image Tree (FIT), a single file containing the built kernel and associated devicetree files. Compression defaults to gzip which gives a good balance of size and performance. The files compress from about 86MB to 24MB using this approach. The FIT can be used by bootloaders which support it, such as U-Boot and Linuxboot. It permits automatic selection of the correct devicetree, matching the compatible string of the running board with the closest compatible string in the FIT. There is no need for filenames or other workarounds. Add a 'make image.fit' build target for arm64, as well. The FIT can be examined using 'dumpimage -l'. This features requires pylibfdt (use 'pip install libfdt'). It also requires compression utilities for the algorithm being used. Supported compression options are the same as the Image.xxx files. For now there is no way to change the compression other than by editing the rule for $(obj)/image.fit While FIT supports a ramdisk / initrd, no attempt is made to support this here, since it must be built separately from the Linux build. Signed-off-by: Simon Glass <sjg@chromium.org> --- Changes in v7: - Add Image as a dependency of image.fit - Drop kbuild tag - Add dependency on dtbs - Drop unnecessary path separator for dtbs - Rebase to -next Changes in v5: - Drop patch previously applied - Correct compression rule which was broken in v4 Changes in v4: - Use single quotes for UIMAGE_NAME Changes in v3: - Drop temporary file image.itk - Drop patch 'Use double quotes for image name' - Drop double quotes in use of UIMAGE_NAME - Drop unnecessary CONFIG_EFI_ZBOOT condition for help - Avoid hard-coding "arm64" for the DT architecture Changes in v2: - Drop patch previously applied - Add .gitignore file - Move fit rule to Makefile.lib using an intermediate file - Drop dependency on CONFIG_EFI_ZBOOT - Pick up .dtb files separately from the kernel - Correct pylint too-many-args warning for write_kernel() - Include the kernel image in the file count - Add a pointer to the FIT spec and mention of its wide industry usage - Mention the kernel version in the FIT description MAINTAINERS | 7 + arch/arm64/Makefile | 9 +- arch/arm64/boot/.gitignore | 1 + arch/arm64/boot/Makefile | 6 +- scripts/Makefile.lib | 13 ++ scripts/make_fit.py | 289 +++++++++++++++++++++++++++++++++++++ 6 files changed, 322 insertions(+), 3 deletions(-) create mode 100755 scripts/make_fit.py