Boot interface for device trees on ARM
Nicolas Pitre
nico at fluxnic.net
Thu May 20 03:52:32 EST 2010
[ Complete boot description and proposal added at the end
for those interested. ]
On Wed, 19 May 2010, Grant Likely wrote:
> On Mon, May 17, 2010 at 10:34 PM, Nicolas Pitre <nico at fluxnic.net> wrote:
> > On Tue, 18 May 2010, Jeremy Kerr wrote:
> >> Some notes about this scheme:
> >>
> >> - This would break compatibility with the existing boot interface:
> >> bootloaders that expect a DT kernel will not be able to boot a non-DT kernel.
> >> However, does this matter? Once the machine support (ie, bootloader and
> >> kernel) is done, we don't expect to have to enable both methods.
> >
> > I think that, for the moment, it is best if the bootloader on already
> > existing subarchitectures where DT is introduced still preserve the
> > already existing ability to boot using ATAGs. This allows for the
> > testing and validation of the DT concept against the legacy ATAG method
> > more easily.
>
> I think we've got an agreement! :-)
Good.
> > On new subarchitectures, it might make sense to go with DT from the
> > start instead of creating setup code for every single machine. In that
> > case the bootloader for those machines would only need to care about DT
> > and forget about ATAGs.
> >
> >> + A simpler boot interface, so less to do (and get wrong) in the bootloader
> >>
> >> + We don't have two potential sources of boot information
> >
> > Those last two are IMHO the biggest reasons for not having both ATAGs
> > and DT at the same time. Otherwise the confusion about which one is
> > authoritative, which one has precedence over the other, and/or whether
> > the information should be obtained from one structure if it is missing
> > from the other will simply bite us eventually for sure, as bootloader
> > writers will get sloppy/lazy and have it wrong. I strongly suggest that
> > we should specify that the kernel must consider either ATAGs _or_ a
> > device tree, and that the bootloader must pass only one of them.
>
> I still disagree on this point. I think it will cause less confusion
> to only have a single method for passing the dtb, but that is a debate
> that we don't need to solve immediately since we've got a way forward
> on passing the dtb now.
I don't understand you here. We still are not in agreement on the
method to pass the DTB yet, but I for sure don't want more than one
method to pass it to the kernel.
There is a proposal to use the ATAG facility to encapsulate a pointer to
the DT data. I think this is unnecessary.
I'd much prefer if r2 contained either a pointer to the ATAG list, or a
pointer to the DTB. That's much cleaner and simpler. As Jeremy pointed
out, both structures include a magic number allowing to distinguish one
from the other, so there is no backward compatibility issues.
> >> This proposal still does not require ATAG_DEVTREE?
> >
> > No.
>
> Hmmm... I misunderstood then. I don't agree that this is the best
> way forward
>
> Doing it this way means a non-compatible break in the interface. It
> means that the bootloader needs to know what interface the kernel is
> expecting for boot; information that is not readily available from the
> image type.
I don't follow your point here.
It is not up to the bootloader to "adjust" to the kernel. But rather
for the kernel to cope with the bootloader's provided information. If
the bootloader passes a specific machine ID with the ATAG list then the
kernel will use that, and if the bootloader passes a DT machine ID with
a DT blob then the kernel will use that. You just have to configure
your kernel with both "machine types" at the same time.
> The user then needs to tell the boot loader which
> interface to use rather than a backwards compatible addition of a blob
> of data.
This is certainly not a show stopper, right? If you planned for your
bootloader to _already_ support both the ATAGs and the DT at the same
time, it is not a big deal to have a config option that the user can
change to select between "legacy" and "DT" boot methods. Since we still
want to preserve the ability to have a DT enabled kernel to boot using
the legacy method, you will need a way to tell the bootloader which
machine ID to use in that case anyway.
> You mention below "shifting the World Order on ARM" and it creating
> resistance for merging DT support. Isn't this much the same thing as
> it creates a non backwards compatible change in the way bootloaders
> pass data to the kernel. The cutover in powerpc from the old
> interface to the new caused no end of confusion and people who could
> no longer get their systems to boot. On PowerPC is was necessary
> because the old method was completely broken, but ATAGs are clean,
> simple and well implemented.
I don't dispute the ATAG implementation. I don't believe it is a good
thing to carry the ATAG and DT as a combined boot requirement going
forward.
On one hand, some people are claiming that the machine ID should be
stuffed in the device tree and whatever is passed into r1 should be
ignored. On the other hand, you are saying that ATAGs should be kept
forever as legacy cruft just to provide a reference to the DT data. To
me those are incompatible design objectives.
> It also means teaching every boot loader two separate methods for
> booting and exposing those differences to the user.
That's overstating the problem. First, any bootloader wanting to use DT
_will_ have to implement such a method. With your proposal, all those
bootloaders will _also_ have to know about ATAGs anyway. So I don't see
what your point is.
Let me start it all over again.
Here's how I think this should be handled on ARM. Sorry PPC folks, but
there is 15 years of ARM legacy to consider here, and what has been done
on PPC might or might not be practical here -- please keep that in mind.
I'm also going to use the present tense so to make this proposal as
unambiguous as possible.
Booting Linux on ARM
====================
Two methods for passing boot information to the Linux kernel on ARM are
possible:
1) the legacy method
2) the DT (device tree) method
The legacy method
-----------------
The legacy method for booting Linux on ARM requires that a unique
machine ID be registered with RMK's machine ID database. It can be
viewed online at http://www.arm.linux.org.uk/developer/machines/.
Before branching into the kernel image, the CPU register r1 must be
initialized by the bootloader with the appropriate machine ID value for
the exact hardware the kernel is booted on. That is a hard requirement.
The kernel must also contain code specific to that machine ID in order
to perform early setup of appropriate resources, create mappings for
basic peripherals such as hardware timers, register relevant platform
devices, etc. That code can be found in those files containing the
MACHINE_START and MACHINE_END macros, typically in arch/arm/mach-*/*.c.
This is also a hard requirement.
Optionally (but strongly recommended), a tagged list (aka ATAGs) may be
created in memory to pass extra information such as the size and
location for RAM banks, size and location for a ramdisk, the kernel
cmdline string, etc. When not provided, the kernel will rely on default
values which may or may not be sufficient to boot the kernel, or fall
back to the information provided by a built-in kernel cmdline string
determined by CONFIG_CMDLINE.
The location of the ATAG list is somewhat problematic. Traditionally it
has been stored at an offset of 0x100 from start of memory. But some
proposals were pushed forward to use r2 to indicate the location of the
ATAG list. Because it is impossible to determine with certainty whether
or not the bootloader does actually initialize r2 with that information,
this requirement was never enforced, which would otherwise cause
backward compatibility issues. The ATAG location is therefore stored
into the machine record structure and therefore hardcoded at compile
time.
When the kernel boots, it looks up into a table of all the machine
records that have been compiled into the kernel for the one that
corresponds with the machine ID passed into r1. This lookup is
performed in the very early boot stage from assembly code (with no stack
available) to set up basic MMU stuff that is necessary for the kernel to
be debuggable until the full fledged MMU support code takes over later
during the boot. Later on, the machine record is used to call init
functions to initialize IO mappings, IRQs, timers, and so on as
described above. While the init_machine field in the machine record is
obviously machine specific, all the other fields in the machine record
end up being pretty much the same across all machines within the same
SOC family.
The DT method
-------------
The DT method requires that a unique ID be registered for each SOC
family for the DT purpose within the same ID space. But instead of
having one ID for each possible machine, only one ID per SOC family is
required. So the bootloader simply has to pass into r1 the ID
corresponding to DT instead of the specific machine ID. This is a hard
requirement.
With this, the kernel can remain largely backward compatible with the
legacy boot method, requiring _no_ change to the existing code, as the
ID is sufficient to distinguish between both boot types. The machine
record remains largely relevant even for a DT boot as the majority of
its content is SOC specific anyway, and given a per SOC ID for DT usage
means that the early boot facilities are still usable as is even in the
DT context. And then the init_machine method in the machine record is
naturally used to parse the device tree and do its work on multiple
machines' behalf instead of relying on compiled-in static data for a
specific machine.
The bootloader must also store in memory the DT data and pass its
location via r2. The r2 initialization therefore becomes a hard
requirement when a DT is used. The boot_params field in the DT machine
record could be set to -1 to disable ATAG parsing when a DT machine ID
is used, and look into the DT data instead.
Backward Compatibility Considerations
-------------------------------------
Now let's have a look at what happens when we mix this with bootloaders.
If a kernel is too old, or was not configured with the "machine support"
for DT, then only one thing may happen when a bootloader passes a DT
machine ID to the kernel. The early boot code will fail to find the
machine record for the provided ID and refuse to boot. If you have
CONFIG_DEBUG_LL and are attempting to boot on the right SOC, then you'll
probably get the error message on a serial port, along with a list of
the actual machines and their IDs supported by that kernel. In other
words, this is not a new failure mode. Two solutions: either reconfigure
your kernel to add DT support for your SOC, or configure your bootloader
to use the legacy boot method. And having a bootloader that can do DT
only for a machine that has only legacy support in the kernel is
senseless, so that case needs no be considered any longer.
The opposite is the kernel being configured for DT _only_, and the
bootloader being too old, or misconfigured, so it passes a non-DT
machine ID to the kernel. In this case the same failure mode as above
will be observed: kernel failing to find the matching machine record and
halting (after attempting to display the error and the available machine
supported). Solution: reconfigure your kernel with legacy support for
that machine.
If the goal is to experiment with DT support in the kernel, and the
bootloader does not support DT, then some shim can be prepended to the
kernel image to fix things up. Most bootloaders already have the
ability to load arbitrary binary data in memory, and branch to arbitrary
location too. So it should be pretty easy to tftp the kernel, then tftp
the DT data, to finally branch into the kernel with appropriate values
in r1 and r2.
Conclusion
----------
I hope I've illustrated clearly why 1) keeping the requirement for
passing a machine ID into r1, even for DT, is useful, and 2) why
dropping ATAGs when using DT is simpler, cleaner, and has no relevant
backward compatibility issues. With #1 the ability to seamlessly use
the existing early debugging code is preserved, and the introduction of
DT fits perfectly well in the existing machine support model on ARM. Yet
the new boot requirements introduced with DT are only for "machine" IDS
that are still not in use, meaning there is no backward compatibilities
introduced at all. With #2 it will be possible to simplify bootloader
support for new platforms being DT enabled without any legacy needs, and
when the kernel is configured only for DT support then it will be
possible to configure out the ATAG support code entirely.
Nicolas
More information about the devicetree-discuss
mailing list